Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

54
vendor/alsa/src/card.rs vendored Normal file
View File

@@ -0,0 +1,54 @@
//! Sound card enumeration
use libc::{c_int, c_char};
use super::error::*;
use crate::alsa;
use std::ffi::CStr;
/// An ALSA sound card, uniquely identified by its index.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Card(c_int);
/// Iterate over existing sound cards.
pub struct Iter(c_int);
impl Iter {
pub fn new() -> Iter { Iter(-1) }
}
impl Iterator for Iter {
type Item = Result<Card>;
fn next(&mut self) -> Option<Result<Card>> {
match acheck!(snd_card_next(&mut self.0)) {
Ok(_) if self.0 == -1 => None,
Ok(_) => Some(Ok(Card(self.0))),
Err(e) => Some(Err(e)),
}
}
}
impl Card {
pub fn new(index: c_int) -> Card { Card(index) }
pub fn from_str(s: &CStr) -> Result<Card> {
acheck!(snd_card_get_index(s.as_ptr())).map(Card)
}
pub fn get_name(&self) -> Result<String> {
let mut c: *mut c_char = ::std::ptr::null_mut();
acheck!(snd_card_get_name(self.0, &mut c))
.and_then(|_| from_alloc("snd_card_get_name", c))
}
pub fn get_longname(&self) -> Result<String> {
let mut c: *mut c_char = ::std::ptr::null_mut();
acheck!(snd_card_get_longname(self.0, &mut c))
.and_then(|_| from_alloc("snd_card_get_longname", c))
}
pub fn get_index(&self) -> c_int { self.0 }
}
#[test]
fn print_cards() {
for a in Iter::new().map(|a| a.unwrap()) {
println!("Card #{}: {} ({})", a.get_index(), a.get_name().unwrap(), a.get_longname().unwrap())
}
}

148
vendor/alsa/src/chmap.rs vendored Normal file
View File

@@ -0,0 +1,148 @@
use crate::alsa;
use std::{fmt, mem, slice};
use super::error::*;
alsa_enum!(
/// [SND_CHMAP_TYPE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants
ChmapType, ALL_CHMAP_TYPES[4],
None = SND_CHMAP_TYPE_NONE,
Fixed = SND_CHMAP_TYPE_FIXED,
Var = SND_CHMAP_TYPE_VAR,
Paired = SND_CHMAP_TYPE_PAIRED,
);
alsa_enum!(
/// [SND_CHMAP_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) constants
ChmapPosition, ALL_CHMAP_POSITIONS[33],
Unknown = SND_CHMAP_UNKNOWN,
NA = SND_CHMAP_NA,
Mono = SND_CHMAP_MONO,
FL = SND_CHMAP_FL,
FR = SND_CHMAP_FR,
RL = SND_CHMAP_RL,
SR = SND_CHMAP_SR,
RC = SND_CHMAP_RC,
FLC = SND_CHMAP_FLC,
FRC = SND_CHMAP_FRC,
RLC = SND_CHMAP_RLC,
RRC = SND_CHMAP_RRC,
FLW = SND_CHMAP_FLW,
FRW = SND_CHMAP_FRW,
FLH = SND_CHMAP_FLH,
FCH = SND_CHMAP_FCH,
FRH = SND_CHMAP_FRH,
TC = SND_CHMAP_TC,
TFL = SND_CHMAP_TFL,
TFR = SND_CHMAP_TFR,
TFC = SND_CHMAP_TFC,
TRL = SND_CHMAP_TRL,
TRR = SND_CHMAP_TRR,
TRC = SND_CHMAP_TRC,
TFLC = SND_CHMAP_TFLC,
TFRC = SND_CHMAP_TFRC,
TSL = SND_CHMAP_TSL,
TSR = SND_CHMAP_TSR,
LLFE = SND_CHMAP_LLFE,
RLFE = SND_CHMAP_RLFE,
BC = SND_CHMAP_BC,
BLC = SND_CHMAP_BLC,
BRC = SND_CHMAP_BRC,
);
impl fmt::Display for ChmapPosition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = unsafe { alsa::snd_pcm_chmap_long_name(*self as libc::c_uint) };
let s = from_const("snd_pcm_chmap_long_name", s)?;
write!(f, "{}", s)
}
}
/// [snd_pcm_chmap_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html) wrapper
pub struct Chmap(*mut alsa::snd_pcm_chmap_t, bool);
impl Drop for Chmap {
fn drop(&mut self) { if self.1 { unsafe { libc::free(self.0 as *mut libc::c_void) }}}
}
impl Chmap {
fn set_channels(&mut self, c: libc::c_uint) { unsafe { (*self.0) .channels = c }}
fn as_slice_mut(&mut self) -> &mut [libc::c_uint] {
unsafe { slice::from_raw_parts_mut((*self.0).pos.as_mut_ptr(), (*self.0).channels as usize) }
}
fn as_slice(&self) -> &[libc::c_uint] {
unsafe { slice::from_raw_parts((*self.0).pos.as_ptr(), (*self.0).channels as usize) }
}
}
impl fmt::Display for Chmap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut buf: Vec<libc::c_char> = vec![0; 512];
acheck!(snd_pcm_chmap_print(self.0, buf.len() as libc::size_t, buf.as_mut_ptr()))?;
let s = from_const("snd_pcm_chmap_print", buf.as_mut_ptr())?;
write!(f, "{}", s)
}
}
impl<'a> From<&'a [ChmapPosition]> for Chmap {
fn from(a: &'a [ChmapPosition]) -> Chmap {
let p = unsafe { libc::malloc((mem::size_of::<alsa::snd_pcm_chmap_t>() + mem::size_of::<libc::c_uint>() * a.len()) as libc::size_t) };
if p.is_null() { panic!("Out of memory") }
let mut r = Chmap(p as *mut alsa::snd_pcm_chmap_t, true);
r.set_channels(a.len() as libc::c_uint);
for (i,v) in r.as_slice_mut().iter_mut().enumerate() { *v = a[i] as libc::c_uint }
r
}
}
impl<'a> From<&'a Chmap> for Vec<ChmapPosition> {
fn from(a: &'a Chmap) -> Vec<ChmapPosition> {
a.as_slice().iter().map(|&v| ChmapPosition::from_c_int(v as libc::c_int, "").unwrap()).collect()
}
}
pub fn chmap_new(a: *mut alsa::snd_pcm_chmap_t) -> Chmap { Chmap(a, true) }
pub fn chmap_handle(a: &Chmap) -> *mut alsa::snd_pcm_chmap_t { a.0 }
/// Iterator over available channel maps - see [snd_pcm_chmap_query_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html)
pub struct ChmapsQuery(*mut *mut alsa::snd_pcm_chmap_query_t, isize);
impl Drop for ChmapsQuery {
fn drop(&mut self) { unsafe { alsa::snd_pcm_free_chmaps(self.0) }}
}
pub fn chmaps_query_new(a: *mut *mut alsa::snd_pcm_chmap_query_t) -> ChmapsQuery { ChmapsQuery(a, 0) }
impl Iterator for ChmapsQuery {
type Item = (ChmapType, Chmap);
fn next(&mut self) -> Option<Self::Item> {
if self.0.is_null() { return None; }
let p = unsafe { *self.0.offset(self.1) };
if p.is_null() { return None; }
self.1 += 1;
let t = ChmapType::from_c_int(unsafe { (*p).type_ } as libc::c_int, "snd_pcm_query_chmaps").unwrap();
let m = Chmap(unsafe { &mut (*p).map }, false);
Some((t, m))
}
}
#[test]
fn chmap_for_first_pcm() {
use super::*;
use std::ffi::CString;
use crate::device_name::HintIter;
let i = HintIter::new(None, &*CString::new("pcm").unwrap()).unwrap();
for p in i.map(|n| n.name.unwrap()) {
println!("Chmaps for {:?}:", p);
match PCM::open(&CString::new(p).unwrap(), Direction::Playback, false) {
Ok(a) => for c in a.query_chmaps() {
println!(" {:?}, {}", c.0, c.1);
},
Err(a) => println!(" {}", a) // It's okay to have entries in the name hint array that can't be opened
}
}
}

496
vendor/alsa/src/ctl_int.rs vendored Normal file
View File

