fn init_audio_subsystem() -> Option<(sdl3::Sdl, sdl3::AudioSubsystem)> {
std::env::set_var("SDL_AUDIODRIVER", "dummy");
let sdl = match sdl3::init() {
Ok(sdl) => sdl,
Err(err) => {
eprintln!("Skipping audio test: failed to init SDL: {err}");
return None;
}
};
let audio = match sdl.audio() {
Ok(audio) => audio,
Err(err) => {
eprintln!("Skipping audio test: failed to init audio subsystem: {err}");
return None;
}
};
Some((sdl, audio))
}
#[test]
fn audio_spec_wav() {
let wav = sdl3::audio::AudioSpecWAV::load_wav("./assets/sine.wav").unwrap();
assert_eq!(wav.freq, 22_050);
assert_eq!(wav.format, sdl3::audio::AudioFormat::S16LE);
assert_eq!(wav.channels, 1);
let buffer = wav.buffer();
assert_eq!(buffer.len(), 4_410);
}
#[test]
fn audio_stream_device_paused_state_transitions() {
let Some((_sdl, audio)) = init_audio_subsystem() else {
return;
};
let device = match audio.open_playback_device(&sdl3::audio::AudioSpec::default()) {
Ok(device) => device,
Err(err) => {
eprintln!("Skipping stream test: failed to open dummy playback device: {err}");
return;
}
};
let stream = match device.open_device_stream(None) {
Ok(stream) => stream,
Err(err) => {
eprintln!("Skipping stream test: failed to open device stream: {err}");
return;
}
};
assert!(stream.device_paused().expect("device_paused failed"));
stream.resume().expect("resume failed");
assert!(
!stream
.device_paused()
.expect("device_paused failed after resume"),
"Device should report unpaused after resuming"
);
stream.pause().expect("pause failed");
assert!(
stream
.device_paused()
.expect("device_paused failed after pause"),
"Device should report paused after pausing"
);
}
#[test]
fn audio_stream_reports_format_and_queue() {
let Some((_sdl, audio)) = init_audio_subsystem() else {
return;
};
let device = match audio.open_playback_device(&sdl3::audio::AudioSpec::default()) {
Ok(device) => device,
Err(err) => {
eprintln!("Skipping format test: failed to open dummy playback device: {err}");
return;
}
};
let stream = match device.open_device_stream(None) {
Ok(stream) => stream,
Err(err) => {
eprintln!("Skipping format test: failed to open device stream: {err}");
return;
}
};
let (src, dst) = stream.get_format().expect("get_format failed");
assert!(
src.is_some() || dst.is_some(),
"Expected dummy driver to report at least one side of the stream format"
);
let avail = stream.available_bytes().expect("available_bytes failed");
assert!(avail >= 0, "available bytes should be non-negative");
let queued = stream.queued_bytes().expect("queued_bytes failed");
assert!(queued >= 0, "queued bytes should be non-negative");
stream.clear().expect("clear failed");
stream.pause().expect("pause failed");
stream.resume().expect("resume failed");
}
#[test]
fn audio_stream_put_and_clear() {
let Some((_sdl, audio)) = init_audio_subsystem() else {
return;
};
let spec = sdl3::audio::AudioSpec {
freq: Some(48_000),
channels: Some(1),
format: Some(sdl3::audio::AudioFormat::F32LE),
};
let stream = match audio.new_stream(Some(&spec), Some(&spec)) {
Ok(stream) => stream,
Err(err) => {
eprintln!("Skipping stream buffer test: {err}");
return;
}
};
let samples = vec![0.0f32; 128];
stream.put_data_f32(&samples).expect("put_data_f32 failed");
let available = stream.available_bytes().expect("available_bytes failed");
assert!(
available >= (samples.len() * std::mem::size_of::<f32>()) as i32,
"available bytes should reflect queued samples"
);
stream.clear().expect("clear failed");
assert_eq!(
stream
.queued_bytes()
.expect("queued_bytes failed after clear"),
0,
"clear should drop queued data"
);
stream.flush().expect("flush failed");
}