1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webaudio/BiquadFilterNode.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,407 @@ 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 "BiquadFilterNode.h" 1.11 +#include "AudioNodeEngine.h" 1.12 +#include "AudioNodeStream.h" 1.13 +#include "AudioDestinationNode.h" 1.14 +#include "PlayingRefChangeHandler.h" 1.15 +#include "WebAudioUtils.h" 1.16 +#include "blink/Biquad.h" 1.17 +#include "mozilla/Preferences.h" 1.18 +#include "AudioParamTimeline.h" 1.19 + 1.20 +namespace mozilla { 1.21 +namespace dom { 1.22 + 1.23 +NS_IMPL_CYCLE_COLLECTION_INHERITED(BiquadFilterNode, AudioNode, 1.24 + mFrequency, mDetune, mQ, mGain) 1.25 + 1.26 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BiquadFilterNode) 1.27 +NS_INTERFACE_MAP_END_INHERITING(AudioNode) 1.28 + 1.29 +NS_IMPL_ADDREF_INHERITED(BiquadFilterNode, AudioNode) 1.30 +NS_IMPL_RELEASE_INHERITED(BiquadFilterNode, AudioNode) 1.31 + 1.32 +static void 1.33 +SetParamsOnBiquad(WebCore::Biquad& aBiquad, 1.34 + float aSampleRate, 1.35 + BiquadFilterType aType, 1.36 + double aFrequency, 1.37 + double aQ, 1.38 + double aGain, 1.39 + double aDetune) 1.40 +{ 1.41 + const double nyquist = aSampleRate * 0.5; 1.42 + double normalizedFrequency = aFrequency / nyquist; 1.43 + 1.44 + if (aDetune) { 1.45 + normalizedFrequency *= std::pow(2.0, aDetune / 1200); 1.46 + } 1.47 + 1.48 + switch (aType) { 1.49 + case BiquadFilterType::Lowpass: 1.50 + aBiquad.setLowpassParams(normalizedFrequency, aQ); 1.51 + break; 1.52 + case BiquadFilterType::Highpass: 1.53 + aBiquad.setHighpassParams(normalizedFrequency, aQ); 1.54 + break; 1.55 + case BiquadFilterType::Bandpass: 1.56 + aBiquad.setBandpassParams(normalizedFrequency, aQ); 1.57 + break; 1.58 + case BiquadFilterType::Lowshelf: 1.59 + aBiquad.setLowShelfParams(normalizedFrequency, aGain); 1.60 + break; 1.61 + case BiquadFilterType::Highshelf: 1.62 + aBiquad.setHighShelfParams(normalizedFrequency, aGain); 1.63 + break; 1.64 + case BiquadFilterType::Peaking: 1.65 + aBiquad.setPeakingParams(normalizedFrequency, aQ, aGain); 1.66 + break; 1.67 + case BiquadFilterType::Notch: 1.68 + aBiquad.setNotchParams(normalizedFrequency, aQ); 1.69 + break; 1.70 + case BiquadFilterType::Allpass: 1.71 + aBiquad.setAllpassParams(normalizedFrequency, aQ); 1.72 + break; 1.73 + default: 1.74 + NS_NOTREACHED("We should never see the alternate names here"); 1.75 + break; 1.76 + } 1.77 +} 1.78 + 1.79 +class BiquadFilterNodeEngine : public AudioNodeEngine 1.80 +{ 1.81 +public: 1.82 + BiquadFilterNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination) 1.83 + : AudioNodeEngine(aNode) 1.84 + , mSource(nullptr) 1.85 + , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream())) 1.86 + // Keep the default values in sync with the default values in 1.87 + // BiquadFilterNode::BiquadFilterNode 1.88 + , mType(BiquadFilterType::Lowpass) 1.89 + , mFrequency(350.f) 1.90 + , mDetune(0.f) 1.91 + , mQ(1.f) 1.92 + , mGain(0.f) 1.93 + { 1.94 + } 1.95 + 1.96 + void SetSourceStream(AudioNodeStream* aSource) 1.97 + { 1.98 + mSource = aSource; 1.99 + } 1.100 + 1.101 + enum Parameteres { 1.102 + TYPE, 1.103 + FREQUENCY, 1.104 + DETUNE, 1.105 + Q, 1.106 + GAIN 1.107 + }; 1.108 + void SetInt32Parameter(uint32_t aIndex, int32_t aValue) MOZ_OVERRIDE 1.109 + { 1.110 + switch (aIndex) { 1.111 + case TYPE: mType = static_cast<BiquadFilterType>(aValue); break; 1.112 + default: 1.113 + NS_ERROR("Bad BiquadFilterNode Int32Parameter"); 1.114 + } 1.115 + } 1.116 + void SetTimelineParameter(uint32_t aIndex, 1.117 + const AudioParamTimeline& aValue, 1.118 + TrackRate aSampleRate) MOZ_OVERRIDE 1.119 + { 1.120 + MOZ_ASSERT(mSource && mDestination); 1.121 + switch (aIndex) { 1.122 + case FREQUENCY: 1.123 + mFrequency = aValue; 1.124 + WebAudioUtils::ConvertAudioParamToTicks(mFrequency, mSource, mDestination); 1.125 + break; 1.126 + case DETUNE: 1.127 + mDetune = aValue; 1.128 + WebAudioUtils::ConvertAudioParamToTicks(mDetune, mSource, mDestination); 1.129 + break; 1.130 + case Q: 1.131 + mQ = aValue; 1.132 + WebAudioUtils::ConvertAudioParamToTicks(mQ, mSource, mDestination); 1.133 + break; 1.134 + case GAIN: 1.135 + mGain = aValue; 1.136 + WebAudioUtils::ConvertAudioParamToTicks(mGain, mSource, mDestination); 1.137 + break; 1.138 + default: 1.139 + NS_ERROR("Bad BiquadFilterNodeEngine TimelineParameter"); 1.140 + } 1.141 + } 1.142 + 1.143 + virtual void ProcessBlock(AudioNodeStream* aStream, 1.144 + const AudioChunk& aInput, 1.145 + AudioChunk* aOutput, 1.146 + bool* aFinished) MOZ_OVERRIDE 1.147 + { 1.148 + float inputBuffer[WEBAUDIO_BLOCK_SIZE]; 1.149 + 1.150 + if (aInput.IsNull()) { 1.151 + bool hasTail = false; 1.152 + for (uint32_t i = 0; i < mBiquads.Length(); ++i) { 1.153 + if (mBiquads[i].hasTail()) { 1.154 + hasTail = true; 1.155 + break; 1.156 + } 1.157 + } 1.158 + if (!hasTail) { 1.159 + if (!mBiquads.IsEmpty()) { 1.160 + mBiquads.Clear(); 1.161 + 1.162 + nsRefPtr<PlayingRefChangeHandler> refchanged = 1.163 + new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::RELEASE); 1.164 + aStream->Graph()-> 1.165 + DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget()); 1.166 + } 1.167 + 1.168 + aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); 1.169 + return; 1.170 + } 1.171 + 1.172 + PodArrayZero(inputBuffer); 1.173 + 1.174 + } else if(mBiquads.Length() != aInput.mChannelData.Length()){ 1.175 + if (mBiquads.IsEmpty()) { 1.176 + nsRefPtr<PlayingRefChangeHandler> refchanged = 1.177 + new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::ADDREF); 1.178 + aStream->Graph()-> 1.179 + DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget()); 1.180 + } else { // Help people diagnose bug 924718 1.181 + NS_WARNING("BiquadFilterNode channel count changes may produce audio glitches"); 1.182 + } 1.183 + 1.184 + // Adjust the number of biquads based on the number of channels 1.185 + mBiquads.SetLength(aInput.mChannelData.Length()); 1.186 + } 1.187 + 1.188 + uint32_t numberOfChannels = mBiquads.Length(); 1.189 + AllocateAudioBlock(numberOfChannels, aOutput); 1.190 + 1.191 + TrackTicks pos = aStream->GetCurrentPosition(); 1.192 + 1.193 + double freq = mFrequency.GetValueAtTime(pos); 1.194 + double q = mQ.GetValueAtTime(pos); 1.195 + double gain = mGain.GetValueAtTime(pos); 1.196 + double detune = mDetune.GetValueAtTime(pos); 1.197 + 1.198 + for (uint32_t i = 0; i < numberOfChannels; ++i) { 1.199 + const float* input; 1.200 + if (aInput.IsNull()) { 1.201 + input = inputBuffer; 1.202 + } else { 1.203 + input = static_cast<const float*>(aInput.mChannelData[i]); 1.204 + if (aInput.mVolume != 1.0) { 1.205 + AudioBlockCopyChannelWithScale(input, aInput.mVolume, inputBuffer); 1.206 + input = inputBuffer; 1.207 + } 1.208 + } 1.209 + SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune); 1.210 + 1.211 + mBiquads[i].process(input, 1.212 + static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])), 1.213 + aInput.GetDuration()); 1.214 + } 1.215 + } 1.216 + 1.217 + virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.218 + { 1.219 + // Not owned: 1.220 + // - mSource - probably not owned 1.221 + // - mDestination - probably not owned 1.222 + // - AudioParamTimelines - counted in the AudioNode 1.223 + size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); 1.224 + amount += mBiquads.SizeOfExcludingThis(aMallocSizeOf); 1.225 + return amount; 1.226 + } 1.227 + 1.228 + virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.229 + { 1.230 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.231 + } 1.232 + 1.233 +private: 1.234 + AudioNodeStream* mSource; 1.235 + AudioNodeStream* mDestination; 1.236 + BiquadFilterType mType; 1.237 + AudioParamTimeline mFrequency; 1.238 + AudioParamTimeline mDetune; 1.239 + AudioParamTimeline mQ; 1.240 + AudioParamTimeline mGain; 1.241 + nsTArray<WebCore::Biquad> mBiquads; 1.242 +}; 1.243 + 1.244 +BiquadFilterNode::BiquadFilterNode(AudioContext* aContext) 1.245 + : AudioNode(aContext, 1.246 + 2, 1.247 + ChannelCountMode::Max, 1.248 + ChannelInterpretation::Speakers) 1.249 + , mType(BiquadFilterType::Lowpass) 1.250 + , mFrequency(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.251 + SendFrequencyToStream, 350.f)) 1.252 + , mDetune(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.253 + SendDetuneToStream, 0.f)) 1.254 + , mQ(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.255 + SendQToStream, 1.f)) 1.256 + , mGain(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.257 + SendGainToStream, 0.f)) 1.258 +{ 1.259 + BiquadFilterNodeEngine* engine = new BiquadFilterNodeEngine(this, aContext->Destination()); 1.260 + mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); 1.261 + engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get())); 1.262 +} 1.263 + 1.264 + 1.265 +size_t 1.266 +BiquadFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 1.267 +{ 1.268 + size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); 1.269 + 1.270 + if (mFrequency) { 1.271 + amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf); 1.272 + } 1.273 + 1.274 + if (mDetune) { 1.275 + amount += mDetune->SizeOfIncludingThis(aMallocSizeOf); 1.276 + } 1.277 + 1.278 + if (mQ) { 1.279 + amount += mQ->SizeOfIncludingThis(aMallocSizeOf); 1.280 + } 1.281 + 1.282 + if (mGain) { 1.283 + amount += mGain->SizeOfIncludingThis(aMallocSizeOf); 1.284 + } 1.285 + 1.286 + return amount; 1.287 +} 1.288 + 1.289 +size_t 1.290 +BiquadFilterNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 1.291 +{ 1.292 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.293 +} 1.294 + 1.295 +JSObject* 1.296 +BiquadFilterNode::WrapObject(JSContext* aCx) 1.297 +{ 1.298 + return BiquadFilterNodeBinding::Wrap(aCx, this); 1.299 +} 1.300 + 1.301 +void 1.302 +BiquadFilterNode::SetType(BiquadFilterType aType) 1.303 +{ 1.304 + if (!Preferences::GetBool("media.webaudio.legacy.BiquadFilterNode")) { 1.305 + // Do not accept the alternate enum values unless the legacy pref 1.306 + // has been turned on. 1.307 + switch (aType) { 1.308 + case BiquadFilterType::_0: 1.309 + case BiquadFilterType::_1: 1.310 + case BiquadFilterType::_2: 1.311 + case BiquadFilterType::_3: 1.312 + case BiquadFilterType::_4: 1.313 + case BiquadFilterType::_5: 1.314 + case BiquadFilterType::_6: 1.315 + case BiquadFilterType::_7: 1.316 + // Do nothing in order to emulate setting an invalid enum value. 1.317 + return; 1.318 + default: 1.319 + // Shut up the compiler warning 1.320 + break; 1.321 + } 1.322 + } 1.323 + 1.324 + // Handle the alternate enum values 1.325 + switch (aType) { 1.326 + case BiquadFilterType::_0: aType = BiquadFilterType::Lowpass; break; 1.327 + case BiquadFilterType::_1: aType = BiquadFilterType::Highpass; break; 1.328 + case BiquadFilterType::_2: aType = BiquadFilterType::Bandpass; break; 1.329 + case BiquadFilterType::_3: aType = BiquadFilterType::Lowshelf; break; 1.330 + case BiquadFilterType::_4: aType = BiquadFilterType::Highshelf; break; 1.331 + case BiquadFilterType::_5: aType = BiquadFilterType::Peaking; break; 1.332 + case BiquadFilterType::_6: aType = BiquadFilterType::Notch; break; 1.333 + case BiquadFilterType::_7: aType = BiquadFilterType::Allpass; break; 1.334 + default: 1.335 + // Shut up the compiler warning 1.336 + break; 1.337 + } 1.338 + 1.339 + mType = aType; 1.340 + SendInt32ParameterToStream(BiquadFilterNodeEngine::TYPE, 1.341 + static_cast<int32_t>(aType)); 1.342 +} 1.343 + 1.344 +void 1.345 +BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz, 1.346 + const Float32Array& aMagResponse, 1.347 + const Float32Array& aPhaseResponse) 1.348 +{ 1.349 + aFrequencyHz.ComputeLengthAndData(); 1.350 + aMagResponse.ComputeLengthAndData(); 1.351 + aPhaseResponse.ComputeLengthAndData(); 1.352 + 1.353 + uint32_t length = std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()), 1.354 + aPhaseResponse.Length()); 1.355 + if (!length) { 1.356 + return; 1.357 + } 1.358 + 1.359 + nsAutoArrayPtr<float> frequencies(new float[length]); 1.360 + float* frequencyHz = aFrequencyHz.Data(); 1.361 + const double nyquist = Context()->SampleRate() * 0.5; 1.362 + 1.363 + // Normalize the frequencies 1.364 + for (uint32_t i = 0; i < length; ++i) { 1.365 + frequencies[i] = static_cast<float>(frequencyHz[i] / nyquist); 1.366 + } 1.367 + 1.368 + const double currentTime = Context()->CurrentTime(); 1.369 + 1.370 + double freq = mFrequency->GetValueAtTime(currentTime); 1.371 + double q = mQ->GetValueAtTime(currentTime); 1.372 + double gain = mGain->GetValueAtTime(currentTime); 1.373 + double detune = mDetune->GetValueAtTime(currentTime); 1.374 + 1.375 + WebCore::Biquad biquad; 1.376 + SetParamsOnBiquad(biquad, Context()->SampleRate(), mType, freq, q, gain, detune); 1.377 + biquad.getFrequencyResponse(int(length), frequencies, aMagResponse.Data(), aPhaseResponse.Data()); 1.378 +} 1.379 + 1.380 +void 1.381 +BiquadFilterNode::SendFrequencyToStream(AudioNode* aNode) 1.382 +{ 1.383 + BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode); 1.384 + SendTimelineParameterToStream(This, BiquadFilterNodeEngine::FREQUENCY, *This->mFrequency); 1.385 +} 1.386 + 1.387 +void 1.388 +BiquadFilterNode::SendDetuneToStream(AudioNode* aNode) 1.389 +{ 1.390 + BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode); 1.391 + SendTimelineParameterToStream(This, BiquadFilterNodeEngine::DETUNE, *This->mDetune); 1.392 +} 1.393 + 1.394 +void 1.395 +BiquadFilterNode::SendQToStream(AudioNode* aNode) 1.396 +{ 1.397 + BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode); 1.398 + SendTimelineParameterToStream(This, BiquadFilterNodeEngine::Q, *This->mQ); 1.399 +} 1.400 + 1.401 +void 1.402 +BiquadFilterNode::SendGainToStream(AudioNode* aNode) 1.403 +{ 1.404 + BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode); 1.405 + SendTimelineParameterToStream(This, BiquadFilterNodeEngine::GAIN, *This->mGain); 1.406 +} 1.407 + 1.408 +} 1.409 +} 1.410 +