use sample::{interpolate, signal, Frame, Signal};
fn main() {
let mut args = std::env::args();
let filename = if let Some(filename) = args.nth(1) {
filename
} else {
eprintln!("usage: playback file");
std::process::exit(-1);
};
match playback(&filename) {
Ok(()) => (),
Err(e) => {
eprintln!("{}", e);
std::process::exit(-1)
}
}
}
fn playback(filename: &str) -> Result<(), Box<std::error::Error>> {
let mp3_data = std::fs::read(filename)?;
let (header, samples) = puremp3::read_mp3(&mp3_data[..])?;
let device = cpal::default_output_device().ok_or("Failed to get default output device")?;
let out_format = device.default_output_format()?;
let event_loop = cpal::EventLoop::new();
let stream_id = event_loop.build_output_stream(&device, &out_format)?;
event_loop.play_stream(stream_id.clone());
let mut signal = signal::from_iter(samples.map(|sample| [sample.0, sample.1]));
let interp = interpolate::Linear::from_source(&mut signal);
let mut signal = signal.from_hz_to_hz(
interp,
header.sample_rate.hz().into(),
out_format.sample_rate.0.into(),
);
use cpal::{StreamData, UnknownTypeOutputBuffer};
event_loop.run(move |_, data| match data {
StreamData::Output {
buffer: UnknownTypeOutputBuffer::F32(mut buffer),
} => write_samples(&mut signal, &out_format, &mut buffer),
StreamData::Output {
buffer: UnknownTypeOutputBuffer::I16(mut buffer),
} => write_samples(&mut signal, &out_format, &mut buffer),
StreamData::Output {
buffer: UnknownTypeOutputBuffer::U16(mut buffer),
} => write_samples(&mut signal, &out_format, &mut buffer),
_ => unreachable!(),
});
}
fn write_samples<S, T>(
in_signal: &mut S,
out_format: &cpal::Format,
out_buffer: &mut cpal::OutputBuffer<'_, T>,
) where
S: sample::Signal<Frame = [f32; 2]>,
T: cpal::Sample + sample::Sample + sample::conv::FromSample<f32>,
{
for out_sample in out_buffer.chunks_mut(out_format.channels as usize) {
if in_signal.is_exhausted() {
std::process::exit(0);
}
let in_sample: [T; 2] = in_signal.next().map(sample::Sample::to_sample);
out_sample.copy_from_slice(&in_sample[..]);
}
}