use super::mdct::{
BlockSize, Mdct, WindowType, LONG_BLOCK_SIZE, SHORTS_PER_GROUP, SHORT_BLOCK_SIZE,
SHORT_GROUP_OFFSET,
};
use super::psychoacoustic::{PsychoacousticModel, NUM_BARK_BANDS};
use crate::core::{ChannelData, Frame, FrameType, ResidualEncoding, I16_MAX_F32, I16_MIN_F32};
use alloc::vec;
use alloc::vec::Vec;
const ATTACK_RATIO: f32 = 8.0;
const DETECTOR_FLOOR_FRACTION: f32 = 0.05;
const HOP_SIZE: usize = LONG_BLOCK_SIZE / 2;
pub struct TransformEncoder {
sample_rate: u32,
channels: u8,
mdct: Mdct,
psy_models: Vec<PsychoacousticModel>,
short_psy_models: Vec<PsychoacousticModel>,
quality: f32,
}
#[derive(Debug, Clone)]
pub struct TransformFrame {
pub coefficients: Vec<Vec<i16>>,
pub scale_factors: Vec<Vec<f32>>,
pub block_size: BlockSize,
pub num_samples: usize,
}
impl TransformEncoder {
pub fn new(sample_rate: u32, channels: u8, quality: f32) -> Self {
let mdct = Mdct::new(channels as usize, WindowType::Vorbis);
let psy_models: Vec<_> = (0..channels)
.map(|_| PsychoacousticModel::new(sample_rate, LONG_BLOCK_SIZE))
.collect();
let short_psy_models: Vec<_> = (0..channels)
.map(|_| PsychoacousticModel::new(sample_rate, SHORT_BLOCK_SIZE))
.collect();
Self {
sample_rate,
channels,
mdct,
psy_models,
short_psy_models,
quality: quality.clamp(0.0, 1.0),
}
}
pub fn set_quality(&mut self, quality: f32) {
self.quality = quality.clamp(0.0, 1.0);
}
pub fn encode_frame(&mut self, samples: &[f32], block_size: BlockSize) -> TransformFrame {
let frame_input = block_size.frame_input_size();
let mut channel_data: Vec<Vec<f32>> = (0..self.channels as usize)
.map(|_| Vec::with_capacity(samples.len() / self.channels as usize))
.collect();
for (i, &s) in samples.iter().enumerate() {
channel_data[i % self.channels as usize].push(s);
}
let mut all_coefficients = Vec::with_capacity(self.channels as usize);
let mut all_scale_factors = Vec::with_capacity(self.channels as usize);
for (ch, data) in channel_data.iter().enumerate() {
let mut frame_data = data.clone();
if frame_data.len() < frame_input {
frame_data.resize(frame_input, 0.0);
}
let coeffs = self.mdct.forward(&frame_data, block_size);
let (quantized, scale_factors) = if block_size.is_short_group() {
self.quantize_short_group(&coeffs, ch)
} else {
self.quantize_long_block(&coeffs, ch)
};
all_coefficients.push(quantized);
all_scale_factors.push(scale_factors);
}
TransformFrame {
coefficients: all_coefficients,
scale_factors: all_scale_factors,
block_size,
num_samples: block_size.frame_coefficients(),
}
}
fn quantize_long_block(&mut self, coeffs: &[f32], ch: usize) -> (Vec<i16>, Vec<f32>) {
let smr = self.psy_models[ch].calculate_smr(coeffs);
let bits_per_coeff = 1.0 + self.quality * 15.0;
let total_bits = (coeffs.len() as f32 * bits_per_coeff) as usize;
let allocation = self.psy_models[ch].allocate_bits(&smr, total_bits);
self.quantize_coefficients(coeffs, &smr, &allocation)
}
fn quantize_short_group(&mut self, coeffs: &[f32], ch: usize) -> (Vec<i16>, Vec<f32>) {
let coeffs_per_window = SHORT_BLOCK_SIZE / 2;
let bits_per_coeff = 1.0 + self.quality * 15.0;
let per_window_bits = (coeffs_per_window as f32 * bits_per_coeff) as usize;
let mut smr = Vec::with_capacity(coeffs.len());
let mut allocation = Vec::with_capacity(coeffs.len());
for w in 0..SHORTS_PER_GROUP {
let window_coeffs = &coeffs[w * coeffs_per_window..(w + 1) * coeffs_per_window];
let model = &mut self.short_psy_models[ch];
let window_smr = model.calculate_smr(window_coeffs);
smr.extend(window_smr.iter());
let window_allocation = model.allocate_bits(&window_smr, per_window_bits);
allocation.extend(window_allocation.iter());
}
let freq_resolution = self.sample_rate as f32 / SHORT_BLOCK_SIZE as f32;
let scale_factors = self.compute_band_scale_factors(
coeffs,
&allocation,
coeffs_per_window,
freq_resolution,
);
let smr_threshold = self.smr_threshold();
let quantized = self.quantize_with_scale(
coeffs,
&smr,
&scale_factors,
coeffs_per_window,
freq_resolution,
smr_threshold,
);
(quantized, scale_factors)
}
pub fn quantize_coefficients(
&self,
coeffs: &[f32],
smr: &[f32],
allocation: &[u8],
) -> (Vec<i16>, Vec<f32>) {
let freq_resolution = self.sample_rate as f32 / LONG_BLOCK_SIZE as f32;
let scale_factors = self.compute_band_scale_factors(
coeffs,
allocation,
LONG_BLOCK_SIZE / 2,
freq_resolution,
);
let smr_threshold = self.smr_threshold();
let quantized = self.quantize_with_scale(
coeffs,
smr,
&scale_factors,
LONG_BLOCK_SIZE / 2,
freq_resolution,
smr_threshold,
);
(quantized, scale_factors)
}
fn freq_for_coeff(k: usize, coeffs_per_window: usize, freq_resolution: f32) -> f32 {
let bin = k % coeffs_per_window;
(bin as f32 + 0.5) * freq_resolution
}
fn compute_band_scale_factors(
&self,
coeffs: &[f32],
allocation: &[u8],
coeffs_per_window: usize,
freq_resolution: f32,
) -> Vec<f32> {
let mut band_max = [0.0f32; NUM_BARK_BANDS];
for (k, &c) in coeffs.iter().enumerate() {
let freq = Self::freq_for_coeff(k, coeffs_per_window, freq_resolution);
let band = PsychoacousticModel::freq_to_bark_band(freq);
band_max[band] = band_max[band].max(c.abs());
}
let mut band_bits_max = [0u8; NUM_BARK_BANDS];
for (k, &bits) in allocation.iter().enumerate() {
let freq = Self::freq_for_coeff(k, coeffs_per_window, freq_resolution);
let band = PsychoacousticModel::freq_to_bark_band(freq);
band_bits_max[band] = band_bits_max[band].max(bits);
}
let max_bits = band_bits_max.iter().copied().max().unwrap_or(0);
let mut scale_factors = vec![1.0f32; NUM_BARK_BANDS];
for (sf, &max_val) in scale_factors.iter_mut().zip(band_max.iter()) {
if max_val > 1e-10 {
*sf = 30000.0 / max_val;
}
}
if max_bits > 0 {
for (sf, &bits) in scale_factors.iter_mut().zip(band_bits_max.iter()) {
let weight = 0.25 + 0.75 * (bits as f32 / max_bits as f32);
*sf *= weight;
}
}
scale_factors
}
fn smr_threshold(&self) -> f32 {
if self.quality >= 0.99 {
-100.0
} else {
let t = (1.0 - self.quality).max(0.001);
-60.0 * (1.0 - libm::powf(t, 0.5))
}
}
fn quantize_with_scale(
&self,
coeffs: &[f32],
smr: &[f32],
scale_factors: &[f32],
coeffs_per_window: usize,
freq_resolution: f32,
smr_threshold: f32,
) -> Vec<i16> {
let mut quantized = vec![0i16; coeffs.len()];
for (k, (q, &c)) in quantized.iter_mut().zip(coeffs.iter()).enumerate() {
let freq = Self::freq_for_coeff(k, coeffs_per_window, freq_resolution);
let band = PsychoacousticModel::freq_to_bark_band(freq);
if smr[k] > smr_threshold {
let scaled = c * scale_factors[band];
*q = libm::roundf(scaled).clamp(I16_MIN_F32, I16_MAX_F32) as i16;
}
}
quantized
}
pub fn reset(&mut self) {
self.mdct.reset();
for model in &mut self.psy_models {
model.reset();
}
for model in &mut self.short_psy_models {
model.reset();
}
}
fn plan_block_types(&self, mono: &[f32], num_hops: usize) -> Vec<BlockSize> {
let mut plan = vec![BlockSize::Long; num_hops];
for h in 1..num_hops {
if self.hop_has_transient(mono, h) {
plan[h] = BlockSize::Short;
}
}
let mut h = 0;
while h < num_hops {
if plan[h] == BlockSize::Short {
let mut run_end = h;
while run_end + 1 < num_hops && plan[run_end + 1] == BlockSize::Short {
run_end += 1;
}
if h > 0 {
plan[h - 1] = BlockSize::Start;
plan[run_end] = BlockSize::Stop;
}
h = run_end + 1;
} else {
h += 1;
}
}
plan
}
fn hop_has_transient(&self, mono: &[f32], h: usize) -> bool {
let grid_start = h * HOP_SIZE;
let mut window_energy = [0.0f32; SHORTS_PER_GROUP];
for w in 0..SHORTS_PER_GROUP {
let off = grid_start + SHORT_GROUP_OFFSET + w * (SHORT_BLOCK_SIZE / 2);
let mut sum = 0.0f32;
for i in 0..SHORT_BLOCK_SIZE {
let v = mono[off + i];
sum += v * v;
}
window_energy[w] = sum / SHORT_BLOCK_SIZE as f32;
}
let mut grid_sum = 0.0f32;
for i in 0..LONG_BLOCK_SIZE {
let v = mono[grid_start + i];
grid_sum += v * v;
}
let floor = grid_sum / LONG_BLOCK_SIZE as f32 * DETECTOR_FLOOR_FRACTION;
for w in 0..SHORTS_PER_GROUP {
let baseline = window_energy[w.saturating_sub(1)].max(floor);
if window_energy[w] > ATTACK_RATIO * baseline {
return true;
}
}
false
}
pub fn encode_to_flo(&mut self, samples: &[f32], metadata: &[u8]) -> crate::FloResult<Vec<u8>> {
let block_samples = LONG_BLOCK_SIZE;
let hop_size = HOP_SIZE;
let num_samples_per_channel = samples.len() / self.channels as usize;
let pre_roll = hop_size;
let total_samples = num_samples_per_channel + pre_roll;
let num_hops = total_samples.div_ceil(hop_size);
let total_samples_needed = (num_hops + 1) * hop_size;
let mut padded = vec![0.0f32; total_samples_needed * self.channels as usize];
for ch in 0..self.channels as usize {
for i in 0..num_samples_per_channel.min(total_samples_needed - pre_roll) {
let src_idx = i * self.channels as usize + ch;
let dst_idx = (i + pre_roll) * self.channels as usize + ch;
if src_idx < samples.len() && dst_idx < padded.len() {
padded[dst_idx] = samples[src_idx];
}
}
}
let mono: Vec<f32> = (0..total_samples_needed)
.map(|i| {
let mut sum = 0.0f32;
for ch in 0..self.channels as usize {
sum += padded[i * self.channels as usize + ch];
}
sum / self.channels as f32
})
.collect();
let plan = self.plan_block_types(&mono, num_hops);
let mut encoded_frames: Vec<Frame> = Vec::new();
for hop_idx in 0..num_hops {
let start = hop_idx * hop_size * self.channels as usize;
let end = start + block_samples * self.channels as usize;
if end > padded.len() {
break;
}
let frame_samples = &padded[start..end];
let transform_frame = self.encode_frame(frame_samples, plan[hop_idx]);
let frame_data = serialize_frame(&transform_frame);
let mut flo_frame = Frame::new(FrameType::Transform as u8, hop_size as u32);
flo_frame.channels.push(ChannelData {
predictor_coeffs: vec![],
shift_bits: 0,
residual_encoding: ResidualEncoding::Raw,
rice_parameter: 0,
residuals: frame_data,
});
encoded_frames.push(flo_frame);
}
let writer = crate::Writer::new();
writer.write_ex(
self.sample_rate,
self.channels,
16, 5, true, (libm::roundf(self.quality * 4.0) as u8).min(4), &encoded_frames,
metadata,
)
}
}
pub fn serialize_frame(frame: &TransformFrame) -> Vec<u8> {
let mut data = Vec::new();
data.push(match frame.block_size {
BlockSize::Long => 0,
BlockSize::Short => 1,
BlockSize::Start => 2,
BlockSize::Stop => 3,
});
data.push(frame.coefficients.len() as u8);
for sf in &frame.scale_factors {
for &s in sf {
let log_sf = if s > 1e-10 {
((libm::log2f(s) * 256.0) + 32768.0).clamp(0.0, 65535.0) as u16
} else {
0
};
data.extend_from_slice(&log_sf.to_le_bytes());
}
}
for quantized in &frame.coefficients {
let encoded = serialize_sparse(quantized);
let len = encoded.len() as u32;
data.extend_from_slice(&len.to_le_bytes());
data.extend_from_slice(&encoded);
}
data
}
pub fn serialize_sparse(coeffs: &[i16]) -> Vec<u8> {
let mut output = Vec::new();
let mut i = 0;
while i < coeffs.len() {
let zero_start = i;
while i < coeffs.len() && coeffs[i] == 0 {
i += 1;
}
let zero_count = i - zero_start;
let non_zero_start = i;
while i < coeffs.len() && coeffs[i] != 0 && (i - non_zero_start) < 255 {
i += 1;
}
let non_zero_count = i - non_zero_start;
encode_varint(&mut output, zero_count as u32);
output.push(non_zero_count as u8);
for j in non_zero_start..non_zero_start + non_zero_count {
output.extend_from_slice(&coeffs[j].to_le_bytes());
}
}
output
}
fn encode_varint(output: &mut Vec<u8>, mut value: u32) {
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
output.push(byte);
if value == 0 {
break;
}
}
}