extern crate sdl2; use sdl2::audio::{AudioCVT, AudioCallback, AudioSpecDesired, AudioSpecWAV}; use std::borrow::Cow; use std::path::{Path, PathBuf}; use std::time::Duration; // NOTE: You probably want to investigate the // mixer feature for real use cases. struct Sound { data: Vec, volume: f32, pos: usize, } impl AudioCallback for Sound { type Channel = u8; fn callback(&mut self, out: &mut [u8]) { for dst in out.iter_mut() { // With channel type u8 the "silence" value is 128 (middle of the 0-2^8 range) so we need // to both fill in the silence and scale the wav data accordingly. Filling the silence // once the wav is finished is trivial, applying the volume is more tricky. We need to: // * Change the range of the values from [0, 255] to [-128, 127] so we can multiply // * Apply the volume by multiplying, this gives us range [-128*volume, 127*volume] // * Move the resulting range to a range centered around the value 128, the final range // is [128 - 128*volume, 128 + 127*volume] – scaled and correctly positioned // // Using value 0 instead of 128 would result in clicking. Scaling by simply multiplying // would not give correct results. let pre_scale = *self.data.get(self.pos).unwrap_or(&128); let scaled_signed_float = (pre_scale as f32 - 128.0) * self.volume; let scaled = (scaled_signed_float + 128.0) as u8; *dst = scaled; self.pos += 1; } } } fn main() -> Result<(), String> { let wav_file: Cow<'static, Path> = match std::env::args().nth(1) { None => Cow::from(Path::new("./assets/sine.wav")), Some(s) => Cow::from(PathBuf::from(s)), }; let sdl_context = sdl2::init()?; let audio_subsystem = sdl_context.audio()?; let desired_spec = AudioSpecDesired { freq: Some(44_100), channels: Some(1), // mono samples: None, // default }; let device = audio_subsystem.open_playback(None, &desired_spec, |spec| { let wav = AudioSpecWAV::load_wav(wav_file).expect("Could not load test WAV file"); let cvt = AudioCVT::new( wav.format, wav.channels, wav.freq, spec.format, spec.channels, spec.freq, ) .expect("Could not convert WAV file"); let data = cvt.convert(wav.buffer().to_vec()); // initialize the audio callback Sound { data, volume: 0.25, pos: 0, } })?; // Start playback device.resume(); // Play for a second std::thread::sleep(Duration::from_millis(1_000)); // Device is automatically closed when dropped Ok(()) }