/src/mozilla-central/dom/media/AudioPacketizer.h
Line | Count | Source (jump to first uncovered line) |
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 | | { |
39 | | public: |
40 | | 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 | | , mStorage(new InputType[aPacketSize * aChannels]) |
47 | | , mLength(aPacketSize * aChannels) |
48 | 0 | { |
49 | 0 | MOZ_ASSERT(aPacketSize > 0 && aChannels > 0, |
50 | 0 | "The packet size and the number of channel should be strictly positive"); |
51 | 0 | } Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::AudioPacketizer(unsigned int, unsigned int) Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::AudioPacketizer(unsigned int, unsigned int) |
52 | | |
53 | | void Input(const InputType* aFrames, uint32_t aFrameCount) |
54 | 0 | { |
55 | 0 | uint32_t inputSamples = aFrameCount * mChannels; |
56 | 0 | // Need to grow the storage. This should rarely happen, if at all, once the |
57 | 0 | // array has the right size. |
58 | 0 | if (inputSamples > EmptySlots()) { |
59 | 0 | // Calls to Input and Output are roughtly interleaved |
60 | 0 | // (Input,Output,Input,Output, etc.), or balanced |
61 | 0 | // (Input,Input,Input,Output,Output,Output), so we update the buffer to |
62 | 0 | // 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 = std::move(mStorage); |
66 | 0 | mStorage = mozilla::MakeUnique<InputType[]>(newLength); |
67 | 0 | // 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 | 0 | } 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 | 0 | firstPartLength); |
78 | 0 | PodCopy(mStorage.get() + firstPartLength, |
79 | 0 | oldStorage.get(), |
80 | 0 | secondPartLength); |
81 | 0 | } |
82 | 0 | mWriteIndex = toCopy; |
83 | 0 | mReadIndex = 0; |
84 | 0 | mLength = newLength; |
85 | 0 | } |
86 | 0 |
|
87 | 0 | if (WriteIndex() + inputSamples <= mLength) { |
88 | 0 | PodCopy(mStorage.get() + WriteIndex(), aFrames, aFrameCount * mChannels); |
89 | 0 | } 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 | 0 | } |
95 | 0 |
|
96 | 0 | mWriteIndex += inputSamples; |
97 | 0 | } Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::Input(short const*, unsigned int) Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::Input(float const*, unsigned int) |
98 | | |
99 | | OutputType* Output() |
100 | 0 | { |
101 | 0 | uint32_t samplesNeeded = mPacketSize * mChannels; |
102 | 0 | OutputType* out = new OutputType[samplesNeeded]; |
103 | 0 |
|
104 | 0 | Output(out); |
105 | 0 |
|
106 | 0 | return out; |
107 | 0 | } |
108 | | |
109 | | void Output(OutputType* aOutputBuffer) |
110 | 0 | { |
111 | 0 | uint32_t samplesNeeded = mPacketSize * mChannels; |
112 | 0 |
|
113 | 0 | // 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 | | uint32_t zeros = samplesNeeded - AvailableSamples(); |
123 | 0 | PodZero(aOutputBuffer + AvailableSamples(), zeros); |
124 | 0 | samplesNeeded -= zeros; |
125 | 0 | } |
126 | 0 | if (ReadIndex() + samplesNeeded <= mLength) { |
127 | 0 | ConvertAudioSamples<InputType,OutputType>(mStorage.get() + ReadIndex(), |
128 | 0 | aOutputBuffer, |
129 | 0 | samplesNeeded); |
130 | 0 | } else { |
131 | 0 | uint32_t firstPartLength = mLength - ReadIndex(); |
132 | 0 | uint32_t secondPartLength = samplesNeeded - firstPartLength; |
133 | 0 | ConvertAudioSamples<InputType, OutputType>(mStorage.get() + ReadIndex(), |
134 | 0 | aOutputBuffer, |
135 | 0 | firstPartLength); |
136 | 0 | ConvertAudioSamples<InputType, OutputType>(mStorage.get(), |
137 | 0 | aOutputBuffer + firstPartLength, |
138 | 0 | secondPartLength); |
139 | 0 | } |
140 | 0 | mReadIndex += samplesNeeded; |
141 | 0 | } Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::Output(short*) Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::Output(float*) |
142 | | |
143 | 0 | uint32_t PacketsAvailable() const { |
144 | 0 | return AvailableSamples() / mChannels / mPacketSize; |
145 | 0 | } Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::PacketsAvailable() const Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::PacketsAvailable() const |
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 | 0 | } Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::PacketSize() const Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::PacketSize() const |
158 | | |
159 | 0 | uint32_t Channels() const { |
160 | 0 | return mChannels; |
161 | 0 | } Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::Channels() const Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::Channels() const |
162 | | |
163 | | private: |
164 | 0 | uint32_t ReadIndex() const { |
165 | 0 | return mReadIndex % mLength; |
166 | 0 | } Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::ReadIndex() const Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::ReadIndex() const |
167 | | |
168 | 0 | uint32_t WriteIndex() const { |
169 | 0 | return mWriteIndex % mLength; |
170 | 0 | } Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::WriteIndex() const Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::WriteIndex() const |
171 | | |
172 | 0 | uint32_t AvailableSamples() const { |
173 | 0 | return mWriteIndex - mReadIndex; |
174 | 0 | } Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::AvailableSamples() const Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::AvailableSamples() const |
175 | | |
176 | 0 | uint32_t EmptySlots() const { |
177 | 0 | return mLength - AvailableSamples(); |
178 | 0 | } Unexecuted instantiation: mozilla::AudioPacketizer<short, short>::EmptySlots() const Unexecuted instantiation: mozilla::AudioPacketizer<float, float>::EmptySlots() const |
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_ |