LCOV - code coverage report
Current view: top level - dom/media/webaudio - ConvolverNode.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 149 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 23 0.0 %
Legend: Lines: hit not hit

          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 "ConvolverNode.h"
       8             : #include "mozilla/dom/ConvolverNodeBinding.h"
       9             : #include "nsAutoPtr.h"
      10             : #include "AlignmentUtils.h"
      11             : #include "AudioNodeEngine.h"
      12             : #include "AudioNodeStream.h"
      13             : #include "blink/Reverb.h"
      14             : #include "PlayingRefChangeHandler.h"
      15             : 
      16             : namespace mozilla {
      17             : namespace dom {
      18             : 
      19           0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(ConvolverNode, AudioNode, mBuffer)
      20             : 
      21           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ConvolverNode)
      22           0 : NS_INTERFACE_MAP_END_INHERITING(AudioNode)
      23             : 
      24           0 : NS_IMPL_ADDREF_INHERITED(ConvolverNode, AudioNode)
      25           0 : NS_IMPL_RELEASE_INHERITED(ConvolverNode, AudioNode)
      26             : 
      27           0 : class ConvolverNodeEngine final : public AudioNodeEngine
      28             : {
      29             :   typedef PlayingRefChangeHandler PlayingRefChanged;
      30             : public:
      31           0 :   ConvolverNodeEngine(AudioNode* aNode, bool aNormalize)
      32           0 :     : AudioNodeEngine(aNode)
      33             :     , mBufferLength(0)
      34             :     , mLeftOverData(INT32_MIN)
      35             :     , mSampleRate(0.0f)
      36           0 :     , mUseBackgroundThreads(!aNode->Context()->IsOffline())
      37           0 :     , mNormalize(aNormalize)
      38             :   {
      39           0 :   }
      40             : 
      41             :   enum Parameters {
      42             :     BUFFER_LENGTH,
      43             :     SAMPLE_RATE,
      44             :     NORMALIZE
      45             :   };
      46           0 :   void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override
      47             :   {
      48           0 :     switch (aIndex) {
      49             :     case BUFFER_LENGTH:
      50             :       // BUFFER_LENGTH is the first parameter that we set when setting a new buffer,
      51             :       // so we should be careful to invalidate the rest of our state here.
      52           0 :       mBuffer = nullptr;
      53           0 :       mSampleRate = 0.0f;
      54           0 :       mBufferLength = aParam;
      55           0 :       mLeftOverData = INT32_MIN;
      56           0 :       break;
      57             :     case SAMPLE_RATE:
      58           0 :       mSampleRate = aParam;
      59           0 :       break;
      60             :     case NORMALIZE:
      61           0 :       mNormalize = !!aParam;
      62           0 :       break;
      63             :     default:
      64           0 :       NS_ERROR("Bad ConvolverNodeEngine Int32Parameter");
      65             :     }
      66           0 :   }
      67           0 :   void SetDoubleParameter(uint32_t aIndex, double aParam) override
      68             :   {
      69           0 :     switch (aIndex) {
      70             :     case SAMPLE_RATE:
      71           0 :       mSampleRate = aParam;
      72           0 :       AdjustReverb();
      73           0 :       break;
      74             :     default:
      75           0 :       NS_ERROR("Bad ConvolverNodeEngine DoubleParameter");
      76             :     }
      77           0 :   }
      78           0 :   void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) override
      79             :   {
      80           0 :     mBuffer = aBuffer;
      81           0 :     AdjustReverb();
      82           0 :   }
      83             : 
      84           0 :   void AdjustReverb()
      85             :   {
      86             :     // Note about empirical tuning (this is copied from Blink)
      87             :     // The maximum FFT size affects reverb performance and accuracy.
      88             :     // If the reverb is single-threaded and processes entirely in the real-time audio thread,
      89             :     // it's important not to make this too high.  In this case 8192 is a good value.
      90             :     // But, the Reverb object is multi-threaded, so we want this as high as possible without losing too much accuracy.
      91             :     // Very large FFTs will have worse phase errors. Given these constraints 32768 is a good compromise.
      92           0 :     const size_t MaxFFTSize = 32768;
      93             : 
      94           0 :     if (!mBuffer || !mBufferLength || !mSampleRate) {
      95           0 :       mReverb = nullptr;
      96           0 :       mLeftOverData = INT32_MIN;
      97           0 :       return;
      98             :     }
      99             : 
     100           0 :     mReverb = new WebCore::Reverb(mBuffer, mBufferLength,
     101           0 :                                   MaxFFTSize, 2, mUseBackgroundThreads,
     102           0 :                                   mNormalize, mSampleRate);
     103             :   }
     104             : 
     105           0 :   void ProcessBlock(AudioNodeStream* aStream,
     106             :                     GraphTime aFrom,
     107             :                     const AudioBlock& aInput,
     108             :                     AudioBlock* aOutput,
     109             :                     bool* aFinished) override
     110             :   {
     111           0 :     if (!mReverb) {
     112           0 :       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
     113           0 :       return;
     114             :     }
     115             : 
     116           0 :     AudioBlock input = aInput;
     117           0 :     if (aInput.IsNull()) {
     118           0 :       if (mLeftOverData > 0) {
     119           0 :         mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
     120           0 :         input.AllocateChannels(1);
     121           0 :         WriteZeroesToAudioBlock(&input, 0, WEBAUDIO_BLOCK_SIZE);
     122             :       } else {
     123           0 :         if (mLeftOverData != INT32_MIN) {
     124           0 :           mLeftOverData = INT32_MIN;
     125           0 :           aStream->ScheduleCheckForInactive();
     126             :           RefPtr<PlayingRefChanged> refchanged =
     127           0 :             new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
     128           0 :           aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
     129           0 :             refchanged.forget());
     130             :         }
     131           0 :         aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
     132           0 :         return;
     133             :       }
     134             :     } else {
     135           0 :       if (aInput.mVolume != 1.0f) {
     136             :         // Pre-multiply the input's volume
     137           0 :         uint32_t numChannels = aInput.ChannelCount();
     138           0 :         input.AllocateChannels(numChannels);
     139           0 :         for (uint32_t i = 0; i < numChannels; ++i) {
     140           0 :           const float* src = static_cast<const float*>(aInput.mChannelData[i]);
     141           0 :           float* dest = input.ChannelFloatsForWrite(i);
     142           0 :           AudioBlockCopyChannelWithScale(src, aInput.mVolume, dest);
     143             :         }
     144             :       }
     145             : 
     146           0 :       if (mLeftOverData <= 0) {
     147             :         RefPtr<PlayingRefChanged> refchanged =
     148           0 :           new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF);
     149           0 :         aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
     150           0 :           refchanged.forget());
     151             :       }
     152           0 :       mLeftOverData = mBufferLength;
     153           0 :       MOZ_ASSERT(mLeftOverData > 0);
     154             :     }
     155           0 :     aOutput->AllocateChannels(2);
     156             : 
     157           0 :     mReverb->process(&input, aOutput);
     158             :   }
     159             : 
     160           0 :   bool IsActive() const override
     161             :   {
     162           0 :     return mLeftOverData != INT32_MIN;
     163             :   }
     164             : 
     165           0 :   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
     166             :   {
     167           0 :     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
     168           0 :     if (mBuffer && !mBuffer->IsShared()) {
     169           0 :       amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
     170             :     }
     171             : 
     172           0 :     if (mReverb) {
     173           0 :       amount += mReverb->sizeOfIncludingThis(aMallocSizeOf);
     174             :     }
     175             : 
     176           0 :     return amount;
     177             :   }
     178             : 
     179           0 :   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
     180             :   {
     181           0 :     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     182             :   }
     183             : 
     184             : private:
     185             :   RefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
     186             :   nsAutoPtr<WebCore::Reverb> mReverb;
     187             :   int32_t mBufferLength;
     188             :   int32_t mLeftOverData;
     189             :   float mSampleRate;
     190             :   bool mUseBackgroundThreads;
     191             :   bool mNormalize;
     192             : };
     193             : 
     194           0 : ConvolverNode::ConvolverNode(AudioContext* aContext)
     195             :   : AudioNode(aContext,
     196             :               2,
     197             :               ChannelCountMode::Clamped_max,
     198             :               ChannelInterpretation::Speakers)
     199           0 :   , mNormalize(true)
     200             : {
     201           0 :   ConvolverNodeEngine* engine = new ConvolverNodeEngine(this, mNormalize);
     202           0 :   mStream = AudioNodeStream::Create(aContext, engine,
     203             :                                     AudioNodeStream::NO_STREAM_FLAGS,
     204           0 :                                     aContext->Graph());
     205           0 : }
     206             : 
     207             : /* static */ already_AddRefed<ConvolverNode>
     208           0 : ConvolverNode::Create(JSContext* aCx, AudioContext& aAudioContext,
     209             :                       const ConvolverOptions& aOptions,
     210             :                       ErrorResult& aRv)
     211             : {
     212           0 :   if (aAudioContext.CheckClosed(aRv)) {
     213           0 :     return nullptr;
     214             :   }
     215             : 
     216           0 :   RefPtr<ConvolverNode> audioNode = new ConvolverNode(&aAudioContext);
     217             : 
     218           0 :   audioNode->Initialize(aOptions, aRv);
     219           0 :   if (NS_WARN_IF(aRv.Failed())) {
     220           0 :     return nullptr;
     221             :   }
     222             : 
     223             :   // This must be done before setting the buffer.
     224           0 :   audioNode->SetNormalize(!aOptions.mDisableNormalization);
     225             : 
     226           0 :   if (aOptions.mBuffer.WasPassed()) {
     227           0 :     MOZ_ASSERT(aCx);
     228           0 :     audioNode->SetBuffer(aCx, aOptions.mBuffer.Value(), aRv);
     229           0 :     if (NS_WARN_IF(aRv.Failed())) {
     230           0 :       return nullptr;
     231             :     }
     232             :   }
     233             : 
     234           0 :   return audioNode.forget();
     235             : }
     236             : 
     237             : size_t
     238           0 : ConvolverNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
     239             : {
     240           0 :   size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
     241           0 :   if (mBuffer) {
     242             :     // NB: mBuffer might be shared with the associated engine, by convention
     243             :     //     the AudioNode will report.
     244           0 :     amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
     245             :   }
     246           0 :   return amount;
     247             : }
     248             : 
     249             : size_t
     250           0 : ConvolverNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
     251             : {
     252           0 :   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     253             : }
     254             : 
     255             : JSObject*
     256           0 : ConvolverNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     257             : {
     258           0 :   return ConvolverNodeBinding::Wrap(aCx, this, aGivenProto);
     259             : }
     260             : 
     261             : void
     262           0 : ConvolverNode::SetBuffer(JSContext* aCx, AudioBuffer* aBuffer, ErrorResult& aRv)
     263             : {
     264           0 :   if (aBuffer) {
     265           0 :     switch (aBuffer->NumberOfChannels()) {
     266             :     case 1:
     267             :     case 2:
     268             :     case 4:
     269             :       // Supported number of channels
     270           0 :       break;
     271             :     default:
     272           0 :       aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     273           0 :       return;
     274             :     }
     275             :   }
     276             : 
     277           0 :   mBuffer = aBuffer;
     278             : 
     279             :   // Send the buffer to the stream
     280           0 :   AudioNodeStream* ns = mStream;
     281           0 :   MOZ_ASSERT(ns, "Why don't we have a stream here?");
     282           0 :   if (mBuffer) {
     283           0 :     uint32_t length = mBuffer->Length();
     284             :     RefPtr<ThreadSharedFloatArrayBufferList> data =
     285           0 :       mBuffer->GetThreadSharedChannelsForRate(aCx);
     286           0 :     if (data && length < WEBAUDIO_BLOCK_SIZE) {
     287             :       // For very small impulse response buffers, we need to pad the
     288             :       // buffer with 0 to make sure that the Reverb implementation
     289             :       // has enough data to compute FFTs from.
     290           0 :       length = WEBAUDIO_BLOCK_SIZE;
     291             :       RefPtr<ThreadSharedFloatArrayBufferList> paddedBuffer =
     292           0 :         new ThreadSharedFloatArrayBufferList(data->GetChannels());
     293           0 :       void* channelData = malloc(sizeof(float) * length * data->GetChannels() + 15);
     294           0 :       float* alignedChannelData = ALIGNED16(channelData);
     295           0 :       ASSERT_ALIGNED16(alignedChannelData);
     296           0 :       for (uint32_t i = 0; i < data->GetChannels(); ++i) {
     297           0 :         PodCopy(alignedChannelData + length * i, data->GetData(i), mBuffer->Length());
     298           0 :         PodZero(alignedChannelData + length * i + mBuffer->Length(), WEBAUDIO_BLOCK_SIZE - mBuffer->Length());
     299           0 :         paddedBuffer->SetData(i, (i == 0) ? channelData : nullptr, free, alignedChannelData);
     300             :       }
     301           0 :       data = paddedBuffer;
     302             :     }
     303           0 :     SendInt32ParameterToStream(ConvolverNodeEngine::BUFFER_LENGTH, length);
     304           0 :     SendDoubleParameterToStream(ConvolverNodeEngine::SAMPLE_RATE,
     305           0 :                                 mBuffer->SampleRate());
     306           0 :     ns->SetBuffer(data.forget());
     307             :   } else {
     308           0 :     ns->SetBuffer(nullptr);
     309             :   }
     310             : }
     311             : 
     312             : void
     313           0 : ConvolverNode::SetNormalize(bool aNormalize)
     314             : {
     315           0 :   mNormalize = aNormalize;
     316           0 :   SendInt32ParameterToStream(ConvolverNodeEngine::NORMALIZE, aNormalize);
     317           0 : }
     318             : 
     319             : } // namespace dom
     320             : } // namespace mozilla

Generated by: LCOV version 1.13