Skip to main content

alsa/
hctl.rs

1//! HCtl API - for mixer control and jack detection
2//!
3//! # Example
4//! Print all jacks and their status
5//!
6//! ```
7//! for a in ::alsa::card::Iter::new().map(|x| x.unwrap()) {
8//!     use std::ffi::CString;
9//!     use alsa::hctl::HCtl;
10//!     let h = HCtl::open(&CString::new(format!("hw:{}", a.get_index())).unwrap(), false).unwrap();
11//!     h.load().unwrap();
12//!     for b in h.elem_iter() {
13//!         use alsa::ctl::ElemIface;
14//!         let id = b.get_id().unwrap();
15//!         if id.get_interface() != ElemIface::Card { continue; }
16//!         let name = id.get_name().unwrap();
17//!         if !name.ends_with(" Jack") { continue; }
18//!         if name.ends_with(" Phantom Jack") {
19//!             println!("{} is always present", &name[..name.len()-13])
20//!         }
21//!         else { println!("{} is {}", &name[..name.len()-5],
22//!             if b.read().unwrap().get_boolean(0).unwrap() { "plugged in" } else { "unplugged" })
23//!         }
24//!     }
25//! }
26//! ```
27
28#![allow(dead_code)]
29/* There is a "field is never read" warning for the ElemIter struct. We still need to hold on
30   to the HCtl pointer somehow; to guarantee that the HCtl does not go out of scope while we use
31   ElemIter, which would make the snd_hctl_elem_t pointer invalid. Hence the decision to allow dead code here.
32   I suppose there is a better solution for this but I'm not sure how.
33*/
34
35
36use crate::{alsa, Card};
37use core::ffi::CStr;
38use ::alloc::ffi::CString;
39use super::error::*;
40use core::ptr;
41use super::{ctl_int, poll};
42use libc::{c_short, c_uint, c_int, pollfd};
43
44
45/// [snd_hctl_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___h_control.html) wrapper
46#[derive(Debug)]
47pub struct HCtl(*mut alsa::snd_hctl_t);
48
49unsafe impl Send for HCtl {}
50
51impl Drop for HCtl {
52    fn drop(&mut self) { unsafe { alsa::snd_hctl_close(self.0) }; }
53}
54
55impl HCtl {
56    /// Wrapper around open that takes a &str instead of a &CStr
57    pub fn new(c: &str, nonblock: bool) -> Result<HCtl> {
58        Self::open(&CString::new(c).unwrap(), nonblock)
59    }
60
61    /// Open does not support async mode (it's not very Rustic anyway)
62    /// Note: You probably want to call `load` afterwards.
63    pub fn open(c: &CStr, nonblock: bool) -> Result<HCtl> {
64        let mut r = ptr::null_mut();
65        let flags = if nonblock { 1 } else { 0 }; // FIXME: alsa::SND_CTL_NONBLOCK does not exist in alsa-sys
66        acheck!(snd_hctl_open(&mut r, c.as_ptr(), flags))
67            .map(|_| HCtl(r))
68    }
69
70    /// Wrapper around open. You probably want to call `load` afterwards.
71    pub fn from_card(c: &Card, nonblock: bool) -> Result<HCtl> {
72        let s = ::alloc::format!("hw:{}", c.get_index());
73        HCtl::new(&s, nonblock)
74    }
75
76    pub fn load(&self) -> Result<()> { acheck!(snd_hctl_load(self.0)).map(|_| ()) }
77
78    pub fn elem_iter(&self) -> ElemIter<'_> { ElemIter(self, ptr::null_mut()) }
79
80    pub fn find_elem(&self, id: &ctl_int::ElemId) -> Option<Elem<'_>> {
81        let p = unsafe { alsa::snd_hctl_find_elem(self.0, ctl_int::elem_id_ptr(id)) };
82        if p.is_null() { None } else { Some(Elem(self, p)) }
83    }
84
85    pub fn handle_events(&self) -> Result<u32> {
86        acheck!(snd_hctl_handle_events(self.0)).map(|x| x as u32)
87    }
88
89    pub fn wait(&self, timeout_ms: Option<u32>) -> Result<bool> {
90        acheck!(snd_hctl_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|i| i == 1) }
91}
92
93impl poll::Descriptors for HCtl {
94    fn count(&self) -> usize {
95        unsafe { alsa::snd_hctl_poll_descriptors_count(self.0) as usize }
96    }
97    fn fill(&self, p: &mut [pollfd]) -> Result<usize> {
98        let z = unsafe { alsa::snd_hctl_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) };
99        from_code("snd_hctl_poll_descriptors", z).map(|_| z as usize)
100    }
101    fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> {
102        let mut r = 0;
103        let z = unsafe { alsa::snd_hctl_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) };
104        from_code("snd_hctl_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short))
105    }
106}
107
108/// Iterates over elements for a `HCtl`
109#[derive(Debug)]
110pub struct ElemIter<'a>(&'a HCtl, *mut alsa::snd_hctl_elem_t);
111
112impl<'a> Iterator for ElemIter<'a> {
113    type Item = Elem<'a>;
114    fn next(&mut self) -> Option<Elem<'a>> {
115        self.1 = if self.1.is_null() { unsafe { alsa::snd_hctl_first_elem((self.0).0) }}
116            else { unsafe { alsa::snd_hctl_elem_next(self.1) }};
117        if self.1.is_null() { None }
118        else { Some(Elem(self.0, self.1)) }
119    }
120}
121
122
123/// [snd_hctl_elem_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___h_control.html) wrapper
124#[derive(Debug)]
125pub struct Elem<'a>(&'a HCtl, *mut alsa::snd_hctl_elem_t);
126
127impl<'a> Elem<'a> {
128    pub fn get_id(&self) -> Result<ctl_int::ElemId> {
129        let v = ctl_int::elem_id_new()?;
130        unsafe { alsa::snd_hctl_elem_get_id(self.1, ctl_int::elem_id_ptr(&v)) };
131        Ok(v)
132    }
133    pub fn info(&self) -> Result<ctl_int::ElemInfo> {
134        let v = ctl_int::elem_info_new()?;
135        acheck!(snd_hctl_elem_info(self.1, ctl_int::elem_info_ptr(&v))).map(|_| v)
136    }
137    pub fn read(&self) -> Result<ctl_int::ElemValue> {
138        let i = self.info()?;
139        let v = ctl_int::elem_value_new(i.get_type(), i.get_count())?;
140        acheck!(snd_hctl_elem_read(self.1, ctl_int::elem_value_ptr(&v))).map(|_| v)
141    }
142
143    pub fn write(&self, v: &ctl_int::ElemValue) -> Result<bool> {
144        acheck!(snd_hctl_elem_write(self.1, ctl_int::elem_value_ptr(v))).map(|e| e > 0)
145    }
146}
147
148#[test]
149fn print_hctls() {
150    extern crate std;
151    for a in super::card::Iter::new().map(|x| x.unwrap()) {
152        use ::alloc::ffi::CString;
153        let h = HCtl::open(&CString::new(::alloc::format!("hw:{}", a.get_index())).unwrap(), false).unwrap();
154        h.load().unwrap();
155        std::println!("Card {}:", a.get_name().unwrap());
156        for b in h.elem_iter() {
157            std::println!("  {:?} - {:?}", b.get_id().unwrap(), b.read().unwrap());
158        }
159    }
160}
161
162#[test]
163fn print_jacks() {
164    extern crate std;
165    for a in super::card::Iter::new().map(|x| x.unwrap()) {
166        use ::alloc::ffi::CString;
167        let h = HCtl::open(&CString::new(::alloc::format!("hw:{}", a.get_index())).unwrap(), false).unwrap();
168        h.load().unwrap();
169        for b in h.elem_iter() {
170            let id = b.get_id().unwrap();
171            if id.get_interface() != super::ctl_int::ElemIface::Card { continue; }
172            let name = id.get_name().unwrap();
173            if !name.ends_with(" Jack") { continue; }
174            if name.ends_with(" Phantom Jack") {
175                std::println!("{} is always present", &name[..name.len()-13])
176            }
177            else { std::println!("{} is {}", &name[..name.len()-5],
178                if b.read().unwrap().get_boolean(0).unwrap() { "plugged in" } else { "unplugged" })
179            }
180        }
181    }
182}