use opus_rs::range_coder::RangeCoder;
use opus_rs::silk::control_codec::*;
use opus_rs::silk::define::*;
use opus_rs::silk::enc_api::silk_encode_frame;
use opus_rs::silk::init_encoder::silk_init_encoder;
use opus_rs::silk::structs::*;
use opus_rs::{Application, OpusEncoder};
use std::f32::consts::PI;
fn generate_voiced_signal(len: usize) -> Vec<i16> {
let mut signal = vec![0i16; len];
let freq = 200.0f64;
let fs = 16000.0f64;
for i in 0..len {
let t = i as f64 / fs;
signal[i] = (10000.0 * (2.0 * std::f64::consts::PI * freq * t).sin()) as i16;
}
signal
}
fn create_wb_encoder(complexity: i32) -> SilkEncoderState {
let mut enc = SilkEncoderState::default();
silk_init_encoder(&mut enc, 0);
silk_control_encoder(&mut enc, 16, 20, 20000, complexity);
enc.s_cmn.snr_db_q7 = 25 * 128; enc
}
#[test]
fn test_silk_encode_frame_no_crash() {
let mut enc = create_wb_encoder(1);
let frame_length = enc.s_cmn.frame_length as usize;
let input = generate_voiced_signal(frame_length);
let mut rc = RangeCoder::new_encoder(1275);
let mut n_bytes_out: i32 = 0;
let ret = silk_encode_frame(
&mut enc,
&input,
&mut rc,
&mut n_bytes_out,
CODE_INDEPENDENTLY,
8000, 0, );
assert_eq!(ret, 0, "silk_encode_frame should return 0 (no error)");
assert!(
n_bytes_out > 0,
"Encoded frame should produce some bytes, got {}",
n_bytes_out
);
let pulse_sum: i32 = enc.pulses.iter().map(|&p| p.abs() as i32).sum();
println!(
"Encoded {} bytes, pulse energy = {}",
n_bytes_out, pulse_sum
);
assert!(pulse_sum > 0, "Pulses should be non-zero for voiced input");
}
#[test]
fn test_silk_encode_two_frames() {
let mut enc = create_wb_encoder(1);
let frame_length = enc.s_cmn.frame_length as usize;
let input1 = generate_voiced_signal(frame_length);
let mut rc = RangeCoder::new_encoder(1275);
let mut n_bytes = 0i32;
let ret = silk_encode_frame(
&mut enc,
&input1,
&mut rc,
&mut n_bytes,
CODE_INDEPENDENTLY,
8000,
0,
);
assert_eq!(ret, 0);
let bytes1 = n_bytes;
enc.s_cmn.n_frames_encoded = 1;
let input2 = generate_voiced_signal(frame_length);
let mut rc2 = RangeCoder::new_encoder(1275);
let mut n_bytes2 = 0i32;
let ret = silk_encode_frame(
&mut enc,
&input2,
&mut rc2,
&mut n_bytes2,
CODE_CONDITIONALLY,
8000,
0,
);
assert_eq!(ret, 0);
println!("Frame 1: {} bytes, Frame 2: {} bytes", bytes1, n_bytes2);
assert!(n_bytes2 > 0, "Second frame should also produce bytes");
}
#[test]
fn test_silk_encode_silent_input() {
let mut enc = create_wb_encoder(1);
let frame_length = enc.s_cmn.frame_length as usize;
let input = vec![0i16; frame_length];
let mut rc = RangeCoder::new_encoder(1275);
let mut n_bytes = 0i32;
let ret = silk_encode_frame(
&mut enc,
&input,
&mut rc,
&mut n_bytes,
CODE_INDEPENDENTLY,
8000,
0,
);
assert_eq!(ret, 0);
println!("Silent frame: {} bytes", n_bytes);
assert!(n_bytes > 0, "Even silence should produce some bytes");
}
#[test]
fn test_lbrr_encoding_enabled() {
let sample_rate = 8000;
let frame_size = 160;
let mut enc_no_fec =
OpusEncoder::new(sample_rate, 1, Application::Voip).expect("Encoder creation failed");
enc_no_fec.bitrate_bps = 20000;
enc_no_fec.use_cbr = false;
enc_no_fec.use_inband_fec = false;
let mut enc_with_fec =
OpusEncoder::new(sample_rate, 1, Application::Voip).expect("Encoder creation failed");
enc_with_fec.bitrate_bps = 20000;
enc_with_fec.use_cbr = false;
enc_with_fec.use_inband_fec = true;
enc_with_fec.packet_loss_perc = 10;
let mut total_bytes_no_fec = 0usize;
let mut total_bytes_with_fec = 0usize;
for frame_idx in 0..5 {
let mut input = vec![0.0f32; frame_size];
for i in 0..frame_size {
let t = (frame_idx * frame_size + i) as f32 / sample_rate as f32;
input[i] = (2.0f32 * PI * 440.0f32 * t).sin();
}
let mut out_no_fec = vec![0u8; 256];
let n_no_fec = enc_no_fec
.encode(&input, frame_size, &mut out_no_fec)
.expect("Encode without FEC failed");
let mut out_with_fec = vec![0u8; 512];
let n_with_fec = enc_with_fec
.encode(&input, frame_size, &mut out_with_fec)
.expect("Encode with FEC failed");
assert!(
n_no_fec >= 3,
"Frame {}: no-FEC packet too short: {}",
frame_idx,
n_no_fec
);
assert!(
n_with_fec >= 3,
"Frame {}: FEC packet too short: {}",
frame_idx,
n_with_fec
);
total_bytes_no_fec += n_no_fec;
total_bytes_with_fec += n_with_fec;
println!(
"Frame {}: no-FEC={} bytes, with-FEC={} bytes",
frame_idx, n_no_fec, n_with_fec
);
}
println!(
"Total: no-FEC={} bytes, with-FEC={} bytes",
total_bytes_no_fec, total_bytes_with_fec
);
println!("✅ LBRR encoding test passed: encoder runs with FEC enabled");
}
#[test]
fn test_lbrr_flag_in_packet() {
let sample_rate = 8000;
let frame_size = 160;
let mut encoder =
OpusEncoder::new(sample_rate, 1, Application::Voip).expect("Encoder creation failed");
encoder.bitrate_bps = 20000;
encoder.use_inband_fec = true;
encoder.packet_loss_perc = 10;
let mut packet_lbrr_flags = Vec::new();
for frame_idx in 0..3 {
let mut input = vec![0.0f32; frame_size];
for i in 0..frame_size {
let t = (frame_idx * frame_size + i) as f32 / sample_rate as f32;
input[i] = (2.0f32 * PI * 440.0f32 * t).sin();
}
let mut output = vec![0u8; 512];
let n = encoder
.encode(&input, frame_size, &mut output)
.expect("Encode failed");
assert!(n >= 3, "Frame {}: packet too short", frame_idx);
let has_lbrr = n > 10; packet_lbrr_flags.push((frame_idx, n, has_lbrr));
println!(
"Frame {}: {} bytes (LBRR data likely present: {})",
frame_idx, n, has_lbrr
);
}
println!("✅ LBRR flag test passed");
}