use std::env;
use std::path::PathBuf;
use std::thread;
use std::time::Duration;
use atome::device::AtomeDevice;
use atome::output::SampleRate;
use atome::plugins::{atome as effects, Params};
use atome::{import, output, AudioEngine, Plugin};
use cpal::traits::StreamTrait;
mod common;
type Sample = f32;
const BLOCK_FRAMES: usize = 1024;
const CHAIN: &[(&str, &str)] = &[
("filter", "cutoff_hz: 1800, resonance: 1.4"),
("compressor", "threshold_db: -24, ratio: 4, attack_ms: 5, release_ms: 80"),
("tremolo", "rate_hz: 4, depth: 0.7"),
("delay", "time_ms: 180, feedback: 0.3, mix: 0.25"),
("limiter", "ceiling_db: -3, release_ms: 40"),
];
fn chain(sample_rate: u32) -> Result<Vec<Plugin>, Box<dyn std::error::Error>> {
CHAIN
.iter()
.map(|(kind, params)| {
let params = Params::parse(params)?;
Ok(effects::create(kind, sample_rate, ¶ms)?)
})
.collect()
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = env::args().nth(1).map(PathBuf::from).unwrap_or_else(|| {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("test_data")
.join("test415hz.mp3")
});
if !path.exists() {
eprintln!("no such file: {}", path.display());
return Ok(());
}
let decoded = import::decode(&path)?;
println!("file: {}", path.display());
println!("encoding: {:?}", import::find_type(&path)?);
println!("sample rate: {} Hz", decoded.sample_rate);
println!("channels: {}", decoded.channels);
println!("length: {:.2} s", decoded.duration());
let outputs = output::list_devices(None)?;
let Some(device) = outputs.first() else {
eprintln!("no output devices on this machine");
return Ok(());
};
let rate = SampleRate::from_hz(decoded.sample_rate).unwrap_or(SampleRate::Hz48k);
let mut engine = AudioEngine::<Sample>::new(
vec![],
vec![AtomeDevice::output(device.clone(), common::host())
.with_plugins(chain(rate.hz() as u32)?)],
rate,
vec![decoded.channels],
None,
vec![],
)?;
common::describe(&engine);
println!("\nchain:");
for plugin in engine.outputs()[0].device().plugins() {
println!(" {:<12} {}", plugin.name, plugin.params());
}
let output = engine.outputs_mut()[0].output_mut();
let mut samples = output.align_samples(
&decoded.samples.to_vec::<Sample>(),
SampleRate::from_hz(decoded.sample_rate).unwrap_or(rate),
decoded.channels,
true,
)?;
let channels = output.channels().max(1) as usize;
let sample_rate = output.sample_rate() as f64;
println!("\nrunning the chain over {} blocks", samples.len().div_ceil(BLOCK_FRAMES * channels));
for block in samples.chunks_mut(BLOCK_FRAMES * channels) {
engine.apply_output_plugins(0, block)?;
}
let output = engine.outputs_mut()[0].output_mut();
let stream = output.build_stream()?;
stream.play()?;
output.add_samples(&samples, 0)?;
let seconds = samples.len() as f64 / sample_rate / channels as f64;
println!("playing {seconds:.2} s through {}", common::name_of(device));
thread::sleep(Duration::from_secs_f64(seconds) + Duration::from_millis(500));
engine.outputs_mut()[0].output_mut().stop();
println!("done");
Ok(())
}