mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-04-30 06:46:49 -04:00
bluez5: add AAC-ELD vendor codec used by Airpods
Add AAC-ELD vendor codec. This is based on the documentation by Sa Xiao: Link: https://github.com/wasdwasd0105/PicoW-usb2bt-audio/blob/main/aac-eld-apple.md
This commit is contained in:
parent
0f8d5c6e57
commit
f06f0db31b
8 changed files with 901 additions and 0 deletions
190
spa/plugins/bluez5/aac-bits.h
Normal file
190
spa/plugins/bluez5/aac-bits.h
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/* 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue