2012-11-01 04:26:03 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "DelayNode.h"
|
|
|
|
#include "mozilla/dom/DelayNodeBinding.h"
|
2013-03-25 16:34:59 +04:00
|
|
|
#include "AudioNodeEngine.h"
|
|
|
|
#include "AudioNodeStream.h"
|
|
|
|
#include "AudioDestinationNode.h"
|
|
|
|
#include "WebAudioUtils.h"
|
2014-03-03 03:49:45 +04:00
|
|
|
#include "DelayBuffer.h"
|
2013-08-15 23:44:14 +04:00
|
|
|
#include "PlayingRefChangeHandler.h"
|
2012-11-01 04:26:03 +04:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
2014-04-25 20:49:00 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(DelayNode, AudioNode,
|
|
|
|
mDelay)
|
2012-11-01 04:26:03 +04:00
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DelayNode)
|
|
|
|
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
|
|
|
|
|
|
|
NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode)
|
|
|
|
NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode)
|
|
|
|
|
2015-04-28 09:42:00 +03:00
|
|
|
class DelayNodeEngine final : public AudioNodeEngine
|
2013-03-25 16:34:59 +04:00
|
|
|
{
|
2013-10-25 03:12:12 +04:00
|
|
|
typedef PlayingRefChangeHandler PlayingRefChanged;
|
2013-03-25 16:34:59 +04:00
|
|
|
public:
|
2013-08-09 02:07:49 +04:00
|
|
|
DelayNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination,
|
2014-03-31 09:32:34 +04:00
|
|
|
double aMaxDelayTicks)
|
2013-04-20 20:16:28 +04:00
|
|
|
: AudioNodeEngine(aNode)
|
|
|
|
, mSource(nullptr)
|
2015-07-02 08:36:07 +03:00
|
|
|
, mDestination(aDestination->Stream())
|
2013-03-25 16:34:59 +04:00
|
|
|
// Keep the default value in sync with the default value in DelayNode::DelayNode.
|
|
|
|
, mDelay(0.f)
|
2013-08-09 02:07:49 +04:00
|
|
|
// Use a smoothing range of 20ms
|
2014-03-31 09:32:34 +04:00
|
|
|
, mBuffer(std::max(aMaxDelayTicks,
|
|
|
|
static_cast<double>(WEBAUDIO_BLOCK_SIZE)),
|
2014-03-03 03:49:45 +04:00
|
|
|
WebAudioUtils::ComputeSmoothingRate(0.02,
|
|
|
|
mDestination->SampleRate()))
|
2014-03-31 09:32:34 +04:00
|
|
|
, mMaxDelay(aMaxDelayTicks)
|
2014-07-17 04:55:55 +04:00
|
|
|
, mHaveProducedBeforeInput(false)
|
2013-04-15 05:52:55 +04:00
|
|
|
, mLeftOverData(INT32_MIN)
|
2013-03-25 16:34:59 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
virtual DelayNodeEngine* AsDelayNodeEngine() override
|
2013-08-26 21:19:36 +04:00
|
|
|
{
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2013-03-25 16:34:59 +04:00
|
|
|
void SetSourceStream(AudioNodeStream* aSource)
|
|
|
|
{
|
|
|
|
mSource = aSource;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum Parameters {
|
|
|
|
DELAY,
|
|
|
|
};
|
2013-05-24 21:10:08 +04:00
|
|
|
void SetTimelineParameter(uint32_t aIndex,
|
|
|
|
const AudioParamTimeline& aValue,
|
2015-03-21 19:28:04 +03:00
|
|
|
TrackRate aSampleRate) override
|
2013-03-25 16:34:59 +04:00
|
|
|
{
|
|
|
|
switch (aIndex) {
|
|
|
|
case DELAY:
|
|
|
|
MOZ_ASSERT(mSource && mDestination);
|
|
|
|
mDelay = aValue;
|
|
|
|
WebAudioUtils::ConvertAudioParamToTicks(mDelay, mSource, mDestination);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NS_ERROR("Bad DelayNodeEngine TimelineParameter");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-05 01:09:49 +04:00
|
|
|
virtual void ProcessBlock(AudioNodeStream* aStream,
|
2015-09-03 10:01:50 +03:00
|
|
|
const AudioBlock& aInput,
|
|
|
|
AudioBlock* aOutput,
|
2015-03-21 19:28:04 +03:00
|
|
|
bool* aFinished) override
|
2013-03-25 16:34:59 +04:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(mSource == aStream, "Invalid source stream");
|
2013-08-09 02:07:49 +04:00
|
|
|
MOZ_ASSERT(aStream->SampleRate() == mDestination->SampleRate());
|
2013-03-25 16:34:59 +04:00
|
|
|
|
2015-04-23 13:25:36 +03:00
|
|
|
if (!aInput.IsSilentOrSubnormal()) {
|
2013-10-01 00:50:04 +04:00
|
|
|
if (mLeftOverData <= 0) {
|
2013-05-16 04:51:47 +04:00
|
|
|
nsRefPtr<PlayingRefChanged> refchanged =
|
|
|
|
new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF);
|
2013-10-25 03:12:33 +04:00
|
|
|
aStream->Graph()->
|
|
|
|
DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
|
2013-05-16 04:51:47 +04:00
|
|
|
}
|
2014-03-05 01:06:57 +04:00
|
|
|
mLeftOverData = mBuffer.MaxDelayTicks();
|
2013-10-25 03:12:13 +04:00
|
|
|
} else if (mLeftOverData > 0) {
|
|
|
|
mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
|
|
|
|
} else {
|
|
|
|
if (mLeftOverData != INT32_MIN) {
|
|
|
|
mLeftOverData = INT32_MIN;
|
2013-10-25 03:12:13 +04:00
|
|
|
// Delete our buffered data now we no longer need it
|
2014-03-03 03:49:45 +04:00
|
|
|
mBuffer.Reset();
|
2013-04-15 05:52:55 +04:00
|
|
|
|
|
|
|
nsRefPtr<PlayingRefChanged> refchanged =
|
2013-04-20 20:16:28 +04:00
|
|
|
new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
|
2013-10-25 05:05:41 +04:00
|
|
|
aStream->Graph()->
|
|
|
|
DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
|
2013-04-15 05:52:55 +04:00
|
|
|
}
|
2013-10-25 03:12:13 +04:00
|
|
|
*aOutput = aInput;
|
|
|
|
return;
|
2013-04-15 05:52:55 +04:00
|
|
|
}
|
2013-03-25 16:34:59 +04:00
|
|
|
|
2014-03-05 01:06:57 +04:00
|
|
|
mBuffer.Write(aInput);
|
2013-09-02 17:15:24 +04:00
|
|
|
|
2014-07-17 04:55:55 +04:00
|
|
|
// Skip output update if mLastChunks has already been set by
|
|
|
|
// ProduceBlockBeforeInput() when in a cycle.
|
|
|
|
if (!mHaveProducedBeforeInput) {
|
|
|
|
UpdateOutputBlock(aOutput, 0.0);
|
|
|
|
}
|
|
|
|
mHaveProducedBeforeInput = false;
|
2014-03-05 01:06:57 +04:00
|
|
|
mBuffer.NextBlock();
|
|
|
|
}
|
|
|
|
|
2015-09-03 10:01:50 +03:00
|
|
|
void UpdateOutputBlock(AudioBlock* aOutput, double minDelay)
|
2014-03-05 01:06:57 +04:00
|
|
|
{
|
2014-03-31 09:32:34 +04:00
|
|
|
double maxDelay = mMaxDelay;
|
2014-03-05 01:06:57 +04:00
|
|
|
double sampleRate = mSource->SampleRate();
|
2014-03-05 01:06:57 +04:00
|
|
|
ChannelInterpretation channelInterpretation =
|
2014-03-05 01:06:57 +04:00
|
|
|
mSource->GetChannelInterpretation();
|
2013-03-25 16:34:59 +04:00
|
|
|
if (mDelay.HasSimpleValue()) {
|
2013-09-02 17:15:24 +04:00
|
|
|
// If this DelayNode is in a cycle, make sure the delay value is at least
|
2014-03-31 09:32:34 +04:00
|
|
|
// one block, even if that is greater than maxDelay.
|
2014-03-03 03:49:46 +04:00
|
|
|
double delayFrames = mDelay.GetValue() * sampleRate;
|
2014-03-31 09:32:34 +04:00
|
|
|
double delayFramesClamped =
|
|
|
|
std::max(minDelay, std::min(delayFrames, maxDelay));
|
2014-03-05 01:06:57 +04:00
|
|
|
mBuffer.Read(delayFramesClamped, aOutput, channelInterpretation);
|
2013-03-25 16:34:59 +04:00
|
|
|
} else {
|
|
|
|
// Compute the delay values for the duration of the input AudioChunk
|
2013-09-02 17:15:24 +04:00
|
|
|
// If this DelayNode is in a cycle, make sure the delay value is at least
|
|
|
|
// one block.
|
2014-09-18 09:20:43 +04:00
|
|
|
StreamTime tick = mSource->GetCurrentPosition();
|
2015-04-22 09:53:33 +03:00
|
|
|
float values[WEBAUDIO_BLOCK_SIZE];
|
|
|
|
mDelay.GetValuesAtTime(tick, values,WEBAUDIO_BLOCK_SIZE);
|
|
|
|
|
2013-08-09 02:07:49 +04:00
|
|
|
double computedDelay[WEBAUDIO_BLOCK_SIZE];
|
2013-03-25 16:34:59 +04:00
|
|
|
for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
|
2015-04-22 09:53:33 +03:00
|
|
|
double delayAtTick = values[counter] * sampleRate;
|
2014-03-31 09:32:34 +04:00
|
|
|
double delayAtTickClamped =
|
|
|
|
std::max(minDelay, std::min(delayAtTick, maxDelay));
|
2013-09-02 17:15:24 +04:00
|
|
|
computedDelay[counter] = delayAtTickClamped;
|
2013-03-25 16:34:59 +04:00
|
|
|
}
|
2014-03-05 01:06:57 +04:00
|
|
|
mBuffer.Read(computedDelay, aOutput, channelInterpretation);
|
2013-03-25 16:34:59 +04:00
|
|
|
}
|
2014-03-05 01:06:57 +04:00
|
|
|
}
|
|
|
|
|
2015-09-03 10:01:50 +03:00
|
|
|
virtual void ProduceBlockBeforeInput(AudioBlock* aOutput) override
|
2014-03-05 01:06:57 +04:00
|
|
|
{
|
|
|
|
if (mLeftOverData <= 0) {
|
|
|
|
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
|
|
|
|
} else {
|
2015-09-02 14:44:37 +03:00
|
|
|
UpdateOutputBlock(aOutput, WEBAUDIO_BLOCK_SIZE);
|
2014-03-05 01:06:57 +04:00
|
|
|
}
|
2014-07-17 04:55:55 +04:00
|
|
|
mHaveProducedBeforeInput = true;
|
2013-03-25 16:34:59 +04:00
|
|
|
}
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
|
2014-04-13 22:08:10 +04:00
|
|
|
{
|
|
|
|
size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
// Not owned:
|
|
|
|
// - mSource - probably not owned
|
|
|
|
// - mDestination - probably not owned
|
|
|
|
// - mDelay - shares ref with AudioNode, don't count
|
|
|
|
amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
return amount;
|
|
|
|
}
|
|
|
|
|
2015-03-21 19:28:04 +03:00
|
|
|
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
|
2014-04-13 22:08:10 +04:00
|
|
|
{
|
|
|
|
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
}
|
|
|
|
|
2013-03-25 16:34:59 +04:00
|
|
|
AudioNodeStream* mSource;
|
|
|
|
AudioNodeStream* mDestination;
|
|
|
|
AudioParamTimeline mDelay;
|
2014-03-03 03:49:45 +04:00
|
|
|
DelayBuffer mBuffer;
|
2014-03-31 09:32:34 +04:00
|
|
|
double mMaxDelay;
|
2014-07-17 04:55:55 +04:00
|
|
|
bool mHaveProducedBeforeInput;
|
2013-04-15 05:52:55 +04:00
|
|
|
// How much data we have in our buffer which needs to be flushed out when our inputs
|
|
|
|
// finish.
|
|
|
|
int32_t mLeftOverData;
|
2013-03-25 16:34:59 +04:00
|
|
|
};
|
|
|
|
|
2012-11-20 00:52:29 +04:00
|
|
|
DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
|
2013-04-28 02:44:50 +04:00
|
|
|
: AudioNode(aContext,
|
|
|
|
2,
|
|
|
|
ChannelCountMode::Max,
|
|
|
|
ChannelInterpretation::Speakers)
|
2015-04-14 18:03:52 +03:00
|
|
|
, mDelay(new AudioParam(this, SendDelayToStream, 0.0f, "delayTime"))
|
2012-11-01 04:26:03 +04:00
|
|
|
{
|
2013-08-09 02:07:49 +04:00
|
|
|
DelayNodeEngine* engine =
|
|
|
|
new DelayNodeEngine(this, aContext->Destination(),
|
2014-03-31 09:32:34 +04:00
|
|
|
aContext->SampleRate() * aMaxDelay);
|
2015-08-12 02:26:24 +03:00
|
|
|
mStream = AudioNodeStream::Create(aContext->Graph(), engine,
|
2015-08-13 07:13:34 +03:00
|
|
|
AudioNodeStream::NO_STREAM_FLAGS);
|
2015-07-02 08:36:07 +03:00
|
|
|
engine->SetSourceStream(mStream);
|
2012-11-01 04:26:03 +04:00
|
|
|
}
|
|
|
|
|
2014-07-09 01:23:17 +04:00
|
|
|
DelayNode::~DelayNode()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-04-13 22:08:10 +04:00
|
|
|
size_t
|
|
|
|
DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
|
|
|
{
|
|
|
|
size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
amount += mDelay->SizeOfIncludingThis(aMallocSizeOf);
|
|
|
|
return amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
DelayNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
|
|
|
|
{
|
|
|
|
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
}
|
|
|
|
|
2012-11-01 04:26:03 +04:00
|
|
|
JSObject*
|
Bug 1117172 part 3. Change the wrappercached WrapObject methods to allow passing in aGivenProto. r=peterv
The only manual changes here are to BindingUtils.h, BindingUtils.cpp,
Codegen.py, Element.cpp, IDBFileRequest.cpp, IDBObjectStore.cpp,
dom/workers/Navigator.cpp, WorkerPrivate.cpp, DeviceStorageRequestChild.cpp,
Notification.cpp, nsGlobalWindow.cpp, MessagePort.cpp, nsJSEnvironment.cpp,
Sandbox.cpp, XPCConvert.cpp, ExportHelpers.cpp, and DataStoreService.cpp. The
rest of this diff was generated by running the following commands:
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObject\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(Binding(?:_workers)?::Wrap\((?:aCx|cx|aContext|aCtx|js), [^,)]+)\)/\1, aGivenProto)/g'
2015-03-19 17:13:33 +03:00
|
|
|
DelayNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
2012-11-01 04:26:03 +04:00
|
|
|
{
|
Bug 1117172 part 3. Change the wrappercached WrapObject methods to allow passing in aGivenProto. r=peterv
The only manual changes here are to BindingUtils.h, BindingUtils.cpp,
Codegen.py, Element.cpp, IDBFileRequest.cpp, IDBObjectStore.cpp,
dom/workers/Navigator.cpp, WorkerPrivate.cpp, DeviceStorageRequestChild.cpp,
Notification.cpp, nsGlobalWindow.cpp, MessagePort.cpp, nsJSEnvironment.cpp,
Sandbox.cpp, XPCConvert.cpp, ExportHelpers.cpp, and DataStoreService.cpp. The
rest of this diff was generated by running the following commands:
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObjectInternal\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapNode\((?:aCx|cx|aContext|aCtx|js))\)/\1, aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(WrapObject\(JSContext *\* *(?:aCx|cx|aContext|aCtx|js))\)/\1, JS::Handle<JSObject*> aGivenProto)/g'
find . -name "*.h" -o -name "*.cpp" | xargs perl -pi -e 'BEGIN { $/ = undef } s/(Binding(?:_workers)?::Wrap\((?:aCx|cx|aContext|aCtx|js), [^,)]+)\)/\1, aGivenProto)/g'
2015-03-19 17:13:33 +03:00
|
|
|
return DelayNodeBinding::Wrap(aCx, this, aGivenProto);
|
2012-11-01 04:26:03 +04:00
|
|
|
}
|
|
|
|
|
2013-03-25 16:34:59 +04:00
|
|
|
void
|
|
|
|
DelayNode::SendDelayToStream(AudioNode* aNode)
|
|
|
|
{
|
|
|
|
DelayNode* This = static_cast<DelayNode*>(aNode);
|
|
|
|
SendTimelineParameterToStream(This, DelayNodeEngine::DELAY, *This->mDelay);
|
|
|
|
}
|
|
|
|
|
2015-07-13 18:25:42 +03:00
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|