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"
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
,
28 BiquadFilterType aType
,
34 const double nyquist
= aSampleRate
* 0.5;
35 double normalizedFrequency
= aFrequency
/ nyquist
;
38 normalizedFrequency
*= std::pow(2.0, aDetune
/ 1200);
42 case BiquadFilterType::Lowpass
:
43 aBiquad
.setLowpassParams(normalizedFrequency
, aQ
);
45 case BiquadFilterType::Highpass
:
46 aBiquad
.setHighpassParams(normalizedFrequency
, aQ
);
48 case BiquadFilterType::Bandpass
:
49 aBiquad
.setBandpassParams(normalizedFrequency
, aQ
);
51 case BiquadFilterType::Lowshelf
:
52 aBiquad
.setLowShelfParams(normalizedFrequency
, aGain
);
54 case BiquadFilterType::Highshelf
:
55 aBiquad
.setHighShelfParams(normalizedFrequency
, aGain
);
57 case BiquadFilterType::Peaking
:
58 aBiquad
.setPeakingParams(normalizedFrequency
, aQ
, aGain
);
60 case BiquadFilterType::Notch
:
61 aBiquad
.setNotchParams(normalizedFrequency
, aQ
);
63 case BiquadFilterType::Allpass
:
64 aBiquad
.setAllpassParams(normalizedFrequency
, aQ
);
67 NS_NOTREACHED("We should never see the alternate names here");
72 class BiquadFilterNodeEngine
: public AudioNodeEngine
75 BiquadFilterNodeEngine(AudioNode
* aNode
, AudioDestinationNode
* aDestination
)
76 : AudioNodeEngine(aNode
)
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
)
89 void SetSourceStream(AudioNodeStream
* aSource
)
101 void SetInt32Parameter(uint32_t aIndex
, int32_t aValue
) MOZ_OVERRIDE
104 case TYPE
: mType
= static_cast<BiquadFilterType
>(aValue
); break;
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
);
117 WebAudioUtils::ConvertAudioParamToTicks(mFrequency
, mSource
, mDestination
);
121 WebAudioUtils::ConvertAudioParamToTicks(mDetune
, mSource
, mDestination
);
125 WebAudioUtils::ConvertAudioParamToTicks(mQ
, mSource
, mDestination
);
129 WebAudioUtils::ConvertAudioParamToTicks(mGain
, mSource
, mDestination
);
132 NS_ERROR("Bad BiquadFilterNodeEngine TimelineParameter");
136 virtual void ProduceAudioBlock(AudioNodeStream
* aStream
,
137 const AudioChunk
& aInput
,
139 bool* aFinished
) MOZ_OVERRIDE
141 if (aInput
.IsNull()) {
142 aOutput
->SetNull(WEBAUDIO_BLOCK_SIZE
);
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());
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()));
196 BiquadFilterNode::WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aScope
)
198 return BiquadFilterNodeBinding::Wrap(aCx
, aScope
, this);
202 BiquadFilterNode::SetType(BiquadFilterType aType
)
204 // Handle the alternate enum values
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;
215 // Shut up the compiler warning
220 SendInt32ParameterToStream(BiquadFilterNodeEngine::TYPE
,
221 static_cast<int32_t>(aType
));
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());
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());
257 BiquadFilterNode::SendFrequencyToStream(AudioNode
* aNode
)
259 BiquadFilterNode
* This
= static_cast<BiquadFilterNode
*>(aNode
);
260 SendTimelineParameterToStream(This
, BiquadFilterNodeEngine::FREQUENCY
, *This
->mFrequency
);
264 BiquadFilterNode::SendDetuneToStream(AudioNode
* aNode
)
266 BiquadFilterNode
* This
= static_cast<BiquadFilterNode
*>(aNode
);
267 SendTimelineParameterToStream(This
, BiquadFilterNodeEngine::DETUNE
, *This
->mDetune
);
271 BiquadFilterNode::SendQToStream(AudioNode
* aNode
)
273 BiquadFilterNode
* This
= static_cast<BiquadFilterNode
*>(aNode
);
274 SendTimelineParameterToStream(This
, BiquadFilterNodeEngine::Q
, *This
->mQ
);
278 BiquadFilterNode::SendGainToStream(AudioNode
* aNode
)
280 BiquadFilterNode
* This
= static_cast<BiquadFilterNode
*>(aNode
);
281 SendTimelineParameterToStream(This
, BiquadFilterNodeEngine::GAIN
, *This
->mGain
);