@@ -0,0 +1,496 @@
use crate::alsa;
use super::pcm::Info;
use std::ffi::{CStr, CString};
use super::Direction;
use super::error::*;
use super::mixer::MilliBel;
use super::Round;
use std::{ptr, mem, fmt, cmp};
use crate::{Card, poll};
use std::cell::UnsafeCell;
use libc::{c_uint, c_void, size_t, c_long, c_int, pollfd, c_short};
/// We prefer not to allocate for every ElemId, ElemInfo or ElemValue.
/// But we don't know if these will increase in the future or on other platforms.
/// Unfortunately, Rust does not support alloca, so hard-code the sizes for now.
const ELEM_ID_SIZE: usize = 64;
// const ELEM_VALUE_SIZE: usize = 1224;
// const ELEM_INFO_SIZE: usize = 272;
/// [snd_ctl_pcm_next_device](https://www.alsa-project.org/alsa-doc/alsa-lib/control_8c.html#accbb0be6e5ca7361ffec0ea304ed1b05) wrapper.
/// Iterate over devices of a card.
pub struct DeviceIter<'a>(&'a Ctl, c_int);
impl<'a> DeviceIter<'a>{
pub fn new(ctl: &'a Ctl) -> DeviceIter {
DeviceIter(ctl, -1)
}
}
impl<'a> Iterator for DeviceIter<'a> {
type Item = c_int;
fn next(&mut self) -> Option<c_int> {
match acheck!(snd_ctl_pcm_next_device(self.0.0, &mut self.1)) {
Ok(_) if self.1 == -1 => None,
Ok(_) => Some(self.1),
Err(_) => None,
}
}
}
/// [snd_ctl_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper
pub struct Ctl(*mut alsa::snd_ctl_t);
unsafe impl Send for Ctl {}
impl Ctl {
/// Wrapper around open that takes a &str instead of a &CStr
pub fn new(c: &str, nonblock: bool) -> Result<Self> {
Self::open(&CString::new(c).unwrap(), nonblock)
}
/// Open does not support async mode (it's not very Rustic anyway)
pub fn open(c: &CStr, nonblock: bool) -> Result<Ctl> {
let mut r = ptr::null_mut();
let flags = if nonblock { 1 } else { 0 }; // FIXME: alsa::SND_CTL_NONBLOCK does not exist in alsa-sys
acheck!(snd_ctl_open(&mut r, c.as_ptr(), flags)).map(|_| Ctl(r))
}
pub fn from_card(c: &Card, nonblock: bool) -> Result<Ctl> {
let s = format!("hw:{}", c.get_index());
Ctl::open(&CString::new(s).unwrap(), nonblock)
}
pub fn card_info(&self) -> Result<CardInfo> { CardInfo::new().and_then(|c|
acheck!(snd_ctl_card_info(self.0, c.0)).map(|_| c)) }
pub fn wait(&self, timeout_ms: Option<u32>) -> Result<bool> {
acheck!(snd_ctl_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|i| i == 1) }
pub fn get_db_range(&self, id: &ElemId) -> Result<(MilliBel, MilliBel)> {
let mut min: c_long = 0;
let mut max: c_long = 0;
acheck!(snd_ctl_get_dB_range(self.0, elem_id_ptr(id), &mut min, &mut max))
.map(|_| (MilliBel(min as i64), MilliBel(max as i64)))
}
pub fn convert_to_db(&self, id: &ElemId, volume: i64) -> Result<MilliBel> {
let mut m: c_long = 0;
acheck!(snd_ctl_convert_to_dB(self.0, elem_id_ptr(id), volume as c_long, &mut m))
.map(|_| (MilliBel(m as i64)))
}
pub fn convert_from_db(&self, id: &ElemId, mb: MilliBel, dir: Round) -> Result<i64> {
let mut m: c_long = 0;
acheck!(snd_ctl_convert_from_dB(self.0, elem_id_ptr(id), mb.0 as c_long, &mut m, dir as c_int))
.map(|_| m as i64)
}
pub fn elem_read(&self, val: &mut ElemValue) -> Result<()> {
acheck!(snd_ctl_elem_read(self.0, elem_value_ptr(val))).map(|_| ())
}
pub fn elem_write(&self, val: &ElemValue) -> Result<()> {
acheck!(snd_ctl_elem_write(self.0, elem_value_ptr(val))).map(|_| ())
}
pub fn elem_lock(&self, id: &ElemId) -> Result<i32> {
acheck!(snd_ctl_elem_lock(self.0, elem_id_ptr(id)))
}
pub fn elem_unlock(&self, id: &ElemId) -> Result<i32> {
acheck!(snd_ctl_elem_unlock(self.0, elem_id_ptr(id)))
}
/// Note: According to alsa-lib documentation, you're also supposed to have functionality for
/// returning whether or not you are subscribed. This does not work in practice, so I'm not
/// including that here.
pub fn subscribe_events(&self, subscribe: bool) -> Result<()> {
acheck!(snd_ctl_subscribe_events(self.0, if subscribe { 1 } else { 0 })).map(|_| ())
}
pub fn read(&self) -> Result<Option<Event>> {
let e = event_new()?;
acheck!(snd_ctl_read(self.0, e.0)).map(|r| if r == 1 { Some(e) } else { None })
}
pub fn pcm_info(&self, device: u32, subdevice: u32, direction: Direction) -> Result<Info> {
Info::new().and_then(|mut info| {
info.set_device(device);
info.set_subdevice(subdevice);
info.set_stream(direction);
acheck!(snd_ctl_pcm_info(self.0, info.0)).map(|_| info )
})
}
}
impl Drop for Ctl {
fn drop(&mut self) { unsafe { alsa::snd_ctl_close(self.0) }; }
}
impl poll::Descriptors for Ctl {
fn count(&self) -> usize {
unsafe { alsa::snd_ctl_poll_descriptors_count(self.0) as usize }
}
fn fill(&self, p: &mut [pollfd]) -> Result<usize> {
let z = unsafe { alsa::snd_ctl_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) };
from_code("snd_ctl_poll_descriptors", z).map(|_| z as usize)
}
fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> {
let mut r = 0;
let z = unsafe { alsa::snd_ctl_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) };
from_code("snd_ctl_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short))
}
}
pub fn ctl_ptr(a: &Ctl) -> *mut alsa::snd_ctl_t { a.0 }
/// [snd_ctl_card_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper
pub struct CardInfo(*mut alsa::snd_ctl_card_info_t);
impl Drop for CardInfo {
fn drop(&mut self) { unsafe { alsa::snd_ctl_card_info_free(self.0) }}
}
impl CardInfo {
fn new() -> Result<CardInfo> {
let mut p = ptr::null_mut();
acheck!(snd_ctl_card_info_malloc(&mut p)).map(|_| CardInfo(p))
}
pub fn get_id(&self) -> Result<&str> {
from_const("snd_ctl_card_info_get_id", unsafe { alsa::snd_ctl_card_info_get_id(self.0) })}
pub fn get_driver(&self) -> Result<&str> {
from_const("snd_ctl_card_info_get_driver", unsafe { alsa::snd_ctl_card_info_get_driver(self.0) })}
pub fn get_components(&self) -> Result<&str> {
from_const("snd_ctl_card_info_get_components", unsafe { alsa::snd_ctl_card_info_get_components(self.0) })}
pub fn get_longname(&self) -> Result<&str> {
from_const("snd_ctl_card_info_get_longname", unsafe { alsa::snd_ctl_card_info_get_longname(self.0) })}
pub fn get_name(&self) -> Result<&str> {
from_const("snd_ctl_card_info_get_name", unsafe { alsa::snd_ctl_card_info_get_name(self.0) })}
pub fn get_mixername(&self) -> Result<&str> {
from_const("snd_ctl_card_info_get_mixername", unsafe { alsa::snd_ctl_card_info_get_mixername(self.0) })}
pub fn get_card(&self) -> Card { Card::new(unsafe { alsa::snd_ctl_card_info_get_card(self.0) })}
}
alsa_enum!(
/// [SND_CTL_ELEM_IFACE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) constants
ElemIface, ALL_ELEMIFACE[7],
Card = SND_CTL_ELEM_IFACE_CARD,
Hwdep = SND_CTL_ELEM_IFACE_HWDEP,
Mixer = SND_CTL_ELEM_IFACE_MIXER,
PCM = SND_CTL_ELEM_IFACE_PCM,
Rawmidi = SND_CTL_ELEM_IFACE_RAWMIDI,
Timer = SND_CTL_ELEM_IFACE_TIMER,
Sequencer = SND_CTL_ELEM_IFACE_SEQUENCER,
);
alsa_enum!(
/// [SND_CTL_ELEM_TYPE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) constants
ElemType, ALL_ELEMTYPE[7],
None = SND_CTL_ELEM_TYPE_NONE,
Boolean = SND_CTL_ELEM_TYPE_BOOLEAN,
Integer = SND_CTL_ELEM_TYPE_INTEGER,
Enumerated = SND_CTL_ELEM_TYPE_ENUMERATED,
Bytes = SND_CTL_ELEM_TYPE_BYTES,
IEC958 = SND_CTL_ELEM_TYPE_IEC958,
Integer64 = SND_CTL_ELEM_TYPE_INTEGER64,
);
/// [snd_ctl_elem_value_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper
pub struct ElemValue {
ptr: *mut alsa::snd_ctl_elem_value_t,
etype: ElemType,
count: u32,
}
impl Drop for ElemValue {
fn drop(&mut self) { unsafe { alsa::snd_ctl_elem_value_free(self.ptr) }; }
}
pub fn elem_value_ptr(a: &ElemValue) -> *mut alsa::snd_ctl_elem_value_t { a.ptr }
pub fn elem_value_new(t: ElemType, count: u32) -> Result<ElemValue> {
let mut p = ptr::null_mut();
acheck!(snd_ctl_elem_value_malloc(&mut p))
.map(|_| ElemValue { ptr: p, etype: t, count })
}
impl ElemValue {
pub fn set_id(&mut self, id: &ElemId) {
unsafe { alsa::snd_ctl_elem_value_set_id(self.ptr, elem_id_ptr(id)) }
}
// Note: The get_bytes hands out a reference to inside the object. Therefore, we can't treat
// the content as "cell"ed, but must take a "&mut self" (to make sure the reference
// from get_bytes has been dropped when calling a set_* function).
pub fn get_boolean(&self, idx: u32) -> Option<bool> {
if self.etype != ElemType::Boolean || idx >= self.count { None }
else { Some( unsafe { alsa::snd_ctl_elem_value_get_boolean(self.ptr, idx as c_uint) } != 0) }
}
pub fn set_boolean(&mut self, idx: u32, val: bool) -> Option<()> {
if self.etype != ElemType::Boolean || idx >= self.count { None }
else { unsafe { alsa::snd_ctl_elem_value_set_boolean(self.ptr, idx as c_uint, if val {1} else {0}) }; Some(()) }
}
pub fn get_integer(&self, idx: u32) -> Option<i32> {
if self.etype != ElemType::Integer || idx >= self.count { None }
else { Some( unsafe { alsa::snd_ctl_elem_value_get_integer(self.ptr, idx as c_uint) } as i32) }
}
pub fn set_integer(&mut self, idx: u32, val: i32) -> Option<()> {
if self.etype != ElemType::Integer || idx >= self.count { None }
else { unsafe { alsa::snd_ctl_elem_value_set_integer(self.ptr, idx as c_uint, val as c_long) }; Some(()) }
}
pub fn get_integer64(&self, idx: u32) -> Option<i64> {
if self.etype != ElemType::Integer64 || idx >= self.count { None }
else { Some( unsafe { alsa::snd_ctl_elem_value_get_integer64(self.ptr, idx as c_uint) } as i64) }
}
pub fn set_integer64(&mut self, idx: u32, val: i64) -> Option<()> {
if self.etype != ElemType::Integer || idx >= self.count { None }
else { unsafe { alsa::snd_ctl_elem_value_set_integer64(self.ptr, idx as c_uint, val) }; Some(()) }
}
pub fn get_enumerated(&self, idx: u32) -> Option<u32> {
if self.etype != ElemType::Enumerated || idx >= self.count { None }
else { Some( unsafe { alsa::snd_ctl_elem_value_get_enumerated(self.ptr, idx as c_uint) } as u32) }
}
pub fn set_enumerated(&mut self, idx: u32, val: u32) -> Option<()> {
if self.etype != ElemType::Enumerated || idx >= self.count { None }
else { unsafe { alsa::snd_ctl_elem_value_set_enumerated(self.ptr, idx as c_uint, val as c_uint) }; Some(()) }
}
pub fn get_byte(&self, idx: u32) -> Option<u8> {
if self.etype != ElemType::Bytes || idx >= self.count { None }
else { Some( unsafe { alsa::snd_ctl_elem_value_get_byte(self.ptr, idx as c_uint) } as u8) }
}
pub fn set_byte(&mut self, idx: u32, val: u8) -> Option<()> {
if self.etype != ElemType::Bytes || idx >= self.count { None }
else { unsafe { alsa::snd_ctl_elem_value_set_byte(self.ptr, idx as c_uint, val) }; Some(()) }
}
pub fn get_bytes(&self) -> Option<&[u8]> {
if self.etype != ElemType::Bytes { None }
else { Some( unsafe { ::std::slice::from_raw_parts(
alsa::snd_ctl_elem_value_get_bytes(self.ptr) as *const u8, self.count as usize) } ) }
}
pub fn set_bytes(&mut self, val: &[u8]) -> Option<()> {
if self.etype != ElemType::Bytes || val.len() != self.count as usize { None }
// Note: the alsa-lib function definition is broken. First, the pointer is declared as mut even
// though it's const, and second, there is a "value" missing between "elem" and "set_bytes".
else { unsafe { alsa::snd_ctl_elem_set_bytes(self.ptr, val.as_ptr() as *mut c_void, val.len() as size_t) }; Some(()) }
}
/// Creates a new ElemValue.
pub fn new(t: ElemType) -> Result<ElemValue> {
// See max length in include/uapi/sound/asound.h in linux kernel for these values
let count = match t {
ElemType::None => 1,
ElemType::Boolean => 128,
ElemType::Integer => 128,
ElemType::Enumerated => 128,
ElemType::Bytes => 512,
ElemType::IEC958 => 1,
ElemType::Integer64 => 64,
};
// if count > maxcount { return Err(Error::new(Some("ElemValue::new - count too large".into()), 1)) }
let ev = elem_value_new(t, count)?;
unsafe { alsa::snd_ctl_elem_value_clear(elem_value_ptr(&ev)) };
Ok(ev)
}
}
impl fmt::Debug for ElemValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::ElemType::*;
write!(f, "ElemValue({:?}", self.etype)?;
for a in 0..self.count { match self.etype {
Boolean => write!(f, ",{:?}", self.get_boolean(a).unwrap()),
Integer => write!(f, ",{:?}", self.get_integer(a).unwrap()),
Integer64 => write!(f, ",{:?}", self.get_integer64(a).unwrap()),
Enumerated => write!(f, ",{:?}", self.get_enumerated(a).unwrap()),
Bytes => write!(f, ",{:?}", self.get_byte(a).unwrap()),
_ => Ok(()),
}?};
write!(f, ")")
}
}
/// [snd_ctl_elem_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper
pub struct ElemInfo(*mut alsa::snd_ctl_elem_info_t);
pub fn elem_info_ptr(a: &ElemInfo) -> *mut alsa::snd_ctl_elem_info_t { a.0 }
impl Drop for ElemInfo {
fn drop(&mut self) { unsafe { alsa::snd_ctl_elem_info_free(self.0) }; }
}
pub fn elem_info_new() -> Result<ElemInfo> {
let mut p = ptr::null_mut();
acheck!(snd_ctl_elem_info_malloc(&mut p)).map(|_| ElemInfo(p))
}
impl ElemInfo {
pub fn get_type(&self) -> ElemType { ElemType::from_c_int(
unsafe { alsa::snd_ctl_elem_info_get_type(self.0) } as c_int, "snd_ctl_elem_info_get_type").unwrap() }
pub fn get_count(&self) -> u32 { unsafe { alsa::snd_ctl_elem_info_get_count(self.0) as u32 } }
}
//
// Non-allocating version of ElemId
//
/// [snd_ctl_elem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper
pub struct ElemId(UnsafeCell<[u8; ELEM_ID_SIZE]>);
pub fn elem_id_new() -> Result<ElemId> {
assert!(unsafe { alsa::snd_ctl_elem_id_sizeof() } as usize <= ELEM_ID_SIZE);
Ok(ElemId(UnsafeCell::new(unsafe { mem::zeroed() })))
}
#[inline]
pub fn elem_id_ptr(a: &ElemId) -> *mut alsa::snd_ctl_elem_id_t { a.0.get() as *mut _ as *mut alsa::snd_ctl_elem_id_t }
unsafe impl Send for ElemId {}
impl Clone for ElemId {
fn clone(&self) -> Self {
ElemId(UnsafeCell::new(unsafe { *self.0.get() }))
}
}
//
// Allocating version of ElemId
//
/*
/// [snd_ctl_elem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper
pub struct ElemId(*mut alsa::snd_ctl_elem_id_t);
impl Drop for ElemId {
fn drop(&mut self) { unsafe { alsa::snd_ctl_elem_id_free(self.0) }; }
}
pub fn elem_id_new() -> Result<ElemId> {
let mut p = ptr::null_mut();
acheck!(snd_ctl_elem_id_malloc(&mut p)).map(|_| ElemId(p))
}
pub fn elem_id_ptr(a: &ElemId) -> *mut alsa::snd_ctl_elem_id_t { a.0 }
*/
impl ElemId {
pub fn get_name(&self) -> Result<&str> {
from_const("snd_hctl_elem_id_get_name", unsafe { alsa::snd_ctl_elem_id_get_name(elem_id_ptr(self)) })}
pub fn get_device(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_device(elem_id_ptr(self)) as u32 }}
pub fn get_subdevice(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_subdevice(elem_id_ptr(self)) as u32 }}
pub fn get_numid(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_numid(elem_id_ptr(self)) as u32 }}
pub fn get_index(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_index(elem_id_ptr(self)) as u32 }}
pub fn get_interface(&self) -> ElemIface { ElemIface::from_c_int(
unsafe { alsa::snd_ctl_elem_id_get_interface(elem_id_ptr(self)) } as c_int, "snd_ctl_elem_id_get_interface").unwrap() }
pub fn set_device(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_device(elem_id_ptr(self), v) }}
pub fn set_subdevice(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_subdevice(elem_id_ptr(self), v) }}
pub fn set_numid(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_numid(elem_id_ptr(self), v) }}
pub fn set_index(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_index(elem_id_ptr(self), v) }}
pub fn set_interface(&mut self, v: ElemIface) { unsafe { alsa::snd_ctl_elem_id_set_interface(elem_id_ptr(self), v as u32) }}
pub fn set_name(&mut self, v: &CStr) { unsafe { alsa::snd_ctl_elem_id_set_name(elem_id_ptr(self), v.as_ptr()) }}
/// Creates a new ElemId.
///
/// To ensure safety (i e make sure we never have an invalid interface enum), we need to supply it to the "new" function.
pub fn new(iface: ElemIface) -> Self {
let mut r = elem_id_new().unwrap();
r.set_interface(iface);
r
}
}
impl cmp::Eq for ElemId {}
impl cmp::PartialEq for ElemId {
fn eq(&self, a: &ElemId) -> bool {
self.get_numid() == a.get_numid() && self.get_interface() == a.get_interface() &&
self.get_index() == a.get_index() && self.get_device() == a.get_device() &&
self.get_subdevice() == a.get_subdevice() && self.get_name().ok() == a.get_name().ok()
}
}
impl fmt::Debug for ElemId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let index = self.get_index();
let device = self.get_device();
let subdevice = self.get_subdevice();
write!(f, "ElemId(#{}, {:?}, {:?}", self.get_numid(), self.get_interface(), self.get_name())?;
if index > 0 { write!(f, ", index={}", index)? };
if device > 0 || subdevice > 0 { write!(f, ", device={}", device)? };
if subdevice > 0 { write!(f, ", subdevice={}", device)? };
write!(f, ")")
}
}
/// [snd_ctl_event_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper
pub struct Event(*mut alsa::snd_ctl_event_t);
impl Drop for Event {
fn drop(&mut self) { unsafe { alsa::snd_ctl_event_free(self.0) }; }
}
pub fn event_new() -> Result<Event> {
let mut p = ptr::null_mut();
acheck!(snd_ctl_event_malloc(&mut p)).map(|_| Event(p))
}
impl Event {
pub fn get_mask(&self) -> EventMask { EventMask(unsafe { alsa::snd_ctl_event_elem_get_mask(self.0) as u32 })}
pub fn get_id(&self) -> ElemId {
let r = elem_id_new().unwrap();
unsafe { alsa::snd_ctl_event_elem_get_id(self.0, elem_id_ptr(&r)) };
r
}
}
/// [SND_CTL_EVENT_MASK_XXX](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) bitmask
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct EventMask(pub u32);
impl EventMask {
pub fn remove(&self) -> bool { return self.0 & 0xffffffff == 0xffffffff }
pub fn value(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 0) != 0); }
pub fn info(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 1) != 0); }
pub fn add(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 2) != 0); }
pub fn tlv(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 3) != 0); }
}
#[test]
fn print_sizeof() {
let elemid = unsafe { alsa::snd_ctl_elem_id_sizeof() } as usize;
let elemvalue = unsafe { alsa::snd_ctl_elem_value_sizeof() } as usize;
let eleminfo = unsafe { alsa::snd_ctl_elem_info_sizeof() } as usize;
assert!(elemid <= ELEM_ID_SIZE);
// assert!(elemvalue <= ELEM_VALUE_SIZE);
// assert!(eleminfo <= ELEM_INFO_SIZE);
println!("Elem id: {}, Elem value: {}, Elem info: {}", elemid, elemvalue, eleminfo);
}

