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