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 && mFrames && mSampleRate, 54 "Mix not called for this cycle?"); 55 for (MixerCallback* cb = mCallbacks.getFirst(); cb != nullptr; 56 cb = cb->getNext()) { 57 MixerCallbackReceiver* receiver = cb->mReceiver; 58 MOZ_ASSERT(receiver); 59 receiver->MixerCallback(mMixedAudio.Elements(), 60 AudioSampleTypeToFormat<AudioDataValue>::Format, 61 mChannels, mFrames, mSampleRate); 62 } 63 PodZero(mMixedAudio.Elements(), mMixedAudio.Length()); 64 mSampleRate = mChannels = mFrames = 0; 65 } 66 67 /* Add a buffer to the mix. The buffer can be null if there's nothing to mix 68 * but the callback is still needed. */ Mix(AudioDataValue * aSamples,uint32_t aChannels,uint32_t aFrames,uint32_t aSampleRate)69 void Mix(AudioDataValue* aSamples, uint32_t aChannels, uint32_t aFrames, 70 uint32_t aSampleRate) { 71 if (!mFrames && !mChannels) { 72 mFrames = aFrames; 73 mChannels = aChannels; 74 mSampleRate = aSampleRate; 75 EnsureCapacityAndSilence(); 76 } 77 78 MOZ_ASSERT(aFrames == mFrames); 79 MOZ_ASSERT(aChannels == mChannels); 80 MOZ_ASSERT(aSampleRate == mSampleRate); 81 82 if (!aSamples) { 83 return; 84 } 85 86 for (uint32_t i = 0; i < aFrames * aChannels; i++) { 87 mMixedAudio[i] += aSamples[i]; 88 } 89 } 90 AddCallback(NotNull<MixerCallbackReceiver * > aReceiver)91 void AddCallback(NotNull<MixerCallbackReceiver*> aReceiver) { 92 mCallbacks.insertBack(new MixerCallback(aReceiver)); 93 } 94 FindCallback(MixerCallbackReceiver * aReceiver)95 bool FindCallback(MixerCallbackReceiver* aReceiver) { 96 for (MixerCallback* cb = mCallbacks.getFirst(); cb != nullptr; 97 cb = cb->getNext()) { 98 if (cb->mReceiver == aReceiver) { 99 return true; 100 } 101 } 102 return false; 103 } 104 RemoveCallback(MixerCallbackReceiver * aReceiver)105 bool RemoveCallback(MixerCallbackReceiver* aReceiver) { 106 for (MixerCallback* cb = mCallbacks.getFirst(); cb != nullptr; 107 cb = cb->getNext()) { 108 if (cb->mReceiver == aReceiver) { 109 cb->remove(); 110 delete cb; 111 return true; 112 } 113 } 114 return false; 115 } 116 117 private: EnsureCapacityAndSilence()118 void EnsureCapacityAndSilence() { 119 if (mFrames * mChannels > mMixedAudio.Length()) { 120 mMixedAudio.SetLength(mFrames * mChannels); 121 } 122 PodZero(mMixedAudio.Elements(), mMixedAudio.Length()); 123 } 124 125 class MixerCallback : public LinkedListElement<MixerCallback> { 126 public: MixerCallback(NotNull<MixerCallbackReceiver * > aReceiver)127 explicit MixerCallback(NotNull<MixerCallbackReceiver*> aReceiver) 128 : mReceiver(aReceiver) {} 129 NotNull<MixerCallbackReceiver*> mReceiver; 130 }; 131 132 /* Function that is called when the mixing is done. */ 133 LinkedList<MixerCallback> mCallbacks; 134 /* Number of frames for this mixing block. */ 135 uint32_t mFrames; 136 /* Number of channels for this mixing block. */ 137 uint32_t mChannels; 138 /* Sample rate the of the mixed data. */ 139 uint32_t mSampleRate; 140 /* Buffer containing the mixed audio data. */ 141 nsTArray<AudioDataValue> mMixedAudio; 142 }; 143 144 } // namespace mozilla 145 146 #endif // MOZILLA_AUDIOMIXER_H_ 147