Line data Source code
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/. */
6 :
7 : #include "BiquadFilterNode.h"
8 : #include "AlignmentUtils.h"
9 : #include "AudioNodeEngine.h"
10 : #include "AudioNodeStream.h"
11 : #include "AudioDestinationNode.h"
12 : #include "PlayingRefChangeHandler.h"
13 : #include "WebAudioUtils.h"
14 : #include "blink/Biquad.h"
15 : #include "mozilla/UniquePtr.h"
16 : #include "AudioParamTimeline.h"
17 :
18 : namespace mozilla {
19 : namespace dom {
20 :
21 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(BiquadFilterNode, AudioNode,
22 : mFrequency, mDetune, mQ, mGain)
23 :
24 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BiquadFilterNode)
25 0 : NS_INTERFACE_MAP_END_INHERITING(AudioNode)
26 :
27 0 : NS_IMPL_ADDREF_INHERITED(BiquadFilterNode, AudioNode)
28 0 : NS_IMPL_RELEASE_INHERITED(BiquadFilterNode, AudioNode)
29 :
30 : static void
31 0 : SetParamsOnBiquad(WebCore::Biquad& aBiquad,
32 : float aSampleRate,
33 : BiquadFilterType aType,
34 : double aFrequency,
35 : double aQ,
36 : double aGain,
37 : double aDetune)
38 : {
39 0 : const double nyquist = aSampleRate * 0.5;
40 0 : double normalizedFrequency = aFrequency / nyquist;
41 :
42 0 : if (aDetune) {
43 0 : normalizedFrequency *= std::pow(2.0, aDetune / 1200);
44 : }
45 :
46 0 : switch (aType) {
47 : case BiquadFilterType::Lowpass:
48 0 : aBiquad.setLowpassParams(normalizedFrequency, aQ);
49 0 : break;
50 : case BiquadFilterType::Highpass:
51 0 : aBiquad.setHighpassParams(normalizedFrequency, aQ);
52 0 : break;
53 : case BiquadFilterType::Bandpass:
54 0 : aBiquad.setBandpassParams(normalizedFrequency, aQ);
55 0 : break;
56 : case BiquadFilterType::Lowshelf:
57 0 : aBiquad.setLowShelfParams(normalizedFrequency, aGain);
58 0 : break;
59 : case BiquadFilterType::Highshelf:
60 0 : aBiquad.setHighShelfParams(normalizedFrequency, aGain);
61 0 : break;
62 : case BiquadFilterType::Peaking:
63 0 : aBiquad.setPeakingParams(normalizedFrequency, aQ, aGain);
64 0 : break;
65 : case BiquadFilterType::Notch:
66 0 : aBiquad.setNotchParams(normalizedFrequency, aQ);
67 0 : break;
68 : case BiquadFilterType::Allpass:
69 0 : aBiquad.setAllpassParams(normalizedFrequency, aQ);
70 0 : break;
71 : default:
72 0 : NS_NOTREACHED("We should never see the alternate names here");
73 0 : break;
74 : }
75 0 : }
76 :
77 0 : class BiquadFilterNodeEngine final : public AudioNodeEngine
78 : {
79 : public:
80 0 : BiquadFilterNodeEngine(AudioNode* aNode,
81 : AudioDestinationNode* aDestination,
82 : uint64_t aWindowID)
83 0 : : AudioNodeEngine(aNode)
84 0 : , mDestination(aDestination->Stream())
85 : // Keep the default values in sync with the default values in
86 : // BiquadFilterNode::BiquadFilterNode
87 : , mType(BiquadFilterType::Lowpass)
88 : , mFrequency(350.f)
89 : , mDetune(0.f)
90 : , mQ(1.f)
91 : , mGain(0.f)
92 0 : , mWindowID(aWindowID)
93 : {
94 0 : }
95 :
96 : enum Parameteres {
97 : TYPE,
98 : FREQUENCY,
99 : DETUNE,
100 : Q,
101 : GAIN
102 : };
103 0 : void SetInt32Parameter(uint32_t aIndex, int32_t aValue) override
104 : {
105 0 : switch (aIndex) {
106 0 : case TYPE: mType = static_cast<BiquadFilterType>(aValue); break;
107 : default:
108 0 : NS_ERROR("Bad BiquadFilterNode Int32Parameter");
109 : }
110 0 : }
111 0 : void RecvTimelineEvent(uint32_t aIndex,
112 : AudioTimelineEvent& aEvent) override
113 : {
114 0 : MOZ_ASSERT(mDestination);
115 :
116 0 : WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent,
117 0 : mDestination);
118 :
119 0 : switch (aIndex) {
120 : case FREQUENCY:
121 0 : mFrequency.InsertEvent<int64_t>(aEvent);
122 0 : break;
123 : case DETUNE:
124 0 : mDetune.InsertEvent<int64_t>(aEvent);
125 0 : break;
126 : case Q:
127 0 : mQ.InsertEvent<int64_t>(aEvent);
128 0 : break;
129 : case GAIN:
130 0 : mGain.InsertEvent<int64_t>(aEvent);
131 0 : break;
132 : default:
133 0 : NS_ERROR("Bad BiquadFilterNodeEngine TimelineParameter");
134 : }
135 0 : }
136 :
137 0 : void ProcessBlock(AudioNodeStream* aStream,
138 : GraphTime aFrom,
139 : const AudioBlock& aInput,
140 : AudioBlock* aOutput,
141 : bool* aFinished) override
142 : {
143 : float inputBuffer[WEBAUDIO_BLOCK_SIZE + 4];
144 0 : float* alignedInputBuffer = ALIGNED16(inputBuffer);
145 0 : ASSERT_ALIGNED16(alignedInputBuffer);
146 :
147 0 : if (aInput.IsNull()) {
148 0 : bool hasTail = false;
149 0 : for (uint32_t i = 0; i < mBiquads.Length(); ++i) {
150 0 : if (mBiquads[i].hasTail()) {
151 0 : hasTail = true;
152 0 : break;
153 : }
154 : }
155 0 : if (!hasTail) {
156 0 : if (!mBiquads.IsEmpty()) {
157 0 : mBiquads.Clear();
158 0 : aStream->ScheduleCheckForInactive();
159 :
160 : RefPtr<PlayingRefChangeHandler> refchanged =
161 0 : new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::RELEASE);
162 0 : aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
163 0 : refchanged.forget());
164 : }
165 :
166 0 : aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
167 0 : return;
168 : }
169 :
170 0 : PodArrayZero(inputBuffer);
171 :
172 0 : } else if(mBiquads.Length() != aInput.ChannelCount()){
173 0 : if (mBiquads.IsEmpty()) {
174 : RefPtr<PlayingRefChangeHandler> refchanged =
175 0 : new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::ADDREF);
176 0 : aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
177 0 : refchanged.forget());
178 : } else { // Help people diagnose bug 924718
179 0 : WebAudioUtils::LogToDeveloperConsole(mWindowID,
180 0 : "BiquadFilterChannelCountChangeWarning");
181 : }
182 :
183 : // Adjust the number of biquads based on the number of channels
184 0 : mBiquads.SetLength(aInput.ChannelCount());
185 : }
186 :
187 0 : uint32_t numberOfChannels = mBiquads.Length();
188 0 : aOutput->AllocateChannels(numberOfChannels);
189 :
190 0 : StreamTime pos = mDestination->GraphTimeToStreamTime(aFrom);
191 :
192 0 : double freq = mFrequency.GetValueAtTime(pos);
193 0 : double q = mQ.GetValueAtTime(pos);
194 0 : double gain = mGain.GetValueAtTime(pos);
195 0 : double detune = mDetune.GetValueAtTime(pos);
196 :
197 0 : for (uint32_t i = 0; i < numberOfChannels; ++i) {
198 : const float* input;
199 0 : if (aInput.IsNull()) {
200 0 : input = alignedInputBuffer;
201 : } else {
202 0 : input = static_cast<const float*>(aInput.mChannelData[i]);
203 0 : if (aInput.mVolume != 1.0) {
204 0 : AudioBlockCopyChannelWithScale(input, aInput.mVolume, alignedInputBuffer);
205 0 : input = alignedInputBuffer;
206 : }
207 : }
208 0 : SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune);
209 :
210 0 : mBiquads[i].process(input,
211 : aOutput->ChannelFloatsForWrite(i),
212 0 : aInput.GetDuration());
213 : }
214 : }
215 :
216 0 : bool IsActive() const override
217 : {
218 0 : return !mBiquads.IsEmpty();
219 : }
220 :
221 0 : size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
222 : {
223 : // Not owned:
224 : // - mDestination - probably not owned
225 : // - AudioParamTimelines - counted in the AudioNode
226 0 : size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
227 0 : amount += mBiquads.ShallowSizeOfExcludingThis(aMallocSizeOf);
228 0 : return amount;
229 : }
230 :
231 0 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
232 : {
233 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
234 : }
235 :
236 : private:
237 : AudioNodeStream* mDestination;
238 : BiquadFilterType mType;
239 : AudioParamTimeline mFrequency;
240 : AudioParamTimeline mDetune;
241 : AudioParamTimeline mQ;
242 : AudioParamTimeline mGain;
243 : nsTArray<WebCore::Biquad> mBiquads;
244 : uint64_t mWindowID;
245 : };
246 :
247 0 : BiquadFilterNode::BiquadFilterNode(AudioContext* aContext)
248 : : AudioNode(aContext,
249 : 2,
250 : ChannelCountMode::Max,
251 : ChannelInterpretation::Speakers)
252 : , mType(BiquadFilterType::Lowpass)
253 : , mFrequency(new AudioParam(this, BiquadFilterNodeEngine::FREQUENCY,
254 : "frequency", 350.f,
255 0 : -(aContext->SampleRate() / 2),
256 0 : aContext->SampleRate() / 2))
257 0 : , mDetune(new AudioParam(this, BiquadFilterNodeEngine::DETUNE, "detune", 0.f))
258 0 : , mQ(new AudioParam(this, BiquadFilterNodeEngine::Q, "Q", 1.f))
259 0 : , mGain(new AudioParam(this, BiquadFilterNodeEngine::GAIN, "gain", 0.f))
260 : {
261 0 : uint64_t windowID = aContext->GetParentObject()->WindowID();
262 0 : BiquadFilterNodeEngine* engine = new BiquadFilterNodeEngine(this, aContext->Destination(), windowID);
263 0 : mStream = AudioNodeStream::Create(aContext, engine,
264 : AudioNodeStream::NO_STREAM_FLAGS,
265 0 : aContext->Graph());
266 0 : }
267 :
268 : /* static */ already_AddRefed<BiquadFilterNode>
269 0 : BiquadFilterNode::Create(AudioContext& aAudioContext,
270 : const BiquadFilterOptions& aOptions,
271 : ErrorResult& aRv)
272 : {
273 0 : if (aAudioContext.CheckClosed(aRv)) {
274 0 : return nullptr;
275 : }
276 :
277 0 : RefPtr<BiquadFilterNode> audioNode = new BiquadFilterNode(&aAudioContext);
278 :
279 0 : audioNode->Initialize(aOptions, aRv);
280 0 : if (NS_WARN_IF(aRv.Failed())) {
281 0 : return nullptr;
282 : }
283 :
284 0 : audioNode->SetType(aOptions.mType);
285 0 : audioNode->Q()->SetValue(aOptions.mQ);
286 0 : audioNode->Detune()->SetValue(aOptions.mDetune);
287 0 : audioNode->Frequency()->SetValue(aOptions.mFrequency);
288 0 : audioNode->Gain()->SetValue(aOptions.mGain);
289 :
290 0 : return audioNode.forget();
291 : }
292 :
293 : size_t
294 0 : BiquadFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
295 : {
296 0 : size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
297 :
298 0 : if (mFrequency) {
299 0 : amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf);
300 : }
301 :
302 0 : if (mDetune) {
303 0 : amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
304 : }
305 :
306 0 : if (mQ) {
307 0 : amount += mQ->SizeOfIncludingThis(aMallocSizeOf);
308 : }
309 :
310 0 : if (mGain) {
311 0 : amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
312 : }
313 :
314 0 : return amount;
315 : }
316 :
317 : size_t
318 0 : BiquadFilterNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
319 : {
320 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
321 : }
322 :
323 : JSObject*
324 0 : BiquadFilterNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
325 : {
326 0 : return BiquadFilterNodeBinding::Wrap(aCx, this, aGivenProto);
327 : }
328 :
329 : void
330 0 : BiquadFilterNode::SetType(BiquadFilterType aType)
331 : {
332 0 : mType = aType;
333 0 : SendInt32ParameterToStream(BiquadFilterNodeEngine::TYPE,
334 0 : static_cast<int32_t>(aType));
335 0 : }
336 :
337 : void
338 0 : BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
339 : const Float32Array& aMagResponse,
340 : const Float32Array& aPhaseResponse)
341 : {
342 0 : aFrequencyHz.ComputeLengthAndData();
343 0 : aMagResponse.ComputeLengthAndData();
344 0 : aPhaseResponse.ComputeLengthAndData();
345 :
346 0 : uint32_t length = std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()),
347 0 : aPhaseResponse.Length());
348 0 : if (!length) {
349 0 : return;
350 : }
351 :
352 0 : auto frequencies = MakeUnique<float[]>(length);
353 0 : float* frequencyHz = aFrequencyHz.Data();
354 0 : const double nyquist = Context()->SampleRate() * 0.5;
355 :
356 : // Normalize the frequencies
357 0 : for (uint32_t i = 0; i < length; ++i) {
358 0 : if (frequencyHz[i] >= 0 && frequencyHz[i] <= nyquist) {
359 0 : frequencies[i] = static_cast<float>(frequencyHz[i] / nyquist);
360 : } else {
361 0 : frequencies[i] = std::numeric_limits<float>::quiet_NaN();
362 : }
363 : }
364 :
365 0 : const double currentTime = Context()->CurrentTime();
366 :
367 0 : double freq = mFrequency->GetValueAtTime(currentTime);
368 0 : double q = mQ->GetValueAtTime(currentTime);
369 0 : double gain = mGain->GetValueAtTime(currentTime);
370 0 : double detune = mDetune->GetValueAtTime(currentTime);
371 :
372 0 : WebCore::Biquad biquad;
373 0 : SetParamsOnBiquad(biquad, Context()->SampleRate(), mType, freq, q, gain, detune);
374 0 : biquad.getFrequencyResponse(int(length), frequencies.get(), aMagResponse.Data(), aPhaseResponse.Data());
375 : }
376 :
377 : } // namespace dom
378 9 : } // namespace mozilla
|