use std::env;
use std::time::Instant;
use ringkernel_audio_fft::prelude::*;
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::from_default_env()
.add_directive("ringkernel_audio_fft=info".parse().unwrap()),
)
.init();
let args: Vec<String> = env::args().collect();
let (input_path, dry_wet, gain_db) = parse_args(&args);
println!("=== RingKernel Audio FFT Separator ===\n");
let (input, sample_rate, duration) = if let Some(path) = &input_path {
println!("Loading audio file: {}", path);
let input = AudioInput::from_file(path)?;
let sr = input.sample_rate();
let dur = input.total_samples().unwrap_or(0) as f64 / sr as f64;
(input, sr, dur)
} else {
println!("No input file provided, using synthetic test signal");
let (input, sr, dur) = create_test_signal();
(input, sr, dur)
};
println!(" Sample rate: {} Hz", sample_rate);
println!(" Duration: {:.2} seconds", duration);
println!();
println!("Initializing processor...");
let start = Instant::now();
let mut processor = AudioFftProcessor::builder()
.fft_size(2048)
.hop_size(512)
.sample_rate(sample_rate)
.music_mode() .mixer_config(
MixerConfig::new()
.with_dry_wet(dry_wet)
.with_output_gain(10.0_f32.powf(gain_db / 20.0)),
)
.build()
.await?;
println!(" Processor initialized in {:?}", start.elapsed());
println!(" FFT size: {}", processor.fft_size());
println!(" Frequency bins: {}", processor.num_bins());
println!();
println!("Processing audio...");
let start = Instant::now();
let output = processor.process(input).await?;
let elapsed = start.elapsed();
println!(" Processing completed in {:?}", elapsed);
println!();
println!("=== Processing Statistics ===");
println!(" Frames processed: {}", output.stats.frames_processed);
println!(" Samples processed: {}", output.stats.samples_processed);
println!(" K2K messages exchanged: {}", output.stats.k2k_messages);
println!(
" Average frame time: {:.1} us",
output.stats.avg_frame_time_us
);
println!(
" Peak direct level: {:.2} ({:.1} dB)",
output.stats.peak_direct,
if output.stats.peak_direct > 1e-10 {
20.0 * output.stats.peak_direct.log10()
} else {
-200.0
}
);
println!(
" Peak ambience level: {:.2} ({:.1} dB)",
output.stats.peak_ambience,
if output.stats.peak_ambience > 1e-10 {
20.0 * output.stats.peak_ambience.log10()
} else {
-200.0
}
);
println!();
println!("=== Output ===");
println!(
" Direct signal: {:.2}s ({} samples)",
output.direct.duration_secs(),
output.direct.samples.len()
);
println!(
" Ambience signal: {:.2}s ({} samples)",
output.ambience.duration_secs(),
output.ambience.samples.len()
);
println!(
" Mixed signal: {:.2}s ({} samples)",
output.mixed.duration_secs(),
output.mixed.samples.len()
);
println!();
if input_path.is_some() {
let base_name = input_path
.as_ref()
.and_then(|p| std::path::Path::new(p).file_stem())
.and_then(|s| s.to_str())
.unwrap_or("output");
println!("Writing output files...");
output
.direct
.write_to_file(format!("{}_direct.wav", base_name))?;
output
.ambience
.write_to_file(format!("{}_ambience.wav", base_name))?;
output
.mixed
.write_to_file(format!("{}_mixed.wav", base_name))?;
println!(" Written: {}_direct.wav", base_name);
println!(" Written: {}_ambience.wav", base_name);
println!(" Written: {}_mixed.wav", base_name);
} else {
println!("Writing test output files...");
output.direct.write_to_file("test_direct.wav")?;
output.ambience.write_to_file("test_ambience.wav")?;
output.mixed.write_to_file("test_mixed.wav")?;
println!(" Written: test_direct.wav");
println!(" Written: test_ambience.wav");
println!(" Written: test_mixed.wav");
}
processor.shutdown().await?;
println!("\nDone!");
Ok(())
}
fn parse_args(args: &[String]) -> (Option<String>, f32, f32) {
let mut input_path = None;
let mut dry_wet = 0.5;
let mut gain_db = 0.0;
let mut i = 1;
while i < args.len() {
match args[i].as_str() {
"--dry-wet" if i + 1 < args.len() => {
dry_wet = args[i + 1].parse().unwrap_or(0.5);
i += 2;
}
"--gain" if i + 1 < args.len() => {
gain_db = args[i + 1].parse().unwrap_or(0.0);
i += 2;
}
"--help" | "-h" => {
println!("Usage: audio_separator [input.wav] [options]");
println!();
println!("Options:");
println!(" --dry-wet <0.0-1.0> Dry/wet mix (0=direct, 1=ambience)");
println!(" --gain <dB> Output gain in dB");
println!(" --help Show this help");
std::process::exit(0);
}
path if !path.starts_with('-') => {
input_path = Some(path.to_string());
i += 1;
}
_ => {
i += 1;
}
}
}
println!("Settings:");
println!(" Dry/wet mix: {:.2}", dry_wet);
println!(" Output gain: {:.1} dB", gain_db);
(input_path, dry_wet, gain_db)
}
fn create_test_signal() -> (AudioInput, u32, f64) {
let sample_rate = 44100;
let duration = 2.0; let num_samples = (sample_rate as f64 * duration) as usize;
let mut samples = Vec::with_capacity(num_samples);
for i in 0..num_samples {
let t = i as f32 / sample_rate as f32;
let direct = 0.3 * (2.0 * std::f32::consts::PI * 440.0 * t).sin()
+ 0.15 * (2.0 * std::f32::consts::PI * 880.0 * t).sin()
+ 0.1 * (2.0 * std::f32::consts::PI * 1320.0 * t).sin();
let ambient = 0.05
* ((2.0 * std::f32::consts::PI * 441.3 * t + 0.1).sin()
+ (2.0 * std::f32::consts::PI * 439.7 * t + 0.3).sin()
+ (2.0 * std::f32::consts::PI * 442.8 * t + 0.5).sin()
+ (2.0 * std::f32::consts::PI * 878.5 * t + 0.7).sin()
+ (2.0 * std::f32::consts::PI * 881.2 * t + 0.9).sin());
let envelope = if t < 0.1 {
t / 0.1
} else if t > duration as f32 - 0.2 {
(duration as f32 - t) / 0.2
} else {
1.0
};
samples.push((direct + ambient) * envelope);
}
let input = AudioInput::from_samples(samples, sample_rate, 1);
(input, sample_rate, duration)
}