content/media/webaudio/DelayNode.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "DelayNode.h"
michael@0 8 #include "mozilla/dom/DelayNodeBinding.h"
michael@0 9 #include "AudioNodeEngine.h"
michael@0 10 #include "AudioNodeStream.h"
michael@0 11 #include "AudioDestinationNode.h"
michael@0 12 #include "WebAudioUtils.h"
michael@0 13 #include "DelayBuffer.h"
michael@0 14 #include "PlayingRefChangeHandler.h"
michael@0 15
michael@0 16 namespace mozilla {
michael@0 17 namespace dom {
michael@0 18
michael@0 19 NS_IMPL_CYCLE_COLLECTION_INHERITED(DelayNode, AudioNode,
michael@0 20 mDelay)
michael@0 21
michael@0 22 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DelayNode)
michael@0 23 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
michael@0 24
michael@0 25 NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode)
michael@0 26 NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode)
michael@0 27
michael@0 28 class DelayNodeEngine : public AudioNodeEngine
michael@0 29 {
michael@0 30 typedef PlayingRefChangeHandler PlayingRefChanged;
michael@0 31 public:
michael@0 32 DelayNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination,
michael@0 33 double aMaxDelayTicks)
michael@0 34 : AudioNodeEngine(aNode)
michael@0 35 , mSource(nullptr)
michael@0 36 , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
michael@0 37 // Keep the default value in sync with the default value in DelayNode::DelayNode.
michael@0 38 , mDelay(0.f)
michael@0 39 // Use a smoothing range of 20ms
michael@0 40 , mBuffer(std::max(aMaxDelayTicks,
michael@0 41 static_cast<double>(WEBAUDIO_BLOCK_SIZE)),
michael@0 42 WebAudioUtils::ComputeSmoothingRate(0.02,
michael@0 43 mDestination->SampleRate()))
michael@0 44 , mMaxDelay(aMaxDelayTicks)
michael@0 45 , mLastOutputPosition(-1)
michael@0 46 , mLeftOverData(INT32_MIN)
michael@0 47 {
michael@0 48 }
michael@0 49
michael@0 50 virtual DelayNodeEngine* AsDelayNodeEngine()
michael@0 51 {
michael@0 52 return this;
michael@0 53 }
michael@0 54
michael@0 55 void SetSourceStream(AudioNodeStream* aSource)
michael@0 56 {
michael@0 57 mSource = aSource;
michael@0 58 }
michael@0 59
michael@0 60 enum Parameters {
michael@0 61 DELAY,
michael@0 62 };
michael@0 63 void SetTimelineParameter(uint32_t aIndex,
michael@0 64 const AudioParamTimeline& aValue,
michael@0 65 TrackRate aSampleRate) MOZ_OVERRIDE
michael@0 66 {
michael@0 67 switch (aIndex) {
michael@0 68 case DELAY:
michael@0 69 MOZ_ASSERT(mSource && mDestination);
michael@0 70 mDelay = aValue;
michael@0 71 WebAudioUtils::ConvertAudioParamToTicks(mDelay, mSource, mDestination);
michael@0 72 break;
michael@0 73 default:
michael@0 74 NS_ERROR("Bad DelayNodeEngine TimelineParameter");
michael@0 75 }
michael@0 76 }
michael@0 77
michael@0 78 virtual void ProcessBlock(AudioNodeStream* aStream,
michael@0 79 const AudioChunk& aInput,
michael@0 80 AudioChunk* aOutput,
michael@0 81 bool* aFinished) MOZ_OVERRIDE
michael@0 82 {
michael@0 83 MOZ_ASSERT(mSource == aStream, "Invalid source stream");
michael@0 84 MOZ_ASSERT(aStream->SampleRate() == mDestination->SampleRate());
michael@0 85
michael@0 86 if (!aInput.IsNull()) {
michael@0 87 if (mLeftOverData <= 0) {
michael@0 88 nsRefPtr<PlayingRefChanged> refchanged =
michael@0 89 new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF);
michael@0 90 aStream->Graph()->
michael@0 91 DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
michael@0 92 }
michael@0 93 mLeftOverData = mBuffer.MaxDelayTicks();
michael@0 94 } else if (mLeftOverData > 0) {
michael@0 95 mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
michael@0 96 } else {
michael@0 97 if (mLeftOverData != INT32_MIN) {
michael@0 98 mLeftOverData = INT32_MIN;
michael@0 99 // Delete our buffered data now we no longer need it
michael@0 100 mBuffer.Reset();
michael@0 101
michael@0 102 nsRefPtr<PlayingRefChanged> refchanged =
michael@0 103 new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
michael@0 104 aStream->Graph()->
michael@0 105 DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
michael@0 106 }
michael@0 107 *aOutput = aInput;
michael@0 108 return;
michael@0 109 }
michael@0 110
michael@0 111 mBuffer.Write(aInput);
michael@0 112
michael@0 113 UpdateOutputBlock(aOutput);
michael@0 114 mBuffer.NextBlock();
michael@0 115 }
michael@0 116
michael@0 117 void UpdateOutputBlock(AudioChunk* aOutput)
michael@0 118 {
michael@0 119 TrackTicks tick = mSource->GetCurrentPosition();
michael@0 120 if (tick == mLastOutputPosition) {
michael@0 121 return; // mLastChunks is already set on the stream
michael@0 122 }
michael@0 123
michael@0 124 mLastOutputPosition = tick;
michael@0 125 bool inCycle = mSource->AsProcessedStream()->InCycle();
michael@0 126 double minDelay = inCycle ? static_cast<double>(WEBAUDIO_BLOCK_SIZE) : 0.0;
michael@0 127 double maxDelay = mMaxDelay;
michael@0 128 double sampleRate = mSource->SampleRate();
michael@0 129 ChannelInterpretation channelInterpretation =
michael@0 130 mSource->GetChannelInterpretation();
michael@0 131 if (mDelay.HasSimpleValue()) {
michael@0 132 // If this DelayNode is in a cycle, make sure the delay value is at least
michael@0 133 // one block, even if that is greater than maxDelay.
michael@0 134 double delayFrames = mDelay.GetValue() * sampleRate;
michael@0 135 double delayFramesClamped =
michael@0 136 std::max(minDelay, std::min(delayFrames, maxDelay));
michael@0 137 mBuffer.Read(delayFramesClamped, aOutput, channelInterpretation);
michael@0 138 } else {
michael@0 139 // Compute the delay values for the duration of the input AudioChunk
michael@0 140 // If this DelayNode is in a cycle, make sure the delay value is at least
michael@0 141 // one block.
michael@0 142 double computedDelay[WEBAUDIO_BLOCK_SIZE];
michael@0 143 for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
michael@0 144 double delayAtTick = mDelay.GetValueAtTime(tick, counter) * sampleRate;
michael@0 145 double delayAtTickClamped =
michael@0 146 std::max(minDelay, std::min(delayAtTick, maxDelay));
michael@0 147 computedDelay[counter] = delayAtTickClamped;
michael@0 148 }
michael@0 149 mBuffer.Read(computedDelay, aOutput, channelInterpretation);
michael@0 150 }
michael@0 151 }
michael@0 152
michael@0 153 virtual void ProduceBlockBeforeInput(AudioChunk* aOutput) MOZ_OVERRIDE
michael@0 154 {
michael@0 155 if (mLeftOverData <= 0) {
michael@0 156 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
michael@0 157 } else {
michael@0 158 UpdateOutputBlock(aOutput);
michael@0 159 }
michael@0 160 }
michael@0 161
michael@0 162 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
michael@0 163 {
michael@0 164 size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
michael@0 165 // Not owned:
michael@0 166 // - mSource - probably not owned
michael@0 167 // - mDestination - probably not owned
michael@0 168 // - mDelay - shares ref with AudioNode, don't count
michael@0 169 amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
michael@0 170 return amount;
michael@0 171 }
michael@0 172
michael@0 173 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
michael@0 174 {
michael@0 175 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 176 }
michael@0 177
michael@0 178 AudioNodeStream* mSource;
michael@0 179 AudioNodeStream* mDestination;
michael@0 180 AudioParamTimeline mDelay;
michael@0 181 DelayBuffer mBuffer;
michael@0 182 double mMaxDelay;
michael@0 183 TrackTicks mLastOutputPosition;
michael@0 184 // How much data we have in our buffer which needs to be flushed out when our inputs
michael@0 185 // finish.
michael@0 186 int32_t mLeftOverData;
michael@0 187 };
michael@0 188
michael@0 189 DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
michael@0 190 : AudioNode(aContext,
michael@0 191 2,
michael@0 192 ChannelCountMode::Max,
michael@0 193 ChannelInterpretation::Speakers)
michael@0 194 , mDelay(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
michael@0 195 SendDelayToStream, 0.0f))
michael@0 196 {
michael@0 197 DelayNodeEngine* engine =
michael@0 198 new DelayNodeEngine(this, aContext->Destination(),
michael@0 199 aContext->SampleRate() * aMaxDelay);
michael@0 200 mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
michael@0 201 engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
michael@0 202 }
michael@0 203
michael@0 204 size_t
michael@0 205 DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 206 {
michael@0 207 size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
michael@0 208 amount += mDelay->SizeOfIncludingThis(aMallocSizeOf);
michael@0 209 return amount;
michael@0 210 }
michael@0 211
michael@0 212 size_t
michael@0 213 DelayNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 214 {
michael@0 215 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 216 }
michael@0 217
michael@0 218 JSObject*
michael@0 219 DelayNode::WrapObject(JSContext* aCx)
michael@0 220 {
michael@0 221 return DelayNodeBinding::Wrap(aCx, this);
michael@0 222 }
michael@0 223
michael@0 224 void
michael@0 225 DelayNode::SendDelayToStream(AudioNode* aNode)
michael@0 226 {
michael@0 227 DelayNode* This = static_cast<DelayNode*>(aNode);
michael@0 228 SendTimelineParameterToStream(This, DelayNodeEngine::DELAY, *This->mDelay);
michael@0 229 }
michael@0 230
michael@0 231 }
michael@0 232 }
michael@0 233

mercurial