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.

     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 }

mercurial