Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/AudioMixer.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#ifndef MOZILLA_AUDIOMIXER_H_
7
#define MOZILLA_AUDIOMIXER_H_
8
9
#include "AudioSampleFormat.h"
10
#include "nsTArray.h"
11
#include "mozilla/PodOperations.h"
12
#include "mozilla/LinkedList.h"
13
#include "AudioStream.h"
14
15
namespace mozilla {
16
17
struct MixerCallbackReceiver {
18
  virtual void MixerCallback(AudioDataValue* aMixedBuffer,
19
                             AudioSampleFormat aFormat,
20
                             uint32_t aChannels,
21
                             uint32_t aFrames,
22
                             uint32_t aSampleRate) = 0;
23
};
24
/**
25
 * This class mixes multiple streams of audio together to output a single audio
26
 * stream.
27
 *
28
 * AudioMixer::Mix is to be called repeatedly with buffers that have the same
29
 * length, sample rate, sample format and channel count. This class works with
30
 * interleaved and plannar buffers, but the buffer mixed must be of the same
31
 * type during a mixing cycle.
32
 *
33
 * When all the tracks have been mixed, calling FinishMixing will call back with
34
 * a buffer containing the mixed audio data.
35
 *
36
 * This class is not thread safe.
37
 */
38
class AudioMixer
39
{
40
public:
41
  AudioMixer()
42
    : mFrames(0),
43
      mChannels(0),
44
      mSampleRate(0)
45
0
  { }
46
47
  ~AudioMixer()
48
0
  {
49
0
    MixerCallback* cb;
50
0
    while ((cb = mCallbacks.popFirst())) {
51
0
      delete cb;
52
0
    }
53
0
  }
54
55
  void StartMixing()
56
0
  {
57
0
    mSampleRate = mChannels = mFrames = 0;
58
0
  }
59
60
  /* Get the data from the mixer. This is supposed to be called when all the
61
   * tracks have been mixed in. The caller should not hold onto the data. */
62
0
  void FinishMixing() {
63
0
    MOZ_ASSERT(mChannels && mFrames && mSampleRate, "Mix not called for this cycle?");
64
0
    for (MixerCallback* cb = mCallbacks.getFirst();
65
0
         cb != nullptr; cb = cb->getNext()) {
66
0
      MixerCallbackReceiver* receiver = cb->mReceiver;
67
0
      receiver->MixerCallback(mMixedAudio.Elements(),
68
0
                              AudioSampleTypeToFormat<AudioDataValue>::Format,
69
0
                              mChannels,
70
0
                              mFrames,
71
0
                              mSampleRate);
72
0
    }
73
0
    PodZero(mMixedAudio.Elements(), mMixedAudio.Length());
74
0
    mSampleRate = mChannels = mFrames = 0;
75
0
  }
76
77
  /* Add a buffer to the mix. The buffer can be null if there's nothing to mix
78
   * but the callback is still needed. */
79
  void Mix(AudioDataValue* aSamples,
80
           uint32_t aChannels,
81
           uint32_t aFrames,
82
0
           uint32_t aSampleRate) {
83
0
    if (!mFrames && !mChannels) {
84
0
      mFrames = aFrames;
85
0
      mChannels = aChannels;
86
0
      mSampleRate = aSampleRate;
87
0
      EnsureCapacityAndSilence();
88
0
    }
89
0
90
0
    MOZ_ASSERT(aFrames == mFrames);
91
0
    MOZ_ASSERT(aChannels == mChannels);
92
0
    MOZ_ASSERT(aSampleRate == mSampleRate);
93
0
94
0
    if (!aSamples) {
95
0
      return;
96
0
    }
97
0
98
0
    for (uint32_t i = 0; i < aFrames * aChannels; i++) {
99
0
      mMixedAudio[i] += aSamples[i];
100
0
    }
101
0
  }
102
103
0
  void AddCallback(MixerCallbackReceiver* aReceiver) {
104
0
    mCallbacks.insertBack(new MixerCallback(aReceiver));
105
0
  }
106
107
0
  bool FindCallback(MixerCallbackReceiver* aReceiver) {
108
0
    for (MixerCallback* cb = mCallbacks.getFirst();
109
0
         cb != nullptr; cb = cb->getNext()) {
110
0
      if (cb->mReceiver == aReceiver) {
111
0
        return true;
112
0
      }
113
0
    }
114
0
    return false;
115
0
  }
116
117
0
  bool RemoveCallback(MixerCallbackReceiver* aReceiver) {
118
0
    for (MixerCallback* cb = mCallbacks.getFirst();
119
0
         cb != nullptr; cb = cb->getNext()) {
120
0
      if (cb->mReceiver == aReceiver) {
121
0
        cb->remove();
122
0
        delete cb;
123
0
        return true;
124
0
      }
125
0
    }
126
0
    return false;
127
0
  }
128
private:
129
0
  void EnsureCapacityAndSilence() {
130
0
    if (mFrames * mChannels > mMixedAudio.Length()) {
131
0
      mMixedAudio.SetLength(mFrames* mChannels);
132
0
    }
133
0
    PodZero(mMixedAudio.Elements(), mMixedAudio.Length());
134
0
  }
135
136
  class MixerCallback : public LinkedListElement<MixerCallback>
137
  {
138
  public:
139
    explicit MixerCallback(MixerCallbackReceiver* aReceiver)
140
      : mReceiver(aReceiver)
141
0
    { }
142
    MixerCallbackReceiver* mReceiver;
143
  };
144
145
  /* Function that is called when the mixing is done. */
146
  LinkedList<MixerCallback> mCallbacks;
147
  /* Number of frames for this mixing block. */
148
  uint32_t mFrames;
149
  /* Number of channels for this mixing block. */
150
  uint32_t mChannels;
151
  /* Sample rate the of the mixed data. */
152
  uint32_t mSampleRate;
153
  /* Buffer containing the mixed audio data. */
154
  nsTArray<AudioDataValue> mMixedAudio;
155
};
156
157
} // namespace mozilla
158
159
#endif // MOZILLA_AUDIOMIXER_H_