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/. */
7 #if !defined(AudioConverter_h)
8 # define AudioConverter_h
10 # include "MediaInfo.h"
12 // Forward declaration
13 typedef struct SpeexResamplerState_ SpeexResamplerState
;
17 template <AudioConfig::SampleFormat T
>
18 struct AudioDataBufferTypeChooser
;
20 struct AudioDataBufferTypeChooser
<AudioConfig::FORMAT_U8
> {
24 struct AudioDataBufferTypeChooser
<AudioConfig::FORMAT_S16
> {
28 struct AudioDataBufferTypeChooser
<AudioConfig::FORMAT_S24LSB
> {
32 struct AudioDataBufferTypeChooser
<AudioConfig::FORMAT_S24
> {
36 struct AudioDataBufferTypeChooser
<AudioConfig::FORMAT_S32
> {
40 struct AudioDataBufferTypeChooser
<AudioConfig::FORMAT_FLT
> {
44 // 'Value' is the type used externally to deal with stored value.
45 // AudioDataBuffer can perform conversion between different SampleFormat
47 template <AudioConfig::SampleFormat Format
,
48 typename Value
= typename AudioDataBufferTypeChooser
<Format
>::Type
>
49 class AudioDataBuffer
{
51 AudioDataBuffer() = default;
52 AudioDataBuffer(Value
* aBuffer
, size_t aLength
) : mBuffer(aBuffer
, aLength
) {}
53 explicit AudioDataBuffer(const AudioDataBuffer
& aOther
)
54 : mBuffer(aOther
.mBuffer
) {}
55 AudioDataBuffer(AudioDataBuffer
&& aOther
)
56 : mBuffer(std::move(aOther
.mBuffer
)) {}
57 template <AudioConfig::SampleFormat OtherFormat
, typename OtherValue
>
58 explicit AudioDataBuffer(
59 const AudioDataBuffer
<OtherFormat
, OtherValue
>& other
) {
60 // TODO: Convert from different type, may use asm routines.
61 MOZ_CRASH("Conversion not implemented yet");
64 // A u8, s16 and float aligned buffer can only be treated as
65 // FORMAT_U8, FORMAT_S16 and FORMAT_FLT respectively.
66 // So allow them as copy and move constructors.
67 explicit AudioDataBuffer(const AlignedByteBuffer
& aBuffer
)
69 static_assert(Format
== AudioConfig::FORMAT_U8
,
70 "Conversion not implemented yet");
72 explicit AudioDataBuffer(const AlignedShortBuffer
& aBuffer
)
74 static_assert(Format
== AudioConfig::FORMAT_S16
,
75 "Conversion not implemented yet");
77 explicit AudioDataBuffer(const AlignedFloatBuffer
& aBuffer
)
79 static_assert(Format
== AudioConfig::FORMAT_FLT
,
80 "Conversion not implemented yet");
82 explicit AudioDataBuffer(AlignedByteBuffer
&& aBuffer
)
83 : mBuffer(std::move(aBuffer
)) {
84 static_assert(Format
== AudioConfig::FORMAT_U8
,
85 "Conversion not implemented yet");
87 explicit AudioDataBuffer(AlignedShortBuffer
&& aBuffer
)
88 : mBuffer(std::move(aBuffer
)) {
89 static_assert(Format
== AudioConfig::FORMAT_S16
,
90 "Conversion not implemented yet");
92 explicit AudioDataBuffer(AlignedFloatBuffer
&& aBuffer
)
93 : mBuffer(std::move(aBuffer
)) {
94 static_assert(Format
== AudioConfig::FORMAT_FLT
,
95 "Conversion not implemented yet");
97 AudioDataBuffer
& operator=(AudioDataBuffer
&& aOther
) {
98 mBuffer
= std::move(aOther
.mBuffer
);
101 AudioDataBuffer
& operator=(const AudioDataBuffer
& aOther
) {
102 mBuffer
= aOther
.mBuffer
;
106 Value
* Data() const { return mBuffer
.Data(); }
107 size_t Length() const { return mBuffer
.Length(); }
108 size_t Size() const { return mBuffer
.Size(); }
109 AlignedBuffer
<Value
> Forget() {
110 // Correct type -> Just give values as-is.
111 return std::move(mBuffer
);
115 AlignedBuffer
<Value
> mBuffer
;
118 typedef AudioDataBuffer
<AudioConfig::FORMAT_DEFAULT
> AudioSampleBuffer
;
120 class AudioConverter
{
122 AudioConverter(const AudioConfig
& aIn
, const AudioConfig
& aOut
);
125 // Convert the AudioDataBuffer.
126 // Conversion will be done in place if possible. Otherwise a new buffer will
128 // Providing an empty buffer and resampling is expected, the resampler
130 template <AudioConfig::SampleFormat Format
, typename Value
>
131 AudioDataBuffer
<Format
, Value
> Process(
132 AudioDataBuffer
<Format
, Value
>&& aBuffer
) {
133 MOZ_DIAGNOSTIC_ASSERT(mIn
.Format() == mOut
.Format() &&
134 mIn
.Format() == Format
);
135 AudioDataBuffer
<Format
, Value
> buffer
= std::move(aBuffer
);
136 if (CanWorkInPlace()) {
137 AlignedBuffer
<Value
> temp
= buffer
.Forget();
138 Process(temp
, temp
.Data(), SamplesInToFrames(temp
.Length()));
139 return AudioDataBuffer
<Format
, Value
>(std::move(temp
));
142 return Process(buffer
);
145 template <AudioConfig::SampleFormat Format
, typename Value
>
146 AudioDataBuffer
<Format
, Value
> Process(
147 const AudioDataBuffer
<Format
, Value
>& aBuffer
) {
148 MOZ_DIAGNOSTIC_ASSERT(mIn
.Format() == mOut
.Format() &&
149 mIn
.Format() == Format
);
150 // Perform the downmixing / reordering in temporary buffer.
151 size_t frames
= SamplesInToFrames(aBuffer
.Length());
152 AlignedBuffer
<Value
> temp1
;
153 if (!temp1
.SetLength(FramesOutToSamples(frames
))) {
154 return AudioDataBuffer
<Format
, Value
>(std::move(temp1
));
156 frames
= ProcessInternal(temp1
.Data(), aBuffer
.Data(), frames
);
157 if (mIn
.Rate() == mOut
.Rate()) {
158 MOZ_ALWAYS_TRUE(temp1
.SetLength(FramesOutToSamples(frames
)));
159 return AudioDataBuffer
<Format
, Value
>(std::move(temp1
));
162 // At this point, temp1 contains the buffer reordered and downmixed.
163 // If we are downsampling we can re-use it.
164 AlignedBuffer
<Value
>* outputBuffer
= &temp1
;
165 AlignedBuffer
<Value
> temp2
;
166 if (!frames
|| mOut
.Rate() > mIn
.Rate()) {
167 // We are upsampling or about to drain, we can't work in place.
168 // Allocate another temporary buffer where the upsampling will occur.
169 if (!temp2
.SetLength(
170 FramesOutToSamples(ResampleRecipientFrames(frames
)))) {
171 return AudioDataBuffer
<Format
, Value
>(std::move(temp2
));
173 outputBuffer
= &temp2
;
176 frames
= DrainResampler(outputBuffer
->Data());
178 frames
= ResampleAudio(outputBuffer
->Data(), temp1
.Data(), frames
);
180 MOZ_ALWAYS_TRUE(outputBuffer
->SetLength(FramesOutToSamples(frames
)));
181 return AudioDataBuffer
<Format
, Value
>(std::move(*outputBuffer
));
184 // Attempt to convert the AudioDataBuffer in place.
185 // Will return 0 if the conversion wasn't possible.
186 template <typename Value
>
187 size_t Process(Value
* aBuffer
, size_t aFrames
) {
188 MOZ_DIAGNOSTIC_ASSERT(mIn
.Format() == mOut
.Format());
189 if (!CanWorkInPlace()) {
192 size_t frames
= ProcessInternal(aBuffer
, aBuffer
, aFrames
);
193 if (frames
&& mIn
.Rate() != mOut
.Rate()) {
194 frames
= ResampleAudio(aBuffer
, aBuffer
, aFrames
);
199 template <typename Value
>
200 size_t Process(AlignedBuffer
<Value
>& aOutBuffer
, const Value
* aInBuffer
,
202 MOZ_DIAGNOSTIC_ASSERT(mIn
.Format() == mOut
.Format());
203 MOZ_ASSERT((aFrames
&& aInBuffer
) || !aFrames
);
204 // Up/down mixing first
205 if (!aOutBuffer
.SetLength(FramesOutToSamples(aFrames
))) {
206 MOZ_ALWAYS_TRUE(aOutBuffer
.SetLength(0));
209 size_t frames
= ProcessInternal(aOutBuffer
.Data(), aInBuffer
, aFrames
);
210 MOZ_ASSERT(frames
== aFrames
);
211 // Check if resampling is needed
212 if (mIn
.Rate() == mOut
.Rate()) {
215 // Prepare output in cases of drain or up-sampling
216 if ((!frames
|| mOut
.Rate() > mIn
.Rate()) &&
217 !aOutBuffer
.SetLength(
218 FramesOutToSamples(ResampleRecipientFrames(frames
)))) {
219 MOZ_ALWAYS_TRUE(aOutBuffer
.SetLength(0));
223 frames
= DrainResampler(aOutBuffer
.Data());
225 frames
= ResampleAudio(aOutBuffer
.Data(), aInBuffer
, frames
);
227 // Update with the actual buffer length
228 MOZ_ALWAYS_TRUE(aOutBuffer
.SetLength(FramesOutToSamples(frames
)));
232 bool CanWorkInPlace() const;
233 bool CanReorderAudio() const {
234 return mIn
.Layout().MappingTable(mOut
.Layout());
236 static bool CanConvert(const AudioConfig
& aIn
, const AudioConfig
& aOut
);
238 const AudioConfig
& InputConfig() const { return mIn
; }
239 const AudioConfig
& OutputConfig() const { return mOut
; }
242 const AudioConfig mIn
;
243 const AudioConfig mOut
;
244 // mChannelOrderMap will be empty if we do not know how to proceed with this
246 AutoTArray
<uint8_t, AudioConfig::ChannelLayout::MAX_CHANNELS
>
251 * aOut : destination buffer where converted samples will be copied
252 * aIn : source buffer
253 * aSamples: number of frames in source buffer
255 * Return Value: number of frames converted or 0 if error
257 size_t ProcessInternal(void* aOut
, const void* aIn
, size_t aFrames
);
258 void ReOrderInterleavedChannels(void* aOut
, const void* aIn
,
259 size_t aFrames
) const;
260 size_t DownmixAudio(void* aOut
, const void* aIn
, size_t aFrames
) const;
261 size_t UpmixAudio(void* aOut
, const void* aIn
, size_t aFrames
) const;
263 size_t FramesOutToSamples(size_t aFrames
) const;
264 size_t SamplesInToFrames(size_t aSamples
) const;
265 size_t FramesOutToBytes(size_t aFrames
) const;
267 // Resampler context.
268 SpeexResamplerState
* mResampler
;
269 size_t ResampleAudio(void* aOut
, const void* aIn
, size_t aFrames
);
270 size_t ResampleRecipientFrames(size_t aFrames
) const;
271 void RecreateResampler();
272 size_t DrainResampler(void* aOut
);
275 } // namespace mozilla
277 #endif /* AudioConverter_h */