AudioBufferUtils.h (7564B)
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_SCRATCHBUFFER_H_ 7 #define MOZILLA_SCRATCHBUFFER_H_ 8 9 #include <algorithm> 10 11 #include "AudioSegment.h" 12 #include "mozilla/PodOperations.h" 13 #include "mozilla/UniquePtr.h" 14 #include "nsDebug.h" 15 16 namespace mozilla { 17 18 /** 19 * The classes in this file provide a interface that uses frames as a unit. 20 * However, they store their offsets in samples (because it's handy for pointer 21 * operations). Those functions can convert between the two units. 22 */ 23 static inline uint32_t FramesToSamples(uint32_t aChannels, uint32_t aFrames) { 24 return aFrames * aChannels; 25 } 26 27 static inline uint32_t SamplesToFrames(uint32_t aChannels, uint32_t aSamples) { 28 MOZ_ASSERT(!(aSamples % aChannels), "Frame alignment is wrong."); 29 return aSamples / aChannels; 30 } 31 32 /** 33 * Class that gets a buffer pointer from an audio callback and provides a safe 34 * interface to manipulate this buffer, and to ensure we are not missing frames 35 * by the end of the callback. 36 */ 37 template <typename T> 38 class AudioCallbackBufferWrapper { 39 public: 40 AudioCallbackBufferWrapper() 41 : mBuffer(nullptr), mSamples(0), mSampleWriteOffset(1), mChannels(0) {} 42 43 explicit AudioCallbackBufferWrapper(uint32_t aChannels) 44 : mBuffer(nullptr), 45 mSamples(0), 46 mSampleWriteOffset(1), 47 mChannels(aChannels) 48 49 { 50 MOZ_ASSERT(aChannels); 51 } 52 53 AudioCallbackBufferWrapper& operator=( 54 const AudioCallbackBufferWrapper& aOther) { 55 MOZ_ASSERT(!aOther.mBuffer, 56 "Don't use this ctor after AudioCallbackDriver::Init"); 57 MOZ_ASSERT(aOther.mSamples == 0, 58 "Don't use this ctor after AudioCallbackDriver::Init"); 59 MOZ_ASSERT(aOther.mSampleWriteOffset == 1, 60 "Don't use this ctor after AudioCallbackDriver::Init"); 61 MOZ_ASSERT(aOther.mChannels != 0); 62 63 mBuffer = nullptr; 64 mSamples = 0; 65 mSampleWriteOffset = 1; 66 mChannels = aOther.mChannels; 67 68 return *this; 69 } 70 71 /** 72 * Set the buffer in this wrapper. This is to be called at the beginning of 73 * the callback. 74 */ 75 void SetBuffer(T* aBuffer, uint32_t aFrames) { 76 MOZ_ASSERT(!mBuffer && !mSamples, "SetBuffer called twice."); 77 mBuffer = aBuffer; 78 mSamples = FramesToSamples(mChannels, aFrames); 79 mSampleWriteOffset = 0; 80 } 81 82 /** 83 * Write some frames to the internal buffer. Free space in the buffer should 84 * be checked prior to calling these. 85 */ 86 void WriteSilence(const uint32_t aFrames) { 87 MOZ_ASSERT(aFrames <= Available(), 88 "Writing more than we can in the audio buffer."); 89 90 std::fill_n(mBuffer + mSampleWriteOffset, aFrames, static_cast<T>(0)); 91 mSampleWriteOffset += FramesToSamples(mChannels, aFrames); 92 } 93 void WriteFrames(T* aBuffer, uint32_t aFrames) { 94 MOZ_ASSERT(aFrames <= Available(), 95 "Writing more than we can in the audio buffer."); 96 97 PodCopy(mBuffer + mSampleWriteOffset, aBuffer, 98 FramesToSamples(mChannels, aFrames)); 99 mSampleWriteOffset += FramesToSamples(mChannels, aFrames); 100 } 101 void WriteFrames(const AudioChunk& aChunk, uint32_t aFrames) { 102 MOZ_ASSERT(aFrames <= Available(), 103 "Writing more than we can in the audio buffer."); 104 105 InterleaveAndConvertBuffer(aChunk.ChannelData<T>().Elements(), aFrames, 106 aChunk.mVolume, aChunk.ChannelCount(), 107 mBuffer + mSampleWriteOffset); 108 mSampleWriteOffset += FramesToSamples(mChannels, aFrames); 109 } 110 111 /** 112 * Number of frames that can be written to the buffer. 113 */ 114 uint32_t Available() { 115 return SamplesToFrames(mChannels, mSamples - mSampleWriteOffset); 116 } 117 118 /** 119 * Check that the buffer is completly filled, and reset internal state so this 120 * instance can be reused. 121 */ 122 void BufferFilled() { 123 MOZ_ASSERT(Available() == 0, "Frames should have been written"); 124 MOZ_ASSERT(mBuffer, "Buffer not set."); 125 mSamples = 0; 126 mSampleWriteOffset = 0; 127 mBuffer = nullptr; 128 } 129 130 private: 131 /* This is not an owned pointer, but the pointer passed to use via the audio 132 * callback. */ 133 T* mBuffer; 134 /* The number of samples of this audio buffer. */ 135 uint32_t mSamples; 136 /* The position at which new samples should be written. We want to return to 137 * the audio callback iff this is equal to mSamples. */ 138 uint32_t mSampleWriteOffset; 139 uint32_t mChannels; 140 }; 141 142 /** 143 * This is a class that interfaces with the AudioCallbackBufferWrapper, and is 144 * responsible for storing the excess of data produced by the MediaTrackGraph 145 * because of different rounding constraints, to be used the next time the audio 146 * backend calls back. 147 */ 148 template <typename T, uint32_t BLOCK_SIZE> 149 class SpillBuffer { 150 public: 151 SpillBuffer() : mBuffer(nullptr), mPosition(0), mChannels(0) {} 152 153 explicit SpillBuffer(uint32_t aChannels) 154 : mPosition(0), mChannels(aChannels) { 155 MOZ_ASSERT(aChannels); 156 mBuffer = MakeUnique<T[]>(BLOCK_SIZE * mChannels); 157 PodZero(mBuffer.get(), BLOCK_SIZE * mChannels); 158 } 159 160 SpillBuffer& operator=(SpillBuffer& aOther) { 161 MOZ_ASSERT(aOther.mPosition == 0, 162 "Don't use this ctor after AudioCallbackDriver::Init"); 163 MOZ_ASSERT(aOther.mChannels != 0); 164 MOZ_ASSERT(aOther.mBuffer); 165 166 mPosition = aOther.mPosition; 167 mChannels = aOther.mChannels; 168 mBuffer = std::move(aOther.mBuffer); 169 170 return *this; 171 } 172 173 SpillBuffer& operator=(SpillBuffer&& aOther) { 174 return this->operator=(aOther); 175 } 176 177 bool IsEmpty() const { return mPosition == 0; } 178 void Empty() { mPosition = 0; } 179 /* Empty the spill buffer into the buffer of the audio callback. This returns 180 * the number of frames written. */ 181 uint32_t Empty(AudioCallbackBufferWrapper<T>& aBuffer) { 182 uint32_t framesToWrite = 183 std::min(aBuffer.Available(), SamplesToFrames(mChannels, mPosition)); 184 185 aBuffer.WriteFrames(mBuffer.get(), framesToWrite); 186 187 mPosition -= FramesToSamples(mChannels, framesToWrite); 188 // If we didn't empty the spill buffer for some reason, shift the remaining 189 // data down 190 if (mPosition > 0) { 191 MOZ_ASSERT(FramesToSamples(mChannels, framesToWrite) + mPosition <= 192 BLOCK_SIZE * mChannels); 193 PodMove(mBuffer.get(), 194 mBuffer.get() + FramesToSamples(mChannels, framesToWrite), 195 mPosition); 196 } 197 198 return framesToWrite; 199 } 200 /* Fill the spill buffer from aInput. 201 * Return the number of frames written to the spill buffer */ 202 uint32_t Fill(const AudioChunk& aInput) { 203 uint32_t framesToWrite = 204 std::min(static_cast<uint32_t>(aInput.mDuration), 205 BLOCK_SIZE - SamplesToFrames(mChannels, mPosition)); 206 207 MOZ_ASSERT(FramesToSamples(mChannels, framesToWrite) + mPosition <= 208 BLOCK_SIZE * mChannels); 209 InterleaveAndConvertBuffer( 210 aInput.ChannelData<T>().Elements(), framesToWrite, aInput.mVolume, 211 aInput.ChannelCount(), mBuffer.get() + mPosition); 212 213 mPosition += FramesToSamples(mChannels, framesToWrite); 214 215 return framesToWrite; 216 } 217 218 private: 219 /* The spilled data. */ 220 UniquePtr<T[]> mBuffer; 221 /* The current write position, in samples, in the buffer when filling, or the 222 * amount of buffer filled when emptying. */ 223 uint32_t mPosition; 224 uint32_t mChannels; 225 }; 226 227 } // namespace mozilla 228 229 #endif // MOZILLA_SCRATCHBUFFER_H_