|
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 #ifndef MOZILLA_AUDIONODEENGINE_H_ |
|
7 #define MOZILLA_AUDIONODEENGINE_H_ |
|
8 |
|
9 #include "AudioSegment.h" |
|
10 #include "mozilla/dom/AudioNode.h" |
|
11 #include "mozilla/MemoryReporting.h" |
|
12 #include "mozilla/Mutex.h" |
|
13 |
|
14 namespace mozilla { |
|
15 |
|
16 namespace dom { |
|
17 struct ThreeDPoint; |
|
18 class AudioParamTimeline; |
|
19 class DelayNodeEngine; |
|
20 } |
|
21 |
|
22 class AudioNodeStream; |
|
23 |
|
24 /** |
|
25 * This class holds onto a set of immutable channel buffers. The storage |
|
26 * for the buffers must be malloced, but the buffer pointers and the malloc |
|
27 * pointers can be different (e.g. if the buffers are contained inside |
|
28 * some malloced object). |
|
29 */ |
|
30 class ThreadSharedFloatArrayBufferList : public ThreadSharedObject { |
|
31 public: |
|
32 /** |
|
33 * Construct with null data. |
|
34 */ |
|
35 ThreadSharedFloatArrayBufferList(uint32_t aCount) |
|
36 { |
|
37 mContents.SetLength(aCount); |
|
38 } |
|
39 |
|
40 struct Storage { |
|
41 Storage() |
|
42 { |
|
43 mDataToFree = nullptr; |
|
44 mSampleData = nullptr; |
|
45 } |
|
46 ~Storage() { free(mDataToFree); } |
|
47 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
|
48 { |
|
49 // NB: mSampleData might not be owned, if it is it just points to |
|
50 // mDataToFree. |
|
51 return aMallocSizeOf(mDataToFree); |
|
52 } |
|
53 void* mDataToFree; |
|
54 const float* mSampleData; |
|
55 }; |
|
56 |
|
57 /** |
|
58 * This can be called on any thread. |
|
59 */ |
|
60 uint32_t GetChannels() const { return mContents.Length(); } |
|
61 /** |
|
62 * This can be called on any thread. |
|
63 */ |
|
64 const float* GetData(uint32_t aIndex) const { return mContents[aIndex].mSampleData; } |
|
65 |
|
66 /** |
|
67 * Call this only during initialization, before the object is handed to |
|
68 * any other thread. |
|
69 */ |
|
70 void SetData(uint32_t aIndex, void* aDataToFree, const float* aData) |
|
71 { |
|
72 Storage* s = &mContents[aIndex]; |
|
73 free(s->mDataToFree); |
|
74 s->mDataToFree = aDataToFree; |
|
75 s->mSampleData = aData; |
|
76 } |
|
77 |
|
78 /** |
|
79 * Put this object into an error state where there are no channels. |
|
80 */ |
|
81 void Clear() { mContents.Clear(); } |
|
82 |
|
83 virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE |
|
84 { |
|
85 size_t amount = ThreadSharedObject::SizeOfExcludingThis(aMallocSizeOf); |
|
86 amount += mContents.SizeOfExcludingThis(aMallocSizeOf); |
|
87 for (size_t i = 0; i < mContents.Length(); i++) { |
|
88 amount += mContents[i].SizeOfExcludingThis(aMallocSizeOf); |
|
89 } |
|
90 |
|
91 return amount; |
|
92 } |
|
93 |
|
94 private: |
|
95 AutoFallibleTArray<Storage,2> mContents; |
|
96 }; |
|
97 |
|
98 /** |
|
99 * Allocates an AudioChunk with fresh buffers of WEBAUDIO_BLOCK_SIZE float samples. |
|
100 * AudioChunk::mChannelData's entries can be cast to float* for writing. |
|
101 */ |
|
102 void AllocateAudioBlock(uint32_t aChannelCount, AudioChunk* aChunk); |
|
103 |
|
104 /** |
|
105 * aChunk must have been allocated by AllocateAudioBlock. |
|
106 */ |
|
107 void WriteZeroesToAudioBlock(AudioChunk* aChunk, uint32_t aStart, uint32_t aLength); |
|
108 |
|
109 /** |
|
110 * Copy with scale. aScale == 1.0f should be optimized. |
|
111 */ |
|
112 void AudioBufferCopyWithScale(const float* aInput, |
|
113 float aScale, |
|
114 float* aOutput, |
|
115 uint32_t aSize); |
|
116 |
|
117 /** |
|
118 * Pointwise multiply-add operation. aScale == 1.0f should be optimized. |
|
119 */ |
|
120 void AudioBufferAddWithScale(const float* aInput, |
|
121 float aScale, |
|
122 float* aOutput, |
|
123 uint32_t aSize); |
|
124 |
|
125 /** |
|
126 * Pointwise multiply-add operation. aScale == 1.0f should be optimized. |
|
127 */ |
|
128 void AudioBlockAddChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE], |
|
129 float aScale, |
|
130 float aOutput[WEBAUDIO_BLOCK_SIZE]); |
|
131 |
|
132 /** |
|
133 * Pointwise copy-scaled operation. aScale == 1.0f should be optimized. |
|
134 * |
|
135 * Buffer size is implicitly assumed to be WEBAUDIO_BLOCK_SIZE. |
|
136 */ |
|
137 void AudioBlockCopyChannelWithScale(const float* aInput, |
|
138 float aScale, |
|
139 float* aOutput); |
|
140 |
|
141 /** |
|
142 * Vector copy-scaled operation. |
|
143 */ |
|
144 void AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE], |
|
145 const float aScale[WEBAUDIO_BLOCK_SIZE], |
|
146 float aOutput[WEBAUDIO_BLOCK_SIZE]); |
|
147 |
|
148 /** |
|
149 * Vector complex multiplication on arbitrary sized buffers. |
|
150 */ |
|
151 void BufferComplexMultiply(const float* aInput, |
|
152 const float* aScale, |
|
153 float* aOutput, |
|
154 uint32_t aSize); |
|
155 |
|
156 /** |
|
157 * Vector maximum element magnitude ( max(abs(aInput)) ). |
|
158 */ |
|
159 float AudioBufferPeakValue(const float* aInput, uint32_t aSize); |
|
160 |
|
161 /** |
|
162 * In place gain. aScale == 1.0f should be optimized. |
|
163 */ |
|
164 void AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE], |
|
165 float aScale); |
|
166 |
|
167 /** |
|
168 * In place gain. aScale == 1.0f should be optimized. |
|
169 */ |
|
170 void AudioBufferInPlaceScale(float* aBlock, |
|
171 float aScale, |
|
172 uint32_t aSize); |
|
173 |
|
174 /** |
|
175 * Upmix a mono input to a stereo output, scaling the two output channels by two |
|
176 * different gain value. |
|
177 * This algorithm is specified in the WebAudio spec. |
|
178 */ |
|
179 void |
|
180 AudioBlockPanMonoToStereo(const float aInput[WEBAUDIO_BLOCK_SIZE], |
|
181 float aGainL, float aGainR, |
|
182 float aOutputL[WEBAUDIO_BLOCK_SIZE], |
|
183 float aOutputR[WEBAUDIO_BLOCK_SIZE]); |
|
184 /** |
|
185 * Pan a stereo source according to right and left gain, and the position |
|
186 * (whether the listener is on the left of the source or not). |
|
187 * This algorithm is specified in the WebAudio spec. |
|
188 */ |
|
189 void |
|
190 AudioBlockPanStereoToStereo(const float aInputL[WEBAUDIO_BLOCK_SIZE], |
|
191 const float aInputR[WEBAUDIO_BLOCK_SIZE], |
|
192 float aGainL, float aGainR, bool aIsOnTheLeft, |
|
193 float aOutputL[WEBAUDIO_BLOCK_SIZE], |
|
194 float aOutputR[WEBAUDIO_BLOCK_SIZE]); |
|
195 |
|
196 /** |
|
197 * Return the sum of squares of all of the samples in the input. |
|
198 */ |
|
199 float |
|
200 AudioBufferSumOfSquares(const float* aInput, uint32_t aLength); |
|
201 |
|
202 /** |
|
203 * All methods of this class and its subclasses are called on the |
|
204 * MediaStreamGraph thread. |
|
205 */ |
|
206 class AudioNodeEngine { |
|
207 public: |
|
208 // This should be compatible with AudioNodeStream::OutputChunks. |
|
209 typedef nsAutoTArray<AudioChunk, 1> OutputChunks; |
|
210 |
|
211 explicit AudioNodeEngine(dom::AudioNode* aNode) |
|
212 : mNode(aNode) |
|
213 , mNodeMutex("AudioNodeEngine::mNodeMutex") |
|
214 , mInputCount(aNode ? aNode->NumberOfInputs() : 1) |
|
215 , mOutputCount(aNode ? aNode->NumberOfOutputs() : 0) |
|
216 { |
|
217 MOZ_ASSERT(NS_IsMainThread()); |
|
218 MOZ_COUNT_CTOR(AudioNodeEngine); |
|
219 } |
|
220 virtual ~AudioNodeEngine() |
|
221 { |
|
222 MOZ_ASSERT(!mNode, "The node reference must be already cleared"); |
|
223 MOZ_COUNT_DTOR(AudioNodeEngine); |
|
224 } |
|
225 |
|
226 virtual dom::DelayNodeEngine* AsDelayNodeEngine() { return nullptr; } |
|
227 |
|
228 virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam) |
|
229 { |
|
230 NS_ERROR("Invalid SetStreamTimeParameter index"); |
|
231 } |
|
232 virtual void SetDoubleParameter(uint32_t aIndex, double aParam) |
|
233 { |
|
234 NS_ERROR("Invalid SetDoubleParameter index"); |
|
235 } |
|
236 virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam) |
|
237 { |
|
238 NS_ERROR("Invalid SetInt32Parameter index"); |
|
239 } |
|
240 virtual void SetTimelineParameter(uint32_t aIndex, |
|
241 const dom::AudioParamTimeline& aValue, |
|
242 TrackRate aSampleRate) |
|
243 { |
|
244 NS_ERROR("Invalid SetTimelineParameter index"); |
|
245 } |
|
246 virtual void SetThreeDPointParameter(uint32_t aIndex, |
|
247 const dom::ThreeDPoint& aValue) |
|
248 { |
|
249 NS_ERROR("Invalid SetThreeDPointParameter index"); |
|
250 } |
|
251 virtual void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) |
|
252 { |
|
253 NS_ERROR("SetBuffer called on engine that doesn't support it"); |
|
254 } |
|
255 // This consumes the contents of aData. aData will be emptied after this returns. |
|
256 virtual void SetRawArrayData(nsTArray<float>& aData) |
|
257 { |
|
258 NS_ERROR("SetRawArrayData called on an engine that doesn't support it"); |
|
259 } |
|
260 |
|
261 /** |
|
262 * Produce the next block of audio samples, given input samples aInput |
|
263 * (the mixed data for input 0). |
|
264 * aInput is guaranteed to have float sample format (if it has samples at all) |
|
265 * and to have been resampled to the sampling rate for the stream, and to have |
|
266 * exactly WEBAUDIO_BLOCK_SIZE samples. |
|
267 * *aFinished is set to false by the caller. If the callee sets it to true, |
|
268 * we'll finish the stream and not call this again. |
|
269 */ |
|
270 virtual void ProcessBlock(AudioNodeStream* aStream, |
|
271 const AudioChunk& aInput, |
|
272 AudioChunk* aOutput, |
|
273 bool* aFinished) |
|
274 { |
|
275 MOZ_ASSERT(mInputCount <= 1 && mOutputCount <= 1); |
|
276 *aOutput = aInput; |
|
277 } |
|
278 /** |
|
279 * Produce the next block of audio samples, before input is provided. |
|
280 * ProcessBlock() will be called later, and it then should not change |
|
281 * aOutput. This is used only for DelayNodeEngine in a feedback loop. |
|
282 */ |
|
283 virtual void ProduceBlockBeforeInput(AudioChunk* aOutput) |
|
284 { |
|
285 NS_NOTREACHED("ProduceBlockBeforeInput called on wrong engine\n"); |
|
286 } |
|
287 |
|
288 /** |
|
289 * Produce the next block of audio samples, given input samples in the aInput |
|
290 * array. There is one input sample per active port in aInput, in order. |
|
291 * This is the multi-input/output version of ProcessBlock. Only one kind |
|
292 * of ProcessBlock is called on each node, depending on whether the |
|
293 * number of inputs and outputs are both 1 or not. |
|
294 * |
|
295 * aInput is always guaranteed to not contain more input AudioChunks than the |
|
296 * maximum number of inputs for the node. It is the responsibility of the |
|
297 * overrides of this function to make sure they will only add a maximum number |
|
298 * of AudioChunks to aOutput as advertized by the AudioNode implementation. |
|
299 * An engine may choose to produce fewer inputs than advertizes by the |
|
300 * corresponding AudioNode, in which case it will be interpreted as a channel |
|
301 * of silence. |
|
302 */ |
|
303 virtual void ProcessBlocksOnPorts(AudioNodeStream* aStream, |
|
304 const OutputChunks& aInput, |
|
305 OutputChunks& aOutput, |
|
306 bool* aFinished) |
|
307 { |
|
308 MOZ_ASSERT(mInputCount > 1 || mOutputCount > 1); |
|
309 // Only produce one output port, and drop all other input ports. |
|
310 aOutput[0] = aInput[0]; |
|
311 } |
|
312 |
|
313 Mutex& NodeMutex() { return mNodeMutex;} |
|
314 |
|
315 bool HasNode() const |
|
316 { |
|
317 return !!mNode; |
|
318 } |
|
319 |
|
320 dom::AudioNode* Node() const |
|
321 { |
|
322 mNodeMutex.AssertCurrentThreadOwns(); |
|
323 return mNode; |
|
324 } |
|
325 |
|
326 dom::AudioNode* NodeMainThread() const |
|
327 { |
|
328 MOZ_ASSERT(NS_IsMainThread()); |
|
329 return mNode; |
|
330 } |
|
331 |
|
332 void ClearNode() |
|
333 { |
|
334 MOZ_ASSERT(NS_IsMainThread()); |
|
335 MOZ_ASSERT(mNode != nullptr); |
|
336 mNodeMutex.AssertCurrentThreadOwns(); |
|
337 mNode = nullptr; |
|
338 } |
|
339 |
|
340 uint16_t InputCount() const { return mInputCount; } |
|
341 uint16_t OutputCount() const { return mOutputCount; } |
|
342 |
|
343 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
|
344 { |
|
345 // NB: |mNode| is tracked separately so it is excluded here. |
|
346 return 0; |
|
347 } |
|
348 |
|
349 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
|
350 { |
|
351 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
|
352 } |
|
353 |
|
354 void SizeOfIncludingThis(MallocSizeOf aMallocSizeOf, |
|
355 AudioNodeSizes& aUsage) const |
|
356 { |
|
357 aUsage.mEngine = SizeOfIncludingThis(aMallocSizeOf); |
|
358 aUsage.mDomNode = mNode->SizeOfIncludingThis(aMallocSizeOf); |
|
359 aUsage.mNodeType = mNode->NodeType(); |
|
360 } |
|
361 |
|
362 private: |
|
363 dom::AudioNode* mNode; |
|
364 Mutex mNodeMutex; |
|
365 const uint16_t mInputCount; |
|
366 const uint16_t mOutputCount; |
|
367 }; |
|
368 |
|
369 } |
|
370 |
|
371 #endif /* MOZILLA_AUDIONODEENGINE_H_ */ |