90
vendor/alsa/src/device_name.rs vendored Normal file
View File

@@ -0,0 +1,90 @@
//! Enumerate devices in the alsa library configuration
//!
//! # Example
//! Print all devices found in various categories.
//!
//! ```
//! use std::ffi::CString;
//! use alsa::device_name::HintIter;
//!
//! for t in &["pcm", "ctl", "rawmidi", "timer", "seq", "hwdep"] {
//! println!("{} devices:", t);
//! let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap();
//! for a in i { println!(" {:?}", a) }
//! }
//! ```
use std::ptr;
use libc::{c_void, c_int};
use crate::alsa;
use super::{Card, Direction};
use super::error::*;
use std::ffi::{CStr, CString};
/// [snd_device_name_hint](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper
pub struct HintIter(*mut *mut c_void, isize);
impl Drop for HintIter {
fn drop(&mut self) { unsafe { alsa::snd_device_name_free_hint(self.0); }}
}
impl HintIter {
/// typical interfaces are: "pcm", "ctl", "rawmidi", "timer", "seq" and "hwdep".
pub fn new(card: Option<&Card>, iface: &CStr) -> Result<HintIter> {
let mut p = ptr::null_mut();
let cnr = card.map(|c| c.get_index()).unwrap_or(-1) as c_int;
acheck!(snd_device_name_hint(cnr, iface.as_ptr(), &mut p))
.map(|_| HintIter(p, 0))
}
/// A constructor variant that takes the interface as a Rust string slice.
pub fn new_str(card: Option<&Card>, iface: &str) -> Result<HintIter> {
HintIter::new(card, &CString::new(iface).unwrap())
}
}
impl Iterator for HintIter {
type Item = Hint;
fn next(&mut self) -> Option<Hint> {
if self.0.is_null() { return None; }
let p = unsafe { *self.0.offset(self.1) };
if p.is_null() { return None; }
self.1 += 1;
Some(Hint::new(p))
}
}
/// [snd_device_name_get_hint](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper
#[derive(Debug, Clone)]
pub struct Hint {
pub name: Option<String>,
pub desc: Option<String>,
pub direction: Option<Direction>,
}
impl Hint {
fn get_str(p: *const c_void, name: &str) -> Option<String> {
let name = CString::new(name).unwrap();
let c = unsafe { alsa::snd_device_name_get_hint(p, name.as_ptr()) };
from_alloc("snd_device_name_get_hint", c).ok()
}
fn new(p: *const c_void) -> Hint {
let d = Hint::get_str(p, "IOID").and_then(|x| match &*x {
"Input" => Some(Direction::Capture),
"Output" => Some(Direction::Playback),
_ => None,
});
Hint { name: Hint::get_str(p, "NAME"), desc: Hint::get_str(p, "DESC"), direction: d }
}
}
#[test]
fn print_hints() {
for t in &["pcm", "ctl", "rawmidi", "timer", "seq", "hwdep"] {
println!("{} devices:", t);
let i = HintIter::new(None, &*CString::new(*t).unwrap()).unwrap();
for a in i { println!(" {:?}", a) }
}
}

5
vendor/alsa/src/direct.rs vendored Normal file
View File

@@ -0,0 +1,5 @@
//! Functions that bypass alsa-lib and talk directly to the kernel.
pub mod pcm;
mod ffi;

6713
vendor/alsa/src/direct/asound_ioctl.rs vendored Normal file

File diff suppressed because it is too large Load Diff

212
vendor/alsa/src/direct/ffi.rs vendored Normal file
View File

@@ -0,0 +1,212 @@
//! Some definitions from the kernel headers
#![allow(non_camel_case_types)]
use cfg_if::cfg_if;
// const SNDRV_PCM_MMAP_OFFSET_DATA: c_uint = 0x00000000;
pub const SNDRV_PCM_MMAP_OFFSET_STATUS: libc::c_uint = 0x80000000;
pub const SNDRV_PCM_MMAP_OFFSET_CONTROL: libc::c_uint = 0x81000000;
pub const SNDRV_PCM_SYNC_PTR_HWSYNC: libc::c_uint = 1;
pub const SNDRV_PCM_SYNC_PTR_APPL: libc::c_uint = 2;
pub const SNDRV_PCM_SYNC_PTR_AVAIL_MIN: libc::c_uint = 4;
// #[repr(C)]
#[allow(non_camel_case_types)]
pub type snd_pcm_state_t = libc::c_int;
// #[repr(C)]
#[allow(non_camel_case_types)]
pub type snd_pcm_uframes_t = libc::c_ulong;
// I think?! Not sure how this will work with X32 ABI?!
#[allow(non_camel_case_types)]
pub type __kernel_off_t = libc::c_long;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct snd_pcm_mmap_status {
pub state: snd_pcm_state_t, /* RO: state - SNDRV_PCM_STATE_XXXX */
pub pad1: libc::c_int, /* Needed for 64 bit alignment */
pub hw_ptr: snd_pcm_uframes_t, /* RO: hw ptr (0...boundary-1) */
pub tstamp: libc::timespec, /* Timestamp */
pub suspended_state: snd_pcm_state_t, /* RO: suspended stream state */
pub audio_tstamp: libc::timespec, /* from sample counter or wall clock */
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct snd_pcm_mmap_control {
pub appl_ptr: snd_pcm_uframes_t, /* RW: appl ptr (0...boundary-1) */
pub avail_min: snd_pcm_uframes_t, /* RW: min available frames for wakeup */
}
#[repr(C)]
#[derive(Debug)]
pub struct snd_pcm_channel_info {
pub channel: libc::c_uint,
pub offset: __kernel_off_t, /* mmap offset */
pub first: libc::c_uint, /* offset to first sample in bits */
pub step: libc::c_uint, /* samples distance in bits */
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union snd_pcm_mmap_status_r {
pub status: snd_pcm_mmap_status,
pub reserved: [libc::c_uchar; 64],
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union snd_pcm_mmap_control_r {
pub control: snd_pcm_mmap_control,
pub reserved: [libc::c_uchar; 64],
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct snd_pcm_sync_ptr {
pub flags: libc::c_uint,
pub s: snd_pcm_mmap_status_r,
pub c: snd_pcm_mmap_control_r,
}
cfg_if! {
if #[cfg(any(target_os = "linux", target_os = "android"))] {
// See <https://github.com/nix-rust/nix/blob/197f55b3ccbce3273bf6ce119d1a8541b5df5d66/src/sys/ioctl/linux.rs>
cfg_if! {
if #[cfg(any(target_os = "android", target_env = "musl"))] {
pub(super) type ioctl_num_type = libc::c_int;
} else {
pub(super) type ioctl_num_type = libc::c_ulong;
}
}
// The READ dir is consistent across arches
pub(super) const READ: ioctl_num_type = 2;
// But WRITE is not, as well as having a different number of bits for the SIZEBITS
cfg_if!{
if #[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "sparc64"
))] {
pub(super) const WRITE: ioctl_num_type = 4;
const SIZEBITS: ioctl_num_type = 13;
} else {
pub(super) const WRITE: ioctl_num_type = 1;
const SIZEBITS: ioctl_num_type = 14;
}
}
const NRSHIFT: ioctl_num_type = 0;
const NRBITS: ioctl_num_type = 8;
const TYPEBITS: ioctl_num_type = 8;
const TYPESHIFT: ioctl_num_type = NRSHIFT + NRBITS;
const SIZESHIFT: ioctl_num_type = TYPESHIFT + TYPEBITS;
const DIRSHIFT: ioctl_num_type = SIZESHIFT + SIZEBITS;
/// Replication of the [`nix::ioc!`](https://github.com/nix-rust/nix/blob/197f55b3ccbce3273bf6ce119d1a8541b5df5d66/src/sys/ioctl/linux.rs#L78-L96)
pub(super) const fn make_request(
dir: ioctl_num_type,
typ: u8,
nr: u8,
size: usize,
) -> ioctl_num_type {
dir << DIRSHIFT
| (typ as ioctl_num_type) << TYPESHIFT
| (nr as ioctl_num_type) << NRSHIFT
| (size as ioctl_num_type) << SIZESHIFT
}
} else if #[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "illumos"
))] {
// See <https://github.com/nix-rust/nix/blob/197f55b3ccbce3273bf6ce119d1a8541b5df5d66/src/sys/ioctl/bsd.rs>
cfg_if! {
if #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] {
pub(super) type ioctl_num_type = libc::c_ulong;
} else {
pub(super) type ioctl_num_type = libc::c_int;
}
}
#[allow(overflowing_literals)]
pub(super) const READ: ioctl_num_type = 0x4000_0000;
#[allow(overflowing_literals)]
pub(super) const WRITE: ioctl_num_type = 0x8000_0000;
const IOCPARM_MASK: ioctl_num_type = 0x1fff;
/// Replication of [`nix::ioc!`](https://github.com/nix-rust/nix/blob/197f55b3ccbce3273bf6ce119d1a8541b5df5d66/src/sys/ioctl/bsd.rs#L31-L42)
pub(super) const fn make_request(
dir: ioctl_num_type,
typ: u8,
nr: u8,
size: usize,
) -> ioctl_num_type {
dir | ((size as ioctl_num_type) & IOCPARM_MASK) << 16
| (typ as ioctl_num_type) << 8
| nr as ioctl_num_type
}
} else {
compile_error!("unknown target platform");
}
}
pub(crate) unsafe fn sndrv_pcm_ioctl_channel_info(
fd: libc::c_int,
data: *mut snd_pcm_channel_info,
) -> Result<(), crate::Error> {
const REQUEST: ioctl_num_type = make_request(
READ,
b'A',
0x32,
std::mem::size_of::<snd_pcm_channel_info>(),
);
unsafe {
if libc::ioctl(fd, REQUEST, data) == -1 {
Err(crate::Error::last("SNDRV_PCM_IOCTL_CHANNEL_INFO"))
} else {
Ok(())
}
}
}
pub(crate) unsafe fn sndrv_pcm_ioctl_sync_ptr(
fd: libc::c_int,
data: *mut snd_pcm_sync_ptr,
) -> Result<(), crate::Error> {
const REQUEST: ioctl_num_type = make_request(
READ | WRITE,
b'A',
0x23,
std::mem::size_of::<snd_pcm_sync_ptr>(),
);
unsafe {
if libc::ioctl(fd, REQUEST, data) == -1 {
Err(crate::Error::last("SNDRV_PCM_IOCTL_SYNC_PTR"))
} else {
Ok(())
}
}
}
pub fn pagesize() -> usize {
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
}

628
vendor/alsa/src/direct/pcm.rs vendored Normal file
View File

