Bug 878875 - Import PannerNode tests from Blink. r=ehsan
[gecko.git] / content / media / webaudio / BiquadFilterNode.cpp
blobc241c2d8576c01641d7bb9a844aed40338192aa8
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "BiquadFilterNode.h"
8 #include "AudioNodeEngine.h"
9 #include "AudioNodeStream.h"
10 #include "AudioDestinationNode.h"
11 #include "WebAudioUtils.h"
12 #include "blink/Biquad.h"
14 namespace mozilla {
15 namespace dom {
17 NS_IMPL_CYCLE_COLLECTION_INHERITED_4(BiquadFilterNode, AudioNode,
18 mFrequency, mDetune, mQ, mGain)
20 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BiquadFilterNode)
21 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
23 NS_IMPL_ADDREF_INHERITED(BiquadFilterNode, AudioNode)
24 NS_IMPL_RELEASE_INHERITED(BiquadFilterNode, AudioNode)
26 void SetParamsOnBiquad(WebCore::Biquad& aBiquad,
27 float aSampleRate,
28 BiquadFilterType aType,
29 double aFrequency,
30 double aQ,
31 double aGain,
32 double aDetune)
34 const double nyquist = aSampleRate * 0.5;
35 double normalizedFrequency = aFrequency / nyquist;
37 if (aDetune) {
38 normalizedFrequency *= std::pow(2.0, aDetune / 1200);
41 switch (aType) {
42 case BiquadFilterType::Lowpass:
43 aBiquad.setLowpassParams(normalizedFrequency, aQ);
44 break;
45 case BiquadFilterType::Highpass:
46 aBiquad.setHighpassParams(normalizedFrequency, aQ);
47 break;
48 case BiquadFilterType::Bandpass:
49 aBiquad.setBandpassParams(normalizedFrequency, aQ);
50 break;
51 case BiquadFilterType::Lowshelf:
52 aBiquad.setLowShelfParams(normalizedFrequency, aGain);
53 break;
54 case BiquadFilterType::Highshelf:
55 aBiquad.setHighShelfParams(normalizedFrequency, aGain);
56 break;
57 case BiquadFilterType::Peaking:
58 aBiquad.setPeakingParams(normalizedFrequency, aQ, aGain);
59 break;
60 case BiquadFilterType::Notch:
61 aBiquad.setNotchParams(normalizedFrequency, aQ);
62 break;
63 case BiquadFilterType::Allpass:
64 aBiquad.setAllpassParams(normalizedFrequency, aQ);
65 break;
66 default:
67 NS_NOTREACHED("We should never see the alternate names here");
68 break;
72 class BiquadFilterNodeEngine : public AudioNodeEngine
74 public:
75 BiquadFilterNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
76 : AudioNodeEngine(aNode)
77 , mSource(nullptr)
78 , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
79 // Keep the default values in sync with the default values in
80 // BiquadFilterNode::BiquadFilterNode
81 , mType(BiquadFilterType::Lowpass)
82 , mFrequency(350.f)
83 , mDetune(0.f)
84 , mQ(1.f)
85 , mGain(0.f)
89 void SetSourceStream(AudioNodeStream* aSource)
91 mSource = aSource;
94 enum Parameteres {
95 TYPE,
96 FREQUENCY,
97 DETUNE,
99 GAIN
101 void SetInt32Parameter(uint32_t aIndex, int32_t aValue) MOZ_OVERRIDE
103 switch (aIndex) {
104 case TYPE: mType = static_cast<BiquadFilterType>(aValue); break;
105 default:
106 NS_ERROR("Bad BiquadFilterNode Int32Parameter");
109 void SetTimelineParameter(uint32_t aIndex,
110 const AudioParamTimeline& aValue,
111 TrackRate aSampleRate) MOZ_OVERRIDE
113 MOZ_ASSERT(mSource && mDestination);
114 switch (aIndex) {
115 case FREQUENCY:
116 mFrequency = aValue;
117 WebAudioUtils::ConvertAudioParamToTicks(mFrequency, mSource, mDestination);
118 break;
119 case DETUNE:
120 mDetune = aValue;
121 WebAudioUtils::ConvertAudioParamToTicks(mDetune, mSource, mDestination);
122 break;
123 case Q:
124 mQ = aValue;
125 WebAudioUtils::ConvertAudioParamToTicks(mQ, mSource, mDestination);
126 break;
127 case GAIN:
128 mGain = aValue;
129 WebAudioUtils::ConvertAudioParamToTicks(mGain, mSource, mDestination);
130 break;
131 default:
132 NS_ERROR("Bad BiquadFilterNodeEngine TimelineParameter");
136 virtual void ProduceAudioBlock(AudioNodeStream* aStream,
137 const AudioChunk& aInput,
138 AudioChunk* aOutput,
139 bool* aFinished) MOZ_OVERRIDE
141 if (aInput.IsNull()) {
142 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
143 return;
146 // Adjust the number of biquads based on the number of channels
147 const uint32_t numberOfChannels = aInput.mChannelData.Length();
148 mBiquads.SetLength(numberOfChannels);
150 AllocateAudioBlock(numberOfChannels, aOutput);
152 TrackTicks pos = aStream->GetCurrentPosition();
154 double freq = mFrequency.GetValueAtTime(pos);
155 double q = mQ.GetValueAtTime(pos);
156 double gain = mGain.GetValueAtTime(pos);
157 double detune = mDetune.GetValueAtTime(pos);
159 for (uint32_t i = 0; i < numberOfChannels; ++i) {
160 SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune);
162 mBiquads[i].process(static_cast<const float*>(aInput.mChannelData[i]),
163 static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])),
164 aInput.GetDuration());
168 private:
169 AudioNodeStream* mSource;
170 AudioNodeStream* mDestination;
171 BiquadFilterType mType;
172 AudioParamTimeline mFrequency;
173 AudioParamTimeline mDetune;
174 AudioParamTimeline mQ;
175 AudioParamTimeline mGain;
176 nsTArray<WebCore::Biquad> mBiquads;
179 BiquadFilterNode::BiquadFilterNode(AudioContext* aContext)
180 : AudioNode(aContext,
182 ChannelCountMode::Max,
183 ChannelInterpretation::Speakers)
184 , mType(BiquadFilterType::Lowpass)
185 , mFrequency(new AudioParam(this, SendFrequencyToStream, 350.f))
186 , mDetune(new AudioParam(this, SendDetuneToStream, 0.f))
187 , mQ(new AudioParam(this, SendQToStream, 1.f))
188 , mGain(new AudioParam(this, SendGainToStream, 0.f))
190 BiquadFilterNodeEngine* engine = new BiquadFilterNodeEngine(this, aContext->Destination());
191 mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
192 engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
195 JSObject*
196 BiquadFilterNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
198 return BiquadFilterNodeBinding::Wrap(aCx, aScope, this);
201 void
202 BiquadFilterNode::SetType(BiquadFilterType aType)
204 // Handle the alternate enum values
205 switch (aType) {
206 case BiquadFilterType::_0: aType = BiquadFilterType::Lowpass; break;
207 case BiquadFilterType::_1: aType = BiquadFilterType::Highpass; break;
208 case BiquadFilterType::_2: aType = BiquadFilterType::Bandpass; break;
209 case BiquadFilterType::_3: aType = BiquadFilterType::Lowshelf; break;
210 case BiquadFilterType::_4: aType = BiquadFilterType::Highshelf; break;
211 case BiquadFilterType::_5: aType = BiquadFilterType::Peaking; break;
212 case BiquadFilterType::_6: aType = BiquadFilterType::Notch; break;
213 case BiquadFilterType::_7: aType = BiquadFilterType::Allpass; break;
214 default:
215 // Shut up the compiler warning
216 break;
219 mType = aType;
220 SendInt32ParameterToStream(BiquadFilterNodeEngine::TYPE,
221 static_cast<int32_t>(aType));
224 void
225 BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
226 Float32Array& aMagResponse,
227 Float32Array& aPhaseResponse)
229 uint32_t length = std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()),
230 aPhaseResponse.Length());
231 if (!length) {
232 return;
235 nsAutoArrayPtr<float> frequencies(new float[length]);
236 float* frequencyHz = aFrequencyHz.Data();
237 const double nyquist = Context()->SampleRate() * 0.5;
239 // Normalize the frequencies
240 for (uint32_t i = 0; i < length; ++i) {
241 frequencies[i] = static_cast<float>(frequencyHz[i] / nyquist);
244 const double currentTime = Context()->CurrentTime();
246 double freq = mFrequency->GetValueAtTime(currentTime);
247 double q = mQ->GetValueAtTime(currentTime);
248 double gain = mGain->GetValueAtTime(currentTime);
249 double detune = mDetune->GetValueAtTime(currentTime);
251 WebCore::Biquad biquad;
252 SetParamsOnBiquad(biquad, Context()->SampleRate(), mType, freq, q, gain, detune);
253 biquad.getFrequencyResponse(int(length), frequencies, aMagResponse.Data(), aPhaseResponse.Data());
256 void
257 BiquadFilterNode::SendFrequencyToStream(AudioNode* aNode)
259 BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode);
260 SendTimelineParameterToStream(This, BiquadFilterNodeEngine::FREQUENCY, *This->mFrequency);
263 void
264 BiquadFilterNode::SendDetuneToStream(AudioNode* aNode)
266 BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode);
267 SendTimelineParameterToStream(This, BiquadFilterNodeEngine::DETUNE, *This->mDetune);
270 void
271 BiquadFilterNode::SendQToStream(AudioNode* aNode)
273 BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode);
274 SendTimelineParameterToStream(This, BiquadFilterNodeEngine::Q, *This->mQ);
277 void
278 BiquadFilterNode::SendGainToStream(AudioNode* aNode)
280 BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode);
281 SendTimelineParameterToStream(This, BiquadFilterNodeEngine::GAIN, *This->mGain);