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 
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
24 
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WaveShaperNode,AudioNode)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
28 
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
33 
34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WaveShaperNode)
35 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
36 
37 NS_IMPL_ADDREF_INHERITED(WaveShaperNode, AudioNode)
38 NS_IMPL_RELEASE_INHERITED(WaveShaperNode, AudioNode)
39 
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 }
51 
52 class Resampler final
53 {
54 public:
Resampler()55   Resampler()
56     : mType(OverSampleType::None)
57     , mUpSampler(nullptr)
58     , mDownSampler(nullptr)
59     , mChannels(0)
60     , mSampleRate(0)
61   {
62   }
63 
~Resampler()64   ~Resampler()
65   {
66     Destroy();
67   }
68 
Reset(uint32_t aChannels,TrackRate aSampleRate,OverSampleType aType)69   void Reset(uint32_t aChannels, TrackRate aSampleRate, OverSampleType aType)
70   {
71     if (aChannels == mChannels &&
72         aSampleRate == mSampleRate &&
73         aType == mType) {
74       return;
75     }
76 
77     mChannels = aChannels;
78     mSampleRate = aSampleRate;
79     mType = aType;
80 
81     Destroy();
82 
83     if (aType == OverSampleType::None) {
84       mBuffer.Clear();
85       return;
86     }
87 
88     mUpSampler = speex_resampler_init(aChannels,
89                                       aSampleRate,
90                                       aSampleRate * ValueOf(aType),
91                                       SPEEX_RESAMPLER_QUALITY_MIN,
92                                       nullptr);
93     mDownSampler = speex_resampler_init(aChannels,
94                                         aSampleRate * ValueOf(aType),
95                                         aSampleRate,
96                                         SPEEX_RESAMPLER_QUALITY_MIN,
97                                         nullptr);
98     mBuffer.SetLength(WEBAUDIO_BLOCK_SIZE*ValueOf(aType));
99   }
100 
UpSample(uint32_t aChannel,const float * aInputData,uint32_t aBlocks)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();
106 
107     MOZ_ASSERT(mBuffer.Length() == outSamples);
108 
109     WebAudioUtils::SpeexResamplerProcess(mUpSampler, aChannel,
110                                          aInputData, &inSamples,
111                                          outputData, &outSamples);
112 
113     MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE && outSamples == WEBAUDIO_BLOCK_SIZE*aBlocks);
114 
115     return outputData;
116   }
117 
DownSample(uint32_t aChannel,float * aOutputData,uint32_t aBlocks)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();
123 
124     MOZ_ASSERT(mBuffer.Length() == inSamples);
125 
126     WebAudioUtils::SpeexResamplerProcess(mDownSampler, aChannel,
127                                          inputData, &inSamples,
128                                          aOutputData, &outSamples);
129 
130     MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE*aBlocks && outSamples == WEBAUDIO_BLOCK_SIZE);
131   }
132 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const133   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.ShallowSizeOfExcludingThis(aMallocSizeOf);
140     return amount;
141   }
142 
143 private:
Destroy()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   }
155 
156 private:
157   OverSampleType mType;
158   SpeexResamplerState* mUpSampler;
159   SpeexResamplerState* mDownSampler;
160   uint32_t mChannels;
161   TrackRate mSampleRate;
162   nsTArray<float> mBuffer;
163 };
164 
165 class WaveShaperNodeEngine final : public AudioNodeEngine
166 {
167 public:
WaveShaperNodeEngine(AudioNode * aNode)168   explicit WaveShaperNodeEngine(AudioNode* aNode)
169     : AudioNodeEngine(aNode)
170     , mType(OverSampleType::None)
171   {
172   }
173 
174   enum Parameteres {
175     TYPE
176   };
177 
SetRawArrayData(nsTArray<float> & aCurve)178   void SetRawArrayData(nsTArray<float>& aCurve) override
179   {
180     mCurve.SwapElements(aCurve);
181   }
182 
SetInt32Parameter(uint32_t aIndex,int32_t aValue)183   void SetInt32Parameter(uint32_t aIndex, int32_t aValue) 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   }
193 
194   template <uint32_t blocks>
ProcessCurve(const float * aInputBuffer,float * aOutputBuffer)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 using an amplitude range of [-1, 1] and
200       // performing a linear interpolation of the neighbor values.
201       float index = (mCurve.Length() - 1) * (aInputBuffer[j] + 1.0f) / 2.0f;
202       if (index < 0.0f) {
203         aOutputBuffer[j] = mCurve[0];
204       } else {
205         int32_t indexLower = index;
206         if (static_cast<uint32_t>(indexLower) >= mCurve.Length() - 1) {
207           aOutputBuffer[j] = mCurve[mCurve.Length() - 1];
208         } else {
209           uint32_t indexHigher = indexLower + 1;
210           float interpolationFactor = index - indexLower;
211           aOutputBuffer[j] = (1.0f - interpolationFactor) * mCurve[indexLower] +
212                                      interpolationFactor  * mCurve[indexHigher];
213         }
214       }
215     }
216   }
217 
ProcessBlock(AudioNodeStream * aStream,GraphTime aFrom,const AudioBlock & aInput,AudioBlock * aOutput,bool * aFinished)218   void ProcessBlock(AudioNodeStream* aStream,
219                     GraphTime aFrom,
220                     const AudioBlock& aInput,
221                     AudioBlock* aOutput,
222                     bool* aFinished) override
223   {
224     uint32_t channelCount = aInput.ChannelCount();
225     if (!mCurve.Length()) {
226       // Optimize the case where we don't have a curve buffer
227       *aOutput = aInput;
228       return;
229     }
230 
231     // If the input is null, check to see if non-null output will be produced
232     bool nullInput = false;
233     if (channelCount == 0) {
234       float index = (mCurve.Length() - 1) * 0.5;
235       uint32_t indexLower = index;
236       uint32_t indexHigher = indexLower + 1;
237       float interpolationFactor = index - indexLower;
238       if ((1.0f - interpolationFactor) * mCurve[indexLower] +
239           interpolationFactor * mCurve[indexHigher] == 0.0) {
240         *aOutput = aInput;
241         return;
242       } else {
243         nullInput = true;
244         channelCount = 1;
245       }
246     }
247 
248     aOutput->AllocateChannels(channelCount);
249     for (uint32_t i = 0; i < channelCount; ++i) {
250       const float* inputSamples;
251       float scaledInput[WEBAUDIO_BLOCK_SIZE + 4];
252       float* alignedScaledInput = ALIGNED16(scaledInput);
253       ASSERT_ALIGNED16(alignedScaledInput);
254       if (!nullInput) {
255         if (aInput.mVolume != 1.0f) {
256           AudioBlockCopyChannelWithScale(
257               static_cast<const float*>(aInput.mChannelData[i]),
258                                         aInput.mVolume,
259                                         alignedScaledInput);
260           inputSamples = alignedScaledInput;
261         } else {
262           inputSamples = static_cast<const float*>(aInput.mChannelData[i]);
263         }
264       } else {
265         PodZero(alignedScaledInput, WEBAUDIO_BLOCK_SIZE);
266         inputSamples = alignedScaledInput;
267       }
268       float* outputBuffer = aOutput->ChannelFloatsForWrite(i);
269       float* sampleBuffer;
270 
271       switch (mType) {
272       case OverSampleType::None:
273         mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::None);
274         ProcessCurve<1>(inputSamples, outputBuffer);
275         break;
276       case OverSampleType::_2x:
277         mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_2x);
278         sampleBuffer = mResampler.UpSample(i, inputSamples, 2);
279         ProcessCurve<2>(sampleBuffer, sampleBuffer);
280         mResampler.DownSample(i, outputBuffer, 2);
281         break;
282       case OverSampleType::_4x:
283         mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_4x);
284         sampleBuffer = mResampler.UpSample(i, inputSamples, 4);
285         ProcessCurve<4>(sampleBuffer, sampleBuffer);
286         mResampler.DownSample(i, outputBuffer, 4);
287         break;
288       default:
289         NS_NOTREACHED("We should never reach here");
290       }
291     }
292   }
293 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const294   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
295   {
296     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
297     amount += mCurve.ShallowSizeOfExcludingThis(aMallocSizeOf);
298     amount += mResampler.SizeOfExcludingThis(aMallocSizeOf);
299     return amount;
300   }
301 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const302   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
303   {
304     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
305   }
306 
307 private:
308   nsTArray<float> mCurve;
309   OverSampleType mType;
310   Resampler mResampler;
311 };
312 
WaveShaperNode(AudioContext * aContext)313 WaveShaperNode::WaveShaperNode(AudioContext* aContext)
314   : AudioNode(aContext,
315               2,
316               ChannelCountMode::Max,
317               ChannelInterpretation::Speakers)
318   , mCurve(nullptr)
319   , mType(OverSampleType::None)
320 {
321   mozilla::HoldJSObjects(this);
322 
323   WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this);
324   mStream = AudioNodeStream::Create(aContext, engine,
325                                     AudioNodeStream::NO_STREAM_FLAGS,
326                                     aContext->Graph());
327 }
328 
~WaveShaperNode()329 WaveShaperNode::~WaveShaperNode()
330 {
331   ClearCurve();
332 }
333 
334 void
ClearCurve()335 WaveShaperNode::ClearCurve()
336 {
337   mCurve = nullptr;
338   mozilla::DropJSObjects(this);
339 }
340 
341 JSObject*
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)342 WaveShaperNode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
343 {
344   return WaveShaperNodeBinding::Wrap(aCx, this, aGivenProto);
345 }
346 
347 void
SetCurve(const Nullable<Float32Array> & aCurve,ErrorResult & aRv)348 WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve, ErrorResult& aRv)
349 {
350   nsTArray<float> curve;
351   if (!aCurve.IsNull()) {
352     const Float32Array& floats = aCurve.Value();
353 
354     floats.ComputeLengthAndData();
355     if (floats.IsShared()) {
356       // Throw if the object is mapping shared memory (must opt in).
357       aRv.ThrowTypeError<MSG_TYPEDARRAY_IS_SHARED>(NS_LITERAL_STRING("Argument of WaveShaperNode.setCurve"));
358       return;
359     }
360 
361     uint32_t argLength = floats.Length();
362     if (argLength < 2) {
363       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
364       return;
365     }
366 
367     if (!curve.SetLength(argLength, fallible)) {
368       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
369       return;
370     }
371 
372     PodCopy(curve.Elements(), floats.Data(), floats.Length());
373 
374     mCurve = floats.Obj();
375   } else {
376     mCurve = nullptr;
377   }
378 
379   AudioNodeStream* ns = mStream;
380   MOZ_ASSERT(ns, "Why don't we have a stream here?");
381   ns->SetRawArrayData(curve);
382 }
383 
384 void
SetOversample(OverSampleType aType)385 WaveShaperNode::SetOversample(OverSampleType aType)
386 {
387   mType = aType;
388   SendInt32ParameterToStream(WaveShaperNodeEngine::TYPE, static_cast<int32_t>(aType));
389 }
390 
391 } // namespace dom
392 } // namespace mozilla
393