@@ -0,0 +1,628 @@
/*!
This module bypasses alsa-lib and directly read and write into memory mapped kernel memory.
In case of the sample memory, this is in many cases the DMA buffers that is transferred to the sound card.
The reasons for doing this are:
* Minimum overhead where it matters most: let alsa-lib do the code heavy setup -
then steal its file descriptor and deal with sample streaming from Rust.
* RT-safety to the maximum extent possible. Creating/dropping any of these structs causes syscalls,
but function calls on these are just read and write from memory. No syscalls, no memory allocations,
not even loops (with the exception of `MmapPlayback::write` that loops over samples to write).
* Possibility to allow Send + Sync for structs
* It's a fun experiment and an interesting deep dive into how alsa-lib does things.
Note: Not all sound card drivers support this direct method of communication; although almost all
modern/common ones do. It only works with hardware devices though (such as "hw:xxx" device strings),
don't expect it to work with, e g, the PulseAudio plugin or so.
For an example of how to use this mode, look in the "synth-example" directory.
*/
use libc;
use std::{mem, ptr, fmt, cmp};
use crate::error::{Error, Result};
use std::os::unix::io::RawFd;
use crate::{pcm, PollDescriptors, Direction};
use crate::pcm::Frames;
use std::marker::PhantomData;
use super::ffi::*;
/// Read PCM status via a simple kernel syscall, bypassing alsa-lib.
///
/// If Status is not available on your architecture, this is the second best option.
pub struct SyncPtrStatus(snd_pcm_mmap_status);
impl SyncPtrStatus {
/// Executes sync_ptr syscall.
///
/// Unsafe because
/// - setting appl_ptr and avail_min might make alsa-lib confused
/// - no check that the fd is really a PCM
pub unsafe fn sync_ptr(fd: RawFd, hwsync: bool, appl_ptr: Option<pcm::Frames>, avail_min: Option<pcm::Frames>) -> Result<Self> {
let mut data = snd_pcm_sync_ptr {
flags: (if hwsync { SNDRV_PCM_SYNC_PTR_HWSYNC } else { 0 }) +
(if appl_ptr.is_some() { SNDRV_PCM_SYNC_PTR_APPL } else { 0 }) +
(if avail_min.is_some() { SNDRV_PCM_SYNC_PTR_AVAIL_MIN } else { 0 }),
c: snd_pcm_mmap_control_r {
control: snd_pcm_mmap_control {
appl_ptr: appl_ptr.unwrap_or(0) as snd_pcm_uframes_t,
avail_min: avail_min.unwrap_or(0) as snd_pcm_uframes_t,
}
},
s: mem::zeroed()
};
sndrv_pcm_ioctl_sync_ptr(fd, &mut data)?;
let i = data.s.status.state;
if (i >= (pcm::State::Open as snd_pcm_state_t)) && (i <= (pcm::State::Disconnected as snd_pcm_state_t)) {
Ok(SyncPtrStatus(data.s.status))
} else {
Err(Error::unsupported("SNDRV_PCM_IOCTL_SYNC_PTR returned broken state"))
}
}
pub fn hw_ptr(&self) -> pcm::Frames { self.0.hw_ptr as pcm::Frames }
pub fn state(&self) -> pcm::State { unsafe { mem::transmute(self.0.state as u8) } /* valid range checked in sync_ptr */ }
pub fn htstamp(&self) -> libc::timespec { self.0.tstamp }
}
/// Read PCM status directly from memory, bypassing alsa-lib.
///
/// This means that it's
/// 1) less overhead for reading status (no syscall, no allocations, no virtual dispatch, just a read from memory)
/// 2) Send + Sync, and
/// 3) will only work for "hw" / "plughw" devices (not e g PulseAudio plugins), and not
/// all of those are supported, although all common ones are (as of 2017, and a kernel from the same decade).
/// Kernel supported archs are: x86, PowerPC, Alpha. Use "SyncPtrStatus" for other archs.
///
/// The values are updated every now and then by the kernel. Many functions will force an update to happen,
/// e g `PCM::avail()` and `PCM::delay()`.
///
/// Note: Even if you close the original PCM device, ALSA will not actually close the device until all
/// Status structs are dropped too.
///
#[derive(Debug)]
pub struct Status(DriverMemory<snd_pcm_mmap_status>);
fn pcm_to_fd(p: &pcm::PCM) -> Result<RawFd> {
let mut fds: [libc::pollfd; 1] = unsafe { mem::zeroed() };
let c = PollDescriptors::fill(p, &mut fds)?;
if c != 1 {
return Err(Error::unsupported("snd_pcm_poll_descriptors returned wrong number of fds"))
}
Ok(fds[0].fd)
}
impl Status {
pub fn new(p: &pcm::PCM) -> Result<Self> { Status::from_fd(pcm_to_fd(p)?) }
pub fn from_fd(fd: RawFd) -> Result<Self> {
DriverMemory::new(fd, 1, SNDRV_PCM_MMAP_OFFSET_STATUS as libc::off_t, false).map(Status)
}
/// Current PCM state.
pub fn state(&self) -> pcm::State {
unsafe {
let i = ptr::read_volatile(&(*self.0.ptr).state);
assert!((i >= (pcm::State::Open as snd_pcm_state_t)) && (i <= (pcm::State::Disconnected as snd_pcm_state_t)));
mem::transmute(i as u8)
}
}
/// Number of frames hardware has read or written
///
/// This number is updated every now and then by the kernel.
/// Calling most functions on the PCM will update it, so will usually a period interrupt.
/// No guarantees given.
///
/// This value wraps at "boundary" (a large value you can read from SwParams).
pub fn hw_ptr(&self) -> pcm::Frames {
unsafe {
ptr::read_volatile(&(*self.0.ptr).hw_ptr) as pcm::Frames
}
}
/// Timestamp - fast version of alsa-lib's Status::get_htstamp
///
/// Note: This just reads the actual value in memory.
/// Unfortunately, the timespec is too big to be read atomically on most archs.
/// Therefore, this function can potentially give bogus result at times, at least in theory...?
pub fn htstamp(&self) -> libc::timespec {
unsafe {
ptr::read_volatile(&(*self.0.ptr).tstamp)
}
}
/// Audio timestamp - fast version of alsa-lib's Status::get_audio_htstamp
///
/// Note: This just reads the actual value in memory.
/// Unfortunately, the timespec is too big to be read atomically on most archs.
/// Therefore, this function can potentially give bogus result at times, at least in theory...?
pub fn audio_htstamp(&self) -> libc::timespec {
unsafe {
ptr::read_volatile(&(*self.0.ptr).audio_tstamp)
}
}
}
/// Write PCM appl ptr directly, bypassing alsa-lib.
///
/// Provides direct access to appl ptr and avail min, without the overhead of
/// alsa-lib or a syscall. Caveats that apply to Status applies to this struct too.
#[derive(Debug)]
pub struct Control(DriverMemory<snd_pcm_mmap_control>);
impl Control {
pub fn new(p: &pcm::PCM) -> Result<Self> { Self::from_fd(pcm_to_fd(p)?) }
pub fn from_fd(fd: RawFd) -> Result<Self> {
DriverMemory::new(fd, 1, SNDRV_PCM_MMAP_OFFSET_CONTROL as libc::off_t, true).map(Control)
}
/// Read number of frames application has read or written
///
/// This value wraps at "boundary" (a large value you can read from SwParams).
pub fn appl_ptr(&self) -> pcm::Frames {
unsafe {
ptr::read_volatile(&(*self.0.ptr).appl_ptr) as pcm::Frames
}
}
/// Set number of frames application has read or written
///
/// When the kernel wakes up due to a period interrupt, this value will
/// be checked by the kernel. An XRUN will happen in case the application
/// has not read or written enough data.
pub fn set_appl_ptr(&self, value: pcm::Frames) {
unsafe {
ptr::write_volatile(&mut (*self.0.ptr).appl_ptr, value as snd_pcm_uframes_t)
}
}
/// Read minimum number of frames in buffer in order to wakeup process
pub fn avail_min(&self) -> pcm::Frames {
unsafe {
ptr::read_volatile(&(*self.0.ptr).avail_min) as pcm::Frames
}
}
/// Write minimum number of frames in buffer in order to wakeup process
pub fn set_avail_min(&self, value: pcm::Frames) {
unsafe {
ptr::write_volatile(&mut (*self.0.ptr).avail_min, value as snd_pcm_uframes_t)
}
}
}
struct DriverMemory<S> {
ptr: *mut S,
size: libc::size_t,
}
impl<S> fmt::Debug for DriverMemory<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DriverMemory({:?})", self.ptr) }
}
impl<S> DriverMemory<S> {
fn new(fd: RawFd, count: usize, offs: libc::off_t, writable: bool) -> Result<Self> {
let mut total = count * mem::size_of::<S>();
let ps = pagesize();
assert!(total > 0);
if total % ps != 0 { total += ps - total % ps };
let flags = if writable { libc::PROT_WRITE | libc::PROT_READ } else { libc::PROT_READ };
let p = unsafe { libc::mmap(ptr::null_mut(), total, flags, libc::MAP_FILE | libc::MAP_SHARED, fd, offs) };
if p.is_null() || p == libc::MAP_FAILED {
Err(Error::last("mmap (of driver memory)"))
} else {
Ok(DriverMemory { ptr: p as *mut S, size: total })
}
}
}
unsafe impl<S> Send for DriverMemory<S> {}
unsafe impl<S> Sync for DriverMemory<S> {}
impl<S> Drop for DriverMemory<S> {
fn drop(&mut self) {
unsafe {{ libc::munmap(self.ptr as *mut libc::c_void, self.size); } }
}
}
#[derive(Debug)]
struct SampleData<S> {
mem: DriverMemory<S>,
frames: pcm::Frames,
channels: u32,
}
impl<S> SampleData<S> {
pub fn new(p: &pcm::PCM) -> Result<Self> {
let params = p.hw_params_current()?;
let bufsize = params.get_buffer_size()?;
let channels = params.get_channels()?;
if params.get_access()? != pcm::Access::MMapInterleaved {
return Err(Error::unsupported("Not MMAP interleaved data"))
}
let fd = pcm_to_fd(p)?;
let info = unsafe {
let mut info: snd_pcm_channel_info = mem::zeroed();
sndrv_pcm_ioctl_channel_info(fd, &mut info)?;
info
};
// println!("{:?}", info);
if (info.step != channels * mem::size_of::<S>() as u32 * 8) || (info.first != 0) {
return Err(Error::unsupported("MMAP data size mismatch"))
}
Ok(SampleData {
mem: DriverMemory::new(fd, (bufsize as usize) * (channels as usize), info.offset as libc::off_t, true)?,
frames: bufsize,
channels,
})
}
}
/// Dummy trait for better generics
pub trait MmapDir: fmt::Debug {
const DIR: Direction;
fn avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames;
}
/// Dummy struct for better generics
#[derive(Copy, Clone, Debug)]
pub struct Playback;
impl MmapDir for Playback {
const DIR: Direction = Direction::Playback;
#[inline]
fn avail(hwptr: Frames, applptr: Frames, buffersize: Frames, boundary: Frames) -> Frames {
let r = hwptr.wrapping_add(buffersize).wrapping_sub(applptr);
let r = if r < 0 { r.wrapping_add(boundary) } else { r };
if r as usize >= boundary as usize { r.wrapping_sub(boundary) } else { r }
}
}
/// Dummy struct for better generics
#[derive(Copy, Clone, Debug)]
pub struct Capture;
impl MmapDir for Capture {
const DIR: Direction = Direction::Capture;
#[inline]
fn avail(hwptr: Frames, applptr: Frames, _buffersize: Frames, boundary: Frames) -> Frames {
let r = hwptr.wrapping_sub(applptr);
if r < 0 { r.wrapping_add(boundary) } else { r }
}
}
pub type MmapPlayback<S> = MmapIO<S, Playback>;
pub type MmapCapture<S> = MmapIO<S, Capture>;
#[derive(Debug)]
/// Struct containing direct I/O functions shared between playback and capture.
pub struct MmapIO<S, D> {
data: SampleData<S>,
c: Control,
ss: Status,
bound: Frames,
dir: PhantomData<*const D>,
}
#[derive(Debug, Clone, Copy)]
/// A raw pointer to samples, and the amount of samples readable or writable.
pub struct RawSamples<S> {
pub ptr: *mut S,
pub frames: Frames,
pub channels: u32,
}
impl<S> RawSamples<S> {
#[inline]
/// Returns `frames` * `channels`, i e the amount of samples (of type `S`) that can be read/written.
pub fn samples(&self) -> isize { self.frames as isize * (self.channels as isize) }
/// Writes samples from an iterator.
///
/// Returns true if iterator was depleted, and the number of samples written.
/// This is just raw read/write of memory.
pub unsafe fn write_samples<I: Iterator<Item=S>>(&self, i: &mut I) -> (bool, isize) {
let mut z = 0;
let max_samples = self.samples();
while z < max_samples {
let b = if let Some(b) = i.next() { b } else { return (true, z) };
ptr::write_volatile(self.ptr.offset(z), b);
z += 1;
};
(false, z)
}
}
impl<S, D: MmapDir> MmapIO<S, D> {
fn new(p: &pcm::PCM) -> Result<Self> {
if p.info()?.get_stream() != D::DIR {
return Err(Error::unsupported("Wrong direction"));
}
let boundary = p.sw_params_current()?.get_boundary()?;
Ok(MmapIO {
data: SampleData::new(p)?,
c: Control::new(p)?,
ss: Status::new(p)?,
bound: boundary,
dir: PhantomData,
})
}
}
pub (crate) fn new_mmap<S, D: MmapDir>(p: &pcm::PCM) -> Result<MmapIO<S, D>> { MmapIO::new(p) }
impl<S, D: MmapDir> MmapIO<S, D> {
/// Read current status
pub fn status(&self) -> &Status { &self.ss }
/// Read current number of frames committed by application
///
/// This number wraps at 'boundary'.
#[inline]
pub fn appl_ptr(&self) -> Frames { self.c.appl_ptr() }
/// Read current number of frames read / written by hardware
///
/// This number wraps at 'boundary'.
#[inline]
pub fn hw_ptr(&self) -> Frames { self.ss.hw_ptr() }
/// The number at which hw_ptr and appl_ptr wraps.
#[inline]
pub fn boundary(&self) -> Frames { self.bound }
/// Total number of frames in hardware buffer
#[inline]
pub fn buffer_size(&self) -> Frames { self.data.frames }
/// Number of channels in stream
#[inline]
pub fn channels(&self) -> u32 { self.data.channels }
/// Notifies the kernel that frames have now been read / written by the application
///
/// This will allow the kernel to write new data into this part of the buffer.
pub fn commit(&self, v: Frames) {
let mut z = self.appl_ptr() + v;
if z + v >= self.boundary() { z -= self.boundary() };
self.c.set_appl_ptr(z)
}
/// Number of frames available to read / write.
///
/// In case of an underrun, this value might be bigger than the buffer size.
pub fn avail(&self) -> Frames { D::avail(self.hw_ptr(), self.appl_ptr(), self.buffer_size(), self.boundary()) }
/// Returns raw pointers to data to read / write.
///
/// Use this if you want to read/write data yourself (instead of using iterators). If you do,
/// using `write_volatile` or `read_volatile` is recommended, since it's DMA memory and can
/// change at any time.
///
/// Since this is a ring buffer, there might be more data to read/write in the beginning
/// of the buffer as well. If so this is returned as the second return value.
pub fn data_ptr(&self) -> (RawSamples<S>, Option<RawSamples<S>>) {
let (hwptr, applptr) = (self.hw_ptr(), self.appl_ptr());
let c = self.channels();
let bufsize = self.buffer_size();
// These formulas mostly mimic the behaviour of
// snd_pcm_mmap_begin (in alsa-lib/src/pcm/pcm.c).
let offs = applptr % bufsize;
let mut a = D::avail(hwptr, applptr, bufsize, self.boundary());
a = cmp::min(a, bufsize);
let b = bufsize - offs;
let more_data = if b < a {
let z = a - b;
a = b;
Some( RawSamples { ptr: self.data.mem.ptr, frames: z, channels: c })
} else { None };
let p = unsafe { self.data.mem.ptr.offset(offs as isize * self.data.channels as isize) };
(RawSamples { ptr: p, frames: a, channels: c }, more_data)
}
}
impl<S> MmapPlayback<S> {
/// Write samples to the kernel ringbuffer.
pub fn write<I: Iterator<Item=S>>(&mut self, i: &mut I) -> Frames {
let (data, more_data) = self.data_ptr();
let (iter_end, samples) = unsafe { data.write_samples(i) };
let mut z = samples / data.channels as isize;
if !iter_end {
if let Some(data2) = more_data {
let (_, samples2) = unsafe { data2.write_samples(i) };
z += samples2 / data2.channels as isize;
}
}
let z = z as Frames;
self.commit(z);
z
}
}
impl<S> MmapCapture<S> {
/// Read samples from the kernel ringbuffer.
///
/// When the iterator is dropped or depleted, the read samples will be committed, i e,
/// the kernel can then write data to the location again. So do this ASAP.
pub fn iter(&mut self) -> CaptureIter<S> {
let (data, more_data) = self.data_ptr();
CaptureIter {
m: self,
samples: data,
p_offs: 0,
read_samples: 0,
next_p: more_data,
}
}
}
/// Iterator over captured samples
pub struct CaptureIter<'a, S: 'static> {
m: &'a MmapCapture<S>,
samples: RawSamples<S>,
p_offs: isize,
read_samples: isize,
next_p: Option<RawSamples<S>>,
}
impl<'a, S: 'static + Copy> CaptureIter<'a, S> {
fn handle_max(&mut self) {
self.p_offs = 0;
if let Some(p2) = self.next_p.take() {
self.samples = p2;
} else {
self.m.commit((self.read_samples / self.samples.channels as isize) as Frames);
self.read_samples = 0;
self.samples.frames = 0; // Shortcut to "None" in case anyone calls us again
}
}
}
impl<'a, S: 'static + Copy> Iterator for CaptureIter<'a, S> {
type Item = S;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.p_offs >= self.samples.samples() {
self.handle_max();
if self.samples.frames <= 0 { return None; }
}
let s = unsafe { ptr::read_volatile(self.samples.ptr.offset(self.p_offs)) };
self.p_offs += 1;
self.read_samples += 1;
Some(s)
}
}
impl<'a, S: 'static> Drop for CaptureIter<'a, S> {
fn drop(&mut self) {
self.m.commit((self.read_samples / self.m.data.channels as isize) as Frames);
}
}
#[test]
#[ignore] // Not everyone has a recording device on plughw:1. So let's ignore this test by default.
fn record_from_plughw_rw() {
use crate::pcm::*;
use crate::{ValueOr, Direction};
use std::ffi::CString;
let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Capture, false).unwrap();
let ss = self::Status::new(&pcm).unwrap();
let c = self::Control::new(&pcm).unwrap();
let hwp = HwParams::any(&pcm).unwrap();
hwp.set_channels(2).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 swp = pcm.sw_params_current().unwrap();
swp.set_tstamp_mode(true).unwrap();
pcm.sw_params(&swp).unwrap();
}
assert_eq!(ss.state(), State::Prepared);
pcm.start().unwrap();
assert_eq!(c.appl_ptr(), 0);
println!("{:?}, {:?}", ss, c);
let mut buf = [0i16; 512*2];
assert_eq!(pcm.io_i16().unwrap().readi(&mut buf).unwrap(), 512);
assert_eq!(c.appl_ptr(), 512);
assert_eq!(ss.state(), State::Running);
assert!(ss.hw_ptr() >= 512);
let t2 = ss.htstamp();
assert!(t2.tv_sec > 0 || t2.tv_nsec > 0);
}
#[test]
#[ignore] // Not everyone has a record device on plughw:1. So let's ignore this test by default.
fn record_from_plughw_mmap() {
use crate::pcm::*;
use crate::{ValueOr, Direction};
use std::ffi::CString;
use std::{thread, time};
let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Capture, false).unwrap();
let hwp = HwParams::any(&pcm).unwrap();
hwp.set_channels(2).unwrap();
hwp.set_rate(44100, ValueOr::Nearest).unwrap();
hwp.set_format(Format::s16()).unwrap();
hwp.set_access(Access::MMapInterleaved).unwrap();
pcm.hw_params(&hwp).unwrap();
let ss = unsafe { SyncPtrStatus::sync_ptr(pcm_to_fd(&pcm).unwrap(), false, None, None).unwrap() };
assert_eq!(ss.state(), State::Prepared);
let mut m = pcm.direct_mmap_capture::<i16>().unwrap();
assert_eq!(m.status().state(), State::Prepared);
assert_eq!(m.appl_ptr(), 0);
assert_eq!(m.hw_ptr(), 0);
println!("{:?}", m);
let now = time::Instant::now();
pcm.start().unwrap();
while m.avail() < 256 { thread::sleep(time::Duration::from_millis(1)) };
assert!(now.elapsed() >= time::Duration::from_millis(256 * 1000 / 44100));
let (ptr1, md) = m.data_ptr();
assert_eq!(ptr1.channels, 2);
assert!(ptr1.frames >= 256);
assert!(md.is_none());
println!("Has {:?} frames at {:?} in {:?}", m.avail(), ptr1.ptr, now.elapsed());
let samples: Vec<i16> = m.iter().collect();
assert!(samples.len() >= ptr1.frames as usize * 2);
println!("Collected {} samples", samples.len());
let (ptr2, _md) = m.data_ptr();
assert!(unsafe { ptr1.ptr.offset(256 * 2) } <= ptr2.ptr);
}
#[test]
#[ignore]
fn playback_to_plughw_mmap() {
use crate::pcm::*;
use crate::{ValueOr, Direction};
use std::ffi::CString;
let pcm = PCM::open(&*CString::new("plughw:1").unwrap(), Direction::Playback, false).unwrap();
let hwp = HwParams::any(&pcm).unwrap();
hwp.set_channels(2).unwrap();
hwp.set_rate(44100, ValueOr::Nearest).unwrap();
hwp.set_format(Format::s16()).unwrap();
hwp.set_access(Access::MMapInterleaved).unwrap();
pcm.hw_params(&hwp).unwrap();
let mut m = pcm.direct_mmap_playback::<i16>().unwrap();
assert_eq!(m.status().state(), State::Prepared);
assert_eq!(m.appl_ptr(), 0);
assert_eq!(m.hw_ptr(), 0);
println!("{:?}", m);
let mut i = (0..(m.buffer_size() * 2)).map(|i|
(((i / 2) as f32 * 2.0 * ::std::f32::consts::PI / 128.0).sin() * 8192.0) as i16);
m.write(&mut i);
assert_eq!(m.appl_ptr(), m.buffer_size());
pcm.start().unwrap();
pcm.drain().unwrap();
assert_eq!(m.appl_ptr(), m.buffer_size());
assert!(m.hw_ptr() >= m.buffer_size());
}

