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