/src/mozilla-central/dom/media/webaudio/AudioNodeEngine.cpp
Line | Count | Source (jump to first uncovered line) |
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 "AudioNodeEngine.h" |
8 | | |
9 | | #include "mozilla/AbstractThread.h" |
10 | | #ifdef BUILD_ARM_NEON |
11 | | #include "mozilla/arm.h" |
12 | | #include "AudioNodeEngineNEON.h" |
13 | | #endif |
14 | | #ifdef USE_SSE2 |
15 | | #include "mozilla/SSE.h" |
16 | | #include "AlignmentUtils.h" |
17 | | #include "AudioNodeEngineSSE2.h" |
18 | | #endif |
19 | | #include "AudioBlock.h" |
20 | | |
21 | | namespace mozilla { |
22 | | |
23 | | already_AddRefed<ThreadSharedFloatArrayBufferList> |
24 | | ThreadSharedFloatArrayBufferList::Create(uint32_t aChannelCount, |
25 | | size_t aLength, |
26 | | const mozilla::fallible_t&) |
27 | 0 | { |
28 | 0 | RefPtr<ThreadSharedFloatArrayBufferList> buffer = |
29 | 0 | new ThreadSharedFloatArrayBufferList(aChannelCount); |
30 | 0 |
|
31 | 0 | for (uint32_t i = 0; i < aChannelCount; ++i) { |
32 | 0 | float* channelData = js_pod_malloc<float>(aLength); |
33 | 0 | if (!channelData) { |
34 | 0 | return nullptr; |
35 | 0 | } |
36 | 0 | |
37 | 0 | buffer->SetData(i, channelData, js_free, channelData); |
38 | 0 | } |
39 | 0 |
|
40 | 0 | return buffer.forget(); |
41 | 0 | } |
42 | | |
43 | | void |
44 | | WriteZeroesToAudioBlock(AudioBlock* aChunk, |
45 | | uint32_t aStart, uint32_t aLength) |
46 | 0 | { |
47 | 0 | MOZ_ASSERT(aStart + aLength <= WEBAUDIO_BLOCK_SIZE); |
48 | 0 | MOZ_ASSERT(!aChunk->IsNull(), "You should pass a non-null chunk"); |
49 | 0 | if (aLength == 0) |
50 | 0 | return; |
51 | 0 | |
52 | 0 | for (uint32_t i = 0; i < aChunk->ChannelCount(); ++i) { |
53 | 0 | PodZero(aChunk->ChannelFloatsForWrite(i) + aStart, aLength); |
54 | 0 | } |
55 | 0 | } |
56 | | |
57 | | void AudioBufferCopyWithScale(const float* aInput, |
58 | | float aScale, |
59 | | float* aOutput, |
60 | | uint32_t aSize) |
61 | 0 | { |
62 | 0 | if (aScale == 1.0f) { |
63 | 0 | PodCopy(aOutput, aInput, aSize); |
64 | 0 | } else { |
65 | 0 | for (uint32_t i = 0; i < aSize; ++i) { |
66 | 0 | aOutput[i] = aInput[i]*aScale; |
67 | 0 | } |
68 | 0 | } |
69 | 0 | } |
70 | | |
71 | | void AudioBufferAddWithScale(const float* aInput, |
72 | | float aScale, |
73 | | float* aOutput, |
74 | | uint32_t aSize) |
75 | 0 | { |
76 | | #ifdef BUILD_ARM_NEON |
77 | | if (mozilla::supports_neon()) { |
78 | | AudioBufferAddWithScale_NEON(aInput, aScale, aOutput, aSize); |
79 | | return; |
80 | | } |
81 | | #endif |
82 | |
|
83 | 0 | #ifdef USE_SSE2 |
84 | 0 | if (mozilla::supports_sse2()) { |
85 | 0 | if (aScale == 1.0f) { |
86 | 0 | while (aSize && (!IS_ALIGNED16(aInput) || !IS_ALIGNED16(aOutput))) { |
87 | 0 | *aOutput += *aInput; |
88 | 0 | ++aOutput; |
89 | 0 | ++aInput; |
90 | 0 | --aSize; |
91 | 0 | } |
92 | 0 | } else { |
93 | 0 | while (aSize && (!IS_ALIGNED16(aInput) || !IS_ALIGNED16(aOutput))) { |
94 | 0 | *aOutput += *aInput*aScale; |
95 | 0 | ++aOutput; |
96 | 0 | ++aInput; |
97 | 0 | --aSize; |
98 | 0 | } |
99 | 0 | } |
100 | 0 |
|
101 | 0 | // we need to round aSize down to the nearest multiple of 16 |
102 | 0 | uint32_t alignedSize = aSize & ~0x0F; |
103 | 0 | if (alignedSize > 0) { |
104 | 0 | AudioBufferAddWithScale_SSE(aInput, aScale, aOutput, alignedSize); |
105 | 0 |
|
106 | 0 | // adjust parameters for use with scalar operations below |
107 | 0 | aInput += alignedSize; |
108 | 0 | aOutput += alignedSize; |
109 | 0 | aSize -= alignedSize; |
110 | 0 | } |
111 | 0 | } |
112 | 0 | #endif |
113 | 0 |
|
114 | 0 | if (aScale == 1.0f) { |
115 | 0 | for (uint32_t i = 0; i < aSize; ++i) { |
116 | 0 | aOutput[i] += aInput[i]; |
117 | 0 | } |
118 | 0 | } else { |
119 | 0 | for (uint32_t i = 0; i < aSize; ++i) { |
120 | 0 | aOutput[i] += aInput[i]*aScale; |
121 | 0 | } |
122 | 0 | } |
123 | 0 | } |
124 | | |
125 | | void |
126 | | AudioBlockAddChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE], |
127 | | float aScale, |
128 | | float aOutput[WEBAUDIO_BLOCK_SIZE]) |
129 | 0 | { |
130 | 0 | AudioBufferAddWithScale(aInput, aScale, aOutput, WEBAUDIO_BLOCK_SIZE); |
131 | 0 | } |
132 | | |
133 | | void |
134 | | AudioBlockCopyChannelWithScale(const float* aInput, |
135 | | float aScale, |
136 | | float* aOutput) |
137 | 0 | { |
138 | 0 | if (aScale == 1.0f) { |
139 | 0 | memcpy(aOutput, aInput, WEBAUDIO_BLOCK_SIZE*sizeof(float)); |
140 | 0 | } else { |
141 | | #ifdef BUILD_ARM_NEON |
142 | | if (mozilla::supports_neon()) { |
143 | | AudioBlockCopyChannelWithScale_NEON(aInput, aScale, aOutput); |
144 | | return; |
145 | | } |
146 | | #endif |
147 | |
|
148 | 0 | #ifdef USE_SSE2 |
149 | 0 | if (mozilla::supports_sse2()) { |
150 | 0 | AudioBlockCopyChannelWithScale_SSE(aInput, aScale, aOutput); |
151 | 0 | return; |
152 | 0 | } |
153 | 0 | #endif |
154 | 0 | |
155 | 0 | for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { |
156 | 0 | aOutput[i] = aInput[i]*aScale; |
157 | 0 | } |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | | void |
162 | | BufferComplexMultiply(const float* aInput, |
163 | | const float* aScale, |
164 | | float* aOutput, |
165 | | uint32_t aSize) |
166 | 0 | { |
167 | 0 |
|
168 | 0 | #ifdef USE_SSE2 |
169 | 0 | if (mozilla::supports_sse()) { |
170 | 0 | BufferComplexMultiply_SSE(aInput, aScale, aOutput, aSize); |
171 | 0 | return; |
172 | 0 | } |
173 | 0 | #endif |
174 | 0 | |
175 | 0 | for (uint32_t i = 0; i < aSize * 2; i += 2) { |
176 | 0 | float real1 = aInput[i]; |
177 | 0 | float imag1 = aInput[i + 1]; |
178 | 0 | float real2 = aScale[i]; |
179 | 0 | float imag2 = aScale[i + 1]; |
180 | 0 | float realResult = real1 * real2 - imag1 * imag2; |
181 | 0 | float imagResult = real1 * imag2 + imag1 * real2; |
182 | 0 | aOutput[i] = realResult; |
183 | 0 | aOutput[i + 1] = imagResult; |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | float |
188 | | AudioBufferPeakValue(const float *aInput, uint32_t aSize) |
189 | 0 | { |
190 | 0 | float max = 0.0f; |
191 | 0 | for (uint32_t i = 0; i < aSize; i++) { |
192 | 0 | float mag = fabs(aInput[i]); |
193 | 0 | if (mag > max) { |
194 | 0 | max = mag; |
195 | 0 | } |
196 | 0 | } |
197 | 0 | return max; |
198 | 0 | } |
199 | | |
200 | | void |
201 | | AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE], |
202 | | const float aScale[WEBAUDIO_BLOCK_SIZE], |
203 | | float aOutput[WEBAUDIO_BLOCK_SIZE]) |
204 | 0 | { |
205 | | #ifdef BUILD_ARM_NEON |
206 | | if (mozilla::supports_neon()) { |
207 | | AudioBlockCopyChannelWithScale_NEON(aInput, aScale, aOutput); |
208 | | return; |
209 | | } |
210 | | #endif |
211 | |
|
212 | 0 | #ifdef USE_SSE2 |
213 | 0 | if (mozilla::supports_sse2()) { |
214 | 0 | AudioBlockCopyChannelWithScale_SSE(aInput, aScale, aOutput); |
215 | 0 | return; |
216 | 0 | } |
217 | 0 | #endif |
218 | 0 | |
219 | 0 | for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { |
220 | 0 | aOutput[i] = aInput[i]*aScale[i]; |
221 | 0 | } |
222 | 0 | } |
223 | | |
224 | | void |
225 | | AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE], |
226 | | float aScale) |
227 | 0 | { |
228 | 0 | AudioBufferInPlaceScale(aBlock, aScale, WEBAUDIO_BLOCK_SIZE); |
229 | 0 | } |
230 | | |
231 | | void |
232 | | AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE], |
233 | | float aScale[WEBAUDIO_BLOCK_SIZE]) |
234 | 0 | { |
235 | 0 | AudioBufferInPlaceScale(aBlock, aScale, WEBAUDIO_BLOCK_SIZE); |
236 | 0 | } |
237 | | |
238 | | void |
239 | | AudioBufferInPlaceScale(float* aBlock, |
240 | | float aScale, |
241 | | uint32_t aSize) |
242 | 0 | { |
243 | 0 | if (aScale == 1.0f) { |
244 | 0 | return; |
245 | 0 | } |
246 | | #ifdef BUILD_ARM_NEON |
247 | | if (mozilla::supports_neon()) { |
248 | | AudioBufferInPlaceScale_NEON(aBlock, aScale, aSize); |
249 | | return; |
250 | | } |
251 | | #endif |
252 | | |
253 | 0 | #ifdef USE_SSE2 |
254 | 0 | if (mozilla::supports_sse2()) { |
255 | 0 | AudioBufferInPlaceScale_SSE(aBlock, aScale, aSize); |
256 | 0 | return; |
257 | 0 | } |
258 | 0 | #endif |
259 | 0 | |
260 | 0 | for (uint32_t i = 0; i < aSize; ++i) { |
261 | 0 | *aBlock++ *= aScale; |
262 | 0 | } |
263 | 0 | } |
264 | | |
265 | | void |
266 | | AudioBufferInPlaceScale(float* aBlock, |
267 | | float* aScale, |
268 | | uint32_t aSize) |
269 | 0 | { |
270 | | #ifdef BUILD_ARM_NEON |
271 | | if (mozilla::supports_neon()) { |
272 | | AudioBufferInPlaceScale_NEON(aBlock, aScale, aSize); |
273 | | return; |
274 | | } |
275 | | #endif |
276 | |
|
277 | 0 | #ifdef USE_SSE2 |
278 | 0 | if (mozilla::supports_sse2()) { |
279 | 0 | AudioBufferInPlaceScale_SSE(aBlock, aScale, aSize); |
280 | 0 | return; |
281 | 0 | } |
282 | 0 | #endif |
283 | 0 | |
284 | 0 | for (uint32_t i = 0; i < aSize; ++i) { |
285 | 0 | *aBlock++ *= *aScale++; |
286 | 0 | } |
287 | 0 | } |
288 | | |
289 | | void |
290 | | AudioBlockPanMonoToStereo(const float aInput[WEBAUDIO_BLOCK_SIZE], |
291 | | float aGainL[WEBAUDIO_BLOCK_SIZE], |
292 | | float aGainR[WEBAUDIO_BLOCK_SIZE], |
293 | | float aOutputL[WEBAUDIO_BLOCK_SIZE], |
294 | | float aOutputR[WEBAUDIO_BLOCK_SIZE]) |
295 | 0 | { |
296 | 0 | AudioBlockCopyChannelWithScale(aInput, aGainL, aOutputL); |
297 | 0 | AudioBlockCopyChannelWithScale(aInput, aGainR, aOutputR); |
298 | 0 | } |
299 | | |
300 | | void |
301 | | AudioBlockPanMonoToStereo(const float aInput[WEBAUDIO_BLOCK_SIZE], |
302 | | float aGainL, float aGainR, |
303 | | float aOutputL[WEBAUDIO_BLOCK_SIZE], |
304 | | float aOutputR[WEBAUDIO_BLOCK_SIZE]) |
305 | 0 | { |
306 | 0 | AudioBlockCopyChannelWithScale(aInput, aGainL, aOutputL); |
307 | 0 | AudioBlockCopyChannelWithScale(aInput, aGainR, aOutputR); |
308 | 0 | } |
309 | | |
310 | | void |
311 | | AudioBlockPanStereoToStereo(const float aInputL[WEBAUDIO_BLOCK_SIZE], |
312 | | const float aInputR[WEBAUDIO_BLOCK_SIZE], |
313 | | float aGainL, float aGainR, bool aIsOnTheLeft, |
314 | | float aOutputL[WEBAUDIO_BLOCK_SIZE], |
315 | | float aOutputR[WEBAUDIO_BLOCK_SIZE]) |
316 | 0 | { |
317 | | #ifdef BUILD_ARM_NEON |
318 | | if (mozilla::supports_neon()) { |
319 | | AudioBlockPanStereoToStereo_NEON(aInputL, aInputR, |
320 | | aGainL, aGainR, aIsOnTheLeft, |
321 | | aOutputL, aOutputR); |
322 | | return; |
323 | | } |
324 | | #endif |
325 | |
|
326 | 0 | #ifdef USE_SSE2 |
327 | 0 | if (mozilla::supports_sse2()) { |
328 | 0 | AudioBlockPanStereoToStereo_SSE(aInputL, aInputR, |
329 | 0 | aGainL, aGainR, aIsOnTheLeft, |
330 | 0 | aOutputL, aOutputR); |
331 | 0 | return; |
332 | 0 | } |
333 | 0 | #endif |
334 | 0 | |
335 | 0 | uint32_t i; |
336 | 0 |
|
337 | 0 | if (aIsOnTheLeft) { |
338 | 0 | for (i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { |
339 | 0 | aOutputL[i] = aInputL[i] + aInputR[i] * aGainL; |
340 | 0 | aOutputR[i] = aInputR[i] * aGainR; |
341 | 0 | } |
342 | 0 | } else { |
343 | 0 | for (i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { |
344 | 0 | aOutputL[i] = aInputL[i] * aGainL; |
345 | 0 | aOutputR[i] = aInputR[i] + aInputL[i] * aGainR; |
346 | 0 | } |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | | void |
351 | | AudioBlockPanStereoToStereo(const float aInputL[WEBAUDIO_BLOCK_SIZE], |
352 | | const float aInputR[WEBAUDIO_BLOCK_SIZE], |
353 | | float aGainL[WEBAUDIO_BLOCK_SIZE], |
354 | | float aGainR[WEBAUDIO_BLOCK_SIZE], |
355 | | bool aIsOnTheLeft[WEBAUDIO_BLOCK_SIZE], |
356 | | float aOutputL[WEBAUDIO_BLOCK_SIZE], |
357 | | float aOutputR[WEBAUDIO_BLOCK_SIZE]) |
358 | 0 | { |
359 | | #ifdef BUILD_ARM_NEON |
360 | | if (mozilla::supports_neon()) { |
361 | | AudioBlockPanStereoToStereo_NEON(aInputL, aInputR, |
362 | | aGainL, aGainR, aIsOnTheLeft, |
363 | | aOutputL, aOutputR); |
364 | | return; |
365 | | } |
366 | | #endif |
367 | |
|
368 | 0 | uint32_t i; |
369 | 0 | for (i = 0; i < WEBAUDIO_BLOCK_SIZE; i++) { |
370 | 0 | if (aIsOnTheLeft[i]) { |
371 | 0 | aOutputL[i] = aInputL[i] + aInputR[i] * aGainL[i]; |
372 | 0 | aOutputR[i] = aInputR[i] * aGainR[i]; |
373 | 0 | } else { |
374 | 0 | aOutputL[i] = aInputL[i] * aGainL[i]; |
375 | 0 | aOutputR[i] = aInputR[i] + aInputL[i] * aGainR[i]; |
376 | 0 | } |
377 | 0 | } |
378 | 0 | } |
379 | | |
380 | | float |
381 | | AudioBufferSumOfSquares(const float* aInput, uint32_t aLength) |
382 | 0 | { |
383 | 0 | float sum = 0.0f; |
384 | 0 |
|
385 | 0 | #ifdef USE_SSE2 |
386 | 0 | if (mozilla::supports_sse()) { |
387 | 0 | const float* alignedInput = ALIGNED16(aInput); |
388 | 0 |
|
389 | 0 | // use scalar operations for any unaligned data at the beginning |
390 | 0 | while (aInput != alignedInput) { |
391 | 0 | if (!aLength) { |
392 | 0 | return sum; |
393 | 0 | } |
394 | 0 | sum += *aInput * *aInput; |
395 | 0 | ++aInput; |
396 | 0 | --aLength; |
397 | 0 | } |
398 | 0 |
|
399 | 0 | uint32_t vLength = (aLength >> 4) << 4; |
400 | 0 | sum += AudioBufferSumOfSquares_SSE(alignedInput, vLength); |
401 | 0 |
|
402 | 0 | // adjust aInput and aLength to use scalar operations for any |
403 | 0 | // remaining values |
404 | 0 | aInput = alignedInput + vLength; |
405 | 0 | aLength -= vLength; |
406 | 0 | } |
407 | 0 | #endif |
408 | 0 |
|
409 | 0 | while (aLength--) { |
410 | 0 | sum += *aInput * *aInput; |
411 | 0 | ++aInput; |
412 | 0 | } |
413 | 0 | return sum; |
414 | 0 | } |
415 | | |
416 | | AudioNodeEngine::AudioNodeEngine(dom::AudioNode* aNode) |
417 | | : mNode(aNode) |
418 | | , mNodeType(aNode ? aNode->NodeType() : nullptr) |
419 | | , mInputCount(aNode ? aNode->NumberOfInputs() : 1) |
420 | | , mOutputCount(aNode ? aNode->NumberOfOutputs() : 0) |
421 | | , mAbstractMainThread( |
422 | | aNode ? aNode->AbstractMainThread() : AbstractThread::MainThread()) |
423 | | { |
424 | | MOZ_ASSERT(NS_IsMainThread()); |
425 | | MOZ_COUNT_CTOR(AudioNodeEngine); |
426 | | } |
427 | | |
428 | | void |
429 | | AudioNodeEngine::ProcessBlock(AudioNodeStream* aStream, |
430 | | GraphTime aFrom, |
431 | | const AudioBlock& aInput, |
432 | | AudioBlock* aOutput, |
433 | | bool* aFinished) |
434 | 0 | { |
435 | 0 | MOZ_ASSERT(mInputCount <= 1 && mOutputCount <= 1); |
436 | 0 | *aOutput = aInput; |
437 | 0 | } |
438 | | |
439 | | void |
440 | | AudioNodeEngine::ProcessBlocksOnPorts(AudioNodeStream* aStream, |
441 | | const OutputChunks& aInput, |
442 | | OutputChunks& aOutput, |
443 | | bool* aFinished) |
444 | 0 | { |
445 | 0 | MOZ_ASSERT(mInputCount > 1 || mOutputCount > 1); |
446 | 0 | // Only produce one output port, and drop all other input ports. |
447 | 0 | aOutput[0] = aInput[0]; |
448 | 0 | } |
449 | | |
450 | | } // namespace mozilla |