1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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 https://mozilla.org/MPL/2.0/. */
6 #ifndef MOZILLA_AUDIOSAMPLEFORMAT_H_
7 #define MOZILLA_AUDIOSAMPLEFORMAT_H_
9 #include "mozilla/Assertions.h"
10 #include "mozilla/PodOperations.h"
12 #include <type_traits>
18 * Audio formats supported in MediaTracks and media elements.
20 * Only one of these is supported by AudioStream, and that is determined
21 * at compile time (roughly, FLOAT32 on desktops, S16 on mobile). Media decoders
22 * produce that format only; queued AudioData always uses that format.
24 enum AudioSampleFormat
{
25 // Silence: format will be chosen later
27 // Native-endian signed 16-bit audio samples
29 // Signed 32-bit float samples
31 // The format used for output by AudioStream.
32 AUDIO_OUTPUT_FORMAT
= AUDIO_FORMAT_FLOAT32
35 enum { MAX_AUDIO_SAMPLE_SIZE
= sizeof(float) };
37 template <AudioSampleFormat Format
>
38 class AudioSampleTraits
;
41 class AudioSampleTraits
<AUDIO_FORMAT_FLOAT32
> {
46 class AudioSampleTraits
<AUDIO_FORMAT_S16
> {
51 using AudioDataValue
= AudioSampleTraits
<AUDIO_OUTPUT_FORMAT
>::Type
;
54 class AudioSampleTypeToFormat
;
57 class AudioSampleTypeToFormat
<float> {
59 static const AudioSampleFormat Format
= AUDIO_FORMAT_FLOAT32
;
63 class AudioSampleTypeToFormat
<short> {
65 static const AudioSampleFormat Format
= AUDIO_FORMAT_S16
;
69 constexpr float MaxAsFloat() {
70 return static_cast<float>(std::numeric_limits
<T
>::max());
74 constexpr float LowestAsFloat() {
75 return static_cast<float>(std::numeric_limits
<T
>::lowest());
78 // The maximum value for an audio sample. If T is signed, the absolute value of
79 // this number is smaller (by exactly 1) than ::Min().
82 return std::numeric_limits
<T
>::max();
85 // The minimum value for an audio sample. If T is signed, the absolute value of
86 // this number is greater (by exactly 1) than ::Max()
89 return std::numeric_limits
<T
>::lowest();
93 constexpr float Max
<float>() {
98 constexpr float Min
<float>() {
102 // The bias value is the middle of the range. In linear PCM audio, if the
103 // values are all equal to the bias value, the audio is silent.
104 template <typename T
>
110 constexpr uint8_t Bias
<uint8_t>() {
114 // Clip a floating point audio sample to its nominal range. This is
115 // destructive, and is only used here for avoiding overflow in some edge cases,
116 // so it's not going to be generally audible.
117 inline float Clip(float aValue
) { return std::clamp(aValue
, -1.0f
, 1.0f
); }
119 template <typename T
>
120 T
FloatToAudioSample(float aValue
) {
121 if constexpr (std::is_same_v
<float, T
>) {
124 if constexpr (std::is_same_v
<uint8_t, T
>) {
125 return static_cast<T
>(std::clamp((aValue
+ 1.0f
) * 128.f
,
126 LowestAsFloat
<T
>(), MaxAsFloat
<T
>()));
127 } else if constexpr (std::is_same_v
<int16_t, T
>) {
128 // This produces correct results accross the range.
129 return static_cast<T
>(std::clamp(aValue
* -LowestAsFloat
<T
>(),
130 LowestAsFloat
<T
>(), MaxAsFloat
<T
>()));
131 } else if constexpr (std::is_same_v
<int32_t, T
>) {
132 // We need to handle this differently because of rounding between INT32_MAX
133 // and float 32-bits, to maximise precision.
135 // if the input sample is greater OR EQUAL to 1.0, then clip and return
138 return std::numeric_limits
<T
>::max();
140 // otherwise cast to a double and map to the positive range.
141 // float 32-bits cannot represent int32_max (but can represent int32_min)
142 constexpr double magnitudePos
= std::numeric_limits
<int32_t>::max();
143 return static_cast<int32_t>(aValue
* magnitudePos
);
145 // Similarly for the negative range.
146 if (aValue
<= -1.0) {
147 return std::numeric_limits
<T
>::lowest();
149 constexpr double magnitudeNegative
=
150 -1.0 * std::numeric_limits
<int32_t>::lowest();
151 return static_cast<int32_t>(aValue
* magnitudeNegative
);
155 template <typename T
>
156 T
UInt8bitToAudioSample(uint8_t aValue
) {
157 if constexpr (std::is_same_v
<uint8_t, T
>) {
159 } else if constexpr (std::is_same_v
<int16_t, T
>) {
160 return (static_cast<int16_t>(aValue
) << 8) - (1 << 15);
161 } else if constexpr (std::is_same_v
<int32_t, T
>) {
162 return (static_cast<int32_t>(aValue
) << 24) - (1 << 31);
163 } else if constexpr (std::is_same_v
<float, T
>) {
164 float biased
= static_cast<float>(aValue
) - Bias
<uint8_t>();
165 if (aValue
>= Bias
<uint8_t>()) {
166 return Clip(biased
/ MaxAsFloat
<int8_t>());
168 return Clip(biased
/ -LowestAsFloat
<int8_t>());
172 template <typename T
>
173 T
Int16ToAudioSample(int16_t aValue
) {
174 if constexpr (std::is_same_v
<uint8_t, T
>) {
175 return static_cast<uint8_t>(aValue
>> 8) + 128;
176 } else if constexpr (std::is_same_v
<int16_t, T
>) {
178 } else if constexpr (std::is_same_v
<int32_t, T
>) {
180 } else if constexpr (std::is_same_v
<float, T
>) {
182 return Clip(static_cast<float>(aValue
) / MaxAsFloat
<int16_t>());
184 return Clip(static_cast<float>(aValue
) / -LowestAsFloat
<int16_t>());
188 // 24-bits audio samples are stored in 32-bits variables.
189 template <typename T
>
190 T
Int24ToAudioSample(int32_t aValue
) {
191 if constexpr (std::is_same_v
<uint8_t, T
>) {
192 return static_cast<uint8_t>(aValue
>> 16) + 128;
193 } else if constexpr (std::is_same_v
<int16_t, T
>) {
194 return static_cast<int16_t>(aValue
>> 8);
195 } else if constexpr (std::is_same_v
<int32_t, T
>) {
197 } else if constexpr (std::is_same_v
<float, T
>) {
198 const int32_t min
= -(2 << 22);
199 const int32_t max
= (2 << 22) - 1;
201 return Clip(static_cast<float>(aValue
) / static_cast<float>(max
));
203 return Clip(static_cast<float>(aValue
) / -static_cast<float>(min
));
207 template <typename T
>
208 T
Int32ToAudioSample(int32_t aValue
) {
209 if constexpr (std::is_same_v
<uint8_t, T
>) {
210 return static_cast<uint8_t>(aValue
>> 24) + 128;
211 } else if constexpr (std::is_same_v
<int16_t, T
>) {
213 } else if constexpr (std::is_same_v
<int32_t, T
>) {
215 } else if constexpr (std::is_same_v
<float, T
>) {
217 return Clip(static_cast<float>(aValue
) / MaxAsFloat
<int32_t>());
219 return Clip(static_cast<float>(aValue
) / -LowestAsFloat
<int32_t>());
223 // This does not handle 24-bits audio, call the function explicitly when
225 template <typename D
, typename S
>
226 inline D
ConvertAudioSample(const S
& aSource
) {
227 if constexpr (std::is_same_v
<S
, D
>) {
229 } else if constexpr (std::is_same_v
<S
, uint8_t>) {
230 return UInt8bitToAudioSample
<D
>(aSource
);
231 } else if constexpr (std::is_same_v
<S
, int16_t>) {
232 return Int16ToAudioSample
<D
>(aSource
);
233 } else if constexpr (std::is_same_v
<S
, int32_t>) {
234 return Int32ToAudioSample
<D
>(aSource
);
235 } else if constexpr (std::is_same_v
<S
, float>) {
236 return FloatToAudioSample
<D
>(aSource
);
240 // Sample buffer conversion
241 template <typename From
, typename To
>
242 inline void ConvertAudioSamples(const From
* aFrom
, To
* aTo
, int aCount
) {
243 if constexpr (std::is_same_v
<From
, To
>) {
244 PodCopy(aTo
, aFrom
, aCount
);
247 for (int i
= 0; i
< aCount
; ++i
) {
248 aTo
[i
] = ConvertAudioSample
<To
>(aFrom
[i
]);
252 // Sample buffer conversion with scale
253 template <typename From
, typename To
>
254 inline void ConvertAudioSamplesWithScale(const From
* aFrom
, To
* aTo
, int aCount
,
256 if (aScale
== 1.0f
) {
257 ConvertAudioSamples(aFrom
, aTo
, aCount
);
260 for (int i
= 0; i
< aCount
; ++i
) {
262 ConvertAudioSample
<To
>(ConvertAudioSample
<float>(aFrom
[i
]) * aScale
);
265 inline void ConvertAudioSamplesWithScale(const int16_t* aFrom
, int16_t* aTo
,
266 int aCount
, float aScale
) {
267 if (aScale
== 1.0f
) {
268 ConvertAudioSamples(aFrom
, aTo
, aCount
);
271 if (0.0f
<= aScale
&& aScale
< 1.0f
) {
272 int32_t scale
= int32_t((1 << 16) * aScale
);
273 for (int i
= 0; i
< aCount
; ++i
) {
274 aTo
[i
] = int16_t((int32_t(aFrom
[i
]) * scale
) >> 16);
278 for (int i
= 0; i
< aCount
; ++i
) {
279 aTo
[i
] = FloatToAudioSample
<int16_t>(ConvertAudioSample
<float>(aFrom
[i
]) *
284 template <typename From
, typename To
>
285 inline void AddAudioSamplesWithScale(const From
* aFrom
, To
* aTo
, int aCount
,
287 for (int i
= 0; i
< aCount
; ++i
) {
289 ConvertAudioSample
<To
>(ConvertAudioSample
<float>(aTo
[i
]) +
290 ConvertAudioSample
<float>(aFrom
[i
]) * aScale
);
294 // In place audio sample scaling.
295 inline void ScaleAudioSamples(float* aBuffer
, int aCount
, float aScale
) {
296 for (int32_t i
= 0; i
< aCount
; ++i
) {
297 aBuffer
[i
] *= aScale
;
301 inline void ScaleAudioSamples(short* aBuffer
, int aCount
, float aScale
) {
302 int32_t volume
= int32_t((1 << 16) * aScale
);
303 for (int32_t i
= 0; i
< aCount
; ++i
) {
304 aBuffer
[i
] = short((int32_t(aBuffer
[i
]) * volume
) >> 16);
308 inline const void* AddAudioSampleOffset(const void* aBase
,
309 AudioSampleFormat aFormat
,
311 static_assert(AUDIO_FORMAT_S16
== 1, "Bad constant");
312 static_assert(AUDIO_FORMAT_FLOAT32
== 2, "Bad constant");
313 MOZ_ASSERT(aFormat
== AUDIO_FORMAT_S16
|| aFormat
== AUDIO_FORMAT_FLOAT32
);
315 return static_cast<const uint8_t*>(aBase
) + aFormat
* 2 * aOffset
;
318 } // namespace mozilla
320 #endif /* MOZILLA_AUDIOSAMPLEFORMAT_H_ */