Backed out changeset 06f41c22f3a6 (bug 1888460) for causing linux xpcshell failures...
[gecko.git] / dom / media / AudioSampleFormat.h
blob7a329d06dfb9adf51b50107a88388e2d39aeb2da
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"
11 #include <algorithm>
12 #include <type_traits>
13 #include <limits>
15 namespace mozilla {
17 /**
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
26 AUDIO_FORMAT_SILENCE,
27 // Native-endian signed 16-bit audio samples
28 AUDIO_FORMAT_S16,
29 // Signed 32-bit float samples
30 AUDIO_FORMAT_FLOAT32,
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;
40 template <>
41 class AudioSampleTraits<AUDIO_FORMAT_FLOAT32> {
42 public:
43 using Type = float;
45 template <>
46 class AudioSampleTraits<AUDIO_FORMAT_S16> {
47 public:
48 using Type = int16_t;
51 using AudioDataValue = AudioSampleTraits<AUDIO_OUTPUT_FORMAT>::Type;
53 template <typename T>
54 class AudioSampleTypeToFormat;
56 template <>
57 class AudioSampleTypeToFormat<float> {
58 public:
59 static const AudioSampleFormat Format = AUDIO_FORMAT_FLOAT32;
62 template <>
63 class AudioSampleTypeToFormat<short> {
64 public:
65 static const AudioSampleFormat Format = AUDIO_FORMAT_S16;
68 template <typename T>
69 constexpr float MaxAsFloat() {
70 return static_cast<float>(std::numeric_limits<T>::max());
73 template <typename T>
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().
80 template <typename T>
81 constexpr T Max() {
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()
87 template <typename T>
88 constexpr T Min() {
89 return std::numeric_limits<T>::lowest();
92 template <>
93 constexpr float Max<float>() {
94 return 1.0f;
97 template <>
98 constexpr float Min<float>() {
99 return -1.0f;
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>
105 constexpr T Bias() {
106 return 0;
109 template <>
110 constexpr uint8_t Bias<uint8_t>() {
111 return 128;
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>) {
122 return aValue;
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.
134 if (aValue >= 0.) {
135 // if the input sample is greater OR EQUAL to 1.0, then clip and return
136 // the max value.
137 if (aValue >= 1.0) {
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>) {
158 return aValue;
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>) {
177 return aValue;
178 } else if constexpr (std::is_same_v<int32_t, T>) {
179 return aValue << 16;
180 } else if constexpr (std::is_same_v<float, T>) {
181 if (aValue >= 0) {
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>) {
196 return aValue << 8;
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;
200 if (aValue >= 0) {
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>) {
212 return aValue >> 16;
213 } else if constexpr (std::is_same_v<int32_t, T>) {
214 return aValue;
215 } else if constexpr (std::is_same_v<float, T>) {
216 if (aValue >= 0) {
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
224 // needed.
225 template <typename D, typename S>
226 inline D ConvertAudioSample(const S& aSource) {
227 if constexpr (std::is_same_v<S, D>) {
228 return aSource;
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);
245 return;
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,
255 float aScale) {
256 if (aScale == 1.0f) {
257 ConvertAudioSamples(aFrom, aTo, aCount);
258 return;
260 for (int i = 0; i < aCount; ++i) {
261 aTo[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);
269 return;
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);
276 return;
278 for (int i = 0; i < aCount; ++i) {
279 aTo[i] = FloatToAudioSample<int16_t>(ConvertAudioSample<float>(aFrom[i]) *
280 aScale);
284 template <typename From, typename To>
285 inline void AddAudioSamplesWithScale(const From* aFrom, To* aTo, int aCount,
286 float aScale) {
287 for (int i = 0; i < aCount; ++i) {
288 aTo[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,
310 int32_t aOffset) {
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_ */