1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webaudio/DelayNode.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,233 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "DelayNode.h" 1.11 +#include "mozilla/dom/DelayNodeBinding.h" 1.12 +#include "AudioNodeEngine.h" 1.13 +#include "AudioNodeStream.h" 1.14 +#include "AudioDestinationNode.h" 1.15 +#include "WebAudioUtils.h" 1.16 +#include "DelayBuffer.h" 1.17 +#include "PlayingRefChangeHandler.h" 1.18 + 1.19 +namespace mozilla { 1.20 +namespace dom { 1.21 + 1.22 +NS_IMPL_CYCLE_COLLECTION_INHERITED(DelayNode, AudioNode, 1.23 + mDelay) 1.24 + 1.25 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DelayNode) 1.26 +NS_INTERFACE_MAP_END_INHERITING(AudioNode) 1.27 + 1.28 +NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode) 1.29 +NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode) 1.30 + 1.31 +class DelayNodeEngine : public AudioNodeEngine 1.32 +{ 1.33 + typedef PlayingRefChangeHandler PlayingRefChanged; 1.34 +public: 1.35 + DelayNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination, 1.36 + double aMaxDelayTicks) 1.37 + : AudioNodeEngine(aNode) 1.38 + , mSource(nullptr) 1.39 + , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream())) 1.40 + // Keep the default value in sync with the default value in DelayNode::DelayNode. 1.41 + , mDelay(0.f) 1.42 + // Use a smoothing range of 20ms 1.43 + , mBuffer(std::max(aMaxDelayTicks, 1.44 + static_cast<double>(WEBAUDIO_BLOCK_SIZE)), 1.45 + WebAudioUtils::ComputeSmoothingRate(0.02, 1.46 + mDestination->SampleRate())) 1.47 + , mMaxDelay(aMaxDelayTicks) 1.48 + , mLastOutputPosition(-1) 1.49 + , mLeftOverData(INT32_MIN) 1.50 + { 1.51 + } 1.52 + 1.53 + virtual DelayNodeEngine* AsDelayNodeEngine() 1.54 + { 1.55 + return this; 1.56 + } 1.57 + 1.58 + void SetSourceStream(AudioNodeStream* aSource) 1.59 + { 1.60 + mSource = aSource; 1.61 + } 1.62 + 1.63 + enum Parameters { 1.64 + DELAY, 1.65 + }; 1.66 + void SetTimelineParameter(uint32_t aIndex, 1.67 + const AudioParamTimeline& aValue, 1.68 + TrackRate aSampleRate) MOZ_OVERRIDE 1.69 + { 1.70 + switch (aIndex) { 1.71 + case DELAY: 1.72 + MOZ_ASSERT(mSource && mDestination); 1.73 + mDelay = aValue; 1.74 + WebAudioUtils::ConvertAudioParamToTicks(mDelay, mSource, mDestination); 1.75 + break; 1.76 + default: 1.77 + NS_ERROR("Bad DelayNodeEngine TimelineParameter"); 1.78 + } 1.79 + } 1.80 + 1.81 + virtual void ProcessBlock(AudioNodeStream* aStream, 1.82 + const AudioChunk& aInput, 1.83 + AudioChunk* aOutput, 1.84 + bool* aFinished) MOZ_OVERRIDE 1.85 + { 1.86 + MOZ_ASSERT(mSource == aStream, "Invalid source stream"); 1.87 + MOZ_ASSERT(aStream->SampleRate() == mDestination->SampleRate()); 1.88 + 1.89 + if (!aInput.IsNull()) { 1.90 + if (mLeftOverData <= 0) { 1.91 + nsRefPtr<PlayingRefChanged> refchanged = 1.92 + new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF); 1.93 + aStream->Graph()-> 1.94 + DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget()); 1.95 + } 1.96 + mLeftOverData = mBuffer.MaxDelayTicks(); 1.97 + } else if (mLeftOverData > 0) { 1.98 + mLeftOverData -= WEBAUDIO_BLOCK_SIZE; 1.99 + } else { 1.100 + if (mLeftOverData != INT32_MIN) { 1.101 + mLeftOverData = INT32_MIN; 1.102 + // Delete our buffered data now we no longer need it 1.103 + mBuffer.Reset(); 1.104 + 1.105 + nsRefPtr<PlayingRefChanged> refchanged = 1.106 + new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE); 1.107 + aStream->Graph()-> 1.108 + DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget()); 1.109 + } 1.110 + *aOutput = aInput; 1.111 + return; 1.112 + } 1.113 + 1.114 + mBuffer.Write(aInput); 1.115 + 1.116 + UpdateOutputBlock(aOutput); 1.117 + mBuffer.NextBlock(); 1.118 + } 1.119 + 1.120 + void UpdateOutputBlock(AudioChunk* aOutput) 1.121 + { 1.122 + TrackTicks tick = mSource->GetCurrentPosition(); 1.123 + if (tick == mLastOutputPosition) { 1.124 + return; // mLastChunks is already set on the stream 1.125 + } 1.126 + 1.127 + mLastOutputPosition = tick; 1.128 + bool inCycle = mSource->AsProcessedStream()->InCycle(); 1.129 + double minDelay = inCycle ? static_cast<double>(WEBAUDIO_BLOCK_SIZE) : 0.0; 1.130 + double maxDelay = mMaxDelay; 1.131 + double sampleRate = mSource->SampleRate(); 1.132 + ChannelInterpretation channelInterpretation = 1.133 + mSource->GetChannelInterpretation(); 1.134 + if (mDelay.HasSimpleValue()) { 1.135 + // If this DelayNode is in a cycle, make sure the delay value is at least 1.136 + // one block, even if that is greater than maxDelay. 1.137 + double delayFrames = mDelay.GetValue() * sampleRate; 1.138 + double delayFramesClamped = 1.139 + std::max(minDelay, std::min(delayFrames, maxDelay)); 1.140 + mBuffer.Read(delayFramesClamped, aOutput, channelInterpretation); 1.141 + } else { 1.142 + // Compute the delay values for the duration of the input AudioChunk 1.143 + // If this DelayNode is in a cycle, make sure the delay value is at least 1.144 + // one block. 1.145 + double computedDelay[WEBAUDIO_BLOCK_SIZE]; 1.146 + for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) { 1.147 + double delayAtTick = mDelay.GetValueAtTime(tick, counter) * sampleRate; 1.148 + double delayAtTickClamped = 1.149 + std::max(minDelay, std::min(delayAtTick, maxDelay)); 1.150 + computedDelay[counter] = delayAtTickClamped; 1.151 + } 1.152 + mBuffer.Read(computedDelay, aOutput, channelInterpretation); 1.153 + } 1.154 + } 1.155 + 1.156 + virtual void ProduceBlockBeforeInput(AudioChunk* aOutput) MOZ_OVERRIDE 1.157 + { 1.158 + if (mLeftOverData <= 0) { 1.159 + aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); 1.160 + } else { 1.161 + UpdateOutputBlock(aOutput); 1.162 + } 1.163 + } 1.164 + 1.165 + virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.166 + { 1.167 + size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); 1.168 + // Not owned: 1.169 + // - mSource - probably not owned 1.170 + // - mDestination - probably not owned 1.171 + // - mDelay - shares ref with AudioNode, don't count 1.172 + amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf); 1.173 + return amount; 1.174 + } 1.175 + 1.176 + virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.177 + { 1.178 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.179 + } 1.180 + 1.181 + AudioNodeStream* mSource; 1.182 + AudioNodeStream* mDestination; 1.183 + AudioParamTimeline mDelay; 1.184 + DelayBuffer mBuffer; 1.185 + double mMaxDelay; 1.186 + TrackTicks mLastOutputPosition; 1.187 + // How much data we have in our buffer which needs to be flushed out when our inputs 1.188 + // finish. 1.189 + int32_t mLeftOverData; 1.190 +}; 1.191 + 1.192 +DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay) 1.193 + : AudioNode(aContext, 1.194 + 2, 1.195 + ChannelCountMode::Max, 1.196 + ChannelInterpretation::Speakers) 1.197 + , mDelay(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.198 + SendDelayToStream, 0.0f)) 1.199 +{ 1.200 + DelayNodeEngine* engine = 1.201 + new DelayNodeEngine(this, aContext->Destination(), 1.202 + aContext->SampleRate() * aMaxDelay); 1.203 + mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); 1.204 + engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get())); 1.205 +} 1.206 + 1.207 +size_t 1.208 +DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 1.209 +{ 1.210 + size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); 1.211 + amount += mDelay->SizeOfIncludingThis(aMallocSizeOf); 1.212 + return amount; 1.213 +} 1.214 + 1.215 +size_t 1.216 +DelayNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 1.217 +{ 1.218 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.219 +} 1.220 + 1.221 +JSObject* 1.222 +DelayNode::WrapObject(JSContext* aCx) 1.223 +{ 1.224 + return DelayNodeBinding::Wrap(aCx, this); 1.225 +} 1.226 + 1.227 +void 1.228 +DelayNode::SendDelayToStream(AudioNode* aNode) 1.229 +{ 1.230 + DelayNode* This = static_cast<DelayNode*>(aNode); 1.231 + SendTimelineParameterToStream(This, DelayNodeEngine::DELAY, *This->mDelay); 1.232 +} 1.233 + 1.234 +} 1.235 +} 1.236 +