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