use rusty_opus::{Application, OpusEncoder};
struct Lcg(u64);
impl Lcg {
fn next_f32(&mut self) -> f32 {
self.0 = self.0.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
((self.0 >> 40) as f32 / (1u32 << 23) as f32) - 1.0
}
}
fn synth_music(rate: u32, channels: usize, secs: f32) -> Vec<f32> {
let n = (rate as f32 * secs) as usize;
let mut rng = Lcg(0x5EED_CAFE_F00D_D00D);
let mut out = vec![0.0f32; n * channels];
let freqs = [220.0f32, 277.18, 329.63, 440.0, 554.37, 659.25];
for i in 0..n {
let t = i as f32 / rate as f32;
let vib = (2.0 * std::f32::consts::PI * 5.0 * t).sin() * 0.002;
let beat = if (t * 2.0).fract() < 0.05 { 1.8 } else { 1.0 };
let mut s = 0.0f32;
for (k, f) in freqs.iter().enumerate() {
let ph = 2.0 * std::f32::consts::PI * f * (1.0 + vib) * t;
s += ph.sin() * (0.22 / (k as f32 + 1.0));
}
s = (s * beat + rng.next_f32() * 0.02).clamp(-0.98, 0.98) * 0.5;
for c in 0..channels {
let sc = if c == 0 { s } else { s * 0.8 + rng.next_f32() * 0.01 };
out[i * channels + c] = sc;
}
}
out
}
fn synth_speech(rate: u32, secs: f32) -> Vec<f32> {
let n = (rate as f32 * secs) as usize;
let mut rng = Lcg(0xBEEF_5A5A_1234_5678);
let mut out = vec![0.0f32; n];
let (mut y1a, mut y2a, mut y1b, mut y2b) = (0.0f32, 0.0f32, 0.0f32, 0.0f32);
let mut phase = 0.0f32;
for i in 0..n {
let t = i as f32 / rate as f32;
let seg = t % 0.6;
let voiced = seg < 0.4;
let f0 = 120.0 + 25.0 * (2.0 * std::f32::consts::PI * 0.7 * t).sin();
phase += f0 / rate as f32;
if phase >= 1.0 {
phase -= 1.0;
}
let exc = if voiced {
(1.0 - phase).powi(3) * if phase < 0.1 { 1.5 } else { 0.3 }
} else {
rng.next_f32() * 0.4
};
let (r, c1) = (0.95f32, (2.0 * std::f32::consts::PI * 700.0 / rate as f32).cos());
let ya = exc + 2.0 * r * c1 * y1a - r * r * y2a;
y2a = y1a;
y1a = ya;
let c2 = (2.0 * std::f32::consts::PI * 1800.0 / rate as f32).cos();
let yb = exc + 2.0 * r * c2 * y1b - r * r * y2b;
y2b = y1b;
y1b = yb;
out[i] = ((ya * 0.6 + yb * 0.4) * 0.05).clamp(-0.98, 0.98);
}
out
}
struct Scenario {
name: &'static str,
rate: u32,
channels: usize,
app: Application,
bitrate: i32,
pcm: Vec<f32>,
}
fn scenarios(secs: f32) -> Vec<Scenario> {
vec![
Scenario {
name: "CELT 48k stereo music @128k (Audio)",
rate: 48000,
channels: 2,
app: Application::Audio,
bitrate: 128_000,
pcm: synth_music(48000, 2, secs),
},
Scenario {
name: "SILK 16k mono speech @24k (Voip) ",
rate: 16000,
channels: 1,
app: Application::Voip,
bitrate: 24_000,
pcm: synth_speech(16000, secs),
},
Scenario {
name: "HYBRID 48k mono speech @32k (Voip) ",
rate: 48000,
channels: 1,
app: Application::Voip,
bitrate: 32_000,
pcm: synth_speech(48000, secs),
},
]
}
fn encode_clip(sc: &Scenario) -> (usize, usize) {
let mut enc = OpusEncoder::new(sc.rate as i32, sc.channels, sc.app).unwrap();
enc.bitrate_bps = sc.bitrate;
if let Some(c) = std::env::var("RUSTY_OPUS_COMPLEXITY").ok().and_then(|s| s.parse::<i32>().ok()) {
enc.complexity = c.clamp(0, 10);
}
let frame = sc.rate as usize / 50; let step = frame * sc.channels;
let mut out = vec![0u8; 4000];
let (mut bytes, mut packets) = (0usize, 0usize);
for chunk in sc.pcm.chunks_exact(step) {
let n = enc.encode(chunk, frame, &mut out).expect("encode");
bytes += n;
packets += 1;
}
(bytes, packets)
}
#[test]
#[ignore]
fn encode_throughput() {
let secs = 30.0f32;
let passes = 7;
for sc in scenarios(secs) {
let (bytes, packets) = encode_clip(&sc); assert!(packets > 0 && bytes > packets); let mut best = f64::INFINITY;
let mut all = Vec::new();
for _ in 0..passes {
let t0 = std::time::Instant::now();
let (b, _) = encode_clip(&sc);
let dt = t0.elapsed().as_secs_f64();
assert_eq!(b, bytes); all.push(dt);
if dt < best {
best = dt;
}
}
all.sort_by(f64::total_cmp);
let median = all[all.len() / 2];
let kbps = bytes as f64 * 8.0 / secs as f64 / 1000.0;
println!(
"{} best {:>7.1}x RT median {:>7.1}x RT ({} pkts, {:.1} kbps)",
sc.name,
secs as f64 / best,
secs as f64 / median,
packets,
kbps,
);
}
}
#[test]
#[ignore]
fn profile_breakdown() {
let secs = 10.0f32;
let passes = 15;
for sc in scenarios(secs) {
let mut per_stage: Vec<Vec<(f64, u64)>> = vec![Vec::new(); rusty_opus::prof::N];
for _ in 0..passes {
rusty_opus::prof::reset();
encode_clip(&sc);
let snap = rusty_opus::prof::snapshot();
for (i, v) in snap.iter().enumerate() {
per_stage[i].push(*v);
}
}
let med = |v: &mut Vec<(f64, u64)>| {
v.sort_by(|a, b| a.0.total_cmp(&b.0));
v[v.len() / 2]
};
let stages: Vec<(f64, u64)> = per_stage.iter_mut().map(med).collect();
let total = stages[rusty_opus::prof::Stage::Total as usize].0.max(1e-9);
let sub: f64 = stages[..rusty_opus::prof::INFO_FIRST]
.iter()
.map(|s| s.0)
.sum();
println!("\n=== {} — {:.1} ms total (median of {passes}) ===", sc.name, total);
let mut rows: Vec<(usize, f64, u64)> = stages[..rusty_opus::prof::Stage::Total as usize]
.iter()
.enumerate()
.filter(|(_, s)| s.1 > 0)
.map(|(i, s)| (i, s.0, s.1))
.collect();
rows.sort_by(|a, b| b.1.total_cmp(&a.1));
for (i, ms, calls) in rows {
println!(
" {:<18} {:>8.2} ms {:>5.1}% ({} calls, {:.0} ns/call)",
rusty_opus::prof::name(i),
ms,
100.0 * ms / total,
calls,
ms * 1e6 / calls as f64,
);
}
println!(
" {:<18} {:>8.2} ms {:>5.1}% <- residue (mode select / control / glue / timer overhead)",
"mgmt/other",
total - sub,
100.0 * (total - sub) / total,
);
}
}