220
vendor/alsa/src/error.rs vendored Normal file
View File

@@ -0,0 +1,220 @@
#![macro_use]
use libc::{c_char, c_int, c_void, free};
use std::error::Error as StdError;
use std::ffi::CStr;
use std::{fmt, str};
/// ALSA error
///
/// Most ALSA functions can return a negative error code.
/// If so, then that error code is wrapped into this `Error` struct.
/// An Error is also returned in case ALSA returns a string that
/// cannot be translated into Rust's UTF-8 strings.
#[derive(Clone, PartialEq, Copy)]
pub struct Error(&'static str, i32);
pub type Result<T> = ::std::result::Result<T, Error>;
macro_rules! acheck {
($f: ident ( $($x: expr),* ) ) => {{
let r = unsafe { alsa::$f( $($x),* ) };
if r < 0 { Err(Error::new(stringify!($f), -r as ::libc::c_int)) }
else { Ok(r) }
}}
}
pub fn from_const<'a>(func: &'static str, s: *const c_char) -> Result<&'a str> {
if s.is_null() {
return Err(invalid_str(func));
};
let cc = unsafe { CStr::from_ptr(s) };
str::from_utf8(cc.to_bytes()).map_err(|_| invalid_str(func))
}
pub fn from_alloc(func: &'static str, s: *mut c_char) -> Result<String> {
if s.is_null() {
return Err(invalid_str(func));
};
let c = unsafe { CStr::from_ptr(s) };
let ss = str::from_utf8(c.to_bytes())
.map_err(|_| {
unsafe {
free(s as *mut c_void);
}
invalid_str(func)
})?
.to_string();
unsafe {
free(s as *mut c_void);
}
Ok(ss)
}
pub fn from_code(func: &'static str, r: c_int) -> Result<c_int> {
if r < 0 {
Err(Error::new(func, r))
} else {
Ok(r)
}
}
impl Error {
pub fn new(func: &'static str, res: c_int) -> Error {
Self(func, res)
}
pub fn last(func: &'static str) -> Error {
Self(
func,
std::io::Error::last_os_error()
.raw_os_error()
.unwrap_or_default(),
)
}
pub fn unsupported(func: &'static str) -> Error {
Error(func, libc::ENOTSUP)
}
/// The function which failed.
pub fn func(&self) -> &'static str {
self.0
}
/// Underlying error
///
/// Match this against the re-export of `nix::Error` in this crate, not against a specific version
/// of the nix crate. The nix crate version might be updated with minor updates of this library.
pub fn errno(&self) -> i32 {
self.1
}
}
pub fn invalid_str(func: &'static str) -> Error {
Error(func, libc::EILSEQ)
}
impl StdError for Error {
fn description(&self) -> &str {
"ALSA error"
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ALSA function '{}' failed with error '{} ({})'",
self.0,
desc(self.1),
self.1,
)
}
}
/// See <https://github.com/nix-rust/nix/blob/197f55b3ccbce3273bf6ce119d1a8541b5df5d66/src/errno.rs#L198>
///
/// Note this doesn't include the total set of possible errno variants, but they
/// can easily be added in the future for better error messages
fn desc(errno: i32) -> &'static str {
match errno {
libc::EPERM => "Operation not permitted",
libc::ENOENT => "No such file or directory",
libc::ESRCH => "No such process",
libc::EINTR => "Interrupted system call",
libc::EIO => "I/O error",
libc::ENXIO => "No such device or address",
libc::E2BIG => "Argument list too long",
libc::ENOEXEC => "Exec format error",
libc::EBADF => "Bad file number",
libc::ECHILD => "No child processes",
libc::EAGAIN => "Try again",
libc::ENOMEM => "Out of memory",
libc::EACCES => "Permission denied",
libc::EFAULT => "Bad address",
libc::ENOTBLK => "Block device required",
libc::EBUSY => "Device or resource busy",
libc::EEXIST => "File exists",
libc::EXDEV => "Cross-device link",
libc::ENODEV => "No such device",
libc::ENOTDIR => "Not a directory",
libc::EISDIR => "Is a directory",
libc::EINVAL => "Invalid argument",
libc::ENFILE => "File table overflow",
libc::EMFILE => "Too many open files",
libc::ENOTTY => "Not a typewriter",
libc::ETXTBSY => "Text file busy",
libc::EFBIG => "File too large",
libc::ENOSPC => "No space left on device",
libc::ESPIPE => "Illegal seek",
libc::EROFS => "Read-only file system",
libc::EMLINK => "Too many links",
libc::EPIPE => "Broken pipe",
libc::EDOM => "Math argument out of domain of func",
libc::ERANGE => "Math result not representable",
libc::EDEADLK => "Resource deadlock would occur",
libc::ENAMETOOLONG => "File name too long",
libc::ENOLCK => "No record locks available",
libc::ENOSYS => "Function not implemented",
libc::ENOTEMPTY => "Directory not empty",
libc::ELOOP => "Too many symbolic links encountered",
libc::ENOMSG => "No message of desired type",
libc::EIDRM => "Identifier removed",
libc::EINPROGRESS => "Operation now in progress",
libc::EALREADY => "Operation already in progress",
libc::ENOTSOCK => "Socket operation on non-socket",
libc::EDESTADDRREQ => "Destination address required",
libc::EMSGSIZE => "Message too long",
libc::EPROTOTYPE => "Protocol wrong type for socket",
libc::ENOPROTOOPT => "Protocol not available",
libc::EPROTONOSUPPORT => "Protocol not supported",
libc::ESOCKTNOSUPPORT => "Socket type not supported",
libc::EPFNOSUPPORT => "Protocol family not supported",
libc::EAFNOSUPPORT => "Address family not supported by protocol",
libc::EADDRINUSE => "Address already in use",
libc::EADDRNOTAVAIL => "Cannot assign requested address",
libc::ENETDOWN => "Network is down",
libc::ENETUNREACH => "Network is unreachable",
libc::ENETRESET => "Network dropped connection because of reset",
libc::ECONNABORTED => "Software caused connection abort",
libc::ECONNRESET => "Connection reset by peer",
libc::ENOBUFS => "No buffer space available",
libc::EISCONN => "Transport endpoint is already connected",
libc::ENOTCONN => "Transport endpoint is not connected",
libc::ESHUTDOWN => "Cannot send after transport endpoint shutdown",
libc::ETOOMANYREFS => "Too many references: cannot splice",
libc::ETIMEDOUT => "Connection timed out",
libc::ECONNREFUSED => "Connection refused",
libc::EHOSTDOWN => "Host is down",
libc::EHOSTUNREACH => "No route to host",
libc::ENOTSUP => "Operation not supported",
_ => "Unknown errno",
}
}
impl From<Error> for fmt::Error {
fn from(_: Error) -> fmt::Error {
fmt::Error
}
}
#[test]
fn broken_pcm_name() {
use std::ffi::CString;
let e = crate::PCM::open(
&*CString::new("this_PCM_does_not_exist").unwrap(),
crate::Direction::Playback,
false,
)
.err()
.unwrap();
assert_eq!(e.func(), "snd_pcm_open");
assert_eq!(e.errno(), libc::ENOENT);
}

168
vendor/alsa/src/hctl.rs vendored Normal file
View File

@@ -0,0 +1,168 @@
//! HCtl API - for mixer control and jack detection
//!
//! # Example
//! Print all jacks and their status
//!
//! ```
//! for a in ::alsa::card::Iter::new().map(|x| x.unwrap()) {
//! use std::ffi::CString;
//! use alsa::hctl::HCtl;
//! let h = HCtl::open(&CString::new(format!("hw:{}", a.get_index())).unwrap(), false).unwrap();
//! h.load().unwrap();
//! for b in h.elem_iter() {
//! use alsa::ctl::ElemIface;
//! let id = b.get_id().unwrap();
//! if id.get_interface() != ElemIface::Card { continue; }
//! let name = id.get_name().unwrap();
//! if !name.ends_with(" Jack") { continue; }
//! if name.ends_with(" Phantom Jack") {
//! println!("{} is always present", &name[..name.len()-13])
//! }
//! else { println!("{} is {}", &name[..name.len()-5],
//! if b.read().unwrap().get_boolean(0).unwrap() { "plugged in" } else { "unplugged" })
//! }
//! }
//! }
//! ```
use crate::{alsa, Card};
use std::ffi::{CStr, CString};
use super::error::*;
use std::ptr;
use super::{ctl_int, poll};
use libc::{c_short, c_uint, c_int, pollfd};
/// [snd_hctl_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___h_control.html) wrapper
pub struct HCtl(*mut alsa::snd_hctl_t);
unsafe impl Send for HCtl {}
impl Drop for HCtl {
fn drop(&mut self) { unsafe { alsa::snd_hctl_close(self.0) }; }
}
impl HCtl {
/// Wrapper around open that takes a &str instead of a &CStr
pub fn new(c: &str, nonblock: bool) -> Result<HCtl> {
Self::open(&CString::new(c).unwrap(), nonblock)
}
/// Open does not support async mode (it's not very Rustic anyway)
/// Note: You probably want to call `load` afterwards.
pub fn open(c: &CStr, nonblock: bool) -> Result<HCtl> {
let mut r = ptr::null_mut();
let flags = if nonblock { 1 } else { 0 }; // FIXME: alsa::SND_CTL_NONBLOCK does not exist in alsa-sys
acheck!(snd_hctl_open(&mut r, c.as_ptr(), flags))
.map(|_| HCtl(r))
}
/// Wrapper around open. You probably want to call `load` afterwards.
pub fn from_card(c: &Card, nonblock: bool) -> Result<HCtl> {
let s = format!("hw:{}", c.get_index());
HCtl::new(&s, nonblock)
}
pub fn load(&self) -> Result<()> { acheck!(snd_hctl_load(self.0)).map(|_| ()) }
pub fn elem_iter(&self) -> ElemIter { ElemIter(self, ptr::null_mut()) }
pub fn find_elem(&self, id: &ctl_int::ElemId) -> Option<Elem> {
let p = unsafe { alsa::snd_hctl_find_elem(self.0, ctl_int::elem_id_ptr(id)) };
if p.is_null() { None } else { Some(Elem(self, p)) }
}
pub fn handle_events(&self) -> Result<u32> {
acheck!(snd_hctl_handle_events(self.0)).map(|x| x as u32)
}
pub fn wait(&self, timeout_ms: Option<u32>) -> Result<bool> {
acheck!(snd_hctl_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|i| i == 1) }
}
impl poll::Descriptors for HCtl {
fn count(&self) -> usize {
unsafe { alsa::snd_hctl_poll_descriptors_count(self.0) as usize }
}
fn fill(&self, p: &mut [pollfd]) -> Result<usize> {
let z = unsafe { alsa::snd_hctl_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) };
from_code("snd_hctl_poll_descriptors", z).map(|_| z as usize)
}
fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> {
let mut r = 0;
let z = unsafe { alsa::snd_hctl_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) };
from_code("snd_hctl_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short))
}
}
/// Iterates over elements for a `HCtl`
pub struct ElemIter<'a>(&'a HCtl, *mut alsa::snd_hctl_elem_t);
impl<'a> Iterator for ElemIter<'a> {
type Item = Elem<'a>;
fn next(&mut self) -> Option<Elem<'a>> {
self.1 = if self.1.is_null() { unsafe { alsa::snd_hctl_first_elem((self.0).0) }}
else { unsafe { alsa::snd_hctl_elem_next(self.1) }};
if self.1.is_null() { None }
else { Some(Elem(self.0, self.1)) }
}
}
/// [snd_hctl_elem_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___h_control.html) wrapper
pub struct Elem<'a>(&'a HCtl, *mut alsa::snd_hctl_elem_t);
impl<'a> Elem<'a> {
pub fn get_id(&self) -> Result<ctl_int::ElemId> {
let v = ctl_int::elem_id_new()?;
unsafe { alsa::snd_hctl_elem_get_id(self.1, ctl_int::elem_id_ptr(&v)) };
Ok(v)
}
pub fn info(&self) -> Result<ctl_int::ElemInfo> {
let v = ctl_int::elem_info_new()?;
acheck!(snd_hctl_elem_info(self.1, ctl_int::elem_info_ptr(&v))).map(|_| v)
}
pub fn read(&self) -> Result<ctl_int::ElemValue> {
let i = self.info()?;
let v = ctl_int::elem_value_new(i.get_type(), i.get_count())?;
acheck!(snd_hctl_elem_read(self.1, ctl_int::elem_value_ptr(&v))).map(|_| v)
}
pub fn write(&self, v: &ctl_int::ElemValue) -> Result<bool> {
acheck!(snd_hctl_elem_write(self.1, ctl_int::elem_value_ptr(v))).map(|e| e > 0)
}
}
#[test]
fn print_hctls() {
for a in super::card::Iter::new().map(|x| x.unwrap()) {
use std::ffi::CString;
let h = HCtl::open(&CString::new(format!("hw:{}", a.get_index())).unwrap(), false).unwrap();
h.load().unwrap();
println!("Card {}:", a.get_name().unwrap());
for b in h.elem_iter() {
println!(" {:?} - {:?}", b.get_id().unwrap(), b.read().unwrap());
}
}
}
#[test]
fn print_jacks() {
for a in super::card::Iter::new().map(|x| x.unwrap()) {
use std::ffi::CString;
let h = HCtl::open(&CString::new(format!("hw:{}", a.get_index())).unwrap(), false).unwrap();
h.load().unwrap();
for b in h.elem_iter() {
let id = b.get_id().unwrap();
if id.get_interface() != super::ctl_int::ElemIface::Card { continue; }
let name = id.get_name().unwrap();
if !name.ends_with(" Jack") { continue; }
if name.ends_with(" Phantom Jack") {
println!("{} is always present", &name[..name.len()-13])
}
else { println!("{} is {}", &name[..name.len()-5],
if b.read().unwrap().get_boolean(0).unwrap() { "plugged in" } else { "unplugged" })
}
}
}
}

