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