/work/obj-fuzz/dist/include/AudioConverter.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #if !defined(AudioConverter_h) |
8 | | #define AudioConverter_h |
9 | | |
10 | | #include "MediaInfo.h" |
11 | | |
12 | | // Forward declaration |
13 | | typedef struct SpeexResamplerState_ SpeexResamplerState; |
14 | | |
15 | | namespace mozilla { |
16 | | |
17 | | template <AudioConfig::SampleFormat T> struct AudioDataBufferTypeChooser; |
18 | | template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_U8> |
19 | | { typedef uint8_t Type; }; |
20 | | template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S16> |
21 | | { typedef int16_t Type; }; |
22 | | template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24LSB> |
23 | | { typedef int32_t Type; }; |
24 | | template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24> |
25 | | { typedef int32_t Type; }; |
26 | | template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S32> |
27 | | { typedef int32_t Type; }; |
28 | | template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_FLT> |
29 | | { typedef float Type; }; |
30 | | |
31 | | // 'Value' is the type used externally to deal with stored value. |
32 | | // AudioDataBuffer can perform conversion between different SampleFormat content. |
33 | | template <AudioConfig::SampleFormat Format, typename Value = typename AudioDataBufferTypeChooser<Format>::Type> |
34 | | class AudioDataBuffer |
35 | | { |
36 | | public: |
37 | | AudioDataBuffer() {} |
38 | | AudioDataBuffer(Value* aBuffer, size_t aLength) |
39 | | : mBuffer(aBuffer, aLength) |
40 | | {} |
41 | | explicit AudioDataBuffer(const AudioDataBuffer& aOther) |
42 | | : mBuffer(aOther.mBuffer) |
43 | | {} |
44 | | AudioDataBuffer(AudioDataBuffer&& aOther) |
45 | | : mBuffer(std::move(aOther.mBuffer)) |
46 | 0 | {} |
47 | | template <AudioConfig::SampleFormat OtherFormat, typename OtherValue> |
48 | | explicit AudioDataBuffer(const AudioDataBuffer<OtherFormat, OtherValue>& other) |
49 | | { |
50 | | // TODO: Convert from different type, may use asm routines. |
51 | | MOZ_CRASH("Conversion not implemented yet"); |
52 | | } |
53 | | |
54 | | // A u8, s16 and float aligned buffer can only be treated as |
55 | | // FORMAT_U8, FORMAT_S16 and FORMAT_FLT respectively. |
56 | | // So allow them as copy and move constructors. |
57 | | explicit AudioDataBuffer(const AlignedByteBuffer& aBuffer) |
58 | | : mBuffer(aBuffer) |
59 | | { |
60 | | static_assert(Format == AudioConfig::FORMAT_U8, |
61 | | "Conversion not implemented yet"); |
62 | | } |
63 | | explicit AudioDataBuffer(const AlignedShortBuffer& aBuffer) |
64 | | : mBuffer(aBuffer) |
65 | | { |
66 | | static_assert(Format == AudioConfig::FORMAT_S16, |
67 | | "Conversion not implemented yet"); |
68 | | } |
69 | | explicit AudioDataBuffer(const AlignedFloatBuffer& aBuffer) |
70 | | : mBuffer(aBuffer) |
71 | | { |
72 | | static_assert(Format == AudioConfig::FORMAT_FLT, |
73 | | "Conversion not implemented yet"); |
74 | | } |
75 | | explicit AudioDataBuffer(AlignedByteBuffer&& aBuffer) |
76 | | : mBuffer(std::move(aBuffer)) |
77 | | { |
78 | | static_assert(Format == AudioConfig::FORMAT_U8, |
79 | | "Conversion not implemented yet"); |
80 | | } |
81 | | explicit AudioDataBuffer(AlignedShortBuffer&& aBuffer) |
82 | | : mBuffer(std::move(aBuffer)) |
83 | | { |
84 | | static_assert(Format == AudioConfig::FORMAT_S16, |
85 | | "Conversion not implemented yet"); |
86 | | } |
87 | | explicit AudioDataBuffer(AlignedFloatBuffer&& aBuffer) |
88 | | : mBuffer(std::move(aBuffer)) |
89 | 0 | { |
90 | 0 | static_assert(Format == AudioConfig::FORMAT_FLT, |
91 | 0 | "Conversion not implemented yet"); |
92 | 0 | } |
93 | | AudioDataBuffer& operator=(AudioDataBuffer&& aOther) |
94 | 0 | { |
95 | 0 | mBuffer = std::move(aOther.mBuffer); |
96 | 0 | return *this; |
97 | 0 | } |
98 | | AudioDataBuffer& operator=(const AudioDataBuffer& aOther) |
99 | | { |
100 | | mBuffer = aOther.mBuffer; |
101 | | return *this; |
102 | | } |
103 | | |
104 | 0 | Value* Data() const { return mBuffer.Data(); } |
105 | 0 | size_t Length() const { return mBuffer.Length(); } |
106 | | size_t Size() const { return mBuffer.Size(); } |
107 | | AlignedBuffer<Value> Forget() |
108 | 0 | { |
109 | 0 | // Correct type -> Just give values as-is. |
110 | 0 | return std::move(mBuffer); |
111 | 0 | } |
112 | | private: |
113 | | AlignedBuffer<Value> mBuffer; |
114 | | }; |
115 | | |
116 | | typedef AudioDataBuffer<AudioConfig::FORMAT_DEFAULT> AudioSampleBuffer; |
117 | | |
118 | | class AudioConverter { |
119 | | public: |
120 | | AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut); |
121 | | ~AudioConverter(); |
122 | | |
123 | | // Convert the AudioDataBuffer. |
124 | | // Conversion will be done in place if possible. Otherwise a new buffer will |
125 | | // be returned. |
126 | | // Providing an empty buffer and resampling is expected, the resampler |
127 | | // will be drained. |
128 | | template <AudioConfig::SampleFormat Format, typename Value> |
129 | | AudioDataBuffer<Format, Value> Process(AudioDataBuffer<Format, Value>&& aBuffer) |
130 | 0 | { |
131 | 0 | MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Format); |
132 | 0 | AudioDataBuffer<Format, Value> buffer = std::move(aBuffer); |
133 | 0 | if (CanWorkInPlace()) { |
134 | 0 | AlignedBuffer<Value> temp = buffer.Forget(); |
135 | 0 | Process(temp, temp.Data(), SamplesInToFrames(temp.Length())); |
136 | 0 | return AudioDataBuffer<Format, Value>(std::move(temp));; |
137 | 0 | } |
138 | 0 | return Process(buffer); |
139 | 0 | } |
140 | | |
141 | | template <AudioConfig::SampleFormat Format, typename Value> |
142 | | AudioDataBuffer<Format, Value> Process(const AudioDataBuffer<Format, Value>& aBuffer) |
143 | 0 | { |
144 | 0 | MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Format); |
145 | 0 | // Perform the downmixing / reordering in temporary buffer. |
146 | 0 | size_t frames = SamplesInToFrames(aBuffer.Length()); |
147 | 0 | AlignedBuffer<Value> temp1; |
148 | 0 | if (!temp1.SetLength(FramesOutToSamples(frames))) { |
149 | 0 | return AudioDataBuffer<Format, Value>(std::move(temp1)); |
150 | 0 | } |
151 | 0 | frames = ProcessInternal(temp1.Data(), aBuffer.Data(), frames); |
152 | 0 | if (mIn.Rate() == mOut.Rate()) { |
153 | 0 | MOZ_ALWAYS_TRUE(temp1.SetLength(FramesOutToSamples(frames))); |
154 | 0 | return AudioDataBuffer<Format, Value>(std::move(temp1)); |
155 | 0 | } |
156 | 0 |
|
157 | 0 | // At this point, temp1 contains the buffer reordered and downmixed. |
158 | 0 | // If we are downsampling we can re-use it. |
159 | 0 | AlignedBuffer<Value>* outputBuffer = &temp1; |
160 | 0 | AlignedBuffer<Value> temp2; |
161 | 0 | if (!frames || mOut.Rate() > mIn.Rate()) { |
162 | 0 | // We are upsampling or about to drain, we can't work in place. |
163 | 0 | // Allocate another temporary buffer where the upsampling will occur. |
164 | 0 | if (!temp2.SetLength(FramesOutToSamples(ResampleRecipientFrames(frames)))) { |
165 | 0 | return AudioDataBuffer<Format, Value>(std::move(temp2)); |
166 | 0 | } |
167 | 0 | outputBuffer = &temp2; |
168 | 0 | } |
169 | 0 | if (!frames) { |
170 | 0 | frames = DrainResampler(outputBuffer->Data()); |
171 | 0 | } else { |
172 | 0 | frames = ResampleAudio(outputBuffer->Data(), temp1.Data(), frames); |
173 | 0 | } |
174 | 0 | MOZ_ALWAYS_TRUE(outputBuffer->SetLength(FramesOutToSamples(frames))); |
175 | 0 | return AudioDataBuffer<Format, Value>(std::move(*outputBuffer)); |
176 | 0 | } |
177 | | |
178 | | // Attempt to convert the AudioDataBuffer in place. |
179 | | // Will return 0 if the conversion wasn't possible. |
180 | | template <typename Value> |
181 | | size_t Process(Value* aBuffer, size_t aFrames) |
182 | | { |
183 | | MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format()); |
184 | | if (!CanWorkInPlace()) { |
185 | | return 0; |
186 | | } |
187 | | size_t frames = ProcessInternal(aBuffer, aBuffer, aFrames); |
188 | | if (frames && mIn.Rate() != mOut.Rate()) { |
189 | | frames = ResampleAudio(aBuffer, aBuffer, aFrames); |
190 | | } |
191 | | return frames; |
192 | | } |
193 | | |
194 | | template <typename Value> |
195 | | size_t Process(AlignedBuffer<Value>& aOutBuffer, const Value* aInBuffer, size_t aFrames) |
196 | 0 | { |
197 | 0 | MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format()); |
198 | 0 | MOZ_ASSERT((aFrames && aInBuffer) || !aFrames); |
199 | 0 | // Up/down mixing first |
200 | 0 | if (!aOutBuffer.SetLength(FramesOutToSamples(aFrames))) { |
201 | 0 | MOZ_ALWAYS_TRUE(aOutBuffer.SetLength(0)); |
202 | 0 | return 0; |
203 | 0 | } |
204 | 0 | size_t frames = ProcessInternal(aOutBuffer.Data(), aInBuffer, aFrames); |
205 | 0 | MOZ_ASSERT(frames == aFrames); |
206 | 0 | // Check if resampling is needed |
207 | 0 | if (mIn.Rate() == mOut.Rate()) { |
208 | 0 | return frames; |
209 | 0 | } |
210 | 0 | // Prepare output in cases of drain or up-sampling |
211 | 0 | if ((!frames || mOut.Rate() > mIn.Rate()) && |
212 | 0 | !aOutBuffer.SetLength(FramesOutToSamples(ResampleRecipientFrames(frames)))) { |
213 | 0 | MOZ_ALWAYS_TRUE(aOutBuffer.SetLength(0)); |
214 | 0 | return 0; |
215 | 0 | } |
216 | 0 | if (!frames) { |
217 | 0 | frames = DrainResampler(aOutBuffer.Data()); |
218 | 0 | } else { |
219 | 0 | frames = ResampleAudio(aOutBuffer.Data(), aInBuffer, frames); |
220 | 0 | } |
221 | 0 | // Update with the actual buffer length |
222 | 0 | MOZ_ALWAYS_TRUE(aOutBuffer.SetLength(FramesOutToSamples(frames))); |
223 | 0 | return frames; |
224 | 0 | } |
225 | | |
226 | | bool CanWorkInPlace() const; |
227 | | bool CanReorderAudio() const |
228 | | { |
229 | | return mIn.Layout().MappingTable(mOut.Layout()); |
230 | | } |
231 | | |
232 | | const AudioConfig& InputConfig() const { return mIn; } |
233 | | const AudioConfig& OutputConfig() const { return mOut; } |
234 | | |
235 | | private: |
236 | | const AudioConfig mIn; |
237 | | const AudioConfig mOut; |
238 | | // mChannelOrderMap will be empty if we do not know how to proceed with this |
239 | | // channel layout. |
240 | | AutoTArray<uint8_t, AudioConfig::ChannelLayout::MAX_CHANNELS> mChannelOrderMap; |
241 | | /** |
242 | | * ProcessInternal |
243 | | * Parameters: |
244 | | * aOut : destination buffer where converted samples will be copied |
245 | | * aIn : source buffer |
246 | | * aSamples: number of frames in source buffer |
247 | | * |
248 | | * Return Value: number of frames converted or 0 if error |
249 | | */ |
250 | | size_t ProcessInternal(void* aOut, const void* aIn, size_t aFrames); |
251 | | void ReOrderInterleavedChannels(void* aOut, const void* aIn, size_t aFrames) const; |
252 | | size_t DownmixAudio(void* aOut, const void* aIn, size_t aFrames) const; |
253 | | size_t UpmixAudio(void* aOut, const void* aIn, size_t aFrames) const; |
254 | | |
255 | | size_t FramesOutToSamples(size_t aFrames) const; |
256 | | size_t SamplesInToFrames(size_t aSamples) const; |
257 | | size_t FramesOutToBytes(size_t aFrames) const; |
258 | | |
259 | | // Resampler context. |
260 | | SpeexResamplerState* mResampler; |
261 | | size_t ResampleAudio(void* aOut, const void* aIn, size_t aFrames); |
262 | | size_t ResampleRecipientFrames(size_t aFrames) const; |
263 | | void RecreateResampler(); |
264 | | size_t DrainResampler(void* aOut); |
265 | | }; |
266 | | |
267 | | } // namespace mozilla |
268 | | |
269 | | #endif /* AudioConverter_h */ |