49
vendor/alsa/src/io.rs vendored Normal file
View File

@@ -0,0 +1,49 @@
use crate::alsa;
use super::error::*;
use std::{slice, ptr, fmt};
/// [snd_output_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___output.html) wrapper
pub struct Output(*mut alsa::snd_output_t);
unsafe impl Send for Output {}
impl Drop for Output {
fn drop(&mut self) { unsafe { alsa::snd_output_close(self.0) }; }
}
impl Output {
pub fn buffer_open() -> Result<Output> {
let mut q = ptr::null_mut();
acheck!(snd_output_buffer_open(&mut q)).map(|_| Output(q))
}
pub fn buffer_string<T, F: FnOnce(&[u8]) -> T>(&self, f: F) -> T {
let b = unsafe {
let mut q = ptr::null_mut();
let s = alsa::snd_output_buffer_string(self.0, &mut q);
slice::from_raw_parts(q as *const u8, s as usize)
};
f(b)
}
}
impl fmt::Debug for Output {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Output(")?;
fmt::Display::fmt(self, f)?;
write!(f, ")")
/* self.buffer_string(|b| f.write_str(try!(str::from_utf8(b).map_err(|_| fmt::Error)))) */
}
}
impl fmt::Display for Output {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.buffer_string(|b| {
let s = String::from_utf8_lossy(b);
f.write_str(&*s)
})
}
}
pub fn output_handle(o: &Output) -> *mut alsa::snd_output_t { o.0 }

