mirror of
https://github.com/doukutsu-rs/rust-sdl2.git
synced 2026-08-24 13:05:30 -07:00
88 lines
2.8 KiB
Rust
88 lines
2.8 KiB
Rust
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<u8>,
|
||
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(())
|
||
}
|