AudioPacketizer.h (6566B)
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 AudioPacketizer_h_ 7 #define AudioPacketizer_h_ 8 9 #include <AudioSampleFormat.h> 10 #include <mozilla/Assertions.h> 11 #include <mozilla/PodOperations.h> 12 #include <mozilla/UniquePtr.h> 13 14 // Enable this to warn when `Output` has been called but not enough data was 15 // buffered. 16 // #define LOG_PACKETIZER_UNDERRUN 17 18 namespace mozilla { 19 /** 20 * This class takes arbitrary input data, and returns packets of a specific 21 * size. In the process, it can convert audio samples from 16bit integers to 22 * float (or vice-versa). 23 * 24 * Input and output, as well as length units in the public interface are 25 * interleaved frames. 26 * 27 * Allocations of output buffer can be performed by this class. Buffers can 28 * simply be delete-d. This is because packets are intended to be sent off to 29 * non-gecko code using normal pointers/length pairs 30 * 31 * Alternatively, consumers can pass in a buffer in which the output is copied. 32 * The buffer needs to be large enough to store a packet worth of audio. 33 * 34 * The implementation uses a circular buffer using absolute virtual indices. 35 */ 36 template <typename InputType, typename OutputType> 37 class AudioPacketizer { 38 public: 39 AudioPacketizer(uint32_t aPacketSize, uint32_t aChannels) 40 : mPacketSize(aPacketSize), 41 mChannels(aChannels), 42 mReadIndex(0), 43 mWriteIndex(0), 44 // Start off with a single packet 45 mStorage(new InputType[aPacketSize * aChannels]), 46 mLength(aPacketSize * aChannels) { 47 MOZ_ASSERT(aPacketSize > 0 && aChannels > 0, 48 "The packet size and the number of channel should be strictly " 49 "positive"); 50 } 51 52 void Input(const InputType* aFrames, uint32_t aFrameCount) { 53 uint32_t inputSamples = aFrameCount * mChannels; 54 // Need to grow the storage. This should rarely happen, if at all, once the 55 // array has the right size. 56 if (inputSamples > EmptySlots()) { 57 // Calls to Input and Output are roughtly interleaved 58 // (Input,Output,Input,Output, etc.), or balanced 59 // (Input,Input,Input,Output,Output,Output), so we update the buffer to 60 // the exact right size in order to not waste space. 61 uint32_t newLength = AvailableSamples() + inputSamples; 62 uint32_t toCopy = AvailableSamples(); 63 UniquePtr<InputType[]> oldStorage = std::move(mStorage); 64 mStorage = mozilla::MakeUnique<InputType[]>(newLength); 65 // Copy the old data at the beginning of the new storage. 66 if (WriteIndex() >= ReadIndex()) { 67 PodCopy(mStorage.get(), oldStorage.get() + ReadIndex(), 68 AvailableSamples()); 69 } else { 70 uint32_t firstPartLength = mLength - ReadIndex(); 71 uint32_t secondPartLength = AvailableSamples() - firstPartLength; 72 PodCopy(mStorage.get(), oldStorage.get() + ReadIndex(), 73 firstPartLength); 74 PodCopy(mStorage.get() + firstPartLength, oldStorage.get(), 75 secondPartLength); 76 } 77 mWriteIndex = toCopy; 78 mReadIndex = 0; 79 mLength = newLength; 80 } 81 82 if (WriteIndex() + inputSamples <= mLength) { 83 PodCopy(mStorage.get() + WriteIndex(), aFrames, aFrameCount * mChannels); 84 } else { 85 uint32_t firstPartLength = mLength - WriteIndex(); 86 uint32_t secondPartLength = inputSamples - firstPartLength; 87 PodCopy(mStorage.get() + WriteIndex(), aFrames, firstPartLength); 88 PodCopy(mStorage.get(), aFrames + firstPartLength, secondPartLength); 89 } 90 91 mWriteIndex += inputSamples; 92 } 93 94 OutputType* Output() { 95 uint32_t samplesNeeded = mPacketSize * mChannels; 96 OutputType* out = new OutputType[samplesNeeded]; 97 98 Output(out); 99 100 return out; 101 } 102 103 // Return the number of actual frames dequeued -- this can be lower than the 104 // packet size when underruning or draining. 105 size_t Output(OutputType* aOutputBuffer) { 106 uint32_t samplesNeeded = mPacketSize * mChannels; 107 size_t rv = 0; 108 109 // Under-run. Pad the end of the buffer with silence. 110 if (AvailableSamples() < samplesNeeded) { 111 rv = AvailableSamples() / mChannels; 112 #ifdef LOG_PACKETIZER_UNDERRUN 113 char buf[256]; 114 snprintf(buf, 256, 115 "AudioPacketizer %p underrun: available: %u, needed: %u\n", this, 116 AvailableSamples(), samplesNeeded); 117 NS_WARNING(buf); 118 #endif 119 uint32_t zeros = samplesNeeded - AvailableSamples(); 120 PodZero(aOutputBuffer + AvailableSamples(), zeros); 121 samplesNeeded -= zeros; 122 } else { 123 rv = mPacketSize; 124 } 125 if (ReadIndex() + samplesNeeded <= mLength) { 126 ConvertAudioSamples<InputType, OutputType>(mStorage.get() + ReadIndex(), 127 aOutputBuffer, samplesNeeded); 128 } else { 129 uint32_t firstPartLength = mLength - ReadIndex(); 130 uint32_t secondPartLength = samplesNeeded - firstPartLength; 131 ConvertAudioSamples<InputType, OutputType>( 132 mStorage.get() + ReadIndex(), aOutputBuffer, firstPartLength); 133 ConvertAudioSamples<InputType, OutputType>( 134 mStorage.get(), aOutputBuffer + firstPartLength, secondPartLength); 135 } 136 mReadIndex += samplesNeeded; 137 return rv; 138 } 139 140 void Clear() { 141 mReadIndex = 0; 142 mWriteIndex = 0; 143 } 144 145 uint32_t PacketsAvailable() const { 146 return AvailableSamples() / mChannels / mPacketSize; 147 } 148 149 uint32_t FramesAvailable() const { return AvailableSamples() / mChannels; } 150 151 bool Empty() const { return mWriteIndex == mReadIndex; } 152 153 bool Full() const { return mWriteIndex - mReadIndex == mLength; } 154 155 // Size of one packet of audio, in frames 156 const uint32_t mPacketSize; 157 // Number of channels of the stream flowing through this packetizer 158 const uint32_t mChannels; 159 160 private: 161 uint32_t ReadIndex() const { return mReadIndex % mLength; } 162 163 uint32_t WriteIndex() const { return mWriteIndex % mLength; } 164 165 uint32_t AvailableSamples() const { return mWriteIndex - mReadIndex; } 166 167 uint32_t EmptySlots() const { return mLength - AvailableSamples(); } 168 169 // Two virtual index into the buffer: the read position and the write 170 // position. 171 uint64_t mReadIndex; 172 uint64_t mWriteIndex; 173 // Storage for the samples 174 mozilla::UniquePtr<InputType[]> mStorage; 175 // Length of the buffer, in samples 176 uint32_t mLength; 177 }; 178 179 } // namespace mozilla 180 181 #endif // AudioPacketizer_h_