libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
audioformat.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_PROAV
13#include <cstddef>
14#include <cstdint>
15#include <limits>
16#include <type_traits>
17#include <promeki/namespace.h>
18#include <promeki/string.h>
19#include <promeki/list.h>
20#include <promeki/fourcc.h>
21#include <promeki/audiocodec.h>
22#include <promeki/result.h>
23#include <promeki/system.h>
24#include <promeki/datatype.h>
25
26PROMEKI_NAMESPACE_BEGIN
27
28class DataStream;
29
93class AudioFormat {
94 public:
95 PROMEKI_DATATYPE(AudioFormat, DataTypeAudioFormat, 1)
96
97
98 Error writeToStream(DataStream &s) const;
100 template <uint32_t V> static Result<AudioFormat> readFromStream(DataStream &s);
101
103 static constexpr int32_t MinS24 = -8388608;
105 static constexpr int32_t MaxS24 = 8388607;
107 static constexpr int32_t MinU24 = 0;
109 static constexpr int32_t MaxU24 = 16777215;
110
118 enum ID {
119 Invalid = 0,
120
121 // -- Interleaved PCM -------------------------------
122 PCMI_Float32LE = 1,
123 PCMI_Float32BE = 2,
124 PCMI_S8 = 3,
125 PCMI_U8 = 4,
126 PCMI_S16LE = 5,
127 PCMI_U16LE = 6,
128 PCMI_S16BE = 7,
129 PCMI_U16BE = 8,
130 PCMI_S24LE = 9,
131 PCMI_U24LE = 10,
132 PCMI_S24BE = 11,
133 PCMI_U24BE = 12,
134 PCMI_S32LE = 13,
135 PCMI_U32LE = 14,
136 PCMI_S32BE = 15,
137 PCMI_U32BE = 16,
138 // 24-bit data carried in a 32-bit container. HB32 = data
139 // occupies the high 3 bytes of the word (low byte is 0);
140 // LB32 = data occupies the low 3 bytes (high byte is 0).
141 // Endianness applies to the 32-bit word as a whole, not
142 // the 24-bit data subset.
143 PCMI_S24LE_HB32 = 17,
144 PCMI_S24LE_LB32 = 18,
145 PCMI_S24BE_HB32 = 19,
146 PCMI_S24BE_LB32 = 20,
147 PCMI_U24LE_HB32 = 21,
148 PCMI_U24LE_LB32 = 22,
149 PCMI_U24BE_HB32 = 23,
150 PCMI_U24BE_LB32 = 24,
151
152 // -- Planar PCM ------------------------------------
153 PCMP_Float32LE = 32,
154 PCMP_Float32BE = 33,
155 PCMP_S8 = 34,
156 PCMP_U8 = 35,
157 PCMP_S16LE = 36,
158 PCMP_U16LE = 37,
159 PCMP_S16BE = 38,
160 PCMP_U16BE = 39,
161 PCMP_S24LE = 40,
162 PCMP_U24LE = 41,
163 PCMP_S24BE = 42,
164 PCMP_U24BE = 43,
165 PCMP_S32LE = 44,
166 PCMP_U32LE = 45,
167 PCMP_S32BE = 46,
168 PCMP_U32BE = 47,
169 // Planar 24-bit-in-32-bit container variants, mirroring
170 // the interleaved set above.
171 PCMP_S24LE_HB32 = 48,
172 PCMP_S24LE_LB32 = 49,
173 PCMP_S24BE_HB32 = 50,
174 PCMP_S24BE_LB32 = 51,
175 PCMP_U24LE_HB32 = 52,
176 PCMP_U24LE_LB32 = 53,
177 PCMP_U24BE_HB32 = 54,
178 PCMP_U24BE_LB32 = 55,
179
180 // -- Compressed ------------------------------------
181 Opus = 64,
182 AAC = 65,
183 FLAC = 66,
184 MP3 = 67,
185 AC3 = 68,
186
187 UserDefined = 1024
188 };
189
191 using IDList = ::promeki::List<ID>;
192
200 static constexpr ID NativeFloat = System::isLittleEndian() ? PCMI_Float32LE : PCMI_Float32BE;
201
213 struct Data {
214 ID id = Invalid;
215 String name;
216 String desc;
217 size_t bytesPerSample = 0;
218 size_t bitsPerSample = 0;
219 bool isSigned = false;
220 bool isFloat = false;
221 bool isPlanar = false;
222 bool isBigEndian = false;
223 bool compressed = false;
224 AudioCodec audioCodec;
225 FourCC::List fourccList;
232 void (*samplesToFloat)(float *out, const uint8_t *in, size_t samples) = nullptr;
239 void (*floatToSamples)(uint8_t *out, const float *in, size_t samples) = nullptr;
240 };
241
247 static ID registerType();
248
259 static void registerData(Data &&data);
260
265 static IDList registeredIDs();
266
274 static Result<AudioFormat> lookup(const String &name);
275
284 static Result<AudioFormat> fromString(const String &name);
285
298 static AudioFormat lookupByFourCC(const FourCC &fcc);
299
304 inline AudioFormat(ID id = Invalid);
305
307 bool isValid() const { return d != nullptr && d->id != Invalid; }
308
310 ID id() const { return d->id; }
311
313 const String &name() const { return d->name; }
314
316 const String &desc() const { return d->desc; }
317
319 size_t bytesPerSample() const { return d->bytesPerSample; }
320
322 size_t bitsPerSample() const { return d->bitsPerSample; }
323
325 bool isSigned() const { return d->isSigned; }
326
328 bool isFloat() const { return d->isFloat; }
329
331 bool isPlanar() const { return d->isPlanar; }
332
334 bool isBigEndian() const { return d->isBigEndian; }
335
337 bool isCompressed() const { return d->compressed; }
338
346 const AudioCodec &audioCodec() const { return d->audioCodec; }
347
349 const FourCC::List &fourccList() const { return d->fourccList; }
350
358 void samplesToFloat(float *out, const uint8_t *in, size_t samples) const {
359 if (d->samplesToFloat != nullptr) d->samplesToFloat(out, in, samples);
360 }
361
368 void floatToSamples(uint8_t *out, const float *in, size_t samples) const {
369 if (d->floatToSamples != nullptr) d->floatToSamples(out, in, samples);
370 }
371
372 // -- Direct (no-float) format-to-format conversion --------
373 //
374 // The library tracks a per-(src,dst) registry of "direct"
375 // converters that bypass the via-float intermediate step.
376 // Direct converters are faster (one memory pass) and
377 // additionally permit @ref isBitAccurateTo, which matters
378 // when audio buffers carry non-PCM payloads (SMPTE 337M
379 // data bursts, AES3 user bits, …) — those bytes survive
380 // a direct integer-to-integer transform but are scrambled
381 // by an int → float → int round-trip.
382
390 using DirectConvertFn = void (*)(void *out, const void *in, size_t samples);
391
398 static DirectConvertFn directConverter(ID src, ID dst);
399
414 static bool isBitAccurate(ID src, ID dst);
415
429 static void registerDirectConverter(ID src, ID dst, DirectConvertFn fn, bool bitAccurate);
430
432 bool hasDirectConverterTo(const AudioFormat &dst) const {
433 return directConverter(id(), dst.id()) != nullptr;
434 }
435
437 bool isBitAccurateTo(const AudioFormat &dst) const { return isBitAccurate(id(), dst.id()); }
438
466 Error convertTo(const AudioFormat &dst, void *out, const void *in, size_t samples,
467 float *scratch = nullptr) const;
468
500 Error convertTo(const AudioFormat &dst, void *out, const void *in, size_t samplesPerChannel,
501 size_t channels, float *scratch = nullptr) const;
502
504 bool operator==(const AudioFormat &o) const { return d == o.d; }
505
507 bool operator!=(const AudioFormat &o) const { return d != o.d; }
508
510 const String &toString() const { return d->name; }
511
513 const Data *data() const { return d; }
514
515 // -- Integer <-> normalized float conversion helpers -------
516 // These are used by the registered PCM formats' sample
517 // conversion functions and are exposed as static utilities
518 // so user-registered formats can reuse them.
519
538 template <typename IntegerType, IntegerType Min, IntegerType Max>
539 static float integerToFloat(IntegerType value) {
540 static_assert(std::is_integral<IntegerType>::value, "IntegerType must be an integer.");
541 if constexpr (Min < 0) {
542 // Signed range: divide by max(|Min|, Max) so an
543 // integer 0 maps to exactly 0.0f. Standard audio
544 // convention; preserves silence through int↔float
545 // round-trips, which the linear-interp asymmetric
546 // mapping does not (it pushes 0 to ~1/(Max-Min)
547 // and that DC bias derails sync detectors that
548 // expect zero-mean silence between codewords).
549 constexpr float scaleNeg = -static_cast<float>(Min);
550 constexpr float scalePos = static_cast<float>(Max);
551 constexpr float scale = scaleNeg > scalePos ? scaleNeg : scalePos;
552 return static_cast<float>(value) / scale;
553 } else {
554 constexpr float min = static_cast<float>(Min);
555 constexpr float max = static_cast<float>(Max);
556 return ((static_cast<float>(value) - min) * 2.0f / (max - min)) - 1.0f;
557 }
558 }
559
566 template <typename IntegerType> static float integerToFloat(IntegerType value) {
567 static_assert(std::is_integral<IntegerType>::value, "IntegerType must be an integer.");
568 return integerToFloat<IntegerType, std::numeric_limits<IntegerType>::min(),
569 std::numeric_limits<IntegerType>::max()>(value);
570 }
571
590 template <typename IntegerType, IntegerType Min, IntegerType Max>
591 static IntegerType floatToInteger(float value) {
592 static_assert(std::is_integral<IntegerType>::value, "IntegerType must be an integer.");
593 if (value <= -1.0f) return Min;
594 if (value >= 1.0f) return Max;
595 if constexpr (Min < 0) {
596 // Symmetric inverse of integerToFloat: scale by
597 // max(|Min|, Max) so 0.0f maps to 0 exactly and
598 // float→int→float round-trips zero without bias.
599 constexpr float scaleNeg = -static_cast<float>(Min);
600 constexpr float scalePos = static_cast<float>(Max);
601 constexpr float scale = scaleNeg > scalePos ? scaleNeg : scalePos;
602 return static_cast<IntegerType>(value * scale);
603 } else {
604 const float min = static_cast<float>(Min);
605 const float max = static_cast<float>(Max);
606 return static_cast<IntegerType>((value + 1.0f) * 0.5f * (max - min) + min);
607 }
608 }
609
616 template <typename IntegerType> static IntegerType floatToInteger(float value) {
617 static_assert(std::is_integral<IntegerType>::value, "IntegerType must be an integer.");
618 return floatToInteger<IntegerType, std::numeric_limits<IntegerType>::min(),
619 std::numeric_limits<IntegerType>::max()>(value);
620 }
621
634 template <typename IntegerType, bool InputIsBigEndian>
635 static void samplesToFloatImpl(float *out, const uint8_t *inbuf, size_t samples) {
636 static_assert(std::is_integral<IntegerType>::value, "IntegerType must be an integer.");
637 const IntegerType *in = reinterpret_cast<const IntegerType *>(inbuf);
638 for (size_t i = 0; i < samples; ++i) {
639 IntegerType val = *in++;
640 if constexpr (InputIsBigEndian != System::isBigEndian()) System::swapEndian(val);
641 *out++ = integerToFloat<IntegerType>(val);
642 }
643 }
644
657 template <typename IntegerType, bool OutputIsBigEndian>
658 static void floatToSamplesImpl(uint8_t *outbuf, const float *in, size_t samples) {
659 static_assert(std::is_integral<IntegerType>::value, "IntegerType must be an integer.");
660 IntegerType *out = reinterpret_cast<IntegerType *>(outbuf);
661 for (size_t i = 0; i < samples; ++i) {
662 IntegerType val = floatToInteger<IntegerType>(*in++);
663 if constexpr (OutputIsBigEndian != System::isBigEndian()) System::swapEndian(val);
664 *out++ = val;
665 }
666 }
667
668 private:
669 const Data *d = nullptr;
670 static const Data *lookupData(ID id);
671};
672
673inline AudioFormat::AudioFormat(ID id) : d(lookupData(id)) {}
674
675PROMEKI_NAMESPACE_END
676
677#endif // PROMEKI_ENABLE_PROAV