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 <mozilla/PodOperations.h>
10 #include <mozilla/Assertions.h>
11 #include <mozilla/UniquePtr.h>
12 #include <AudioSampleFormat.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:
AudioPacketizer(uint32_t aPacketSize,uint32_t aChannels)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 
Input(const InputType * aFrames,uint32_t aFrameCount)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 
Output()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 
Output(OutputType * aOutputBuffer)103   void Output(OutputType* aOutputBuffer) {
104     uint32_t samplesNeeded = mPacketSize * mChannels;
105 
106     // Under-run. Pad the end of the buffer with silence.
107     if (AvailableSamples() < samplesNeeded) {
108 #ifdef LOG_PACKETIZER_UNDERRUN
109       char buf[256];
110       snprintf(buf, 256,
111                "AudioPacketizer %p underrun: available: %u, needed: %u\n", this,
112                AvailableSamples(), samplesNeeded);
113       NS_WARNING(buf);
114 #endif
115       uint32_t zeros = samplesNeeded - AvailableSamples();
116       PodZero(aOutputBuffer + AvailableSamples(), zeros);
117       samplesNeeded -= zeros;
118     }
119     if (ReadIndex() + samplesNeeded <= mLength) {
120       ConvertAudioSamples<InputType, OutputType>(mStorage.get() + ReadIndex(),
121                                                  aOutputBuffer, samplesNeeded);
122     } else {
123       uint32_t firstPartLength = mLength - ReadIndex();
124       uint32_t secondPartLength = samplesNeeded - firstPartLength;
125       ConvertAudioSamples<InputType, OutputType>(
126           mStorage.get() + ReadIndex(), aOutputBuffer, firstPartLength);
127       ConvertAudioSamples<InputType, OutputType>(
128           mStorage.get(), aOutputBuffer + firstPartLength, secondPartLength);
129     }
130     mReadIndex += samplesNeeded;
131   }
132 
Clear()133   void Clear() {
134     mReadIndex = 0;
135     mWriteIndex = 0;
136   }
137 
PacketsAvailable()138   uint32_t PacketsAvailable() const {
139     return AvailableSamples() / mChannels / mPacketSize;
140   }
141 
FramesAvailable()142   uint32_t FramesAvailable() const { return AvailableSamples() / mChannels; }
143 
Empty()144   bool Empty() const { return mWriteIndex == mReadIndex; }
145 
Full()146   bool Full() const { return mWriteIndex - mReadIndex == mLength; }
147 
148   // Size of one packet of audio, in frames
149   const uint32_t mPacketSize;
150   // Number of channels of the stream flowing through this packetizer
151   const uint32_t mChannels;
152 
153  private:
ReadIndex()154   uint32_t ReadIndex() const { return mReadIndex % mLength; }
155 
WriteIndex()156   uint32_t WriteIndex() const { return mWriteIndex % mLength; }
157 
AvailableSamples()158   uint32_t AvailableSamples() const { return mWriteIndex - mReadIndex; }
159 
EmptySlots()160   uint32_t EmptySlots() const { return mLength - AvailableSamples(); }
161 
162   // Two virtual index into the buffer: the read position and the write
163   // position.
164   uint64_t mReadIndex;
165   uint64_t mWriteIndex;
166   // Storage for the samples
167   mozilla::UniquePtr<InputType[]> mStorage;
168   // Length of the buffer, in samples
169   uint32_t mLength;
170 };
171 
172 }  // namespace mozilla
173 
174 #endif  // AudioPacketizer_h_
175