use oxideav_core::bits::BitWriter;
use crate::adts::ADTS_SAMPLE_RATES_HZ;
fn write_sampling_frequency(w: &mut BitWriter, rate: u32) {
match ADTS_SAMPLE_RATES_HZ.iter().position(|&r| r == rate) {
Some(i) => w.write_u32(i as u32, 4),
None => {
w.write_u32(0xf, 4);
w.write_u32(rate & 0x00ff_ffff, 24);
}
}
}
fn write_aot(w: &mut BitWriter, aot: u8) {
if aot < 32 {
w.write_u32(u32::from(aot), 5);
} else {
w.write_u32(31, 5);
w.write_u32(u32::from(aot - 32), 6);
}
}
fn write_ga_specific_config(w: &mut BitWriter) {
w.write_bit(false); w.write_bit(false); w.write_bit(false); }
pub fn aac_lc_asc(sample_rate: u32, channel_configuration: u8) -> Vec<u8> {
let mut w = BitWriter::new();
write_aot(&mut w, 2);
write_sampling_frequency(&mut w, sample_rate);
w.write_u32(u32::from(channel_configuration & 0xf), 4);
write_ga_specific_config(&mut w);
w.align_to_byte_zero();
w.finish()
}
pub fn he_aac_v1_asc(
core_rate: u32,
sbr_rate: u32,
channel_configuration: u8,
hierarchical: bool,
) -> Vec<u8> {
let mut w = BitWriter::new();
if hierarchical {
write_aot(&mut w, 5);
write_sampling_frequency(&mut w, core_rate);
w.write_u32(u32::from(channel_configuration & 0xf), 4);
write_sampling_frequency(&mut w, sbr_rate);
write_aot(&mut w, 2);
write_ga_specific_config(&mut w);
} else {
write_aot(&mut w, 2);
write_sampling_frequency(&mut w, core_rate);
w.write_u32(u32::from(channel_configuration & 0xf), 4);
write_ga_specific_config(&mut w);
w.write_u32(u32::from(crate::asc::SYNC_EXTENSION_TYPE_SBR), 11);
write_aot(&mut w, 5);
w.write_bit(true); write_sampling_frequency(&mut w, sbr_rate);
}
w.align_to_byte_zero();
w.finish()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::asc::AudioSpecificConfig;
#[test]
fn aac_lc_asc_is_the_canonical_two_bytes() {
assert_eq!(aac_lc_asc(44_100, 2), vec![0x12, 0x10]);
let (asc, _) = AudioSpecificConfig::parse(&aac_lc_asc(48_000, 1)).unwrap();
assert_eq!(asc.aot, 2);
assert_eq!(asc.sample_rate, 48_000);
assert_eq!(asc.channel_configuration, 1);
assert!(!asc.sbr_present);
}
#[test]
fn backward_compatible_he_asc_parses_with_sbr_probe() {
let bytes = he_aac_v1_asc(22_050, 44_100, 2, false);
assert_eq!(bytes.len(), 5);
let (asc, _) = AudioSpecificConfig::parse(&bytes).unwrap();
assert_eq!(asc.outer_aot, 2);
assert_eq!(asc.aot, 2);
assert_eq!(asc.sample_rate, 22_050);
let probe = asc.trailing_sbr_probe.expect("0x2b7 trailer");
assert_eq!(probe.extension_audio_object_type, 5);
assert!(probe.sbr_present_flag);
assert_eq!(probe.extension_sample_rate, Some(44_100));
}
#[test]
fn hierarchical_he_asc_parses_as_aot_5() {
let bytes = he_aac_v1_asc(24_000, 48_000, 1, true);
let (asc, _) = AudioSpecificConfig::parse(&bytes).unwrap();
assert_eq!(asc.outer_aot, 5);
assert_eq!(asc.aot, 2);
assert!(asc.sbr_present);
assert_eq!(asc.sample_rate, 24_000);
assert_eq!(asc.extension_sample_rate, Some(48_000));
assert_eq!(asc.channel_configuration, 1);
}
#[test]
fn escaped_rates_round_trip() {
let bytes = he_aac_v1_asc(22_050, 44_100 + 2, 2, true);
let (asc, _) = AudioSpecificConfig::parse(&bytes).unwrap();
assert_eq!(asc.extension_sample_rate, Some(44_102));
}
}