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