Line data Source code
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 0 : class AudioPacketizer
38 : {
39 : public:
40 0 : AudioPacketizer(uint32_t aPacketSize, uint32_t aChannels)
41 : : mPacketSize(aPacketSize)
42 : , mChannels(aChannels)
43 : , mReadIndex(0)
44 : , mWriteIndex(0)
45 : // Start off with a single packet
46 0 : , mStorage(new InputType[aPacketSize * aChannels])
47 0 : , mLength(aPacketSize * aChannels)
48 : {
49 0 : MOZ_ASSERT(aPacketSize > 0 && aChannels > 0,
50 : "The packet size and the number of channel should be strictly positive");
51 0 : }
52 :
53 0 : void Input(const InputType* aFrames, uint32_t aFrameCount)
54 : {
55 0 : uint32_t inputSamples = aFrameCount * mChannels;
56 : // Need to grow the storage. This should rarely happen, if at all, once the
57 : // array has the right size.
58 0 : if (inputSamples > EmptySlots()) {
59 : // Calls to Input and Output are roughtly interleaved
60 : // (Input,Output,Input,Output, etc.), or balanced
61 : // (Input,Input,Input,Output,Output,Output), so we update the buffer to
62 : // the exact right size in order to not waste space.
63 0 : uint32_t newLength = AvailableSamples() + inputSamples;
64 0 : uint32_t toCopy = AvailableSamples();
65 0 : UniquePtr<InputType[]> oldStorage = mozilla::Move(mStorage);
66 0 : mStorage = mozilla::MakeUnique<InputType[]>(newLength);
67 : // Copy the old data at the beginning of the new storage.
68 0 : if (WriteIndex() >= ReadIndex()) {
69 0 : PodCopy(mStorage.get(),
70 0 : oldStorage.get() + ReadIndex(),
71 0 : AvailableSamples());
72 : } else {
73 0 : uint32_t firstPartLength = mLength - ReadIndex();
74 0 : uint32_t secondPartLength = AvailableSamples() - firstPartLength;
75 0 : PodCopy(mStorage.get(),
76 0 : oldStorage.get() + ReadIndex(),
77 : firstPartLength);
78 0 : PodCopy(mStorage.get() + firstPartLength,
79 : oldStorage.get(),
80 : secondPartLength);
81 : }
82 0 : mWriteIndex = toCopy;
83 0 : mReadIndex = 0;
84 0 : mLength = newLength;
85 : }
86 :
87 0 : if (WriteIndex() + inputSamples <= mLength) {
88 0 : PodCopy(mStorage.get() + WriteIndex(), aFrames, aFrameCount * mChannels);
89 : } else {
90 0 : uint32_t firstPartLength = mLength - WriteIndex();
91 0 : uint32_t secondPartLength = inputSamples - firstPartLength;
92 0 : PodCopy(mStorage.get() + WriteIndex(), aFrames, firstPartLength);
93 0 : PodCopy(mStorage.get(), aFrames + firstPartLength, secondPartLength);
94 : }
95 :
96 0 : mWriteIndex += inputSamples;
97 0 : }
98 :
99 : OutputType* Output()
100 : {
101 : uint32_t samplesNeeded = mPacketSize * mChannels;
102 : OutputType* out = new OutputType[samplesNeeded];
103 :
104 : Output(out);
105 :
106 : return out;
107 : }
108 :
109 0 : void Output(OutputType* aOutputBuffer)
110 : {
111 0 : uint32_t samplesNeeded = mPacketSize * mChannels;
112 :
113 : // Under-run. Pad the end of the buffer with silence.
114 0 : if (AvailableSamples() < samplesNeeded) {
115 : #ifdef LOG_PACKETIZER_UNDERRUN
116 : char buf[256];
117 : snprintf(buf, 256,
118 : "AudioPacketizer %p underrun: available: %u, needed: %u\n",
119 : this, AvailableSamples(), samplesNeeded);
120 : NS_WARNING(buf);
121 : #endif
122 0 : uint32_t zeros = samplesNeeded - AvailableSamples();
123 0 : PodZero(aOutputBuffer + AvailableSamples(), zeros);
124 0 : samplesNeeded -= zeros;
125 : }
126 0 : if (ReadIndex() + samplesNeeded <= mLength) {
127 0 : ConvertAudioSamples<InputType,OutputType>(mStorage.get() + ReadIndex(),
128 : aOutputBuffer,
129 : samplesNeeded);
130 : } else {
131 0 : uint32_t firstPartLength = mLength - ReadIndex();
132 0 : uint32_t secondPartLength = samplesNeeded - firstPartLength;
133 0 : ConvertAudioSamples<InputType, OutputType>(mStorage.get() + ReadIndex(),
134 : aOutputBuffer,
135 : firstPartLength);
136 0 : ConvertAudioSamples<InputType, OutputType>(mStorage.get(),
137 0 : aOutputBuffer + firstPartLength,
138 : secondPartLength);
139 : }
140 0 : mReadIndex += samplesNeeded;
141 0 : }
142 :
143 0 : uint32_t PacketsAvailable() const {
144 0 : return AvailableSamples() / mChannels / mPacketSize;
145 : }
146 :
147 : bool Empty() const {
148 : return mWriteIndex == mReadIndex;
149 : }
150 :
151 : bool Full() const {
152 : return mWriteIndex - mReadIndex == mLength;
153 : }
154 :
155 0 : uint32_t PacketSize() const {
156 0 : return mPacketSize;
157 : }
158 :
159 0 : uint32_t Channels() const {
160 0 : return mChannels;
161 : }
162 :
163 : private:
164 0 : uint32_t ReadIndex() const {
165 0 : return mReadIndex % mLength;
166 : }
167 :
168 0 : uint32_t WriteIndex() const {
169 0 : return mWriteIndex % mLength;
170 : }
171 :
172 0 : uint32_t AvailableSamples() const {
173 0 : return mWriteIndex - mReadIndex;
174 : }
175 :
176 0 : uint32_t EmptySlots() const {
177 0 : return mLength - AvailableSamples();
178 : }
179 :
180 : // Size of one packet of audio, in frames
181 : uint32_t mPacketSize;
182 : // Number of channels of the stream flowing through this packetizer
183 : uint32_t mChannels;
184 : // Two virtual index into the buffer: the read position and the write
185 : // position.
186 : uint64_t mReadIndex;
187 : uint64_t mWriteIndex;
188 : // Storage for the samples
189 : mozilla::UniquePtr<InputType[]> mStorage;
190 : // Length of the buffer, in samples
191 : uint32_t mLength;
192 : };
193 :
194 : } // mozilla
195 :
196 : #endif // AudioPacketizer_h_
|