diff --git a/changelog.md b/changelog.md index 85fceb1..6547424 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,8 @@ when upgrading from a version of rust-sdl2 to another. ### Next +[PR #1464](https://github.com/Rust-SDL2/rust-sdl2/pull/1464) Added binding for `Mix_OpenAudioDevice` + [PR #1451](https://github.com/Rust-SDL2/rust-sdl2/pull/1451) Add `gamma_ramp_arrays` and `calculate_gamma_ramp`. [PR #1459](https://github.com/Rust-SDL2/rust-sdl2/pull/1459) Fix image and mixer init flag logic diff --git a/src/sdl2/mixer/mod.rs b/src/sdl2/mixer/mod.rs index 7b7ca92..42b475d 100644 --- a/src/sdl2/mixer/mod.rs +++ b/src/sdl2/mixer/mod.rs @@ -27,12 +27,12 @@ use libc::{c_double, c_int, c_uint}; use rwops::RWops; use std::borrow::ToOwned; use std::convert::TryInto; -use std::default; use std::ffi::{CStr, CString}; use std::fmt; use std::marker::PhantomData; use std::path::Path; use std::str::from_utf8; +use std::{default, ptr}; use sys; use sys::mixer; use version::Version; @@ -106,6 +106,16 @@ bitflags!( } ); +bitflags!( + /// Which audio format changes are allowed when opening a device ([`open_audio_device`]). + pub struct AllowChangeFlag: u32 { + const FREQUENCY = sys::SDL_AUDIO_ALLOW_FREQUENCY_CHANGE; + const FORMAT = sys::SDL_AUDIO_ALLOW_FORMAT_CHANGE; + const CHANNELS = sys::SDL_AUDIO_ALLOW_CHANNELS_CHANGE; + const SAMPLES = sys::SDL_AUDIO_ALLOW_SAMPLES_CHANGE; + } +); + impl fmt::Display for InitFlag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::fmt(self, f) @@ -147,7 +157,8 @@ pub fn init(flags: InitFlag) -> Result { } } -/// Open the mixer with a certain audio format. +/// Opens the default audio device for playback. If you need to select a specific audio device +/// or require more fine-grained control over the device configuration, use [`open_audio_device`]. /// /// * `chunksize`: It is recommended to choose values between 256 and 1024, depending on whether /// you prefer latency or compatibility. Small values reduce latency but may not @@ -175,6 +186,60 @@ pub fn open_audio( } } +/// Open a specific audio device for playback. +/// +/// (A slightly simpler version of this function is available in [`open_audio`], which still might +/// meet most applications' needs.) +/// +/// The `allowed_changes` parameter specifies what settings are flexible. These tell `SDL_mixer` +/// that the app doesn't mind if a specific setting changes. For example, the app might need stereo +/// data in [`i16`] format, but if the sample rate or chunk size changes, the app can handle that. +/// In that case, the app would specify `AllowChangeFlag::FORMAT | AllowChangeFlag::SAMPLES`. In +/// this case, if the system's hardware requires something other than the requested format, +/// `SDL_mixer` can select what the hardware demands instead of the app. For a given +/// [`AllowChangeFlag`], If it is not specified, `SDL_mixer` must convert data behind the scenes +/// between what the app demands and what the hardware requires. If your app needs precisely what +/// is requested, specify [`AllowChangeFlag::empty`]. +/// +/// * `frequency`: The frequency to playback audio at (in Hz). +/// * `format`: Audio format ([`AudioFormat`]). +/// * `channels`: Number of channels (1 is mono, 2 is stereo, etc). +/// * `chunksize`: Audio buffer size in sample FRAMES (total samples divided by channel count). +/// The lower the number, the lower the latency, but you risk dropouts if it gets +/// too low. +/// * `device`: The device name to open, or [`None`] to choose a reasonable default. +/// * `allowed_changes`: Allow change flags ([`AllowChangeFlag`]). +/// +pub fn open_audio_device<'a, D>( + frequency: i32, + format: AudioFormat, + channels: i32, + chunksize: i32, + device: D, + allowed_changes: AllowChangeFlag, +) -> Result<(), String> +where + D: Into>, +{ + let ret = unsafe { + let device = device.into().map(|device| CString::new(device).unwrap()); + let device_ptr = device.as_ref().map_or(ptr::null(), |s| s.as_ptr()); + mixer::Mix_OpenAudioDevice( + frequency as c_int, + format, + channels as c_int, + chunksize as c_int, + device_ptr, + allowed_changes.bits() as c_int, + ) + }; + if ret == 0 { + Ok(()) + } else { + Err(get_error()) + } +} + /// Shutdown and cleanup the mixer API. pub fn close_audio() { unsafe { mixer::Mix_CloseAudio() }