Coverage Report

Created: 2018-09-25 14:53

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