mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-04-30 06:46:49 -04:00
191 lines
4.2 KiB
C
191 lines
4.2 KiB
C
|
|
/* 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 <spa/utils/defs.h>
|
||
|
|
|
||
|
|
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
|