diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/alsa/src/ctl_int.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/alsa/src/ctl_int.rs')
-rw-r--r-- | third_party/rust/alsa/src/ctl_int.rs | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/third_party/rust/alsa/src/ctl_int.rs b/third_party/rust/alsa/src/ctl_int.rs new file mode 100644 index 0000000000..1be29dbdc3 --- /dev/null +++ b/third_party/rust/alsa/src/ctl_int.rs @@ -0,0 +1,443 @@ + +use crate::alsa; +use std::ffi::{CStr, CString}; +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_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) + } + + /// 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 }) + } +} + +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 { + + // 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); +} |