133
vendor/alsa/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,133 @@
//! Thin but safe wrappers for [ALSA](https://alsa-project.org).
//!
//! [GitHub repo](https://github.com/diwic/alsa-rs)
//!
//! [Crates.io](https://crates.io/crates/alsa)
//!
//! This ALSA API wrapper/binding is WIP - the ALSA API is huge, and new
//! functions and structs might be added as requested.
//!
//! Most functions map 1-to-1 to alsa-lib functions, e g, `ctl::CardInfo::get_id()` is a wrapper around
//! `snd_ctl_card_info_get_id` and the [alsa-lib documentation](https://www.alsa-project.org/alsa-doc/alsa-lib/)
//! can be consulted for additional information.
//!
//! Enjoy!
#![allow(clippy::all)]
#![warn(clippy::correctness, clippy::suspicious, clippy::perf)]
extern crate alsa_sys as alsa;
extern crate libc;
#[macro_use]
extern crate bitflags;
macro_rules! alsa_enum {
($(#[$attr:meta])+ $name:ident, $static_name:ident [$count:expr], $( $a:ident = $b:ident),* ,) =>
{
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
$(#[$attr])*
pub enum $name {
$(
$a = alsa::$b as isize,
)*
}
static $static_name: [$name; $count] =
[ $( $name::$a, )* ];
impl $name {
/// Returns a slice of all possible values; useful for iteration
pub fn all() -> &'static [$name] { &$static_name[..] }
#[allow(dead_code)]
fn from_c_int(c: ::libc::c_int, s: &'static str) -> Result<$name> {
Self::all().iter().find(|&&x| c == x as ::libc::c_int).map(|&x| x)
.ok_or_else(|| Error::unsupported(s))
}
#[allow(dead_code)]
fn to_c_int(&self) -> ::libc::c_int {
return *self as ::libc::c_int;
}
}
}
}
/// Replaces constants ending with PLAYBACK/CAPTURE as well as
/// INPUT/OUTPUT
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Direction {
Playback,
Capture
}
impl Direction {
#[inline]
pub fn input() -> Direction { Direction::Capture }
#[inline]
pub fn output() -> Direction { Direction::Playback }
}
/// Used to restrict hw parameters. In case the submitted
/// value is unavailable, in which direction should one search
/// for available values?
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ValueOr {
/// The value set is the submitted value, or less
Less = -1,
/// The value set is the submitted value, or the nearest
Nearest = 0,
/// The value set is the submitted value, or greater
Greater = 1,
}
/// Rounding mode (used in some mixer related calls)
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Round {
/// Round down (towards negative infinity)
Floor = 0,
/// Round up (towards positive infinity)
Ceil = 1,
}
mod error;
pub use crate::error::{Error, Result};
pub mod card;
pub use crate::card::Card as Card;
mod ctl_int;
pub mod ctl {
//! Control device API
pub use super::ctl_int::{Ctl, CardInfo, DeviceIter, ElemIface, ElemId, ElemType, ElemValue, ElemInfo};
}
pub use crate::ctl::Ctl as Ctl;
pub mod hctl;
pub use crate::hctl::HCtl as HCtl;
pub mod pcm;
pub use crate::pcm::PCM as PCM;
pub mod rawmidi;
pub use crate::rawmidi::Rawmidi as Rawmidi;
pub mod device_name;
pub mod poll;
pub use crate::poll::Descriptors as PollDescriptors;
pub mod mixer;
pub use crate::mixer::Mixer as Mixer;
pub mod seq;
pub use crate::seq::Seq as Seq;
mod io;
pub use crate::io::Output;
// Reexported inside PCM module
mod chmap;
pub mod direct;

665
vendor/alsa/src/mixer.rs vendored Normal file
View File

@@ -0,0 +1,665 @@
//! Mixer API - Simple Mixer API for mixer control
//!
use std::ffi::{CStr, CString};
use std::{ptr, mem, fmt, ops};
use libc::{c_long, c_int, c_uint, c_short, pollfd};
use crate::poll;
use crate::alsa;
use super::Round;
use super::error::*;
const SELEM_ID_SIZE: usize = 64;
/// wraps [snd_mixer_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___mixer.html)
#[derive(Debug)]
pub struct Mixer(*mut alsa::snd_mixer_t);
unsafe impl Send for Mixer {}
impl Mixer {
/// Opens a mixer and attaches it to a card identified by its name (like hw:0) and loads the
/// mixer after registering a Selem.
pub fn new(name: &str, nonblock: bool) -> Result<Mixer> {
let mut mixer = Mixer::open(nonblock)?;
mixer.attach(&CString::new(name).unwrap())?;
Selem::register(&mut mixer)?;
mixer.load()?;
Ok(mixer)
}
/// Creates a Selem by looking for a specific selem by name given a mixer (of a card)
pub fn find_selem(&self, id: &SelemId) -> Option<Selem> {
let selem = unsafe { alsa::snd_mixer_find_selem(self.0, id.as_ptr()) };
if selem.is_null() { None }
else { Some(Selem(Elem {handle: selem, _mixer: self})) }
}
pub fn open(nonblock: bool) -> Result<Mixer> {
let mut r = ptr::null_mut();
let flags = if nonblock { 1 } else { 0 }; // FIXME: alsa::SND_CTL_NONBLOCK does not exist in alsa-sys
acheck!(snd_mixer_open(&mut r, flags)).map(|_| Mixer(r))
}
pub fn attach(&mut self, name: &CStr) -> Result<()> {
acheck!(snd_mixer_attach(self.0, name.as_ptr())).map(|_| ())
}
pub fn load(&mut self) -> Result<()> {
acheck!(snd_mixer_load(self.0)).map(|_| ())
}
pub fn iter(&self) -> Iter {
Iter {
last_handle: ptr::null_mut(),
mixer: self
}
}
pub fn handle_events(&self) -> Result<u32> {
acheck!(snd_mixer_handle_events(self.0)).map(|x| x as u32)
}
pub fn wait(&self, timeout_ms: Option<u32>) -> Result<()> {
acheck!(snd_mixer_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|_| ()) }
}
/// Closes mixer and frees used resources
impl Drop for Mixer {
fn drop(&mut self) {
unsafe { alsa::snd_mixer_close(self.0) };
}
}
impl poll::Descriptors for Mixer {
fn count(&self) -> usize {
unsafe { alsa::snd_mixer_poll_descriptors_count(self.0) as usize }
}
fn fill(&self, p: &mut [pollfd]) -> Result<usize> {
let z = unsafe { alsa::snd_mixer_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) };
from_code("snd_mixer_poll_descriptors", z).map(|_| z as usize)
}
fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> {
let mut r = 0;
let z = unsafe { alsa::snd_mixer_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) };
from_code("snd_mixer_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short))
}
}
/// Wrapper for a mB (millibel) value.
///
/// Despite some ALSA functions named "dB", they actually take mB values instead.
/// This is a wrapper type to help with those calculations. Its interior is the
/// actual mB value.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MilliBel(pub i64);
impl MilliBel {
pub fn to_db(self) -> f32 { (self.0 as f32) / 100.0 }
pub fn from_db(db: f32) -> Self { MilliBel((db * 100.0) as i64) }
}
impl ops::Deref for MilliBel {
type Target = i64;
fn deref(&self) -> &i64 { &self.0 }
}
impl ops::Add for MilliBel {
type Output = MilliBel;
fn add(self, rhs: Self) -> Self { MilliBel(self.0 + rhs.0) }
}
impl ops::AddAssign for MilliBel {
fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 }
}
impl ops::Sub for MilliBel {
type Output = MilliBel;
fn sub(self, rhs: Self) -> Self { MilliBel(self.0 - rhs.0) }
}
impl ops::SubAssign for MilliBel {
fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0 }
}
/// Wraps [snd_mixer_elem_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___mixer.html)
#[derive(Copy, Clone, Debug)]
pub struct Elem<'a>{
handle: *mut alsa::snd_mixer_elem_t,
_mixer: &'a Mixer
}
/// Iterator for all elements of mixer
#[derive(Copy, Clone)]
pub struct Iter<'a>{
last_handle: *mut alsa::snd_mixer_elem_t,
mixer: &'a Mixer
}
impl<'a> Iterator for Iter<'a> {
type Item = Elem<'a>;
fn next(&mut self) -> Option<Elem<'a>> {
let elem = if self.last_handle.is_null() {
unsafe { alsa::snd_mixer_first_elem(self.mixer.0) }
} else {
unsafe { alsa::snd_mixer_elem_next(self.last_handle) }
};
if elem.is_null() {
None
} else {
self.last_handle = elem;
Some(Elem { handle: elem, _mixer: self.mixer})
}
}
}
/// Wrapper for [snd_mixer_selem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___simple_mixer.html)
/// No allocation (uses fixed array)
// #[derive(Copy, Clone, Debug)]
pub struct SelemId([u8; SELEM_ID_SIZE]);
impl SelemId {
pub fn new(name: &str, index: u32) -> SelemId {
let mut s = SelemId::empty();
s.set_name(&CString::new(name).unwrap());
s.set_index(index);
s
}
/// Returns an empty (zeroed) SelemId. This id is not a usable id and need to be initialized
/// like `SelemId::new()` does
pub fn empty() -> SelemId {
assert!(unsafe { alsa::snd_mixer_selem_id_sizeof() } as usize <= SELEM_ID_SIZE);
// Create empty selem_id and fill from mixer
SelemId(unsafe { mem::zeroed() })
}
/// Convert SelemId into ``*mut snd_mixer_selem_id_t` that the alsa call needs.
/// See [snd_mixer_selem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___simple_mixer.html)
#[inline]
fn as_ptr(&self) -> *mut alsa::snd_mixer_selem_id_t {
self.0.as_ptr() as *const _ as *mut alsa::snd_mixer_selem_id_t
}
pub fn get_name(&self) -> Result<&str> {
let c = unsafe { alsa::snd_mixer_selem_id_get_name(self.as_ptr()) };
from_const("snd_mixer_selem_id_get_name", c)
}
pub fn get_index(&self) -> u32 {
unsafe { alsa::snd_mixer_selem_id_get_index(self.as_ptr()) }
}
pub fn set_name(&mut self, name: &CStr) {
unsafe { alsa::snd_mixer_selem_id_set_name(self.as_ptr(), name.as_ptr()) };
}
pub fn set_index(&mut self, index: u32) {
unsafe { alsa::snd_mixer_selem_id_set_index(self.as_ptr(), index) };
}
}
/// Wraps an Elem as a Selem
// #[derive(Copy, Clone)]
pub struct Selem<'a>(Elem<'a>);
impl<'a> Selem<'a> {
/// Creates a Selem by wrapping `elem`.
pub fn new(elem: Elem<'a>) -> Option<Selem<'a>> {
if unsafe { alsa::snd_mixer_elem_get_type(elem.handle) } == alsa::SND_MIXER_ELEM_SIMPLE
{ Some(Selem(elem)) } else { None }
}
/// TODO: This function might change to support regopt and to return the mixer class
pub fn register(mixer: &mut Mixer) -> Result<()> {
acheck!(snd_mixer_selem_register(mixer.0, ptr::null_mut(), ptr::null_mut())).map(|_| ())
}
pub fn get_id(&self) -> SelemId {
let id = SelemId::empty();
unsafe { alsa::snd_mixer_selem_get_id(self.handle, id.as_ptr()) };
id
}
pub fn has_capture_volume(&self) -> bool {
unsafe { alsa::snd_mixer_selem_has_capture_volume(self.handle) > 0 }
}
pub fn has_capture_switch(&self) -> bool {
unsafe { alsa::snd_mixer_selem_has_capture_switch(self.handle) > 0 }
}
pub fn has_playback_volume(&self) -> bool {
unsafe { alsa::snd_mixer_selem_has_playback_volume(self.handle) > 0 }
}
pub fn has_playback_switch(&self) -> bool {
unsafe { alsa::snd_mixer_selem_has_playback_switch(self.handle) > 0 }
}
pub fn can_capture(&self) -> bool {
self.has_capture_volume() || self.has_capture_switch()
}
pub fn can_playback(&self) -> bool {
self.has_playback_volume() || self.has_playback_switch()
}
pub fn has_volume(&self) -> bool {
self.has_capture_volume() || self.has_playback_volume()
}
/// returns range for capture volume as (min, max) values
pub fn get_capture_volume_range(&self) -> (i64, i64) {
let mut min: c_long = 0;
let mut max: c_long = 0;
unsafe { alsa::snd_mixer_selem_get_capture_volume_range(self.handle, &mut min, &mut max) };
(min as i64, max as i64)
}
/// returns (min, max) values.
pub fn get_capture_db_range(&self) -> (MilliBel, MilliBel) {
let mut min: c_long = 0;
let mut max: c_long = 0;
unsafe { alsa::snd_mixer_selem_get_capture_dB_range(self.handle, &mut min, &mut max) };
(MilliBel(min as i64), MilliBel(max as i64))
}
/// returns (min, max) values.
pub fn get_playback_volume_range(&self) -> (i64, i64) {
let mut min: c_long = 0;
let mut max: c_long = 0;
unsafe { alsa::snd_mixer_selem_get_playback_volume_range(self.handle, &mut min, &mut max) };
(min as i64, max as i64)
}
/// returns (min, max) values.
pub fn get_playback_db_range(&self) -> (MilliBel, MilliBel) {
let mut min: c_long = 0;
let mut max: c_long = 0;
unsafe { alsa::snd_mixer_selem_get_playback_dB_range(self.handle, &mut min, &mut max) };
(MilliBel(min as i64), MilliBel(max as i64))
}
pub fn is_capture_mono(&self) -> bool {
unsafe { alsa::snd_mixer_selem_is_capture_mono(self.handle) == 1 }
}
pub fn is_playback_mono(&self) -> bool {
unsafe { alsa::snd_mixer_selem_is_playback_mono(self.handle) == 1 }
}
pub fn has_capture_channel(&self, channel: SelemChannelId) -> bool {
unsafe { alsa::snd_mixer_selem_has_capture_channel(self.handle, channel as i32) > 0 }
}
pub fn has_playback_channel(&self, channel: SelemChannelId) -> bool {
unsafe { alsa::snd_mixer_selem_has_playback_channel(self.handle, channel as i32) > 0 }
}
/// Gets name from snd_mixer_selem_channel_name
pub fn channel_name(channel: SelemChannelId) -> Result<&'static str> {
let c = unsafe { alsa::snd_mixer_selem_channel_name(channel as i32) };
from_const("snd_mixer_selem_channel_name", c)
}
pub fn get_playback_volume(&self, channel: SelemChannelId) -> Result<i64> {
let mut value: c_long = 0;
acheck!(snd_mixer_selem_get_playback_volume(self.handle, channel as i32, &mut value)).and_then(|_| Ok(value as i64))
}
/// returns volume in millibels.
pub fn get_playback_vol_db(&self, channel: SelemChannelId) -> Result<MilliBel> {
self.get_playback_volume(channel)
.and_then(|volume| self.ask_playback_vol_db(volume))
}
/// Asks alsa to convert playback volume to millibels.
pub fn ask_playback_vol_db(&self, volume: i64) -> Result<MilliBel> {
let mut decibel_value: c_long = 0;
acheck!(snd_mixer_selem_ask_playback_vol_dB(self.handle, volume as c_long, &mut decibel_value))
.map(|_| MilliBel(decibel_value as i64))
}
// Asks alsa to convert millibels to playback volume.
pub fn ask_playback_db_vol(&self, db: MilliBel, dir: Round) -> Result<i64> {
let mut raw_volume: c_long = 0;
acheck!(snd_mixer_selem_ask_playback_dB_vol(self.handle, db.0 as c_long, dir as c_int, &mut raw_volume))
.map(|_| raw_volume as i64)
}
pub fn get_capture_volume(&self, channel: SelemChannelId) -> Result<i64> {
let mut value: c_long = 0;
acheck!(snd_mixer_selem_get_capture_volume(self.handle, channel as i32, &mut value)).map(|_| value as i64)
}
/// returns volume in millibels.
pub fn get_capture_vol_db(&self, channel: SelemChannelId) -> Result<MilliBel> {
self.get_capture_volume(channel)
.and_then(|volume| self.ask_capture_vol_db(volume))
}
/// Asks alsa to convert capture volume to millibels
pub fn ask_capture_vol_db(&self, volume: i64) -> Result<MilliBel> {
let mut decibel_value: c_long = 0;
acheck!(snd_mixer_selem_ask_capture_vol_dB (self.handle, volume as c_long, &mut decibel_value))
.map(|_| MilliBel(decibel_value as i64))
}
// Asks alsa to convert millibels to capture volume.
pub fn ask_capture_db_vol(&self, db: MilliBel, dir: Round) -> Result<i64> {
let mut raw_volume: c_long = 0;
acheck!(snd_mixer_selem_ask_capture_dB_vol(self.handle, db.0 as c_long, dir as c_int, &mut raw_volume))
.map(|_| raw_volume as i64)
}
pub fn set_playback_volume(&self, channel: SelemChannelId, value: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_volume(self.handle, channel as i32, value as c_long)).map(|_| ())
}
pub fn set_playback_volume_range(&self, min: i64, max: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_volume_range(self.handle, min as c_long, max as c_long)).map(|_| ())
}
pub fn set_playback_volume_all(&self, value: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_volume_all(self.handle, value as c_long)).map(|_| ())
}
pub fn set_playback_db(&self, channel: SelemChannelId, value: MilliBel, dir: Round) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_dB(self.handle, channel as i32, *value as c_long, dir as c_int)).map(|_| ())
}
pub fn set_capture_db(&self, channel: SelemChannelId, value: MilliBel, dir: Round) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_dB(self.handle, channel as i32, *value as c_long, dir as c_int)).map(|_| ())
}
pub fn set_playback_db_all(&self, value: MilliBel, dir: Round) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_dB_all(self.handle, *value as c_long, dir as c_int)).map(|_| ())
}
pub fn set_capture_db_all(&self, value: MilliBel, dir: Round) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_dB_all(self.handle, *value as c_long, dir as c_int)).map(|_| ())
}
pub fn set_capture_volume(&self, channel: SelemChannelId, value: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_volume(self.handle, channel as i32, value as c_long)).map(|_| ())
}
pub fn set_capture_volume_range(&self, min: i64, max: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_volume_range(self.handle, min as c_long, max as c_long)).map(|_| ())
}
pub fn set_capture_volume_all(&self, value: i64) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_volume_all(self.handle, value as c_long)).map(|_| ())
}
pub fn set_playback_switch(&self, channel: SelemChannelId, value: i32) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_switch(self.handle, channel as i32, value)).map(|_| ())
}
pub fn set_playback_switch_all(&self, value: i32) -> Result<()> {
acheck!(snd_mixer_selem_set_playback_switch_all(self.handle, value)).map(|_| ())
}
pub fn set_capture_switch(&self, channel: SelemChannelId, value: i32) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_switch(self.handle, channel as i32, value)).map(|_| ())
}
pub fn set_capture_switch_all(&self, value: i32) -> Result<()> {
acheck!(snd_mixer_selem_set_capture_switch_all(self.handle, value)).map(|_| ())
}
pub fn get_playback_switch(&self, channel: SelemChannelId) -> Result<i32> {
let mut value: i32 = 0;
acheck!(snd_mixer_selem_get_playback_switch(self.handle, channel as i32, &mut value)).map(|_| value)
}
pub fn get_capture_switch(&self, channel: SelemChannelId) -> Result<i32> {
let mut value: i32 = 0;
acheck!(snd_mixer_selem_get_capture_switch(self.handle, channel as i32, &mut value)).map(|_| value)
}
pub fn is_enumerated(&self) -> bool {
unsafe { alsa::snd_mixer_selem_is_enumerated(self.handle) == 1 }
}
pub fn is_enum_playback(&self) -> bool {
unsafe { alsa::snd_mixer_selem_is_enum_playback(self.handle) == 1 }
}
pub fn is_enum_capture(&self) -> bool {
unsafe { alsa::snd_mixer_selem_is_enum_capture(self.handle) == 1 }
}
pub fn get_enum_items(&self) -> Result<u32> {
acheck!(snd_mixer_selem_get_enum_items(self.handle)).map(|v| v as u32)
}
pub fn get_enum_item_name(&self, idx: u32) -> Result<String> {
let mut temp = [0 as ::libc::c_char; 128];
acheck!(snd_mixer_selem_get_enum_item_name(self.handle, idx, temp.len()-1, temp.as_mut_ptr()))
.and_then(|_| from_const("snd_mixer_selem_get_enum_item_name", temp.as_ptr()))
.map(|v| v.into())
}
/// Enumerates over valid Enum values
pub fn iter_enum(&self) -> Result<IterEnum> {
Ok(IterEnum(self, 0, self.get_enum_items()?))
}
pub fn get_enum_item(&self, channel: SelemChannelId) -> Result<u32> {
let mut temp = 0;
acheck!(snd_mixer_selem_get_enum_item(self.handle, channel as i32, &mut temp))
.map(|_| temp)
}
pub fn set_enum_item(&self, channel: SelemChannelId, idx: u32) -> Result<()> {
acheck!(snd_mixer_selem_set_enum_item(self.handle, channel as i32, idx))
.map(|_| ())
}
}
impl<'a> ops::Deref for Selem<'a> {
type Target = Elem<'a>;
/// returns the elem of this selem
fn deref(&self) -> &Elem<'a> {
&self.0
}
}
pub struct IterEnum<'a>(&'a Selem<'a>, u32, u32);
impl<'a> Iterator for IterEnum<'a> {
type Item = Result<String>;
fn next(&mut self) -> Option<Self::Item> {
if self.1 >= self.2 { None }
else { self.1 += 1; Some(self.0.get_enum_item_name(self.1-1)) }
}
}
alsa_enum!(
/// Wrapper for [SND_MIXER_SCHN_*](http://www.alsa-project.org/alsa-doc/alsa-lib/group___simple_mixer.html) constants
SelemChannelId, ALL_SELEM_CHANNEL_ID[11],
Unknown = SND_MIXER_SCHN_UNKNOWN,
FrontLeft = SND_MIXER_SCHN_FRONT_LEFT,
FrontRight = SND_MIXER_SCHN_FRONT_RIGHT,
RearLeft = SND_MIXER_SCHN_REAR_LEFT,
RearRight = SND_MIXER_SCHN_REAR_RIGHT,
FrontCenter = SND_MIXER_SCHN_FRONT_CENTER,
Woofer = SND_MIXER_SCHN_WOOFER,
SideLeft = SND_MIXER_SCHN_SIDE_LEFT,
SideRight = SND_MIXER_SCHN_SIDE_RIGHT,
RearCenter = SND_MIXER_SCHN_REAR_CENTER,
Last = SND_MIXER_SCHN_LAST,
);
impl SelemChannelId {
pub fn mono() -> SelemChannelId { SelemChannelId::FrontLeft }
}
impl fmt::Display for SelemChannelId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", Selem::channel_name(*self).unwrap())
}
}
#[test]
fn print_mixer_of_cards() {
use super::card;
for card in card::Iter::new().map(|c| c.unwrap()) {
println!("Card #{}: {} ({})", card.get_index(), card.get_name().unwrap(), card.get_longname().unwrap());
let mixer = Mixer::new(&format!("hw:{}", card.get_index()), false).unwrap();
for selem in mixer.iter().filter_map(|e| Selem::new(e)) {
let sid = selem.get_id();
println!("\tMixer element {},{}:", sid.get_name().unwrap(), sid.get_index());
if selem.has_volume() {
print!("\t Volume limits: ");
if selem.has_capture_volume() {
let (vmin, vmax) = selem.get_capture_volume_range();
let (mbmin, mbmax) = selem.get_capture_db_range();
print!("Capture = {} - {}", vmin, vmax);
print!(" ({} dB - {} dB)", mbmin.to_db(), mbmax.to_db());
}
if selem.has_playback_volume() {
let (vmin, vmax) = selem.get_playback_volume_range();
let (mbmin, mbmax) = selem.get_playback_db_range();
print!("Playback = {} - {}", vmin, vmax);
print!(" ({} dB - {} dB)", mbmin.to_db(), mbmax.to_db());
}
println!();
}
if selem.is_enumerated() {
print!("\t Valid values: ");
for v in selem.iter_enum().unwrap() { print!("{}, ", v.unwrap()) };
print!("\n\t Current values: ");
for v in SelemChannelId::all().iter().filter_map(|&v| selem.get_enum_item(v).ok()) {
print!("{}, ", selem.get_enum_item_name(v).unwrap());
}
println!();
}
if selem.can_capture() {
print!("\t Capture channels: ");
if selem.is_capture_mono() {
print!("Mono");
} else {
for channel in SelemChannelId::all() {
if selem.has_capture_channel(*channel) { print!("{}, ", channel) };
}
}
println!();
print!("\t Capture volumes: ");
for channel in SelemChannelId::all() {
if selem.has_capture_channel(*channel) { print!("{}: {} ({} dB), ", channel,
match selem.get_capture_volume(*channel) {Ok(v) => format!("{}", v), Err(_) => "n/a".to_string()},
match selem.get_capture_vol_db(*channel) {Ok(v) => format!("{}", v.to_db()), Err(_) => "n/a".to_string()}
);}
}
println!();
}
if selem.can_playback() {
print!("\t Playback channels: ");
if selem.is_playback_mono() {
print!("Mono");
} else {
for channel in SelemChannelId::all() {
if selem.has_playback_channel(*channel) { print!("{}, ", channel) };
}
}
println!();
if selem.has_playback_volume() {
print!("\t Playback volumes: ");
for channel in SelemChannelId::all() {
if selem.has_playback_channel(*channel) { print!("{}: {} / {}dB, ",
channel,
match selem.get_playback_volume(*channel) {Ok(v) => format!("{}", v), Err(_) => "n/a".to_string()},
match selem.get_playback_vol_db(*channel) {Ok(v) => format!("{}", v.to_db()), Err(_) => "n/a".to_string()}
);}
}
println!();
}
}
}
}
}
#[test]
#[ignore]
fn get_and_set_playback_volume() {
let mixer = Mixer::new("hw:1", false).unwrap();
let selem = mixer.find_selem(&SelemId::new("Master", 0)).unwrap();
let (rmin, rmax) = selem.get_playback_volume_range();
let mut channel = SelemChannelId::mono();
for c in SelemChannelId::all().iter() {
if selem.has_playback_channel(*c) { channel = *c; break }
}
println!("Testing on {} with limits {}-{} on channel {}", selem.get_id().get_name().unwrap(), rmin, rmax, channel);
let old: i64 = selem.get_playback_volume(channel).unwrap();
let new: i64 = rmax / 2;
assert_ne!(new, old);
println!("Changing volume of {} from {} to {}", channel, old, new);
selem.set_playback_volume(channel, new).unwrap();
let mut result: i64 = selem.get_playback_volume(channel).unwrap();
assert_eq!(new, result);
// return volume to old value
selem.set_playback_volume(channel, old).unwrap();
result = selem.get_playback_volume(channel).unwrap();
assert_eq!(old, result);
}
#[test]
#[ignore]
fn get_and_set_capture_volume() {
let mixer = Mixer::new("hw:1", false).unwrap();
let selem = mixer.find_selem(&SelemId::new("Capture", 0)).unwrap();
let (rmin, rmax) = selem.get_capture_volume_range();
let mut channel = SelemChannelId::mono();
for c in SelemChannelId::all().iter() {
if selem.has_playback_channel(*c) { channel = *c; break }
}
println!("Testing on {} with limits {}-{} on channel {}", selem.get_id().get_name().unwrap(), rmin, rmax, channel);
let old: i64 = selem.get_capture_volume(channel).unwrap();
let new: i64 = rmax / 2;
assert_ne!(new, old);
println!("Changing volume of {} from {} to {}", channel, old, new);
selem.set_capture_volume(channel, new).unwrap();
let mut result: i64 = selem.get_capture_volume(channel).unwrap();
assert_eq!(new, result);
// return volume to old value
selem.set_capture_volume(channel, old).unwrap();
result = selem.get_capture_volume(channel).unwrap();
assert_eq!(old, result);
}
#[test]
fn print_sizeof() {
let selemid = unsafe { alsa::snd_mixer_selem_id_sizeof() } as usize;
assert!(selemid <= SELEM_ID_SIZE);
println!("Selem id: {}", selemid);
}

