Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
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_
9 #include "AudioSegment.h"
10 #include "mozilla/dom/AudioNode.h"
11 #include "mozilla/MemoryReporting.h"
12 #include "mozilla/Mutex.h"
14 namespace mozilla {
16 namespace dom {
17 struct ThreeDPoint;
18 class AudioParamTimeline;
19 class DelayNodeEngine;
20 }
22 class AudioNodeStream;
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 }
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 };
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; }
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 }
78 /**
79 * Put this object into an error state where there are no channels.
80 */
81 void Clear() { mContents.Clear(); }
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 }
91 return amount;
92 }
94 private:
95 AutoFallibleTArray<Storage,2> mContents;
96 };
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);
104 /**
105 * aChunk must have been allocated by AllocateAudioBlock.
106 */
107 void WriteZeroesToAudioBlock(AudioChunk* aChunk, uint32_t aStart, uint32_t aLength);
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);
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);
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]);
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);
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]);
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);
156 /**
157 * Vector maximum element magnitude ( max(abs(aInput)) ).
158 */
159 float AudioBufferPeakValue(const float* aInput, uint32_t aSize);
161 /**
162 * In place gain. aScale == 1.0f should be optimized.
163 */
164 void AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE],
165 float aScale);
167 /**
168 * In place gain. aScale == 1.0f should be optimized.
169 */
170 void AudioBufferInPlaceScale(float* aBlock,
171 float aScale,
172 uint32_t aSize);
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]);
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);
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;
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 }
226 virtual dom::DelayNodeEngine* AsDelayNodeEngine() { return nullptr; }
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 }
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 }
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 }
313 Mutex& NodeMutex() { return mNodeMutex;}
315 bool HasNode() const
316 {
317 return !!mNode;
318 }
320 dom::AudioNode* Node() const
321 {
322 mNodeMutex.AssertCurrentThreadOwns();
323 return mNode;
324 }
326 dom::AudioNode* NodeMainThread() const
327 {
328 MOZ_ASSERT(NS_IsMainThread());
329 return mNode;
330 }
332 void ClearNode()
333 {
334 MOZ_ASSERT(NS_IsMainThread());
335 MOZ_ASSERT(mNode != nullptr);
336 mNodeMutex.AssertCurrentThreadOwns();
337 mNode = nullptr;
338 }
340 uint16_t InputCount() const { return mInputCount; }
341 uint16_t OutputCount() const { return mOutputCount; }
343 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
344 {
345 // NB: |mNode| is tracked separately so it is excluded here.
346 return 0;
347 }
349 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
350 {
351 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
352 }
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 }
362 private:
363 dom::AudioNode* mNode;
364 Mutex mNodeMutex;
365 const uint16_t mInputCount;
366 const uint16_t mOutputCount;
367 };
369 }
371 #endif /* MOZILLA_AUDIONODEENGINE_H_ */