content/media/webaudio/WaveShaperNode.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 "WaveShaperNode.h"
     8 #include "mozilla/dom/WaveShaperNodeBinding.h"
     9 #include "AudioNode.h"
    10 #include "AudioNodeEngine.h"
    11 #include "AudioNodeStream.h"
    12 #include "mozilla/PodOperations.h"
    13 #include "speex/speex_resampler.h"
    15 namespace mozilla {
    16 namespace dom {
    18 NS_IMPL_CYCLE_COLLECTION_CLASS(WaveShaperNode)
    20 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WaveShaperNode, AudioNode)
    21   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    22   tmp->ClearCurve();
    23 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    25 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WaveShaperNode, AudioNode)
    26   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
    27 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    29 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WaveShaperNode)
    30   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
    31   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCurve)
    32 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WaveShaperNode)
    35 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
    37 NS_IMPL_ADDREF_INHERITED(WaveShaperNode, AudioNode)
    38 NS_IMPL_RELEASE_INHERITED(WaveShaperNode, AudioNode)
    40 static uint32_t ValueOf(OverSampleType aType)
    41 {
    42   switch (aType) {
    43   case OverSampleType::None: return 1;
    44   case OverSampleType::_2x:  return 2;
    45   case OverSampleType::_4x:  return 4;
    46   default:
    47     NS_NOTREACHED("We should never reach here");
    48     return 1;
    49   }
    50 }
    52 class Resampler
    53 {
    54 public:
    55   Resampler()
    56     : mType(OverSampleType::None)
    57     , mUpSampler(nullptr)
    58     , mDownSampler(nullptr)
    59     , mChannels(0)
    60     , mSampleRate(0)
    61   {
    62   }
    64   ~Resampler()
    65   {
    66     Destroy();
    67   }
    69   void Reset(uint32_t aChannels, TrackRate aSampleRate, OverSampleType aType)
    70   {
    71     if (aChannels == mChannels &&
    72         aSampleRate == mSampleRate &&
    73         aType == mType) {
    74       return;
    75     }
    77     mChannels = aChannels;
    78     mSampleRate = aSampleRate;
    79     mType = aType;
    81     Destroy();
    83     if (aType == OverSampleType::None) {
    84       mBuffer.Clear();
    85       return;
    86     }
    88     mUpSampler = speex_resampler_init(aChannels,
    89                                       aSampleRate,
    90                                       aSampleRate * ValueOf(aType),
    91                                       SPEEX_RESAMPLER_QUALITY_DEFAULT,
    92                                       nullptr);
    93     mDownSampler = speex_resampler_init(aChannels,
    94                                         aSampleRate * ValueOf(aType),
    95                                         aSampleRate,
    96                                         SPEEX_RESAMPLER_QUALITY_DEFAULT,
    97                                         nullptr);
    98     mBuffer.SetLength(WEBAUDIO_BLOCK_SIZE*ValueOf(aType));
    99   }
   101   float* UpSample(uint32_t aChannel, const float* aInputData, uint32_t aBlocks)
   102   {
   103     uint32_t inSamples = WEBAUDIO_BLOCK_SIZE;
   104     uint32_t outSamples = WEBAUDIO_BLOCK_SIZE*aBlocks;
   105     float* outputData = mBuffer.Elements();
   107     MOZ_ASSERT(mBuffer.Length() == outSamples);
   109     WebAudioUtils::SpeexResamplerProcess(mUpSampler, aChannel,
   110                                          aInputData, &inSamples,
   111                                          outputData, &outSamples);
   113     MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE && outSamples == WEBAUDIO_BLOCK_SIZE*aBlocks);
   115     return outputData;
   116   }
   118   void DownSample(uint32_t aChannel, float* aOutputData, uint32_t aBlocks)
   119   {
   120     uint32_t inSamples = WEBAUDIO_BLOCK_SIZE*aBlocks;
   121     uint32_t outSamples = WEBAUDIO_BLOCK_SIZE;
   122     const float* inputData = mBuffer.Elements();
   124     MOZ_ASSERT(mBuffer.Length() == inSamples);
   126     WebAudioUtils::SpeexResamplerProcess(mDownSampler, aChannel,
   127                                          inputData, &inSamples,
   128                                          aOutputData, &outSamples);
   130     MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE*aBlocks && outSamples == WEBAUDIO_BLOCK_SIZE);
   131   }
   133   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   134   {
   135     size_t amount = 0;
   136     // Future: properly measure speex memory
   137     amount += aMallocSizeOf(mUpSampler);
   138     amount += aMallocSizeOf(mDownSampler);
   139     amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
   140     return amount;
   141   }
   143 private:
   144   void Destroy()
   145   {
   146     if (mUpSampler) {
   147       speex_resampler_destroy(mUpSampler);
   148       mUpSampler = nullptr;
   149     }
   150     if (mDownSampler) {
   151       speex_resampler_destroy(mDownSampler);
   152       mDownSampler = nullptr;
   153     }
   154   }
   156 private:
   157   OverSampleType mType;
   158   SpeexResamplerState* mUpSampler;
   159   SpeexResamplerState* mDownSampler;
   160   uint32_t mChannels;
   161   TrackRate mSampleRate;
   162   nsTArray<float> mBuffer;
   163 };
   165 class WaveShaperNodeEngine : public AudioNodeEngine
   166 {
   167 public:
   168   explicit WaveShaperNodeEngine(AudioNode* aNode)
   169     : AudioNodeEngine(aNode)
   170     , mType(OverSampleType::None)
   171   {
   172   }
   174   enum Parameteres {
   175     TYPE
   176   };
   178   virtual void SetRawArrayData(nsTArray<float>& aCurve) MOZ_OVERRIDE
   179   {
   180     mCurve.SwapElements(aCurve);
   181   }
   183   virtual void SetInt32Parameter(uint32_t aIndex, int32_t aValue) MOZ_OVERRIDE
   184   {
   185     switch (aIndex) {
   186     case TYPE:
   187       mType = static_cast<OverSampleType>(aValue);
   188       break;
   189     default:
   190       NS_ERROR("Bad WaveShaperNode Int32Parameter");
   191     }
   192   }
   194   template <uint32_t blocks>
   195   void ProcessCurve(const float* aInputBuffer, float* aOutputBuffer)
   196   {
   197     for (uint32_t j = 0; j < WEBAUDIO_BLOCK_SIZE*blocks; ++j) {
   198       // Index into the curve array based on the amplitude of the
   199       // incoming signal by clamping the amplitude to [-1, 1] and
   200       // performing a linear interpolation of the neighbor values.
   201       float index = std::max(0.0f, std::min(float(mCurve.Length() - 1),
   202                                             mCurve.Length() * (aInputBuffer[j] + 1) / 2));
   203       uint32_t indexLower = uint32_t(index);
   204       uint32_t indexHigher = uint32_t(index + 1.0f);
   205       if (indexHigher == mCurve.Length()) {
   206         aOutputBuffer[j] = mCurve[indexLower];
   207       } else {
   208         float interpolationFactor = index - indexLower;
   209         aOutputBuffer[j] = (1.0f - interpolationFactor) * mCurve[indexLower] +
   210                                    interpolationFactor  * mCurve[indexHigher];
   211       }
   212     }
   213   }
   215   virtual void ProcessBlock(AudioNodeStream* aStream,
   216                             const AudioChunk& aInput,
   217                             AudioChunk* aOutput,
   218                             bool* aFinished)
   219   {
   220     uint32_t channelCount = aInput.mChannelData.Length();
   221     if (!mCurve.Length() || !channelCount) {
   222       // Optimize the case where we don't have a curve buffer,
   223       // or the input is null.
   224       *aOutput = aInput;
   225       return;
   226     }
   228     AllocateAudioBlock(channelCount, aOutput);
   229     for (uint32_t i = 0; i < channelCount; ++i) {
   230       const float* inputBuffer = static_cast<const float*>(aInput.mChannelData[i]);
   231       float* outputBuffer = const_cast<float*> (static_cast<const float*>(aOutput->mChannelData[i]));
   232       float* sampleBuffer;
   234       switch (mType) {
   235       case OverSampleType::None:
   236         mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::None);
   237         ProcessCurve<1>(inputBuffer, outputBuffer);
   238         break;
   239       case OverSampleType::_2x:
   240         mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_2x);
   241         sampleBuffer = mResampler.UpSample(i, inputBuffer, 2);
   242         ProcessCurve<2>(sampleBuffer, sampleBuffer);
   243         mResampler.DownSample(i, outputBuffer, 2);
   244         break;
   245       case OverSampleType::_4x:
   246         mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_4x);
   247         sampleBuffer = mResampler.UpSample(i, inputBuffer, 4);
   248         ProcessCurve<4>(sampleBuffer, sampleBuffer);
   249         mResampler.DownSample(i, outputBuffer, 4);
   250         break;
   251       default:
   252         NS_NOTREACHED("We should never reach here");
   253       }
   254     }
   255   }
   257   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   258   {
   259     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
   260     amount += mCurve.SizeOfExcludingThis(aMallocSizeOf);
   261     amount += mResampler.SizeOfExcludingThis(aMallocSizeOf);
   262     return amount;
   263   }
   265   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   266   {
   267     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   268   }
   270 private:
   271   nsTArray<float> mCurve;
   272   OverSampleType mType;
   273   Resampler mResampler;
   274 };
   276 WaveShaperNode::WaveShaperNode(AudioContext* aContext)
   277   : AudioNode(aContext,
   278               2,
   279               ChannelCountMode::Max,
   280               ChannelInterpretation::Speakers)
   281   , mCurve(nullptr)
   282   , mType(OverSampleType::None)
   283 {
   284   mozilla::HoldJSObjects(this);
   286   WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this);
   287   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   288 }
   290 WaveShaperNode::~WaveShaperNode()
   291 {
   292   ClearCurve();
   293 }
   295 void
   296 WaveShaperNode::ClearCurve()
   297 {
   298   mCurve = nullptr;
   299   mozilla::DropJSObjects(this);
   300 }
   302 JSObject*
   303 WaveShaperNode::WrapObject(JSContext *aCx)
   304 {
   305   return WaveShaperNodeBinding::Wrap(aCx, this);
   306 }
   308 void
   309 WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve)
   310 {
   311   nsTArray<float> curve;
   312   if (!aCurve.IsNull()) {
   313     const Float32Array& floats = aCurve.Value();
   315     mCurve = floats.Obj();
   317     floats.ComputeLengthAndData();
   319     curve.SetLength(floats.Length());
   320     PodCopy(curve.Elements(), floats.Data(), floats.Length());
   321   } else {
   322     mCurve = nullptr;
   323   }
   325   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
   326   MOZ_ASSERT(ns, "Why don't we have a stream here?");
   327   ns->SetRawArrayData(curve);
   328 }
   330 void
   331 WaveShaperNode::SetOversample(OverSampleType aType)
   332 {
   333   mType = aType;
   334   SendInt32ParameterToStream(WaveShaperNodeEngine::TYPE, static_cast<int32_t>(aType));
   335 }
   337 }
   338 }

mercurial