//! Audio playback and capture //! //! # Example //! Playback a sine wave through the "default" device. //! //! ``` //! use alsa::{Direction, ValueOr}; //! use alsa::pcm::{PCM, HwParams, Format, Access, State}; //! //! // Open default playback device //! let pcm = PCM::new("default", Direction::Playback, false).unwrap(); //! //! // Set hardware parameters: 44100 Hz / Mono / 16 bit //! let hwp = HwParams::any(&pcm).unwrap(); //! hwp.set_channels(1).unwrap(); //! hwp.set_rate(44100, ValueOr::Nearest).unwrap(); //! hwp.set_format(Format::s16()).unwrap(); //! hwp.set_access(Access::RWInterleaved).unwrap(); //! pcm.hw_params(&hwp).unwrap(); //! let io = pcm.io_i16().unwrap(); //! //! // Make sure we don't start the stream too early //! let hwp = pcm.hw_params_current().unwrap(); //! let swp = pcm.sw_params_current().unwrap(); //! swp.set_start_threshold(hwp.get_buffer_size().unwrap()).unwrap(); //! pcm.sw_params(&swp).unwrap(); //! //! // Make a sine wave //! let mut buf = [0i16; 1024]; //! for (i, a) in buf.iter_mut().enumerate() { //! *a = ((i as f32 * 2.0 * ::std::f32::consts::PI / 128.0).sin() * 8192.0) as i16 //! } //! //! // Play it back for 2 seconds. //! for _ in 0..2*44100/1024 { //! assert_eq!(io.writei(&buf[..]).unwrap(), 1024); //! } //! //! // In case the buffer was larger than 2 seconds, start the stream manually. //! if pcm.state() != State::Running { pcm.start().unwrap() }; //! // Wait for the stream to finish playback. //! pcm.drain().unwrap(); //! ```
use libc::{c_int, c_uint, c_void, ssize_t, c_short, timespec, pollfd}; usecrate::alsa; use std::convert::Infallible; use std::marker::PhantomData; use std::mem::size_of; use std::ffi::{CStr, CString}; use std::str::FromStr; use std::{io, fmt, ptr, cell}; usesuper::error::*; usesuper::{Direction, Output, poll, ValueOr, chmap};
pubfn get_id(&self) -> Result<&str> { let c = unsafe { alsa::snd_pcm_info_get_id(self.0) };
from_const("snd_pcm_info_get_id", c)
}
pubfn get_name(&self) -> Result<&str> { let c = unsafe { alsa::snd_pcm_info_get_name(self.0) };
from_const("snd_pcm_info_get_name", c)
}
pubfn get_subdevice_name(&self) -> Result<&str> { let c = unsafe { alsa::snd_pcm_info_get_subdevice_name(self.0) };
from_const("snd_pcm_info_get_subdevice_name", c)
}
pubfn get_stream(&self) -> Direction { matchunsafe { alsa::snd_pcm_info_get_stream(self.0) } {
alsa::SND_PCM_STREAM_CAPTURE => Direction::Capture,
alsa::SND_PCM_STREAM_PLAYBACK => Direction::Playback,
n @ _ => panic!("snd_pcm_info_get_stream invalid direction '{}'", n),
}
}
/// Wrapper around open that takes a &str instead of a &CStr pubfn new(name: &str, dir: Direction, nonblock: bool) -> Result<PCM> { Self::open(&CString::new(name).unwrap(), dir, nonblock)
}
// Does not offer async mode (it's not very Rustic anyway) pubfn open(name: &CStr, dir: Direction, nonblock: bool) -> Result<PCM> { letmut r = ptr::null_mut(); let stream = match dir {
Direction::Capture => alsa::SND_PCM_STREAM_CAPTURE,
Direction::Playback => alsa::SND_PCM_STREAM_PLAYBACK
}; let flags = if nonblock { alsa::SND_PCM_NONBLOCK } else { 0 };
acheck!(snd_pcm_open(&mut r, name.as_ptr(), stream, flags)).map(|_| PCM(r, cell::Cell::new(false)))
}
/// Wrapper around snd_pcm_recover. /// /// Returns Ok if the error was successfully recovered from, or the original /// error if the error was unhandled. pubfn try_recover(&self, err: Error, silent: bool) -> Result<()> { self.recover(err.errno() as c_int, silent)
}
pubfn wait(&self, timeout_ms: Option<u32>) -> Result<bool> {
acheck!(snd_pcm_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|i| i == 1) }
pubfn state(&self) -> State { let rawstate = self.state_raw(); iflet Ok(state) = State::from_c_int(rawstate, "snd_pcm_state") {
state
} else {
panic!("snd_pcm_state returned an invalid value of {}", rawstate);
}
}
/// Only used internally, and for debugging the alsa library. Please use the "state" function instead. pubfn state_raw(&self) -> c_int { unsafe { alsa::snd_pcm_state(self.0) as c_int } }
pubfn bytes_to_frames(&self, i: isize) -> Frames { unsafe { alsa::snd_pcm_bytes_to_frames(self.0, i as ssize_t) }} pubfn frames_to_bytes(&self, i: Frames) -> isize { unsafe { alsa::snd_pcm_frames_to_bytes(self.0, i) as isize }}
/// Creates IO without checking [`S`] is valid type. /// /// SAFETY: Caller must guarantee [`S`] is valid type for this PCM stream /// and that no other IO objects exist at the same time for the same stream /// (or in some other way guarantee mmap safety) pubunsafefn io_unchecked<S: IoFormat>(&self) -> IO<S> {
IO::new_unchecked(self)
}
impl Drop for PCM { fn drop(&mutself) { unsafe { alsa::snd_pcm_close(self.0) }; }
}
impl poll::Descriptors for PCM { fn count(&self) -> usize { unsafe { alsa::snd_pcm_poll_descriptors_count(self.0) as usize }
} fn fill(&self, p: &mut [pollfd]) -> Result<usize> { let z = unsafe { alsa::snd_pcm_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) };
from_code("snd_pcm_poll_descriptors", z).map(|_| z as usize)
} fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> { letmut r = 0; let z = unsafe { alsa::snd_pcm_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) };
from_code("snd_pcm_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short))
}
}
/// Sample format dependent struct for reading from and writing data to a `PCM`. /// Also implements `std::io::Read` and `std::io::Write`. /// /// Note: Only one IO object is allowed in scope at a time (for mmap safety). pubstruct IO<'a, S: Copy>(&'a PCM, PhantomData<S>);
impl<'a, S: Copy> Drop for IO<'a, S> { fn drop(&mutself) { (self.0).1.set(false) }
}
fn to_frames(&self, b: usize) -> alsa::snd_pcm_uframes_t { // TODO: Do we need to check for overflow here? self.0.bytes_to_frames((b * size_of::<S>()) as isize) as alsa::snd_pcm_uframes_t
}
fn from_frames(&self, b: alsa::snd_pcm_uframes_t) -> usize { // TODO: Do we need to check for overflow here?
(self.0.frames_to_bytes(b as Frames) as usize) / size_of::<S>()
}
/// On success, returns number of *frames* written. /// (Multiply with number of channels to get number of items in buf successfully written.) pubfn writei(&self, buf: &[S]) -> Result<usize> {
acheck!(snd_pcm_writei((self.0).0, buf.as_ptr() as *const c_void, self.to_frames(buf.len()))).map(|r| r as usize)
}
/// On success, returns number of *frames* read. /// (Multiply with number of channels to get number of items in buf successfully read.) pubfn readi(&self, buf: &mut [S]) -> Result<usize> {
acheck!(snd_pcm_readi((self.0).0, buf.as_mut_ptr() as *mut c_void, self.to_frames(buf.len()))).map(|r| r as usize)
}
/// Wrapper around snd_pcm_mmap_begin and snd_pcm_mmap_commit. /// /// You can read/write into the sound card's buffer during the call to the closure. /// According to alsa-lib docs, you should call avail_update before calling this function. /// /// All calculations are in *frames*, i e, the closure should return number of frames processed. /// Also, there might not be as many frames to read/write as requested, and there can even be /// an empty buffer supplied to the closure. /// /// Note: This function works only with interleaved access mode. pubfn mmap<F: FnOnce(&mut [S]) -> usize>(&self, frames: usize, func: F) -> Result<usize> { letmut f = frames as alsa::snd_pcm_uframes_t; letmut offs: alsa::snd_pcm_uframes_t = 0; letmut areas = ptr::null();
acheck!(snd_pcm_mmap_begin((self.0).0, &mut areas, &='color:red'>mut offs, &mut f))?;
let (first, step) = unsafe { ((*areas).first, (*areas).step) }; if first != 0 || step as isize != self.0.frames_to_bytes(1) * 8 { unsafe { alsa::snd_pcm_mmap_commit((self.0).0, offs, 0) }; // let s = format!("Can only mmap a single interleaved buffer (first = {:?}, step = {:?})", first, step); return Err(Error::unsupported("snd_pcm_mmap_begin"));
}
let buf = unsafe { let p = ((*areas).addr as *mut S).add(self.from_frames(offs));
::std::slice::from_raw_parts_mut(p, self.from_frames(f))
}; let fres = func(buf);
debug_assert!(fres <= f as usize);
acheck!(snd_pcm_mmap_commit((self.0).0, offs, fres as alsa::snd_pcm_uframes_t)).map(|r| r as usize)
}
}
impl<'a, S: Copy> io::Read for IO<'a, S> { fn read(&mutself, buf: &mut [u8]) -> io::Result<usize> { let size = self.0.bytes_to_frames(buf.len() as isize) as alsa::snd_pcm_uframes_t; // TODO: Do we need to check for overflow here? let r = unsafe { alsa::snd_pcm_readi((self.0).0, buf.as_mut_ptr() as *mut c_void, size) }; if r < 0 { Err(io::Error::from_raw_os_error(r as i32)) } else { Ok(self.0.frames_to_bytes(r) as usize) }
}
}
impl<'a, S: Copy> io::Write for IO<'a, S> { fn write(&mutself, buf: &[u8]) -> io::Result<usize> { let size = self.0.bytes_to_frames(buf.len() as isize) as alsa::snd_pcm_uframes_t; // TODO: Do we need to check for overflow here? let r = unsafe { alsa::snd_pcm_writei((self.0).0, buf.as_ptr() as *const c_void, size) }; if r < 0 { Err(io::Error::from_raw_os_error(r as i32)) } else { Ok(self.0.frames_to_bytes(r) as usize) }
} fn flush(&mutself) -> io::Result<()> { Ok(()) }
}
impl Format { pubconstfn s16() -> Format { <i16 as IoFormat>::FORMAT } pubconstfn u16() -> Format { <u16 as IoFormat>::FORMAT } pubconstfn s32() -> Format { <i32 as IoFormat>::FORMAT } pubconstfn u32() -> Format { <u32 as IoFormat>::FORMAT } pubconstfn float() -> Format { <f32 as IoFormat>::FORMAT } pubconstfn float64() -> Format { <f64 as IoFormat>::FORMAT }
#[cfg(target_endian = "little")] pubconstfn s24() -> Format { Format::S24LE } #[cfg(target_endian = "big")] pubconstfn s24() -> Format { Format::S24BE }
#[cfg(target_endian = "little")] pubconstfn s24_3() -> Format { Format::S243LE } #[cfg(target_endian = "big")] pubconstfn s24_3() -> Format { Format::S243BE }
#[cfg(target_endian = "little")] pubconstfn u24() -> Format { Format::U24LE } #[cfg(target_endian = "big")] pubconstfn u24() -> Format { Format::U24BE }
#[cfg(target_endian = "little")] pubconstfn u24_3() -> Format { Format::U243LE } #[cfg(target_endian = "big")] pubconstfn u24_3() -> Format { Format::U243BE }
#[cfg(target_endian = "little")] pubconstfn s20_3() -> Format { Format::S203LE } #[cfg(target_endian = "big")] pubconstfn s20_3() -> Format { Format::S203BE }
#[cfg(target_endian = "little")] pubconstfn u20_3() -> Format { Format::U203LE } #[cfg(target_endian = "big")] pubconstfn u20_3() -> Format { Format::U203BE }
#[cfg(target_endian = "little")] pubconstfn s18_3() -> Format { Format::S183LE } #[cfg(target_endian = "big")] pubconstfn s18_3() -> Format { Format::S183BE }
#[cfg(target_endian = "little")] pubconstfn u18_3() -> Format { Format::U183LE } #[cfg(target_endian = "big")] pubconstfn u18_3() -> Format { Format::U183BE }
#[cfg(target_endian = "little")] pubconstfn dsd_u16() -> Format { Format::DSDU16LE } #[cfg(target_endian = "big")] pubconstfn dsd_u16() -> Format { Format::DSDU16BE }
#[cfg(target_endian = "little")] pubconstfn dsd_u32() -> Format { Format::DSDU32LE } #[cfg(target_endian = "big")] pubconstfn dsd_u32() -> Format { Format::DSDU32BE }
#[cfg(target_endian = "little")] pubconstfn iec958_subframe() -> Format { Format::IEC958SubframeLE } #[cfg(target_endian = "big")] pubconstfn iec958_subframe() -> Format { Format::IEC958SubframeBE }
pubfn set_channels_near(&self, v: u32) -> Result<u32> { letmut r = v as c_uint;
acheck!(snd_pcm_hw_params_set_channels_near((self.1).0, self.0, &mut r)).map(|_| r)
}
pubfn set_channels(&self, v: u32) -> Result<()> {
acheck!(snd_pcm_hw_params_set_channels((self.1).0, self.0, v as c_uint)).map(|_| ())
}
pubfn get_channels(&self) -> Result<u32> { letmut v = 0;
acheck!(snd_pcm_hw_params_get_channels(self.0, &mut v)).map(|_| v as u32)
}
pubfn get_channels_max(&self) -> Result<u32> { letmut v = 0;
acheck!(snd_pcm_hw_params_get_channels_max(self.0, &mut v)).map(|_| v as u32)
}
pubfn get_channels_min(&self) -> Result<u32> { letmut v = 0;
acheck!(snd_pcm_hw_params_get_channels_min(self.0, &mut v)).map(|_| v as u32)
}
pubfn test_channels(&self, v: u32) -> Result<()> {
acheck!(snd_pcm_hw_params_test_channels((self.1).0, self.0, v as c_uint)).map(|_| ())
}
pubfn set_rate_near(&self, v: u32, dir: ValueOr) -> Result<u32> { letmut d = dir as c_int; letmut r = v as c_uint;
acheck!(snd_pcm_hw_params_set_rate_near((self.1).0, self.0, &>mut r, &mut d)).map(|_| r)
}
pubfn set_rate(&self, v: u32, dir: ValueOr) -> Result<()> {
acheck!(snd_pcm_hw_params_set_rate((self.1).0, self.0, v as c_uint, dir as c_int)).map(|_| ())
}
pubfn get_rate(&self) -> Result<u32> { let (mut v, mut d) = (0,0);
acheck!(snd_pcm_hw_params_get_rate(self.0, &mut v, &='color:red'>mut d)).map(|_| v as u32)
}
pubfn get_rate_max(&self) -> Result<u32> { letmut v = 0; // Note on the null ptr: if this ptr is not null, then the value behind it is replaced with // -1 if the suprenum is not in the set (i.e. it's an open range), 0 otherwise. This could // be returned along with the value, but it's safe to pass a null ptr in, in which case the // pointer is not dereferenced.
acheck!(snd_pcm_hw_params_get_rate_max(self.0, &mut v, ptr::null_mut())).map(|_| v as u32)
}
pubfn get_rate_min(&self) -> Result<u32> { letmut v = 0; // Note on the null ptr: see get_rate_max but read +1 and infinum instead of -1 and // suprenum.
acheck!(snd_pcm_hw_params_get_rate_min(self.0, &mut v, ptr::null_mut())).map(|_| v as u32)
}
pubfn test_format(&self, v: Format) -> Result<()> {
acheck!(snd_pcm_hw_params_test_format((self.1).0, self.0, v as c_int)).map(|_| ())
}
pubfn set_access(&self, v: Access) -> Result<()> {
acheck!(snd_pcm_hw_params_set_access((self.1).0, self.0, v as c_uint)).map(|_| ())
}
pubfn get_access(&self) -> Result<Access> { letmut v = 0;
acheck!(snd_pcm_hw_params_get_access(self.0, &mut v))
.and_then(|_| Access::from_c_int(v as c_int, "snd_pcm_hw_params_get_access"))
}
pubfn set_period_size_near(&self, v: Frames, dir: ValueOr) -> Result<Frames> { letmut d = dir as c_int; letmut r = v as alsa::snd_pcm_uframes_t;
acheck!(snd_pcm_hw_params_set_period_size_near((self.1).0, self.0, &mut r, &mut d)).map(|_| r as Frames)
}
pubfn set_period_size(&self, v: Frames, dir: ValueOr) -> Result<()> {
acheck!(snd_pcm_hw_params_set_period_size((self.1).0, self.0, v as alsa::snd_pcm_uframes_t, dir as c_int)).map(|_| ())
}
pubfn set_period_time_near(&self, v: u32, dir: ValueOr) -> Result<u32> { letmut d = dir as c_int; letmut r = v as c_uint;
acheck!(snd_pcm_hw_params_set_period_time_near((self.1).0, self.0, &mut r, &mut d)).map(|_| r as u32)
}
pubfn get_period_size(&self) -> Result<Frames> { let (mut v, mut d) = (0,0);
acheck!(snd_pcm_hw_params_get_period_size(self.0, &mut v, &n style='color:red'>mut d)).map(|_| v as Frames)
}
pubfn get_period_size_min(&self) -> Result<Frames> { let (mut v, mut d) = (0,0);
acheck!(snd_pcm_hw_params_get_period_size_min(self.0, &mut v, &mut d)).map(|_| v as Frames)
}
pubfn get_period_size_max(&self) -> Result<Frames> { let (mut v, mut d) = (0,0);
acheck!(snd_pcm_hw_params_get_period_size_max(self.0, &mut v, &mut d)).map(|_| v as Frames)
}
pubfn set_periods(&self, v: u32, dir: ValueOr) -> Result<()> {
acheck!(snd_pcm_hw_params_set_periods((self.1).0, self.0, v as c_uint, dir as c_int)).map(|_| ())
}
pubfn get_periods(&self) -> Result<u32> { let (mut v, mut d) = (0,0);
acheck!(snd_pcm_hw_params_get_periods(self.0, &mut v, &yle='color:red'>mut d)).map(|_| v as u32)
}
pubfn set_buffer_size_near(&self, v: Frames) -> Result<Frames> { letmut r = v as alsa::snd_pcm_uframes_t;
acheck!(snd_pcm_hw_params_set_buffer_size_near((self.1).0, self.0, &mut r)).map(|_| r as Frames)
}
pubfn set_buffer_size_max(&self, v: Frames) -> Result<Frames> { letmut r = v as alsa::snd_pcm_uframes_t;
acheck!(snd_pcm_hw_params_set_buffer_size_max((self.1).0, self.0, &mut r)).map(|_| r as Frames)
}
pubfn set_buffer_size_min(&self, v: Frames) -> Result<Frames> { letmut r = v as alsa::snd_pcm_uframes_t;
acheck!(snd_pcm_hw_params_set_buffer_size_min((self.1).0, self.0, &mut r)).map(|_| r as Frames)
}
pubfn set_buffer_size(&self, v: Frames) -> Result<()> {
acheck!(snd_pcm_hw_params_set_buffer_size((self.1).0, self.0, v as alsa::snd_pcm_uframes_t)).map(|_| ())
}
pubfn set_buffer_time_near(&self, v: u32, dir: ValueOr) -> Result<u32> { letmut d = dir as c_int; letmut r = v as c_uint;
acheck!(snd_pcm_hw_params_set_buffer_time_near((self.1).0, self.0, &mut r, &mut d)).map(|_| r as u32)
}
pubfn get_buffer_size(&self) -> Result<Frames> { letmut v = 0;
acheck!(snd_pcm_hw_params_get_buffer_size(self.0, &mut v)).map(|_| v as Frames)
}
pubfn get_buffer_size_min(&self) -> Result<Frames> { letmut v = 0;
acheck!(snd_pcm_hw_params_get_buffer_size_min(self.0, &mut v)).map(|_| v as Frames)
}
pubfn get_buffer_size_max(&self) -> Result<Frames> { letmut v = 0;
acheck!(snd_pcm_hw_params_get_buffer_size_max(self.0, &mut v)).map(|_| v as Frames)
}
pubfn get_buffer_time_min(&self) -> Result<u32> { let (mut v, mut d) = (0,0);
acheck!(snd_pcm_hw_params_get_buffer_time_min(self.0, &mut v, &mut d)).map(|_| v as u32)
}
pubfn get_buffer_time_max(&self) -> Result<u32> { let (mut v, mut d) = (0,0);
acheck!(snd_pcm_hw_params_get_buffer_time_max(self.0, &mut v, &mut d)).map(|_| v as u32)
}
/// Returns true if the alsa stream can be paused, false if not. /// /// This function should only be called when the configuration space contains a single /// configuration. Call `PCM::hw_params` to choose a single configuration from the /// configuration space. pubfn can_pause(&self) -> bool { unsafe { alsa::snd_pcm_hw_params_can_pause(self.0) != 0 }
}
/// Returns true if the alsa stream can be resumed, false if not. /// /// This function should only be called when the configuration space contains a single /// configuration. Call `PCM::hw_params` to choose a single configuration from the /// configuration space. pubfn can_resume(&self) -> bool { unsafe { alsa::snd_pcm_hw_params_can_resume(self.0) != 0 }
}
/// Returns true if the alsa stream supports the provided `AudioTstampType`, false if not. /// /// This function should only be called when the configuration space contains a single /// configuration. Call `PCM::hw_params` to choose a single configuration from the /// configuration space. pubfn supports_audio_ts_type(&self, type_: AudioTstampType) -> bool { unsafe { alsa::snd_pcm_hw_params_supports_audio_ts_type(self.0, type_ as libc::c_int) != 0 }
}
/// Builder for [`Status`]. /// /// Allows setting the audio timestamp configuration before retrieving the /// status from the stream. pubstruct StatusBuilder(Status);
#[test] fn info_from_default() { use std::ffi::CString; let pcm = PCM::open(&*CString::new("default").unwrap(), Direction::Capture, false).unwrap(); let info = pcm.info().unwrap();
println!("PCM Info:");
println!("\tCard: {}", info.get_card());
println!("\tDevice: {}", info.get_device());
println!("\tSubdevice: {}", info.get_subdevice());
println!("\tId: {}", info.get_id().unwrap());
println!("\tName: {}", info.get_name().unwrap());
println!("\tSubdevice Name: {}", info.get_subdevice_name().unwrap());
}
#[test] fn drop() { use std::ffi::CString; let pcm = PCM::open(&*CString::new("default").unwrap(), Direction::Capture, false).unwrap(); // Verify that this does not cause a naming conflict (issue #14) let _ = pcm.drop();
}
#[test] fn playback_to_default() { use std::ffi::CString; let pcm = PCM::open(&*CString::new("default").unwrap(), Direction::Playback, false).unwrap(); let hwp = HwParams::any(&pcm).unwrap();
hwp.set_channels(1).unwrap();
hwp.set_rate(44100, ValueOr::Nearest).unwrap();
hwp.set_format(Format::s16()).unwrap();
hwp.set_access(Access::RWInterleaved).unwrap();
pcm.hw_params(&hwp).unwrap();
let hwp = pcm.hw_params_current().unwrap(); let swp = pcm.sw_params_current().unwrap();
swp.set_start_threshold(hwp.get_buffer_size().unwrap()).unwrap();
pcm.sw_params(&swp).unwrap();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.