Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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 "PlayingRefChangeHandler.h"
12 #include "WebAudioUtils.h"
13 #include "blink/Biquad.h"
14 #include "mozilla/Preferences.h"
15 #include "AudioParamTimeline.h"
17 namespace mozilla {
18 namespace dom {
20 NS_IMPL_CYCLE_COLLECTION_INHERITED(BiquadFilterNode, AudioNode,
21 mFrequency, mDetune, mQ, mGain)
23 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BiquadFilterNode)
24 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
26 NS_IMPL_ADDREF_INHERITED(BiquadFilterNode, AudioNode)
27 NS_IMPL_RELEASE_INHERITED(BiquadFilterNode, AudioNode)
29 static void
30 SetParamsOnBiquad(WebCore::Biquad& aBiquad,
31 float aSampleRate,
32 BiquadFilterType aType,
33 double aFrequency,
34 double aQ,
35 double aGain,
36 double aDetune)
37 {
38 const double nyquist = aSampleRate * 0.5;
39 double normalizedFrequency = aFrequency / nyquist;
41 if (aDetune) {
42 normalizedFrequency *= std::pow(2.0, aDetune / 1200);
43 }
45 switch (aType) {
46 case BiquadFilterType::Lowpass:
47 aBiquad.setLowpassParams(normalizedFrequency, aQ);
48 break;
49 case BiquadFilterType::Highpass:
50 aBiquad.setHighpassParams(normalizedFrequency, aQ);
51 break;
52 case BiquadFilterType::Bandpass:
53 aBiquad.setBandpassParams(normalizedFrequency, aQ);
54 break;
55 case BiquadFilterType::Lowshelf:
56 aBiquad.setLowShelfParams(normalizedFrequency, aGain);
57 break;
58 case BiquadFilterType::Highshelf:
59 aBiquad.setHighShelfParams(normalizedFrequency, aGain);
60 break;
61 case BiquadFilterType::Peaking:
62 aBiquad.setPeakingParams(normalizedFrequency, aQ, aGain);
63 break;
64 case BiquadFilterType::Notch:
65 aBiquad.setNotchParams(normalizedFrequency, aQ);
66 break;
67 case BiquadFilterType::Allpass:
68 aBiquad.setAllpassParams(normalizedFrequency, aQ);
69 break;
70 default:
71 NS_NOTREACHED("We should never see the alternate names here");
72 break;
73 }
74 }
76 class BiquadFilterNodeEngine : public AudioNodeEngine
77 {
78 public:
79 BiquadFilterNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
80 : AudioNodeEngine(aNode)
81 , mSource(nullptr)
82 , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
83 // Keep the default values in sync with the default values in
84 // BiquadFilterNode::BiquadFilterNode
85 , mType(BiquadFilterType::Lowpass)
86 , mFrequency(350.f)
87 , mDetune(0.f)
88 , mQ(1.f)
89 , mGain(0.f)
90 {
91 }
93 void SetSourceStream(AudioNodeStream* aSource)
94 {
95 mSource = aSource;
96 }
98 enum Parameteres {
99 TYPE,
100 FREQUENCY,
101 DETUNE,
102 Q,
103 GAIN
104 };
105 void SetInt32Parameter(uint32_t aIndex, int32_t aValue) MOZ_OVERRIDE
106 {
107 switch (aIndex) {
108 case TYPE: mType = static_cast<BiquadFilterType>(aValue); break;
109 default:
110 NS_ERROR("Bad BiquadFilterNode Int32Parameter");
111 }
112 }
113 void SetTimelineParameter(uint32_t aIndex,
114 const AudioParamTimeline& aValue,
115 TrackRate aSampleRate) MOZ_OVERRIDE
116 {
117 MOZ_ASSERT(mSource && mDestination);
118 switch (aIndex) {
119 case FREQUENCY:
120 mFrequency = aValue;
121 WebAudioUtils::ConvertAudioParamToTicks(mFrequency, mSource, mDestination);
122 break;
123 case DETUNE:
124 mDetune = aValue;
125 WebAudioUtils::ConvertAudioParamToTicks(mDetune, mSource, mDestination);
126 break;
127 case Q:
128 mQ = aValue;
129 WebAudioUtils::ConvertAudioParamToTicks(mQ, mSource, mDestination);
130 break;
131 case GAIN:
132 mGain = aValue;
133 WebAudioUtils::ConvertAudioParamToTicks(mGain, mSource, mDestination);
134 break;
135 default:
136 NS_ERROR("Bad BiquadFilterNodeEngine TimelineParameter");
137 }
138 }
140 virtual void ProcessBlock(AudioNodeStream* aStream,
141 const AudioChunk& aInput,
142 AudioChunk* aOutput,
143 bool* aFinished) MOZ_OVERRIDE
144 {
145 float inputBuffer[WEBAUDIO_BLOCK_SIZE];
147 if (aInput.IsNull()) {
148 bool hasTail = false;
149 for (uint32_t i = 0; i < mBiquads.Length(); ++i) {
150 if (mBiquads[i].hasTail()) {
151 hasTail = true;
152 break;
153 }
154 }
155 if (!hasTail) {
156 if (!mBiquads.IsEmpty()) {
157 mBiquads.Clear();
159 nsRefPtr<PlayingRefChangeHandler> refchanged =
160 new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::RELEASE);
161 aStream->Graph()->
162 DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
163 }
165 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
166 return;
167 }
169 PodArrayZero(inputBuffer);
171 } else if(mBiquads.Length() != aInput.mChannelData.Length()){
172 if (mBiquads.IsEmpty()) {
173 nsRefPtr<PlayingRefChangeHandler> refchanged =
174 new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::ADDREF);
175 aStream->Graph()->
176 DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
177 } else { // Help people diagnose bug 924718
178 NS_WARNING("BiquadFilterNode channel count changes may produce audio glitches");
179 }
181 // Adjust the number of biquads based on the number of channels
182 mBiquads.SetLength(aInput.mChannelData.Length());
183 }
185 uint32_t numberOfChannels = mBiquads.Length();
186 AllocateAudioBlock(numberOfChannels, aOutput);
188 TrackTicks pos = aStream->GetCurrentPosition();
190 double freq = mFrequency.GetValueAtTime(pos);
191 double q = mQ.GetValueAtTime(pos);
192 double gain = mGain.GetValueAtTime(pos);
193 double detune = mDetune.GetValueAtTime(pos);
195 for (uint32_t i = 0; i < numberOfChannels; ++i) {
196 const float* input;
197 if (aInput.IsNull()) {
198 input = inputBuffer;
199 } else {
200 input = static_cast<const float*>(aInput.mChannelData[i]);
201 if (aInput.mVolume != 1.0) {
202 AudioBlockCopyChannelWithScale(input, aInput.mVolume, inputBuffer);
203 input = inputBuffer;
204 }
205 }
206 SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune);
208 mBiquads[i].process(input,
209 static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])),
210 aInput.GetDuration());
211 }
212 }
214 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
215 {
216 // Not owned:
217 // - mSource - probably not owned
218 // - mDestination - probably not owned
219 // - AudioParamTimelines - counted in the AudioNode
220 size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
221 amount += mBiquads.SizeOfExcludingThis(aMallocSizeOf);
222 return amount;
223 }
225 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
226 {
227 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
228 }
230 private:
231 AudioNodeStream* mSource;
232 AudioNodeStream* mDestination;
233 BiquadFilterType mType;
234 AudioParamTimeline mFrequency;
235 AudioParamTimeline mDetune;
236 AudioParamTimeline mQ;
237 AudioParamTimeline mGain;
238 nsTArray<WebCore::Biquad> mBiquads;
239 };
241 BiquadFilterNode::BiquadFilterNode(AudioContext* aContext)
242 : AudioNode(aContext,
243 2,
244 ChannelCountMode::Max,
245 ChannelInterpretation::Speakers)
246 , mType(BiquadFilterType::Lowpass)
247 , mFrequency(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
248 SendFrequencyToStream, 350.f))
249 , mDetune(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
250 SendDetuneToStream, 0.f))
251 , mQ(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
252 SendQToStream, 1.f))
253 , mGain(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
254 SendGainToStream, 0.f))
255 {
256 BiquadFilterNodeEngine* engine = new BiquadFilterNodeEngine(this, aContext->Destination());
257 mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
258 engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
259 }
262 size_t
263 BiquadFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
264 {
265 size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
267 if (mFrequency) {
268 amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf);
269 }
271 if (mDetune) {
272 amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
273 }
275 if (mQ) {
276 amount += mQ->SizeOfIncludingThis(aMallocSizeOf);
277 }
279 if (mGain) {
280 amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
281 }
283 return amount;
284 }
286 size_t
287 BiquadFilterNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
288 {
289 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
290 }
292 JSObject*
293 BiquadFilterNode::WrapObject(JSContext* aCx)
294 {
295 return BiquadFilterNodeBinding::Wrap(aCx, this);
296 }
298 void
299 BiquadFilterNode::SetType(BiquadFilterType aType)
300 {
301 if (!Preferences::GetBool("media.webaudio.legacy.BiquadFilterNode")) {
302 // Do not accept the alternate enum values unless the legacy pref
303 // has been turned on.
304 switch (aType) {
305 case BiquadFilterType::_0:
306 case BiquadFilterType::_1:
307 case BiquadFilterType::_2:
308 case BiquadFilterType::_3:
309 case BiquadFilterType::_4:
310 case BiquadFilterType::_5:
311 case BiquadFilterType::_6:
312 case BiquadFilterType::_7:
313 // Do nothing in order to emulate setting an invalid enum value.
314 return;
315 default:
316 // Shut up the compiler warning
317 break;
318 }
319 }
321 // Handle the alternate enum values
322 switch (aType) {
323 case BiquadFilterType::_0: aType = BiquadFilterType::Lowpass; break;
324 case BiquadFilterType::_1: aType = BiquadFilterType::Highpass; break;
325 case BiquadFilterType::_2: aType = BiquadFilterType::Bandpass; break;
326 case BiquadFilterType::_3: aType = BiquadFilterType::Lowshelf; break;
327 case BiquadFilterType::_4: aType = BiquadFilterType::Highshelf; break;
328 case BiquadFilterType::_5: aType = BiquadFilterType::Peaking; break;
329 case BiquadFilterType::_6: aType = BiquadFilterType::Notch; break;
330 case BiquadFilterType::_7: aType = BiquadFilterType::Allpass; break;
331 default:
332 // Shut up the compiler warning
333 break;
334 }
336 mType = aType;
337 SendInt32ParameterToStream(BiquadFilterNodeEngine::TYPE,
338 static_cast<int32_t>(aType));
339 }
341 void
342 BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
343 const Float32Array& aMagResponse,
344 const Float32Array& aPhaseResponse)
345 {
346 aFrequencyHz.ComputeLengthAndData();
347 aMagResponse.ComputeLengthAndData();
348 aPhaseResponse.ComputeLengthAndData();
350 uint32_t length = std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()),
351 aPhaseResponse.Length());
352 if (!length) {
353 return;
354 }
356 nsAutoArrayPtr<float> frequencies(new float[length]);
357 float* frequencyHz = aFrequencyHz.Data();
358 const double nyquist = Context()->SampleRate() * 0.5;
360 // Normalize the frequencies
361 for (uint32_t i = 0; i < length; ++i) {
362 frequencies[i] = static_cast<float>(frequencyHz[i] / nyquist);
363 }
365 const double currentTime = Context()->CurrentTime();
367 double freq = mFrequency->GetValueAtTime(currentTime);
368 double q = mQ->GetValueAtTime(currentTime);
369 double gain = mGain->GetValueAtTime(currentTime);
370 double detune = mDetune->GetValueAtTime(currentTime);
372 WebCore::Biquad biquad;
373 SetParamsOnBiquad(biquad, Context()->SampleRate(), mType, freq, q, gain, detune);
374 biquad.getFrequencyResponse(int(length), frequencies, aMagResponse.Data(), aPhaseResponse.Data());
375 }
377 void
378 BiquadFilterNode::SendFrequencyToStream(AudioNode* aNode)
379 {
380 BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode);
381 SendTimelineParameterToStream(This, BiquadFilterNodeEngine::FREQUENCY, *This->mFrequency);
382 }
384 void
385 BiquadFilterNode::SendDetuneToStream(AudioNode* aNode)
386 {
387 BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode);
388 SendTimelineParameterToStream(This, BiquadFilterNodeEngine::DETUNE, *This->mDetune);
389 }
391 void
392 BiquadFilterNode::SendQToStream(AudioNode* aNode)
393 {
394 BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode);
395 SendTimelineParameterToStream(This, BiquadFilterNodeEngine::Q, *This->mQ);
396 }
398 void
399 BiquadFilterNode::SendGainToStream(AudioNode* aNode)
400 {
401 BiquadFilterNode* This = static_cast<BiquadFilterNode*>(aNode);
402 SendTimelineParameterToStream(This, BiquadFilterNodeEngine::GAIN, *This->mGain);
403 }
405 }
406 }