/* Spa AAC bits */ /* SPDX-FileCopyrightText: Copyright © 2025 Pauli Virtanen */ /* SPDX-License-Identifier: MIT */ #ifndef SPA_BLUEZ5_AAC_BITS_H #define SPA_BLUEZ5_AAC_BITS_H #include struct bits_out { uint8_t *buf; size_t size; size_t pos; }; #define BITS_OUT_INIT(buf, size) ((struct bits_out) { (buf), (size), 0 }) static inline void bits_push(struct bits_out *b, uint8_t nbits, uint8_t value) { size_t pos = b->pos; /* Maximally simple, doesn't need to be fast... */ spa_assert(nbits <= 8); value = ((uint16_t)value) << (8 - nbits); b->pos += nbits; while (nbits) { size_t n = pos / 8; size_t bit = pos % 8; if (n >= b->size) break; if (bit == 0) b->buf[n] = 0; if (value & 0x80) b->buf[n] |= 1 << (7 - bit); pos++; nbits--; value = ((uint16_t)value) << 1; } } enum aac_aot_type { AAC_AOT_AAC_LC = 2, AAC_AOT_ER_AAC_ELD = 39, }; static inline int aac_frequency_index(int frequency) { switch (frequency) { case 96000: return 0x0; case 88200: return 0x1; case 64000: return 0x2; case 48000: return 0x3; case 44100: return 0x4; case 32000: return 0x5; case 24000: return 0x6; case 22050: return 0x7; case 16000: return 0x8; case 12000: return 0x9; case 11025: return 0xa; case 8000: return 0xb; case 7350: return 0xc; default: spa_assert_not_reached(); return -1; } } static inline int aac_channel_index(int channels) { switch (channels) { case 1: return 0x1; case 2: return 0x2; default: spa_assert_not_reached(); return -1; } } /** Write AudioSpecificConfig to given buffer */ static inline int aac_make_asc(void *buf, size_t buf_size, enum aac_aot_type aot, int frequency, int downscale_frequency, int channels, bool sbr) { int freq, down_freq, chan; struct bits_out b = BITS_OUT_INIT(buf, buf_size); if ((freq = aac_frequency_index(frequency)) < 0) return -1; if ((down_freq = aac_frequency_index(downscale_frequency)) < 0) return -1; if ((chan = aac_channel_index(channels)) < 0) return -1; switch (aot) { case AAC_AOT_AAC_LC: case AAC_AOT_ER_AAC_ELD: break; default: spa_assert_not_reached(); return -EINVAL; } if (aot <= 31) { bits_push(&b, 5, aot); } else { bits_push(&b, 5, 31); bits_push(&b, 6, aot - 32); } bits_push(&b, 4, freq); /* frequency index */ bits_push(&b, 4, chan); /* channel configuration */ switch (aot) { case AAC_AOT_AAC_LC: /* GASpecificConfig */ bits_push(&b, 1, 0x0); /* frame length flag (1024 length) */ bits_push(&b, 1, 0x0); /* depends on core coder */ bits_push(&b, 1, 0x0); /* extension flag */ break; case AAC_AOT_ER_AAC_ELD: /* ELDSpecificConfig */ bits_push(&b, 1, 0x1); /* frame length flag (480 length) */ bits_push(&b, 1, 0x0); /* SectionDataResilience? */ bits_push(&b, 1, 0x0); /* ScalefactorDataResilience? */ bits_push(&b, 1, 0x0); /* SpectralDataResilience? */ bits_push(&b, 1, sbr ? 0x1 : 0x0); /* SBR */ if (sbr) { bits_push(&b, 1, 0x0); /* ldSbrSamplingRate */ bits_push(&b, 1, 0x0); /* ldSbrCrcFlag */ /* ld_sbr_header */ if (channels != 1 && channels != 2) return -EINVAL; /* sbr_header: * These are just the FDK-AAC default values for 48000/48000... */ if (freq != down_freq) return -EINVAL; bits_push(&b, 1, 1); /* bs_amp_res */ bits_push(&b, 4, 13); /* bs_start_freq */ bits_push(&b, 4, 7); /* bs_stop_freq */ bits_push(&b, 3, 0); /* bs_xover_band */ bits_push(&b, 2, 0x0); /* bs_reserved */ bits_push(&b, 1, 0x1); /* bs_header_extra_1 */ bits_push(&b, 1, 0x0); /* bs_header_extra_2 */ /* bs_header_extra_1 */ bits_push(&b, 2, 0x1); /* bs_freq_scale */ bits_push(&b, 1, 0x1); /* bs_alter_scale */ bits_push(&b, 2, 0x3); /* bs_noise_bands */ } if (freq != down_freq) { bits_push(&b, 4, 0x3); /* ELDEXT_DOWNSCALEINFO */ bits_push(&b, 4, 0x1); bits_push(&b, 4, down_freq); bits_push(&b, 4, 0x0); } bits_push(&b, 4, 0x0); /* ELDEXT_TERM */ /* epConfig */ bits_push(&b, 2, 0x0); break; } /* byte align */ if (b.pos % 8) bits_push(&b, 8 - (b.pos % 8), 0x0); spa_assert(b.pos % 8 == 0); if (b.pos / 8 >= buf_size) { spa_assert_not_reached(); return -EINVAL; } return b.pos / 8; } #endif