1273
vendor/alsa/src/pcm.rs vendored Normal file

File diff suppressed because it is too large Load Diff

70
vendor/alsa/src/poll.rs vendored Normal file
View File

@@ -0,0 +1,70 @@
//! Tiny poll ffi
//!
//! A tiny wrapper around libc's poll system call.
use libc;
use super::error::*;
use std::io;
pub use libc::pollfd;
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Flags: ::libc::c_short {
const IN = ::libc::POLLIN;
const PRI = ::libc::POLLPRI;
const OUT = ::libc::POLLOUT;
const ERR = ::libc::POLLERR;
const HUP = ::libc::POLLHUP;
const NVAL = ::libc::POLLNVAL;
}
}
pub trait Descriptors {
fn count(&self) -> usize;
fn fill(&self, _: &mut [pollfd]) -> Result<usize>;
fn revents(&self, _: &[pollfd]) -> Result<Flags>;
/// Wrapper around count and fill - returns an array of pollfds
fn get(&self) -> Result<Vec<pollfd>> {
let mut v = vec![pollfd { fd: 0, events: 0, revents: 0 }; self.count()];
if self.fill(&mut v)? != v.len() { Err(Error::unsupported("did not fill the poll descriptors array")) }
else { Ok(v) }
}
}
impl Descriptors for pollfd {
fn count(&self) -> usize { 1 }
fn fill(&self, a: &mut [pollfd]) -> Result<usize> { a[0] = *self; Ok(1) }
fn revents(&self, a: &[pollfd]) -> Result<Flags> { Ok(Flags::from_bits_truncate(a[0].revents)) }
}
/// Wrapper around the libc poll call.
pub fn poll(fds: &mut[pollfd], timeout: i32) -> Result<usize> {
let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::nfds_t, timeout as libc::c_int) };
if r >= 0 { Ok(r as usize) } else {
from_code("poll", -io::Error::last_os_error().raw_os_error().unwrap()).map(|_| unreachable!())
}
}
/// Builds a pollfd array, polls it, and returns the poll descriptors which have non-zero revents.
pub fn poll_all<'a>(desc: &[&'a dyn Descriptors], timeout: i32) -> Result<Vec<(&'a dyn Descriptors, Flags)>> {
let mut pollfds: Vec<pollfd> = vec!();
let mut indices = vec!();
for v2 in desc.iter().map(|q| q.get()) {
let v = v2?;
indices.push(pollfds.len() .. pollfds.len()+v.len());
pollfds.extend(v);
};
poll(&mut pollfds, timeout)?;
let mut res = vec!();
for (i, r) in indices.into_iter().enumerate() {
let z = desc[i].revents(&pollfds[r])?;
if !z.is_empty() { res.push((desc[i], z)); }
}
Ok(res)
}

211
vendor/alsa/src/rawmidi.rs vendored Normal file
View File

@@ -0,0 +1,211 @@
//! MIDI devices I/O and enumeration
use libc::{c_int, c_uint, c_void, size_t, c_short, pollfd};
use super::ctl_int::{ctl_ptr, Ctl};
use super::{Direction, poll};
use super::error::*;
use crate::alsa;
use std::{ptr, io};
use std::ffi::{CStr, CString};
/// Iterator over [Rawmidi](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) devices and subdevices
pub struct Iter<'a> {
ctl: &'a Ctl,
device: c_int,
in_count: i32,
out_count: i32,
current: i32,
}
/// [snd_rawmidi_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) wrapper
pub struct Info(*mut alsa::snd_rawmidi_info_t);
impl Drop for Info {
fn drop(&mut self) { unsafe { alsa::snd_rawmidi_info_free(self.0) }; }
}
impl Info {
fn new() -> Result<Info> {
let mut p = ptr::null_mut();
acheck!(snd_rawmidi_info_malloc(&mut p)).map(|_| Info(p))
}
fn from_iter(c: &Ctl, device: i32, sub: i32, dir: Direction) -> Result<Info> {
let r = Info::new()?;
unsafe { alsa::snd_rawmidi_info_set_device(r.0, device as c_uint) };
let d = match dir {
Direction::Playback => alsa::SND_RAWMIDI_STREAM_OUTPUT,
Direction::Capture => alsa::SND_RAWMIDI_STREAM_INPUT,
};
unsafe { alsa::snd_rawmidi_info_set_stream(r.0, d) };
unsafe { alsa::snd_rawmidi_info_set_subdevice(r.0, sub as c_uint) };
acheck!(snd_ctl_rawmidi_info(ctl_ptr(c), r.0)).map(|_| r)
}
fn subdev_count(c: &Ctl, device: c_int) -> Result<(i32, i32)> {
let i = Info::from_iter(c, device, 0, Direction::Capture)?;
let o = Info::from_iter(c, device, 0, Direction::Playback)?;
Ok((unsafe { alsa::snd_rawmidi_info_get_subdevices_count(o.0) as i32 },
unsafe { alsa::snd_rawmidi_info_get_subdevices_count(i.0) as i32 }))
}
pub fn get_device(&self) -> i32 { unsafe { alsa::snd_rawmidi_info_get_device(self.0) as i32 }}
pub fn get_subdevice(&self) -> i32 { unsafe { alsa::snd_rawmidi_info_get_subdevice(self.0) as i32 }}
pub fn get_stream(&self) -> super::Direction {
if unsafe { alsa::snd_rawmidi_info_get_stream(self.0) } == alsa::SND_RAWMIDI_STREAM_OUTPUT { super::Direction::Playback }
else { super::Direction::Capture }
}
pub fn get_subdevice_name(&self) -> Result<String> {
let c = unsafe { alsa::snd_rawmidi_info_get_subdevice_name(self.0) };
from_const("snd_rawmidi_info_get_subdevice_name", c).map(|s| s.to_string())
}
pub fn get_id(&self) -> Result<String> {
let c = unsafe { alsa::snd_rawmidi_info_get_id(self.0) };
from_const("snd_rawmidi_info_get_id", c).map(|s| s.to_string())
}
}
/// [snd_rawmidi_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) wrapper
pub struct Status(*mut alsa::snd_rawmidi_status_t);
impl Status {
fn new() -> Result<Self> {
let mut p = ptr::null_mut();
acheck!(snd_rawmidi_status_malloc(&mut p)).map(|_| Status(p))
}
}
impl Status {
pub fn get_avail(&self) -> usize { unsafe { alsa::snd_rawmidi_status_get_avail(self.0 as *const _) } }
pub fn get_xruns(&self) -> usize { unsafe { alsa::snd_rawmidi_status_get_xruns(self.0 as *const _) } }
}
impl Drop for Status {
fn drop(&mut self) { unsafe { alsa::snd_rawmidi_status_free(self.0) }; }
}
impl<'a> Iter<'a> {
pub fn new(c: &'a Ctl) -> Iter<'a> { Iter { ctl: c, device: -1, in_count: 0, out_count: 0, current: 0 }}
}
impl<'a> Iterator for Iter<'a> {
type Item = Result<Info>;
fn next(&mut self) -> Option<Result<Info>> {
if self.current < self.in_count {
self.current += 1;
return Some(Info::from_iter(self.ctl, self.device, self.current-1, Direction::Capture));
}
if self.current - self.in_count < self.out_count {
self.current += 1;
return Some(Info::from_iter(self.ctl, self.device, self.current-1-self.in_count, Direction::Playback));
}
let r = acheck!(snd_ctl_rawmidi_next_device(ctl_ptr(self.ctl), &mut self.device));
match r {
Err(e) => return Some(Err(e)),
Ok(_) if self.device == -1 => return None,
_ => {},
}
self.current = 0;
match Info::subdev_count(self.ctl, self.device) {
Err(e) => Some(Err(e)),
Ok((oo, ii)) => {
self.in_count = ii;
self.out_count = oo;
self.next()
}
}
}
}
/// [snd_rawmidi_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) wrapper
pub struct Rawmidi(*mut alsa::snd_rawmidi_t);
unsafe impl Send for Rawmidi {}
impl Drop for Rawmidi {
fn drop(&mut self) { unsafe { alsa::snd_rawmidi_close(self.0) }; }
}
impl Rawmidi {
/// Wrapper around open that takes a &str instead of a &CStr
pub fn new(name: &str, dir: Direction, nonblock: bool) -> Result<Self> {
Self::open(&CString::new(name).unwrap(), dir, nonblock)
}
pub fn open(name: &CStr, dir: Direction, nonblock: bool) -> Result<Rawmidi> {
let mut h = ptr::null_mut();
let flags = if nonblock { 2 } else { 0 }; // FIXME: alsa::SND_RAWMIDI_NONBLOCK does not exist in alsa-sys
acheck!(snd_rawmidi_open(
if dir == Direction::Capture { &mut h } else { ptr::null_mut() },
if dir == Direction::Playback { &mut h } else { ptr::null_mut() },
name.as_ptr(), flags))
.map(|_| Rawmidi(h))
}
pub fn info(&self) -> Result<Info> {
Info::new().and_then(|i| acheck!(snd_rawmidi_info(self.0, i.0)).map(|_| i))
}
pub fn status(&self) -> Result<Status> {
Status::new().and_then(|i| acheck!(snd_rawmidi_status(self.0, i.0)).map(|_| i))
}
pub fn drop(&self) -> Result<()> { acheck!(snd_rawmidi_drop(self.0)).map(|_| ()) }
pub fn drain(&self) -> Result<()> { acheck!(snd_rawmidi_drain(self.0)).map(|_| ()) }
pub fn name(&self) -> Result<String> {
let c = unsafe { alsa::snd_rawmidi_name(self.0) };
from_const("snd_rawmidi_name", c).map(|s| s.to_string())
}
pub fn io(&self) -> IO { IO(self) }
}
impl poll::Descriptors for Rawmidi {
fn count(&self) -> usize {
unsafe { alsa::snd_rawmidi_poll_descriptors_count(self.0) as usize }
}
fn fill(&self, p: &mut [pollfd]) -> Result<usize> {
let z = unsafe { alsa::snd_rawmidi_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) };
from_code("snd_rawmidi_poll_descriptors", z).map(|_| z as usize)
}
fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> {
let mut r = 0;
let z = unsafe { alsa::snd_rawmidi_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) };
from_code("snd_rawmidi_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short))
}
}
/// Implements `std::io::Read` and `std::io::Write` for `Rawmidi`
pub struct IO<'a>(&'a Rawmidi);
impl<'a> io::Read for IO<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let r = unsafe { alsa::snd_rawmidi_read((self.0).0, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) };
if r < 0 { Err(io::Error::from_raw_os_error(r as i32)) }
else { Ok(r as usize) }
}
}
impl<'a> io::Write for IO<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let r = unsafe { alsa::snd_rawmidi_write((self.0).0, buf.as_ptr() as *const c_void, buf.len() as size_t) };
if r < 0 { Err(io::Error::from_raw_os_error(r as i32)) }
else { Ok(r as usize) }
}
fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
#[test]
fn print_rawmidis() {
for a in super::card::Iter::new().map(|a| a.unwrap()) {
for b in Iter::new(&Ctl::from_card(&a, false).unwrap()).map(|b| b.unwrap()) {
println!("Rawmidi {:?} (hw:{},{},{}) {} - {}", b.get_stream(), a.get_index(), b.get_device(), b.get_subdevice(),
a.get_name().unwrap(), b.get_subdevice_name().unwrap())
}
}
}

1578
vendor/alsa/src/seq.rs vendored Normal file

File diff suppressed because it is too large Load Diff