use std::collections::VecDeque;
use crate::adts::ADTS_SAMPLE_RATES_HZ;
use crate::encoder::{EncoderConfig, StreamEncoder, FRAME_LEN};
use crate::sbr_encoder::{SbrEncoder, SbrEncoderConfig, SbrFrame, RATE, SBR_ENC_COLS, T_HF_GEN};
use crate::sbr_qmf::{Complex, EncoderAnalysisQmf};
use crate::{Error, Result};
pub const HE_FRAME_LEN: usize = 2 * FRAME_LEN;
pub const FIR_TAPS: usize = 65;
pub const FIR_DELAY: usize = (FIR_TAPS - 1) / 2;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct HeAacConfig {
pub sample_rate: u32,
pub channels: u8,
pub bitrate: u32,
pub crossover_hz: Option<f64>,
pub sbr_crc: bool,
pub add_harmonic: bool,
pub coupling: bool,
pub header_interval: u32,
pub interpol_freq: bool,
}
impl HeAacConfig {
pub fn new(sample_rate: u32, channels: u8, bitrate: u32) -> Self {
HeAacConfig {
sample_rate,
channels,
bitrate,
crossover_hz: None,
sbr_crc: false,
add_harmonic: true,
coupling: false,
header_interval: 8,
interpol_freq: true,
}
}
pub fn crossover(&self) -> f64 {
let fs = f64::from(self.sample_rate);
self.crossover_hz.unwrap_or_else(|| {
let per_ch = f64::from(self.bitrate) / f64::from(self.channels.max(1));
(0.115 * fs * (per_ch / 24_000.0).sqrt()).clamp(0.09 * fs, 0.22 * fs)
})
}
pub fn stop_hz(&self) -> f64 {
(0.36 * f64::from(self.sample_rate)).min(16_500.0)
}
}
fn design_lowpass(fc: f64) -> Vec<f64> {
let n = FIR_TAPS;
let m = (n - 1) as f64 / 2.0;
let mut h: Vec<f64> = (0..n)
.map(|i| {
let x = i as f64 - m;
let sinc = if x == 0.0 {
2.0 * fc
} else {
(2.0 * core::f64::consts::PI * fc * x).sin() / (core::f64::consts::PI * x)
};
let w = 0.42 - 0.5 * (2.0 * core::f64::consts::PI * i as f64 / (n - 1) as f64).cos()
+ 0.08 * (4.0 * core::f64::consts::PI * i as f64 / (n - 1) as f64).cos();
sinc * w
})
.collect();
let sum: f64 = h.iter().sum();
for v in h.iter_mut() {
*v /= sum;
}
h
}
#[derive(Debug, Clone)]
struct ChannelFrontEnd {
fir_hist: Vec<f64>,
qmf_delay: Vec<f64>,
bank: EncoderAnalysisQmf,
cols: VecDeque<[Complex; 64]>,
}
const RING_COLS: usize = 96;
impl ChannelFrontEnd {
fn new() -> Self {
let mut cols = VecDeque::with_capacity(RING_COLS + 32);
for _ in 0..RING_COLS {
cols.push_back([Complex::default(); 64]);
}
ChannelFrontEnd {
fir_hist: vec![0.0; FIR_TAPS - 1],
qmf_delay: vec![0.0; FIR_DELAY],
bank: EncoderAnalysisQmf::new(),
cols,
}
}
}
#[derive(Debug, Clone)]
pub struct HeAacEncoder {
config: HeAacConfig,
core: StreamEncoder,
sbr: SbrEncoder,
fir: Vec<f64>,
fe: Vec<ChannelFrontEnd>,
frames: u64,
last_sbr: Option<SbrFrame>,
}
impl HeAacEncoder {
pub fn new(config: HeAacConfig) -> Result<Self> {
if !(1..=2).contains(&config.channels) || config.bitrate == 0 {
return Err(Error::EncoderInvalidConfig);
}
if config.sample_rate % 2 != 0 {
return Err(Error::EncoderInvalidConfig);
}
let core_rate = config.sample_rate / 2;
if !ADTS_SAMPLE_RATES_HZ.contains(&core_rate) {
return Err(Error::EncoderInvalidConfig);
}
let mut sbr_cfg = SbrEncoderConfig::new(
config.sample_rate,
usize::from(config.channels),
config.crossover(),
config.stop_hz(),
)?;
sbr_cfg.crc = config.sbr_crc;
sbr_cfg.add_harmonic = config.add_harmonic;
sbr_cfg.coupling = config.coupling;
sbr_cfg.header_interval = config.header_interval;
sbr_cfg.interpol_freq = config.interpol_freq;
let sbr = SbrEncoder::new(sbr_cfg)?;
let k_x = f64::from(sbr.bands().k_x);
let fs = f64::from(config.sample_rate);
let xo_hz = (k_x * fs / 128.0).min(0.225 * fs);
let fir = design_lowpass(xo_hz / fs);
let core = StreamEncoder::new(EncoderConfig {
sample_rate: core_rate,
channels: config.channels,
bitrate: config.bitrate,
})?;
let n = usize::from(config.channels);
Ok(HeAacEncoder {
config,
core,
sbr,
fir,
fe: (0..n).map(|_| ChannelFrontEnd::new()).collect(),
frames: 0,
last_sbr: None,
})
}
pub fn config(&self) -> &HeAacConfig {
&self.config
}
pub fn sbr(&self) -> &SbrEncoder {
&self.sbr
}
pub fn core_sample_rate(&self) -> u32 {
self.config.sample_rate / 2
}
pub fn last_sbr_frame(&self) -> Option<&SbrFrame> {
self.last_sbr.as_ref()
}
fn front_end(&mut self, c: usize, hop: &[f64]) -> Vec<f64> {
let taps = self.fir.len();
let fe = &mut self.fe[c];
let mut buf = Vec::with_capacity(taps - 1 + hop.len());
buf.extend_from_slice(&fe.fir_hist);
buf.extend_from_slice(hop);
let mut core = Vec::with_capacity(hop.len() / 2);
for m in 0..hop.len() / 2 {
let end = 2 * m + taps - 1;
let mut acc = 0.0;
for (j, &h) in self.fir.iter().enumerate() {
acc += h * buf[end - j];
}
core.push(acc);
}
let keep = buf.len() - (taps - 1);
fe.fir_hist.clear();
fe.fir_hist.extend_from_slice(&buf[keep..]);
let mut delayed = Vec::with_capacity(hop.len());
delayed.extend_from_slice(&fe.qmf_delay);
delayed.extend_from_slice(&hop[..hop.len() - FIR_DELAY]);
fe.qmf_delay.clear();
fe.qmf_delay
.extend_from_slice(&hop[hop.len() - FIR_DELAY..]);
for slot in delayed.chunks_exact(64) {
let x = fe.bank.push_slot(slot).expect("64-sample slot");
fe.cols.push_back(x);
if fe.cols.len() > RING_COLS {
fe.cols.pop_front();
}
}
core
}
fn frame_window(&self, c: usize) -> Vec<[Complex; 64]> {
let cols = &self.fe[c].cols;
let newest = cols.len();
let start = newest - (2 * RATE * 16 + T_HF_GEN);
(start..start + SBR_ENC_COLS).map(|i| cols[i]).collect()
}
pub fn encode_frame(&mut self, interleaved: &[i16]) -> Result<Vec<u8>> {
let ch = usize::from(self.config.channels);
if interleaved.len() > HE_FRAME_LEN * ch || interleaved.len() % ch != 0 {
return Err(Error::EncoderInvalidConfig);
}
let mut hops: Vec<Vec<f64>> = vec![vec![0.0; HE_FRAME_LEN]; ch];
for (j, hop) in hops.iter_mut().enumerate() {
for (n, v) in hop.iter_mut().take(interleaved.len() / ch).enumerate() {
*v = f64::from(interleaved[n * ch + j]);
}
}
self.encode_hops(&hops)
}
fn encode_hops(&mut self, hops: &[Vec<f64>]) -> Result<Vec<u8>> {
let ch = hops.len();
let mut core_pcm: Vec<Vec<f64>> = Vec::with_capacity(ch);
for (c, hop) in hops.iter().enumerate() {
core_pcm.push(self.front_end(c, hop));
}
let windows: Vec<Vec<[Complex; 64]>> = (0..ch).map(|c| self.frame_window(c)).collect();
let refs: Vec<&[[Complex; 64]]> = windows.iter().map(|w| w.as_slice()).collect();
let sbr_frame = self.sbr.encode_frame(&refs)?;
let fills = vec![sbr_frame.payload.clone()];
self.last_sbr = Some(sbr_frame);
let mut inter = Vec::with_capacity(FRAME_LEN * ch);
for n in 0..FRAME_LEN {
for pcm in core_pcm.iter() {
inter.push(crate::pcm::to_s16(pcm[n]));
}
}
self.frames += 1;
self.core.encode_frame_with_fills(&inter, &fills)
}
pub fn finish(&mut self) -> Result<Vec<u8>> {
let ch = usize::from(self.config.channels);
let zeros: Vec<Vec<f64>> = vec![vec![0.0; HE_FRAME_LEN]; ch];
self.encode_hops(&zeros)
}
pub fn encode_all(&mut self, interleaved: &[i16]) -> Result<Vec<u8>> {
let ch = usize::from(self.config.channels);
if interleaved.len() % ch != 0 {
return Err(Error::EncoderInvalidConfig);
}
let mut out = Vec::new();
let hop = HE_FRAME_LEN * ch;
let mut chunks = interleaved.chunks(hop);
let first = chunks.next().unwrap_or(&[]);
out.extend_from_slice(&self.encode_frame(first)?);
for chunk in chunks {
out.extend_from_slice(&self.encode_frame(chunk)?);
}
out.extend_from_slice(&self.finish()?);
Ok(out)
}
pub fn audio_specific_config(&self, hierarchical: bool) -> Vec<u8> {
crate::asc_writer::he_aac_v1_asc(
self.core_sample_rate(),
self.config.sample_rate,
self.config.channels,
hierarchical,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn config_validation() {
assert!(HeAacEncoder::new(HeAacConfig::new(44_100, 1, 32_000)).is_ok());
assert!(HeAacEncoder::new(HeAacConfig::new(48_000, 2, 64_000)).is_ok());
assert!(HeAacEncoder::new(HeAacConfig::new(44_100, 3, 32_000)).is_err());
assert!(HeAacEncoder::new(HeAacConfig::new(44_100, 1, 0)).is_err());
assert!(HeAacEncoder::new(HeAacConfig::new(44_055, 1, 32_000)).is_err());
assert!(HeAacEncoder::new(HeAacConfig::new(8_000, 1, 32_000)).is_err());
}
#[test]
fn crossover_tracks_bitrate() {
let lo = HeAacConfig::new(44_100, 2, 32_000).crossover();
let hi = HeAacConfig::new(44_100, 2, 128_000).crossover();
assert!(lo < hi);
assert!(lo >= 0.09 * 44_100.0 && hi <= 0.22 * 44_100.0);
let explicit = HeAacConfig {
crossover_hz: Some(7_000.0),
..HeAacConfig::new(44_100, 2, 32_000)
};
assert_eq!(explicit.crossover(), 7_000.0);
}
#[test]
fn lowpass_is_unity_dc_and_attenuates_above_cutoff() {
let h = design_lowpass(0.125);
assert!((h.iter().sum::<f64>() - 1.0).abs() < 1e-12);
let resp = |f: f64| -> f64 {
let (mut re, mut im) = (0.0, 0.0);
for (i, &v) in h.iter().enumerate() {
let a = 2.0 * core::f64::consts::PI * f * i as f64;
re += v * a.cos();
im -= v * a.sin();
}
(re * re + im * im).sqrt()
};
assert!((resp(0.02) - 1.0).abs() < 0.02);
assert!(resp(0.25) < 0.01, "stopband {}", resp(0.25));
}
#[test]
fn frames_decode_as_sbr_active_at_full_rate() {
let mut enc = HeAacEncoder::new(HeAacConfig::new(44_100, 1, 40_000)).unwrap();
let n = 4 * HE_FRAME_LEN;
let pcm: Vec<i16> = (0..n)
.map(|i| (6000.0 * (0.02 * i as f64).sin() + 3000.0 * (0.9 * i as f64).sin()) as i16)
.collect();
let stream = enc.encode_all(&pcm).unwrap();
let (h, _) = crate::adts::AdtsHeader::parse(&stream).unwrap();
assert_eq!(h.sample_rate(), 22_050);
assert_eq!(h.profile, 1);
let mut dec = crate::decode::StreamDecoder::new();
let frames = dec.decode_all(&stream).unwrap();
assert_eq!(frames.len(), 5);
assert!(frames.iter().all(|f| f.sample_rate == 44_100));
assert!(frames.iter().all(|f| f.pcm.len() == HE_FRAME_LEN));
assert!(enc.last_sbr_frame().is_some());
}
}