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 "WaveShaperNode.h"
8 #include "mozilla/dom/WaveShaperNodeBinding.h"
9 #include "AlignmentUtils.h"
10 #include "AudioNode.h"
11 #include "AudioNodeEngine.h"
12 #include "AudioNodeStream.h"
13 #include "mozilla/PodOperations.h"
14 
15 namespace mozilla {
16 namespace dom {
17 
18 NS_IMPL_CYCLE_COLLECTION_CLASS(WaveShaperNode)
19 
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WaveShaperNode,AudioNode)20 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WaveShaperNode, AudioNode)
21   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
22 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
23 
24 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WaveShaperNode, AudioNode)
25 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
26 
27 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WaveShaperNode)
28   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
29 NS_IMPL_CYCLE_COLLECTION_TRACE_END
30 
31 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WaveShaperNode)
32 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
33 
34 NS_IMPL_ADDREF_INHERITED(WaveShaperNode, AudioNode)
35 NS_IMPL_RELEASE_INHERITED(WaveShaperNode, AudioNode)
36 
37 static uint32_t ValueOf(OverSampleType aType) {
38   switch (aType) {
39     case OverSampleType::None:
40       return 1;
41     case OverSampleType::_2x:
42       return 2;
43     case OverSampleType::_4x:
44       return 4;
45     default:
46       NS_NOTREACHED("We should never reach here");
47       return 1;
48   }
49 }
50 
51 class Resampler final {
52  public:
Resampler()53   Resampler()
54       : mType(OverSampleType::None),
55         mUpSampler(nullptr),
56         mDownSampler(nullptr),
57         mChannels(0),
58         mSampleRate(0) {}
59 
~Resampler()60   ~Resampler() { Destroy(); }
61 
Reset(uint32_t aChannels,TrackRate aSampleRate,OverSampleType aType)62   void Reset(uint32_t aChannels, TrackRate aSampleRate, OverSampleType aType) {
63     if (aChannels == mChannels && aSampleRate == mSampleRate &&
64         aType == mType) {
65       return;
66     }
67 
68     mChannels = aChannels;
69     mSampleRate = aSampleRate;
70     mType = aType;
71 
72     Destroy();
73 
74     if (aType == OverSampleType::None) {
75       mBuffer.Clear();
76       return;
77     }
78 
79     mUpSampler = speex_resampler_init(aChannels, aSampleRate,
80                                       aSampleRate * ValueOf(aType),
81                                       SPEEX_RESAMPLER_QUALITY_MIN, nullptr);
82     mDownSampler =
83         speex_resampler_init(aChannels, aSampleRate * ValueOf(aType),
84                              aSampleRate, SPEEX_RESAMPLER_QUALITY_MIN, nullptr);
85     mBuffer.SetLength(WEBAUDIO_BLOCK_SIZE * ValueOf(aType));
86   }
87 
UpSample(uint32_t aChannel,const float * aInputData,uint32_t aBlocks)88   float* UpSample(uint32_t aChannel, const float* aInputData,
89                   uint32_t aBlocks) {
90     uint32_t inSamples = WEBAUDIO_BLOCK_SIZE;
91     uint32_t outSamples = WEBAUDIO_BLOCK_SIZE * aBlocks;
92     float* outputData = mBuffer.Elements();
93 
94     MOZ_ASSERT(mBuffer.Length() == outSamples);
95 
96     WebAudioUtils::SpeexResamplerProcess(mUpSampler, aChannel, aInputData,
97                                          &inSamples, outputData, &outSamples);
98 
99     MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE &&
100                outSamples == WEBAUDIO_BLOCK_SIZE * aBlocks);
101 
102     return outputData;
103   }
104 
DownSample(uint32_t aChannel,float * aOutputData,uint32_t aBlocks)105   void DownSample(uint32_t aChannel, float* aOutputData, uint32_t aBlocks) {
106     uint32_t inSamples = WEBAUDIO_BLOCK_SIZE * aBlocks;
107     uint32_t outSamples = WEBAUDIO_BLOCK_SIZE;
108     const float* inputData = mBuffer.Elements();
109 
110     MOZ_ASSERT(mBuffer.Length() == inSamples);
111 
112     WebAudioUtils::SpeexResamplerProcess(mDownSampler, aChannel, inputData,
113                                          &inSamples, aOutputData, &outSamples);
114 
115     MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE * aBlocks &&
116                outSamples == WEBAUDIO_BLOCK_SIZE);
117   }
118 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const119   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
120     size_t amount = 0;
121     // Future: properly measure speex memory
122     amount += aMallocSizeOf(mUpSampler);
123     amount += aMallocSizeOf(mDownSampler);
124     amount += mBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf);
125     return amount;
126   }
127 
128  private:
Destroy()129   void Destroy() {
130     if (mUpSampler) {
131       speex_resampler_destroy(mUpSampler);
132       mUpSampler = nullptr;
133     }
134     if (mDownSampler) {
135       speex_resampler_destroy(mDownSampler);
136       mDownSampler = nullptr;
137     }
138   }
139 
140  private:
141   OverSampleType mType;
142   SpeexResamplerState* mUpSampler;
143   SpeexResamplerState* mDownSampler;
144   uint32_t mChannels;
145   TrackRate mSampleRate;
146   nsTArray<float> mBuffer;
147 };
148 
149 class WaveShaperNodeEngine final : public AudioNodeEngine {
150  public:
WaveShaperNodeEngine(AudioNode * aNode)151   explicit WaveShaperNodeEngine(AudioNode* aNode)
152       : AudioNodeEngine(aNode), mType(OverSampleType::None) {}
153 
154   enum Parameteres { TYPE };
155 
SetRawArrayData(nsTArray<float> & aCurve)156   void SetRawArrayData(nsTArray<float>& aCurve) override {
157     mCurve.SwapElements(aCurve);
158   }
159 
SetInt32Parameter(uint32_t aIndex,int32_t aValue)160   void SetInt32Parameter(uint32_t aIndex, int32_t aValue) override {
161     switch (aIndex) {
162       case TYPE:
163         mType = static_cast<OverSampleType>(aValue);
164         break;
165       default:
166         NS_ERROR("Bad WaveShaperNode Int32Parameter");
167     }
168   }
169 
170   template <uint32_t blocks>
ProcessCurve(const float * aInputBuffer,float * aOutputBuffer)171   void ProcessCurve(const float* aInputBuffer, float* aOutputBuffer) {
172     for (uint32_t j = 0; j < WEBAUDIO_BLOCK_SIZE * blocks; ++j) {
173       // Index into the curve array based on the amplitude of the
174       // incoming signal by using an amplitude range of [-1, 1] and
175       // performing a linear interpolation of the neighbor values.
176       float index = (mCurve.Length() - 1) * (aInputBuffer[j] + 1.0f) / 2.0f;
177       if (index < 0.0f) {
178         aOutputBuffer[j] = mCurve[0];
179       } else {
180         int32_t indexLower = index;
181         if (static_cast<uint32_t>(indexLower) >= mCurve.Length() - 1) {
182           aOutputBuffer[j] = mCurve[mCurve.Length() - 1];
183         } else {
184           uint32_t indexHigher = indexLower + 1;
185           float interpolationFactor = index - indexLower;
186           aOutputBuffer[j] = (1.0f - interpolationFactor) * mCurve[indexLower] +
187                              interpolationFactor * mCurve[indexHigher];
188         }
189       }
190     }
191   }
192 
ProcessBlock(AudioNodeStream * aStream,GraphTime aFrom,const AudioBlock & aInput,AudioBlock * aOutput,bool * aFinished)193   void ProcessBlock(AudioNodeStream* aStream, GraphTime aFrom,
194                     const AudioBlock& aInput, AudioBlock* aOutput,
195                     bool* aFinished) override {
196     uint32_t channelCount = aInput.ChannelCount();
197     if (!mCurve.Length()) {
198       // Optimize the case where we don't have a curve buffer
199       *aOutput = aInput;
200       return;
201     }
202 
203     // If the input is null, check to see if non-null output will be produced
204     bool nullInput = false;
205     if (channelCount == 0) {
206       float index = (mCurve.Length() - 1) * 0.5;
207       uint32_t indexLower = index;
208       uint32_t indexHigher = indexLower + 1;
209       float interpolationFactor = index - indexLower;
210       if ((1.0f - interpolationFactor) * mCurve[indexLower] +
211               interpolationFactor * mCurve[indexHigher] ==
212           0.0) {
213         *aOutput = aInput;
214         return;
215       } else {
216         nullInput = true;
217         channelCount = 1;
218       }
219     }
220 
221     aOutput->AllocateChannels(channelCount);
222     for (uint32_t i = 0; i < channelCount; ++i) {
223       const float* inputSamples;
224       float scaledInput[WEBAUDIO_BLOCK_SIZE + 4];
225       float* alignedScaledInput = ALIGNED16(scaledInput);
226       ASSERT_ALIGNED16(alignedScaledInput);
227       if (!nullInput) {
228         if (aInput.mVolume != 1.0f) {
229           AudioBlockCopyChannelWithScale(
230               static_cast<const float*>(aInput.mChannelData[i]), aInput.mVolume,
231               alignedScaledInput);
232           inputSamples = alignedScaledInput;
233         } else {
234           inputSamples = static_cast<const float*>(aInput.mChannelData[i]);
235         }
236       } else {
237         PodZero(alignedScaledInput, WEBAUDIO_BLOCK_SIZE);
238         inputSamples = alignedScaledInput;
239       }
240       float* outputBuffer = aOutput->ChannelFloatsForWrite(i);
241       float* sampleBuffer;
242 
243       switch (mType) {
244         case OverSampleType::None:
245           mResampler.Reset(channelCount, aStream->SampleRate(),
246                            OverSampleType::None);
247           ProcessCurve<1>(inputSamples, outputBuffer);
248           break;
249         case OverSampleType::_2x:
250           mResampler.Reset(channelCount, aStream->SampleRate(),
251                            OverSampleType::_2x);
252           sampleBuffer = mResampler.UpSample(i, inputSamples, 2);
253           ProcessCurve<2>(sampleBuffer, sampleBuffer);
254           mResampler.DownSample(i, outputBuffer, 2);
255           break;
256         case OverSampleType::_4x:
257           mResampler.Reset(channelCount, aStream->SampleRate(),
258                            OverSampleType::_4x);
259           sampleBuffer = mResampler.UpSample(i, inputSamples, 4);
260           ProcessCurve<4>(sampleBuffer, sampleBuffer);
261           mResampler.DownSample(i, outputBuffer, 4);
262           break;
263         default:
264           NS_NOTREACHED("We should never reach here");
265       }
266     }
267   }
268 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const269   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override {
270     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
271     amount += mCurve.ShallowSizeOfExcludingThis(aMallocSizeOf);
272     amount += mResampler.SizeOfExcludingThis(aMallocSizeOf);
273     return amount;
274   }
275 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const276   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
277     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
278   }
279 
280  private:
281   nsTArray<float> mCurve;
282   OverSampleType mType;
283   Resampler mResampler;
284 };
285 
WaveShaperNode(AudioContext * aContext)286 WaveShaperNode::WaveShaperNode(AudioContext* aContext)
287     : AudioNode(aContext, 2, ChannelCountMode::Max,
288                 ChannelInterpretation::Speakers),
289       mType(OverSampleType::None) {
290   WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this);
291   mStream = AudioNodeStream::Create(
292       aContext, engine, AudioNodeStream::NO_STREAM_FLAGS, aContext->Graph());
293 }
294 
Create(AudioContext & aAudioContext,const WaveShaperOptions & aOptions,ErrorResult & aRv)295 /* static */ already_AddRefed<WaveShaperNode> WaveShaperNode::Create(
296     AudioContext& aAudioContext, const WaveShaperOptions& aOptions,
297     ErrorResult& aRv) {
298   if (aAudioContext.CheckClosed(aRv)) {
299     return nullptr;
300   }
301 
302   RefPtr<WaveShaperNode> audioNode = new WaveShaperNode(&aAudioContext);
303 
304   audioNode->Initialize(aOptions, aRv);
305   if (NS_WARN_IF(aRv.Failed())) {
306     return nullptr;
307   }
308 
309   if (aOptions.mCurve.WasPassed()) {
310     audioNode->SetCurveInternal(aOptions.mCurve.Value(), aRv);
311     if (NS_WARN_IF(aRv.Failed())) {
312       return nullptr;
313     }
314   }
315 
316   audioNode->SetOversample(aOptions.mOversample);
317   return audioNode.forget();
318 }
319 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)320 JSObject* WaveShaperNode::WrapObject(JSContext* aCx,
321                                      JS::Handle<JSObject*> aGivenProto) {
322   return WaveShaperNodeBinding::Wrap(aCx, this, aGivenProto);
323 }
324 
SetCurve(const Nullable<Float32Array> & aCurve,ErrorResult & aRv)325 void WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve,
326                               ErrorResult& aRv) {
327   // Let's purge the cached value for the curve attribute.
328   WaveShaperNodeBinding::ClearCachedCurveValue(this);
329 
330   if (aCurve.IsNull()) {
331     CleanCurveInternal();
332     return;
333   }
334 
335   const Float32Array& floats = aCurve.Value();
336 
337   floats.ComputeLengthAndData();
338   if (floats.IsShared()) {
339     // Throw if the object is mapping shared memory (must opt in).
340     aRv.ThrowTypeError<MSG_TYPEDARRAY_IS_SHARED>(
341         NS_LITERAL_STRING("Argument of WaveShaperNode.setCurve"));
342     return;
343   }
344 
345   nsTArray<float> curve;
346   uint32_t argLength = floats.Length();
347   if (!curve.SetLength(argLength, fallible)) {
348     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
349     return;
350   }
351 
352   PodCopy(curve.Elements(), floats.Data(), argLength);
353   SetCurveInternal(curve, aRv);
354 }
355 
SetCurveInternal(const nsTArray<float> & aCurve,ErrorResult & aRv)356 void WaveShaperNode::SetCurveInternal(const nsTArray<float>& aCurve,
357                                       ErrorResult& aRv) {
358   if (aCurve.Length() < 2) {
359     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
360     return;
361   }
362 
363   mCurve = aCurve;
364   SendCurveToStream();
365 }
366 
CleanCurveInternal()367 void WaveShaperNode::CleanCurveInternal() {
368   mCurve.Clear();
369   SendCurveToStream();
370 }
371 
SendCurveToStream()372 void WaveShaperNode::SendCurveToStream() {
373   AudioNodeStream* ns = mStream;
374   MOZ_ASSERT(ns, "Why don't we have a stream here?");
375 
376   nsTArray<float> copyCurve(mCurve);
377   ns->SetRawArrayData(copyCurve);
378 }
379 
GetCurve(JSContext * aCx,JS::MutableHandle<JSObject * > aRetval)380 void WaveShaperNode::GetCurve(JSContext* aCx,
381                               JS::MutableHandle<JSObject*> aRetval) {
382   // Let's return a null value if the list is empty.
383   if (mCurve.IsEmpty()) {
384     aRetval.set(nullptr);
385     return;
386   }
387 
388   MOZ_ASSERT(mCurve.Length() >= 2);
389   aRetval.set(
390       Float32Array::Create(aCx, this, mCurve.Length(), mCurve.Elements()));
391 }
392 
SetOversample(OverSampleType aType)393 void WaveShaperNode::SetOversample(OverSampleType aType) {
394   mType = aType;
395   SendInt32ParameterToStream(WaveShaperNodeEngine::TYPE,
396                              static_cast<int32_t>(aType));
397 }
398 
399 }  // namespace dom
400 }  // namespace mozilla
401