AudioSampleFormat.h (10387B)
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_ 8 9 #include <algorithm> 10 #include <limits> 11 #include <type_traits> 12 13 #include "mozilla/Assertions.h" 14 #include "mozilla/PodOperations.h" 15 16 namespace mozilla { 17 18 /** 19 * Audio formats supported in MediaTracks and media elements. 20 * 21 * Only one of these is supported by AudioStream, and that is determined 22 * at compile time (roughly, FLOAT32 on desktops, S16 on mobile). Media decoders 23 * produce that format only; queued AudioData always uses that format. 24 */ 25 enum AudioSampleFormat { 26 // Silence: format will be chosen later 27 AUDIO_FORMAT_SILENCE, 28 // Native-endian signed 16-bit audio samples 29 AUDIO_FORMAT_S16, 30 // Signed 32-bit float samples 31 AUDIO_FORMAT_FLOAT32, 32 // The format used for output by AudioStream. 33 AUDIO_OUTPUT_FORMAT = AUDIO_FORMAT_FLOAT32 34 }; 35 36 enum { MAX_AUDIO_SAMPLE_SIZE = sizeof(float) }; 37 38 template <AudioSampleFormat Format> 39 class AudioSampleTraits; 40 41 template <> 42 class AudioSampleTraits<AUDIO_FORMAT_FLOAT32> { 43 public: 44 using Type = float; 45 }; 46 template <> 47 class AudioSampleTraits<AUDIO_FORMAT_S16> { 48 public: 49 using Type = int16_t; 50 }; 51 52 using AudioDataValue = AudioSampleTraits<AUDIO_OUTPUT_FORMAT>::Type; 53 54 template <typename T> 55 class AudioSampleTypeToFormat; 56 57 template <> 58 class AudioSampleTypeToFormat<float> { 59 public: 60 static const AudioSampleFormat Format = AUDIO_FORMAT_FLOAT32; 61 }; 62 63 template <> 64 class AudioSampleTypeToFormat<short> { 65 public: 66 static const AudioSampleFormat Format = AUDIO_FORMAT_S16; 67 }; 68 69 template <typename T> 70 constexpr float MaxAsFloat() { 71 return static_cast<float>(std::numeric_limits<T>::max()); 72 } 73 74 template <typename T> 75 constexpr float LowestAsFloat() { 76 return static_cast<float>(std::numeric_limits<T>::lowest()); 77 } 78 79 // The maximum value for an audio sample. If T is signed, the absolute value of 80 // this number is smaller (by exactly 1) than ::Min(). 81 template <typename T> 82 constexpr T Max() { 83 return std::numeric_limits<T>::max(); 84 } 85 86 // The minimum value for an audio sample. If T is signed, the absolute value of 87 // this number is greater (by exactly 1) than ::Max() 88 template <typename T> 89 constexpr T Min() { 90 return std::numeric_limits<T>::lowest(); 91 } 92 93 template <> 94 constexpr float Max<float>() { 95 return 1.0f; 96 } 97 98 template <> 99 constexpr float Min<float>() { 100 return -1.0f; 101 } 102 103 // The bias value is the middle of the range. In linear PCM audio, if the 104 // values are all equal to the bias value, the audio is silent. 105 template <typename T> 106 constexpr T Bias() { 107 return 0; 108 } 109 110 template <> 111 constexpr uint8_t Bias<uint8_t>() { 112 return 128; 113 } 114 115 // Clip a floating point audio sample to its nominal range. This is 116 // destructive, and is only used here for avoiding overflow in some edge cases, 117 // so it's not going to be generally audible. 118 inline float Clip(float aValue) { return std::clamp(aValue, -1.0f, 1.0f); } 119 120 template <typename T> 121 T FloatToAudioSample(float aValue) { 122 if constexpr (std::is_same_v<float, T>) { 123 return aValue; 124 } 125 if constexpr (std::is_same_v<uint8_t, T>) { 126 return static_cast<T>(std::clamp((aValue + 1.0f) * 128.f, 127 LowestAsFloat<T>(), MaxAsFloat<T>())); 128 } else if constexpr (std::is_same_v<int16_t, T>) { 129 // This produces correct results accross the range. 130 return static_cast<T>(std::clamp(aValue * -LowestAsFloat<T>(), 131 LowestAsFloat<T>(), MaxAsFloat<T>())); 132 } else if constexpr (std::is_same_v<int32_t, T>) { 133 // We need to handle this differently because of rounding between INT32_MAX 134 // and float 32-bits, to maximise precision. 135 if (aValue >= 0.) { 136 // if the input sample is greater OR EQUAL to 1.0, then clip and return 137 // the max value. 138 if (aValue >= 1.0) { 139 return std::numeric_limits<T>::max(); 140 } 141 // otherwise cast to a double and map to the positive range. 142 // float 32-bits cannot represent int32_max (but can represent int32_min) 143 constexpr double magnitudePos = std::numeric_limits<int32_t>::max(); 144 return static_cast<int32_t>(aValue * magnitudePos); 145 } 146 // Similarly for the negative range. 147 if (aValue <= -1.0) { 148 return std::numeric_limits<T>::lowest(); 149 } 150 constexpr double magnitudeNegative = 151 -1.0 * std::numeric_limits<int32_t>::lowest(); 152 return static_cast<int32_t>(aValue * magnitudeNegative); 153 } 154 } 155 156 template <typename T> 157 T UInt8bitToAudioSample(uint8_t aValue) { 158 if constexpr (std::is_same_v<uint8_t, T>) { 159 return aValue; 160 } else if constexpr (std::is_same_v<int16_t, T>) { 161 return (static_cast<int16_t>(aValue) << 8) - (1 << 15); 162 } else if constexpr (std::is_same_v<int32_t, T>) { 163 return (static_cast<int32_t>(aValue) << 24) - (1 << 31); 164 } else if constexpr (std::is_same_v<float, T>) { 165 float biased = static_cast<float>(aValue) - Bias<uint8_t>(); 166 if (aValue >= Bias<uint8_t>()) { 167 return Clip(biased / MaxAsFloat<int8_t>()); 168 } 169 return Clip(biased / -LowestAsFloat<int8_t>()); 170 } 171 } 172 173 template <typename T> 174 T Int16ToAudioSample(int16_t aValue) { 175 if constexpr (std::is_same_v<uint8_t, T>) { 176 return static_cast<uint8_t>(aValue >> 8) + 128; 177 } else if constexpr (std::is_same_v<int16_t, T>) { 178 return aValue; 179 } else if constexpr (std::is_same_v<int32_t, T>) { 180 return aValue << 16; 181 } else if constexpr (std::is_same_v<float, T>) { 182 if (aValue >= 0) { 183 return Clip(static_cast<float>(aValue) / MaxAsFloat<int16_t>()); 184 } 185 return Clip(static_cast<float>(aValue) / -LowestAsFloat<int16_t>()); 186 } 187 } 188 189 // 24-bits audio samples are stored in 32-bits variables. 190 template <typename T> 191 T Int24ToAudioSample(int32_t aValue) { 192 if constexpr (std::is_same_v<uint8_t, T>) { 193 return static_cast<uint8_t>(aValue >> 16) + 128; 194 } else if constexpr (std::is_same_v<int16_t, T>) { 195 return static_cast<int16_t>(aValue >> 8); 196 } else if constexpr (std::is_same_v<int32_t, T>) { 197 return aValue << 8; 198 } else if constexpr (std::is_same_v<float, T>) { 199 const int32_t min = -(2 << 22); 200 const int32_t max = (2 << 22) - 1; 201 if (aValue >= 0) { 202 return Clip(static_cast<float>(aValue) / static_cast<float>(max)); 203 } 204 return Clip(static_cast<float>(aValue) / -static_cast<float>(min)); 205 } 206 } 207 208 template <typename T> 209 T Int32ToAudioSample(int32_t aValue) { 210 if constexpr (std::is_same_v<uint8_t, T>) { 211 return static_cast<uint8_t>(aValue >> 24) + 128; 212 } else if constexpr (std::is_same_v<int16_t, T>) { 213 return aValue >> 16; 214 } else if constexpr (std::is_same_v<int32_t, T>) { 215 return aValue; 216 } else if constexpr (std::is_same_v<float, T>) { 217 if (aValue >= 0) { 218 return Clip(static_cast<float>(aValue) / MaxAsFloat<int32_t>()); 219 } 220 return Clip(static_cast<float>(aValue) / -LowestAsFloat<int32_t>()); 221 } 222 } 223 224 // This does not handle 24-bits audio, call the function explicitly when 225 // needed. 226 template <typename D, typename S> 227 inline D ConvertAudioSample(const S& aSource) { 228 if constexpr (std::is_same_v<S, D>) { 229 return aSource; 230 } else if constexpr (std::is_same_v<S, uint8_t>) { 231 return UInt8bitToAudioSample<D>(aSource); 232 } else if constexpr (std::is_same_v<S, int16_t>) { 233 return Int16ToAudioSample<D>(aSource); 234 } else if constexpr (std::is_same_v<S, int32_t>) { 235 return Int32ToAudioSample<D>(aSource); 236 } else if constexpr (std::is_same_v<S, float>) { 237 return FloatToAudioSample<D>(aSource); 238 } 239 } 240 241 // Sample buffer conversion 242 template <typename From, typename To> 243 inline void ConvertAudioSamples(const From* aFrom, To* aTo, int aCount) { 244 if constexpr (std::is_same_v<From, To>) { 245 PodCopy(aTo, aFrom, aCount); 246 return; 247 } 248 for (int i = 0; i < aCount; ++i) { 249 aTo[i] = ConvertAudioSample<To>(aFrom[i]); 250 } 251 } 252 253 // Sample buffer conversion with scale 254 template <typename From, typename To> 255 inline void ConvertAudioSamplesWithScale(const From* aFrom, To* aTo, int aCount, 256 float aScale) { 257 if (aScale == 1.0f) { 258 ConvertAudioSamples(aFrom, aTo, aCount); 259 return; 260 } 261 for (int i = 0; i < aCount; ++i) { 262 aTo[i] = 263 ConvertAudioSample<To>(ConvertAudioSample<float>(aFrom[i]) * aScale); 264 } 265 } 266 inline void ConvertAudioSamplesWithScale(const int16_t* aFrom, int16_t* aTo, 267 int aCount, float aScale) { 268 if (aScale == 1.0f) { 269 ConvertAudioSamples(aFrom, aTo, aCount); 270 return; 271 } 272 if (0.0f <= aScale && aScale < 1.0f) { 273 int32_t scale = int32_t((1 << 16) * aScale); 274 for (int i = 0; i < aCount; ++i) { 275 aTo[i] = int16_t((int32_t(aFrom[i]) * scale) >> 16); 276 } 277 return; 278 } 279 for (int i = 0; i < aCount; ++i) { 280 aTo[i] = FloatToAudioSample<int16_t>(ConvertAudioSample<float>(aFrom[i]) * 281 aScale); 282 } 283 } 284 285 template <typename From, typename To> 286 inline void AddAudioSamplesWithScale(const From* aFrom, To* aTo, int aCount, 287 float aScale) { 288 for (int i = 0; i < aCount; ++i) { 289 aTo[i] = 290 ConvertAudioSample<To>(ConvertAudioSample<float>(aTo[i]) + 291 ConvertAudioSample<float>(aFrom[i]) * aScale); 292 } 293 } 294 295 // In place audio sample scaling. 296 inline void ScaleAudioSamples(float* aBuffer, int aCount, float aScale) { 297 for (int32_t i = 0; i < aCount; ++i) { 298 aBuffer[i] *= aScale; 299 } 300 } 301 302 inline void ScaleAudioSamples(short* aBuffer, int aCount, float aScale) { 303 int32_t volume = int32_t((1 << 16) * aScale); 304 for (int32_t i = 0; i < aCount; ++i) { 305 aBuffer[i] = short((int32_t(aBuffer[i]) * volume) >> 16); 306 } 307 } 308 309 inline const void* AddAudioSampleOffset(const void* aBase, 310 AudioSampleFormat aFormat, 311 int32_t aOffset) { 312 static_assert(AUDIO_FORMAT_S16 == 1, "Bad constant"); 313 static_assert(AUDIO_FORMAT_FLOAT32 == 2, "Bad constant"); 314 MOZ_ASSERT(aFormat == AUDIO_FORMAT_S16 || aFormat == AUDIO_FORMAT_FLOAT32); 315 316 return static_cast<const uint8_t*>(aBase) + aFormat * 2 * aOffset; 317 } 318 319 } // namespace mozilla 320 321 #endif /* MOZILLA_AUDIOSAMPLEFORMAT_H_ */