use rusty_opus::silk::define::*;
use rusty_opus::silk::lpc_analysis::{silk_burg_modified_fix, silk_find_lpc_fix};
use rusty_opus::silk::structs::SilkEncoderStateCommon;
use std::f32::consts::PI;
fn create_test_encoder_state() -> SilkEncoderStateCommon {
SilkEncoderStateCommon {
fs_khz: 16, frame_length: 320, subfr_length: 80, nb_subfr: 4, predict_lpc_order: 16, shaping_lpc_order: 16,
pitch_lpc_win_length: 640,
first_frame_after_reset: 1,
la_pitch: 16,
la_shape: 5,
shape_win_length: 15,
ltp_mem_length: 20,
pitch_estimation_complexity: 2,
complexity: 2,
prev_signal_type: 0,
..Default::default()
}
}
#[test]
fn test_lpc_sinusoid() {
let fs = 16000;
let frame_samples = 320;
let freq = 1000.0; let mut x = vec![0i16; frame_samples];
for i in 0..frame_samples {
let t = i as f32 / fs as f32;
let sample = (2.0 * PI * freq * t).sin() * 16000.0;
x[i] = sample as i16;
}
let mut res_nrg = 0;
let mut res_nrg_q = 0;
let mut a_q16 = [0i32; MAX_LPC_ORDER];
silk_burg_modified_fix(
&mut res_nrg,
&mut res_nrg_q,
&mut a_q16,
&x,
1 << 26, 80, 4, 10, );
println!("Sinusoid Test:");
println!(" Residual energy: {} (Q{})", res_nrg, res_nrg_q);
println!(" LPC coefficients (Q16): {:?}", &a_q16[..10]);
assert!(res_nrg != 0, "Residual energy should be non-zero");
let non_zero_count = a_q16[..10].iter().filter(|&&x| x != 0).count();
assert!(
non_zero_count >= 2,
"Should have at least 2 non-zero LPC coefficients"
);
println!("✅ Sinusoid LPC test passed");
}
#[test]
fn test_lpc_voiced_speech_like() {
let fs = 16000;
let frame_samples = 320;
let f0 = 120.0; let mut x = vec![0i16; frame_samples];
for i in 0..frame_samples {
let t = i as f32 / fs as f32;
let mut sample = 0.0;
for h in 1..=8 {
let amplitude = 10000.0 / (h as f32);
sample += (2.0 * PI * f0 * (h as f32) * t).sin() * amplitude;
}
x[i] = sample as i16;
}
let mut res_nrg = 0;
let mut res_nrg_q = 0;
let mut a_q16 = [0i32; MAX_LPC_ORDER];
silk_burg_modified_fix(
&mut res_nrg,
&mut res_nrg_q,
&mut a_q16,
&x,
1 << 26,
80,
4,
16, );
println!("\nVoiced Speech-like Test:");
println!(" Residual energy: {} (Q{})", res_nrg, res_nrg_q);
println!(" First 5 LPC coefficients: {:?}", &a_q16[..5]);
assert!(res_nrg != 0, "Residual energy should be non-zero");
let non_zero_count = a_q16[..16].iter().filter(|&&x| x != 0).count();
assert!(
non_zero_count >= 1,
"Complex signal should have at least one LPC coefficient, got {}",
non_zero_count
);
println!(" Non-zero coefficients: {}/16", non_zero_count);
println!("✅ Voiced speech-like LPC test passed");
}
#[test]
fn test_lpc_white_noise() {
let frame_samples = 320;
let mut x = vec![0i16; frame_samples];
let mut seed = 12345u32;
for i in 0..frame_samples {
seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
let noise = ((seed >> 16) as i16) as i32;
x[i] = ((noise * 8000) >> 16) as i16;
}
let mut res_nrg = 0;
let mut res_nrg_q = 0;
let mut a_q16 = [0i32; MAX_LPC_ORDER];
silk_burg_modified_fix(
&mut res_nrg,
&mut res_nrg_q,
&mut a_q16,
&x,
1 << 26,
80,
4,
10,
);
println!("\nWhite Noise Test:");
println!(" Residual energy: {} (Q{})", res_nrg, res_nrg_q);
println!(
" LPC coefficients magnitude sum: {}",
a_q16[..10].iter().map(|&x| x.abs()).sum::<i32>()
);
assert!(res_nrg != 0, "Residual energy should be non-zero");
let coef_sum: i64 = a_q16[..10].iter().map(|&x| x.abs() as i64).sum();
println!(" Coefficient sum: {}", coef_sum);
println!("✅ White noise LPC test passed");
}
#[test]
fn test_find_lpc_fix_integration() {
let mut ps_enc_c = create_test_encoder_state();
let required_samples =
(ps_enc_c.subfr_length + ps_enc_c.predict_lpc_order) as usize * ps_enc_c.nb_subfr as usize;
let mut x = vec![0i16; required_samples];
let fs = 16000.0;
for i in 0..required_samples {
let t = i as f32 / fs;
let _f = 500.0 + 1500.0 * t; let phase = 2.0 * PI * (500.0 * t + 750.0 * t * t);
x[i] = (phase.sin() * 15000.0) as i16;
}
let mut nlsf_q15 = [0i16; MAX_LPC_ORDER];
let min_inv_gain_q30 = 1 << 26;
silk_find_lpc_fix(&mut ps_enc_c, &mut nlsf_q15, &x, min_inv_gain_q30);
println!("\nFull LPC Analysis Test (Chirp Signal):");
println!(" NLSF (Q15) first 8 values: {:?}", &nlsf_q15[..8]);
for i in 0..ps_enc_c.predict_lpc_order as usize {
assert!(
nlsf_q15[i] >= 0,
"NLSF[{}] = {} should be non-negative",
i,
nlsf_q15[i]
);
}
for i in 1..ps_enc_c.predict_lpc_order as usize {
assert!(
nlsf_q15[i] >= nlsf_q15[i - 1],
"NLSF not ordered: nlsf[{}]={} < nlsf[{}]={}",
i,
nlsf_q15[i],
i - 1,
nlsf_q15[i - 1]
);
}
let non_zero = nlsf_q15[..ps_enc_c.predict_lpc_order as usize]
.iter()
.filter(|&&x| x > 0)
.count();
assert!(
non_zero >= ps_enc_c.predict_lpc_order as usize / 2,
"Too many zero NLSFs: {}/{}",
non_zero,
ps_enc_c.predict_lpc_order
);
println!("✅ Full LPC analysis integration test passed");
}
#[test]
fn test_lpc_stability() {
let frame_samples = 320;
let mut x = vec![0i16; frame_samples];
for i in 0..frame_samples {
if i % 40 < 20 {
x[i] = 20000;
} else {
x[i] = -20000;
}
}
let mut res_nrg = 0;
let mut res_nrg_q = 0;
let mut a_q16 = [0i32; MAX_LPC_ORDER];
silk_burg_modified_fix(
&mut res_nrg,
&mut res_nrg_q,
&mut a_q16,
&x,
1 << 26,
80,
4,
10,
);
println!("\nStability Test (Square Wave):");
println!(" Residual energy: {} (Q{})", res_nrg, res_nrg_q);
println!(" Algorithm completed without panic ✅");
println!(" Completed without errors");
println!("✅ LPC stability test passed");
}