content/media/webaudio/BiquadFilterNode.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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.

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

mercurial