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