|
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 "AudioNode.h" |
|
10 #include "AudioNodeEngine.h" |
|
11 #include "AudioNodeStream.h" |
|
12 #include "mozilla/PodOperations.h" |
|
13 #include "speex/speex_resampler.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 |
|
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 |
|
53 { |
|
54 public: |
|
55 Resampler() |
|
56 : mType(OverSampleType::None) |
|
57 , mUpSampler(nullptr) |
|
58 , mDownSampler(nullptr) |
|
59 , mChannels(0) |
|
60 , mSampleRate(0) |
|
61 { |
|
62 } |
|
63 |
|
64 ~Resampler() |
|
65 { |
|
66 Destroy(); |
|
67 } |
|
68 |
|
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_DEFAULT, |
|
92 nullptr); |
|
93 mDownSampler = speex_resampler_init(aChannels, |
|
94 aSampleRate * ValueOf(aType), |
|
95 aSampleRate, |
|
96 SPEEX_RESAMPLER_QUALITY_DEFAULT, |
|
97 nullptr); |
|
98 mBuffer.SetLength(WEBAUDIO_BLOCK_SIZE*ValueOf(aType)); |
|
99 } |
|
100 |
|
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 |
|
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 |
|
133 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.SizeOfExcludingThis(aMallocSizeOf); |
|
140 return amount; |
|
141 } |
|
142 |
|
143 private: |
|
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 : public AudioNodeEngine |
|
166 { |
|
167 public: |
|
168 explicit WaveShaperNodeEngine(AudioNode* aNode) |
|
169 : AudioNodeEngine(aNode) |
|
170 , mType(OverSampleType::None) |
|
171 { |
|
172 } |
|
173 |
|
174 enum Parameteres { |
|
175 TYPE |
|
176 }; |
|
177 |
|
178 virtual void SetRawArrayData(nsTArray<float>& aCurve) MOZ_OVERRIDE |
|
179 { |
|
180 mCurve.SwapElements(aCurve); |
|
181 } |
|
182 |
|
183 virtual void SetInt32Parameter(uint32_t aIndex, int32_t aValue) MOZ_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> |
|
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 clamping the amplitude to [-1, 1] and |
|
200 // performing a linear interpolation of the neighbor values. |
|
201 float index = std::max(0.0f, std::min(float(mCurve.Length() - 1), |
|
202 mCurve.Length() * (aInputBuffer[j] + 1) / 2)); |
|
203 uint32_t indexLower = uint32_t(index); |
|
204 uint32_t indexHigher = uint32_t(index + 1.0f); |
|
205 if (indexHigher == mCurve.Length()) { |
|
206 aOutputBuffer[j] = mCurve[indexLower]; |
|
207 } else { |
|
208 float interpolationFactor = index - indexLower; |
|
209 aOutputBuffer[j] = (1.0f - interpolationFactor) * mCurve[indexLower] + |
|
210 interpolationFactor * mCurve[indexHigher]; |
|
211 } |
|
212 } |
|
213 } |
|
214 |
|
215 virtual void ProcessBlock(AudioNodeStream* aStream, |
|
216 const AudioChunk& aInput, |
|
217 AudioChunk* aOutput, |
|
218 bool* aFinished) |
|
219 { |
|
220 uint32_t channelCount = aInput.mChannelData.Length(); |
|
221 if (!mCurve.Length() || !channelCount) { |
|
222 // Optimize the case where we don't have a curve buffer, |
|
223 // or the input is null. |
|
224 *aOutput = aInput; |
|
225 return; |
|
226 } |
|
227 |
|
228 AllocateAudioBlock(channelCount, aOutput); |
|
229 for (uint32_t i = 0; i < channelCount; ++i) { |
|
230 const float* inputBuffer = static_cast<const float*>(aInput.mChannelData[i]); |
|
231 float* outputBuffer = const_cast<float*> (static_cast<const float*>(aOutput->mChannelData[i])); |
|
232 float* sampleBuffer; |
|
233 |
|
234 switch (mType) { |
|
235 case OverSampleType::None: |
|
236 mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::None); |
|
237 ProcessCurve<1>(inputBuffer, outputBuffer); |
|
238 break; |
|
239 case OverSampleType::_2x: |
|
240 mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_2x); |
|
241 sampleBuffer = mResampler.UpSample(i, inputBuffer, 2); |
|
242 ProcessCurve<2>(sampleBuffer, sampleBuffer); |
|
243 mResampler.DownSample(i, outputBuffer, 2); |
|
244 break; |
|
245 case OverSampleType::_4x: |
|
246 mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_4x); |
|
247 sampleBuffer = mResampler.UpSample(i, inputBuffer, 4); |
|
248 ProcessCurve<4>(sampleBuffer, sampleBuffer); |
|
249 mResampler.DownSample(i, outputBuffer, 4); |
|
250 break; |
|
251 default: |
|
252 NS_NOTREACHED("We should never reach here"); |
|
253 } |
|
254 } |
|
255 } |
|
256 |
|
257 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE |
|
258 { |
|
259 size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); |
|
260 amount += mCurve.SizeOfExcludingThis(aMallocSizeOf); |
|
261 amount += mResampler.SizeOfExcludingThis(aMallocSizeOf); |
|
262 return amount; |
|
263 } |
|
264 |
|
265 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE |
|
266 { |
|
267 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
|
268 } |
|
269 |
|
270 private: |
|
271 nsTArray<float> mCurve; |
|
272 OverSampleType mType; |
|
273 Resampler mResampler; |
|
274 }; |
|
275 |
|
276 WaveShaperNode::WaveShaperNode(AudioContext* aContext) |
|
277 : AudioNode(aContext, |
|
278 2, |
|
279 ChannelCountMode::Max, |
|
280 ChannelInterpretation::Speakers) |
|
281 , mCurve(nullptr) |
|
282 , mType(OverSampleType::None) |
|
283 { |
|
284 mozilla::HoldJSObjects(this); |
|
285 |
|
286 WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this); |
|
287 mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); |
|
288 } |
|
289 |
|
290 WaveShaperNode::~WaveShaperNode() |
|
291 { |
|
292 ClearCurve(); |
|
293 } |
|
294 |
|
295 void |
|
296 WaveShaperNode::ClearCurve() |
|
297 { |
|
298 mCurve = nullptr; |
|
299 mozilla::DropJSObjects(this); |
|
300 } |
|
301 |
|
302 JSObject* |
|
303 WaveShaperNode::WrapObject(JSContext *aCx) |
|
304 { |
|
305 return WaveShaperNodeBinding::Wrap(aCx, this); |
|
306 } |
|
307 |
|
308 void |
|
309 WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve) |
|
310 { |
|
311 nsTArray<float> curve; |
|
312 if (!aCurve.IsNull()) { |
|
313 const Float32Array& floats = aCurve.Value(); |
|
314 |
|
315 mCurve = floats.Obj(); |
|
316 |
|
317 floats.ComputeLengthAndData(); |
|
318 |
|
319 curve.SetLength(floats.Length()); |
|
320 PodCopy(curve.Elements(), floats.Data(), floats.Length()); |
|
321 } else { |
|
322 mCurve = nullptr; |
|
323 } |
|
324 |
|
325 AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get()); |
|
326 MOZ_ASSERT(ns, "Why don't we have a stream here?"); |
|
327 ns->SetRawArrayData(curve); |
|
328 } |
|
329 |
|
330 void |
|
331 WaveShaperNode::SetOversample(OverSampleType aType) |
|
332 { |
|
333 mType = aType; |
|
334 SendInt32ParameterToStream(WaveShaperNodeEngine::TYPE, static_cast<int32_t>(aType)); |
|
335 } |
|
336 |
|
337 } |
|
338 } |