Skip to main content

alsa/
mixer.rs

1//! Mixer API - Simple Mixer API for mixer control
2//!
3use core::ffi::CStr;
4use ::alloc::ffi::CString;
5use ::alloc::string::String;
6use core::{ptr, mem, fmt, ops};
7use libc::{c_long, c_int, c_uint, c_short, pollfd};
8use crate::poll;
9
10use crate::alsa;
11use super::Round;
12use super::error::*;
13
14const SELEM_ID_SIZE: usize = 64;
15
16/// wraps [snd_mixer_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___mixer.html)
17#[derive(Debug)]
18pub struct Mixer(*mut alsa::snd_mixer_t);
19
20unsafe impl Send for Mixer {}
21
22impl Mixer {
23    /// Opens a mixer and attaches it to a card identified by its name (like hw:0) and loads the
24    /// mixer after registering a Selem.
25    pub fn new(name: &str, nonblock: bool) -> Result<Mixer> {
26        let mut mixer = Mixer::open(nonblock)?;
27        mixer.attach(&CString::new(name).unwrap())?;
28        Selem::register(&mut mixer)?;
29        mixer.load()?;
30        Ok(mixer)
31    }
32
33    /// Creates a Selem by looking for a specific selem by name given a mixer (of a card)
34    pub fn find_selem(&self, id: &SelemId) -> Option<Selem<'_>> {
35        let selem = unsafe { alsa::snd_mixer_find_selem(self.0, id.as_ptr()) };
36
37        if selem.is_null() { None }
38        else { Some(Selem(Elem {handle: selem, _mixer: self})) }
39    }
40
41    pub fn open(nonblock: bool) -> Result<Mixer> {
42        let mut r = ptr::null_mut();
43        let flags = if nonblock { 1 } else { 0 }; // FIXME: alsa::SND_CTL_NONBLOCK does not exist in alsa-sys
44        acheck!(snd_mixer_open(&mut r, flags)).map(|_| Mixer(r))
45    }
46
47    pub fn attach(&mut self, name: &CStr) -> Result<()> {
48        acheck!(snd_mixer_attach(self.0, name.as_ptr())).map(|_| ())
49    }
50
51    pub fn load(&mut self) -> Result<()> {
52        acheck!(snd_mixer_load(self.0)).map(|_| ())
53    }
54
55    pub fn iter(&self) -> Iter<'_> {
56        Iter {
57            last_handle: ptr::null_mut(),
58            mixer: self
59        }
60    }
61
62    pub fn handle_events(&self) -> Result<u32> {
63        acheck!(snd_mixer_handle_events(self.0)).map(|x| x as u32)
64    }
65
66    pub fn wait(&self, timeout_ms: Option<u32>) -> Result<()> {
67        acheck!(snd_mixer_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|_| ()) }
68}
69
70/// Closes mixer and frees used resources
71impl Drop for Mixer {
72    fn drop(&mut self) {
73        unsafe { alsa::snd_mixer_close(self.0) };
74    }
75}
76
77
78impl poll::Descriptors for Mixer {
79    fn count(&self) -> usize {
80        unsafe { alsa::snd_mixer_poll_descriptors_count(self.0) as usize }
81    }
82    fn fill(&self, p: &mut [pollfd]) -> Result<usize> {
83        let z = unsafe { alsa::snd_mixer_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) };
84        from_code("snd_mixer_poll_descriptors", z).map(|_| z as usize)
85    }
86    fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> {
87        let mut r = 0;
88        let z = unsafe { alsa::snd_mixer_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) };
89        from_code("snd_mixer_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(r as c_short))
90    }
91}
92
93
94/// Wrapper for a mB (millibel) value.
95///
96/// Despite some ALSA functions named "dB", they actually take mB values instead.
97/// This is a wrapper type to help with those calculations. Its interior is the
98/// actual mB value.
99#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
100pub struct MilliBel(pub i64);
101
102impl MilliBel {
103    pub fn to_db(self) -> f32 { (self.0 as f32) / 100.0 }
104    pub fn from_db(db: f32) -> Self { MilliBel((db * 100.0) as i64) }
105}
106
107impl ops::Deref for MilliBel {
108    type Target = i64;
109    fn deref(&self) -> &i64 { &self.0 }
110}
111
112impl ops::Add for MilliBel {
113    type Output = MilliBel;
114    fn add(self, rhs: Self) -> Self { MilliBel(self.0 + rhs.0) }
115}
116
117impl ops::AddAssign for MilliBel {
118    fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 }
119}
120
121impl ops::Sub for MilliBel {
122    type Output = MilliBel;
123    fn sub(self, rhs: Self) -> Self { MilliBel(self.0 - rhs.0) }
124}
125
126impl ops::SubAssign for MilliBel {
127    fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0 }
128}
129
130/// Wraps [snd_mixer_elem_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___mixer.html)
131#[derive(Copy, Clone, Debug)]
132pub struct Elem<'a>{
133    handle: *mut alsa::snd_mixer_elem_t,
134    _mixer: &'a Mixer
135}
136
137/// Iterator for all elements of mixer
138#[derive(Copy, Clone, Debug)]
139pub struct Iter<'a>{
140    last_handle: *mut alsa::snd_mixer_elem_t,
141    mixer: &'a Mixer
142}
143
144impl<'a> Iterator for Iter<'a> {
145    type Item = Elem<'a>;
146
147    fn next(&mut self) -> Option<Elem<'a>> {
148        let elem = if self.last_handle.is_null() {
149            unsafe { alsa::snd_mixer_first_elem(self.mixer.0) }
150        } else {
151            unsafe { alsa::snd_mixer_elem_next(self.last_handle) }
152        };
153
154        if elem.is_null() {
155            None
156        } else {
157            self.last_handle = elem;
158            Some(Elem { handle: elem, _mixer: self.mixer})
159        }
160    }
161
162}
163
164/// Wrapper for [snd_mixer_selem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___simple_mixer.html)
165/// No allocation (uses fixed array)
166// #[derive(Copy, Clone)]
167#[derive(Debug)]
168pub struct SelemId([u8; SELEM_ID_SIZE]);
169
170impl SelemId {
171
172    pub fn new(name: &str, index: u32) -> SelemId {
173        let mut s = SelemId::empty();
174        s.set_name(&CString::new(name).unwrap());
175        s.set_index(index);
176        s
177    }
178
179    /// Returns an empty (zeroed) SelemId. This id is not a usable id and need to be initialized
180    /// like `SelemId::new()` does
181    pub fn empty() -> SelemId {
182        assert!(unsafe { alsa::snd_mixer_selem_id_sizeof() } as usize <= SELEM_ID_SIZE);
183        // Create empty selem_id and fill from mixer
184        SelemId(unsafe { mem::zeroed() })
185    }
186
187    /// Convert SelemId into ``*mut snd_mixer_selem_id_t` that the alsa call needs.
188    /// See [snd_mixer_selem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___simple_mixer.html)
189    #[inline]
190    fn as_ptr(&self) -> *mut alsa::snd_mixer_selem_id_t {
191        self.0.as_ptr() as *const _ as *mut alsa::snd_mixer_selem_id_t
192    }
193
194    pub fn get_name(&self) -> Result<&str> {
195        let c = unsafe { alsa::snd_mixer_selem_id_get_name(self.as_ptr()) };
196        from_const("snd_mixer_selem_id_get_name", c)
197    }
198
199    pub fn get_index(&self) -> u32 {
200        unsafe { alsa::snd_mixer_selem_id_get_index(self.as_ptr()) }
201    }
202
203    pub fn set_name(&mut self, name: &CStr) {
204        unsafe { alsa::snd_mixer_selem_id_set_name(self.as_ptr(), name.as_ptr()) };
205    }
206
207    pub fn set_index(&mut self, index: u32) {
208        unsafe { alsa::snd_mixer_selem_id_set_index(self.as_ptr(), index) };
209    }
210
211}
212
213/// Wraps an Elem as a Selem
214// #[derive(Copy, Clone)]
215#[derive(Debug)]
216pub struct Selem<'a>(Elem<'a>);
217
218impl<'a> Selem<'a> {
219    /// Creates a Selem by wrapping `elem`.
220    pub fn new(elem: Elem<'a>) -> Option<Selem<'a>> {
221        if unsafe { alsa::snd_mixer_elem_get_type(elem.handle) } == alsa::SND_MIXER_ELEM_SIMPLE
222           { Some(Selem(elem)) } else { None }
223    }
224
225    /// TODO: This function might change to support regopt and to return the mixer class
226    pub fn register(mixer: &mut Mixer) -> Result<()> {
227        acheck!(snd_mixer_selem_register(mixer.0, ptr::null_mut(), ptr::null_mut())).map(|_| ())
228    }
229
230    pub fn get_id(&self) -> SelemId {
231        let id = SelemId::empty();
232        unsafe { alsa::snd_mixer_selem_get_id(self.handle, id.as_ptr()) };
233        id
234    }
235
236    pub fn has_capture_volume(&self) -> bool {
237        unsafe { alsa::snd_mixer_selem_has_capture_volume(self.handle) > 0 }
238    }
239
240    pub fn has_capture_switch(&self) -> bool {
241        unsafe { alsa::snd_mixer_selem_has_capture_switch(self.handle) > 0 }
242    }
243
244    pub fn has_playback_volume(&self) -> bool {
245        unsafe { alsa::snd_mixer_selem_has_playback_volume(self.handle) > 0 }
246    }
247
248    pub fn has_playback_switch(&self) -> bool {
249        unsafe { alsa::snd_mixer_selem_has_playback_switch(self.handle) > 0 }
250    }
251
252    pub fn can_capture(&self) -> bool {
253        self.has_capture_volume() || self.has_capture_switch()
254    }
255
256    pub fn can_playback(&self) -> bool {
257        self.has_playback_volume() || self.has_playback_switch()
258    }
259
260    pub fn has_volume(&self) -> bool {
261        self.has_capture_volume() || self.has_playback_volume()
262    }
263
264    /// returns range for capture volume as (min, max) values
265    pub fn get_capture_volume_range(&self) -> (i64, i64) {
266        let mut min: c_long = 0;
267        let mut max: c_long = 0;
268        unsafe { alsa::snd_mixer_selem_get_capture_volume_range(self.handle, &mut min, &mut max) };
269        (min as i64, max as i64)
270    }
271
272    /// returns (min, max) values.
273    pub fn get_capture_db_range(&self) -> (MilliBel, MilliBel) {
274        let mut min: c_long = 0;
275        let mut max: c_long = 0;
276        unsafe { alsa::snd_mixer_selem_get_capture_dB_range(self.handle, &mut min, &mut max) };
277        (MilliBel(min as i64), MilliBel(max as i64))
278    }
279
280    /// returns (min, max) values.
281    pub fn get_playback_volume_range(&self) -> (i64, i64) {
282        let mut min: c_long = 0;
283        let mut max: c_long = 0;
284        unsafe { alsa::snd_mixer_selem_get_playback_volume_range(self.handle, &mut min, &mut max) };
285        (min as i64, max as i64)
286    }
287
288    /// returns (min, max) values.
289    pub fn get_playback_db_range(&self) -> (MilliBel, MilliBel) {
290        let mut min: c_long = 0;
291        let mut max: c_long = 0;
292        unsafe { alsa::snd_mixer_selem_get_playback_dB_range(self.handle, &mut min, &mut max) };
293        (MilliBel(min as i64), MilliBel(max as i64))
294    }
295
296    pub fn is_capture_mono(&self) -> bool {
297        unsafe { alsa::snd_mixer_selem_is_capture_mono(self.handle) == 1 }
298    }
299
300    pub fn is_playback_mono(&self) -> bool {
301        unsafe { alsa::snd_mixer_selem_is_playback_mono(self.handle) == 1 }
302    }
303
304    pub fn has_capture_channel(&self, channel: SelemChannelId) -> bool {
305        unsafe { alsa::snd_mixer_selem_has_capture_channel(self.handle, channel as i32) > 0 }
306    }
307
308    pub fn has_playback_channel(&self, channel: SelemChannelId) -> bool {
309        unsafe { alsa::snd_mixer_selem_has_playback_channel(self.handle, channel as i32) > 0 }
310    }
311
312    /// Gets name from snd_mixer_selem_channel_name
313    pub fn channel_name(channel: SelemChannelId) -> Result<&'static str> {
314        let c = unsafe { alsa::snd_mixer_selem_channel_name(channel as i32) };
315        from_const("snd_mixer_selem_channel_name", c)
316    }
317
318    pub fn get_playback_volume(&self, channel: SelemChannelId) -> Result<i64> {
319        let mut value: c_long = 0;
320        acheck!(snd_mixer_selem_get_playback_volume(self.handle, channel as i32, &mut value)).and_then(|_| Ok(value as i64))
321    }
322
323    /// returns volume in millibels.
324    pub fn get_playback_vol_db(&self, channel: SelemChannelId) -> Result<MilliBel> {
325        self.get_playback_volume(channel)
326            .and_then(|volume| self.ask_playback_vol_db(volume))
327    }
328
329    /// Asks alsa to convert playback volume to millibels.
330    pub fn ask_playback_vol_db(&self, volume: i64) -> Result<MilliBel> {
331        let mut decibel_value: c_long = 0;
332        acheck!(snd_mixer_selem_ask_playback_vol_dB(self.handle, volume as c_long, &mut decibel_value))
333            .map(|_| MilliBel(decibel_value as i64))
334    }
335
336    // Asks alsa to convert millibels to playback volume.
337    pub fn ask_playback_db_vol(&self, db: MilliBel, dir: Round) -> Result<i64> {
338        let mut raw_volume: c_long = 0;
339        acheck!(snd_mixer_selem_ask_playback_dB_vol(self.handle, db.0 as c_long, dir as c_int, &mut raw_volume))
340            .map(|_| raw_volume as i64)
341    }
342
343    pub fn get_capture_volume(&self, channel: SelemChannelId) -> Result<i64> {
344        let mut value: c_long = 0;
345        acheck!(snd_mixer_selem_get_capture_volume(self.handle, channel as i32, &mut value)).map(|_| value as i64)
346    }
347
348    /// returns volume in millibels.
349    pub fn get_capture_vol_db(&self, channel: SelemChannelId) -> Result<MilliBel> {
350        self.get_capture_volume(channel)
351            .and_then(|volume| self.ask_capture_vol_db(volume))
352    }
353
354    /// Asks alsa to convert capture volume to millibels
355    pub fn ask_capture_vol_db(&self, volume: i64) -> Result<MilliBel> {
356        let mut decibel_value: c_long = 0;
357        acheck!(snd_mixer_selem_ask_capture_vol_dB (self.handle, volume as c_long, &mut decibel_value))
358            .map(|_| MilliBel(decibel_value as i64))
359    }
360
361    // Asks alsa to convert millibels to capture volume.
362    pub fn ask_capture_db_vol(&self, db: MilliBel, dir: Round) -> Result<i64> {
363        let mut raw_volume: c_long = 0;
364        acheck!(snd_mixer_selem_ask_capture_dB_vol(self.handle, db.0 as c_long, dir as c_int, &mut raw_volume))
365            .map(|_| raw_volume as i64)
366    }
367
368    pub fn set_playback_volume(&self, channel: SelemChannelId, value: i64) -> Result<()> {
369        acheck!(snd_mixer_selem_set_playback_volume(self.handle, channel as i32, value as c_long)).map(|_| ())
370    }
371
372    pub fn set_playback_volume_range(&self, min: i64, max: i64) -> Result<()> {
373        acheck!(snd_mixer_selem_set_playback_volume_range(self.handle, min as c_long, max as c_long)).map(|_| ())
374    }
375
376    pub fn set_playback_volume_all(&self, value: i64) -> Result<()> {
377        acheck!(snd_mixer_selem_set_playback_volume_all(self.handle, value as c_long)).map(|_| ())
378    }
379
380    pub fn set_playback_db(&self, channel: SelemChannelId, value: MilliBel, dir: Round) -> Result<()> {
381        acheck!(snd_mixer_selem_set_playback_dB(self.handle, channel as i32, *value as c_long, dir as c_int)).map(|_| ())
382    }
383
384    pub fn set_capture_db(&self, channel: SelemChannelId, value: MilliBel, dir: Round) -> Result<()> {
385        acheck!(snd_mixer_selem_set_capture_dB(self.handle, channel as i32, *value as c_long, dir as c_int)).map(|_| ())
386    }
387
388    pub fn set_playback_db_all(&self, value: MilliBel, dir: Round) -> Result<()> {
389        acheck!(snd_mixer_selem_set_playback_dB_all(self.handle, *value as c_long, dir as c_int)).map(|_| ())
390    }
391
392    pub fn set_capture_db_all(&self, value: MilliBel, dir: Round) -> Result<()> {
393        acheck!(snd_mixer_selem_set_capture_dB_all(self.handle, *value as c_long, dir as c_int)).map(|_| ())
394    }
395
396    pub fn set_capture_volume(&self, channel: SelemChannelId, value: i64) -> Result<()> {
397        acheck!(snd_mixer_selem_set_capture_volume(self.handle, channel as i32, value as c_long)).map(|_| ())
398    }
399
400    pub fn set_capture_volume_range(&self, min: i64, max: i64) -> Result<()> {
401        acheck!(snd_mixer_selem_set_capture_volume_range(self.handle, min as c_long, max as c_long)).map(|_| ())
402    }
403
404    pub fn set_capture_volume_all(&self, value: i64) -> Result<()> {
405        acheck!(snd_mixer_selem_set_capture_volume_all(self.handle, value as c_long)).map(|_| ())
406    }
407
408    pub fn set_playback_switch(&self, channel: SelemChannelId, value: i32) -> Result<()> {
409        acheck!(snd_mixer_selem_set_playback_switch(self.handle, channel as i32, value)).map(|_| ())
410    }
411
412    pub fn set_playback_switch_all(&self, value: i32) -> Result<()> {
413        acheck!(snd_mixer_selem_set_playback_switch_all(self.handle, value)).map(|_| ())
414    }
415
416    pub fn set_capture_switch(&self, channel: SelemChannelId, value: i32) -> Result<()> {
417        acheck!(snd_mixer_selem_set_capture_switch(self.handle, channel as i32, value)).map(|_| ())
418    }
419
420    pub fn set_capture_switch_all(&self, value: i32) -> Result<()> {
421        acheck!(snd_mixer_selem_set_capture_switch_all(self.handle, value)).map(|_| ())
422    }
423
424    pub fn get_playback_switch(&self, channel: SelemChannelId) -> Result<i32> {
425        let mut value: i32 = 0;
426        acheck!(snd_mixer_selem_get_playback_switch(self.handle, channel as i32, &mut value)).map(|_| value)
427    }
428
429    pub fn get_capture_switch(&self, channel: SelemChannelId) -> Result<i32> {
430        let mut value: i32 = 0;
431        acheck!(snd_mixer_selem_get_capture_switch(self.handle, channel as i32, &mut value)).map(|_| value)
432    }
433
434    pub fn is_enumerated(&self) -> bool {
435        unsafe { alsa::snd_mixer_selem_is_enumerated(self.handle) == 1 }
436    }
437
438    pub fn is_enum_playback(&self) -> bool {
439        unsafe { alsa::snd_mixer_selem_is_enum_playback(self.handle) == 1 }
440    }
441
442    pub fn is_enum_capture(&self) -> bool {
443        unsafe { alsa::snd_mixer_selem_is_enum_capture(self.handle) == 1 }
444    }
445
446    pub fn get_enum_items(&self) -> Result<u32> {
447        acheck!(snd_mixer_selem_get_enum_items(self.handle)).map(|v| v as u32)
448    }
449
450    pub fn get_enum_item_name(&self, idx: u32) -> Result<String> {
451        let mut temp = [0 as ::libc::c_char; 128];
452        acheck!(snd_mixer_selem_get_enum_item_name(self.handle, idx, temp.len()-1, temp.as_mut_ptr()))
453            .and_then(|_| from_const("snd_mixer_selem_get_enum_item_name", temp.as_ptr()))
454            .map(|v| v.into())
455    }
456
457    /// Enumerates over valid Enum values
458    pub fn iter_enum(&self) -> Result<IterEnum<'_>> {
459        Ok(IterEnum(self, 0, self.get_enum_items()?))
460    }
461
462    pub fn get_enum_item(&self, channel: SelemChannelId) -> Result<u32> {
463        let mut temp = 0;
464        acheck!(snd_mixer_selem_get_enum_item(self.handle, channel as i32, &mut temp))
465            .map(|_| temp)
466    }
467
468    pub fn set_enum_item(&self, channel: SelemChannelId, idx: u32) -> Result<()> {
469        acheck!(snd_mixer_selem_set_enum_item(self.handle, channel as i32, idx))
470            .map(|_| ())
471    }
472}
473
474impl<'a> ops::Deref for Selem<'a> {
475    type Target = Elem<'a>;
476
477    /// returns the elem of this selem
478    fn deref(&self) -> &Elem<'a> {
479        &self.0
480    }
481}
482
483#[derive(Debug)]
484pub struct IterEnum<'a>(&'a Selem<'a>, u32, u32);
485
486impl<'a> Iterator for IterEnum<'a> {
487    type Item = Result<String>;
488    fn next(&mut self) -> Option<Self::Item> {
489        if self.1 >= self.2 { None }
490        else { self.1 += 1; Some(self.0.get_enum_item_name(self.1-1)) }
491    }
492}
493
494alsa_enum!(
495    /// Wrapper for [SND_MIXER_SCHN_*](http://www.alsa-project.org/alsa-doc/alsa-lib/group___simple_mixer.html) constants
496    SelemChannelId, ALL_SELEM_CHANNEL_ID[11],
497
498    Unknown     = SND_MIXER_SCHN_UNKNOWN,
499    FrontLeft   = SND_MIXER_SCHN_FRONT_LEFT,
500    FrontRight  = SND_MIXER_SCHN_FRONT_RIGHT,
501    RearLeft    = SND_MIXER_SCHN_REAR_LEFT,
502    RearRight   = SND_MIXER_SCHN_REAR_RIGHT,
503    FrontCenter = SND_MIXER_SCHN_FRONT_CENTER,
504    Woofer      = SND_MIXER_SCHN_WOOFER,
505    SideLeft    = SND_MIXER_SCHN_SIDE_LEFT,
506    SideRight   = SND_MIXER_SCHN_SIDE_RIGHT,
507    RearCenter  = SND_MIXER_SCHN_REAR_CENTER,
508    Last        = SND_MIXER_SCHN_LAST,
509);
510
511impl SelemChannelId {
512    pub fn mono() -> SelemChannelId { SelemChannelId::FrontLeft }
513}
514
515impl fmt::Display for SelemChannelId {
516    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
517        write!(f, "{}", Selem::channel_name(*self).unwrap())
518    }
519}
520
521#[test]
522fn print_mixer_of_cards() {
523    extern crate std;
524    use std::{println, print};
525    use ::alloc::format;
526    use ::alloc::string::ToString;
527    use super::card;
528
529    for card in card::Iter::new().map(|c| c.unwrap()) {
530        println!("Card #{}: {} ({})", card.get_index(), card.get_name().unwrap(), card.get_longname().unwrap());
531
532        let mixer = Mixer::new(&format!("hw:{}", card.get_index()), false).unwrap();
533        for selem in mixer.iter().filter_map(|e| Selem::new(e)) {
534
535            let sid = selem.get_id();
536            println!("\tMixer element {},{}:", sid.get_name().unwrap(), sid.get_index());
537
538            if selem.has_volume() {
539                print!("\t  Volume limits: ");
540                if selem.has_capture_volume() {
541                    let (vmin, vmax) = selem.get_capture_volume_range();
542                    let (mbmin, mbmax) = selem.get_capture_db_range();
543                    print!("Capture = {} - {}", vmin, vmax);
544                    print!(" ({} dB - {} dB)", mbmin.to_db(), mbmax.to_db());
545                }
546                if selem.has_playback_volume() {
547                    let (vmin, vmax) = selem.get_playback_volume_range();
548                    let (mbmin, mbmax) = selem.get_playback_db_range();
549                    print!("Playback = {} - {}", vmin, vmax);
550                    print!(" ({} dB - {} dB)", mbmin.to_db(), mbmax.to_db());
551                }
552                println!();
553            }
554
555            if selem.is_enumerated() {
556                print!("\t  Valid values: ");
557                for v in selem.iter_enum().unwrap() { print!("{}, ", v.unwrap()) };
558                print!("\n\t  Current values: ");
559                for v in SelemChannelId::all().iter().filter_map(|&v| selem.get_enum_item(v).ok()) {
560                    print!("{}, ", selem.get_enum_item_name(v).unwrap());
561                }
562                println!();
563            }
564
565            if selem.can_capture() {
566                print!("\t  Capture channels: ");
567                if selem.is_capture_mono() {
568                    print!("Mono");
569                } else {
570                    for channel in SelemChannelId::all() {
571                        if selem.has_capture_channel(*channel) { print!("{}, ", channel) };
572                    }
573                }
574                println!();
575                print!("\t  Capture volumes: ");
576                for channel in SelemChannelId::all() {
577                    if selem.has_capture_channel(*channel) { print!("{}: {} ({} dB), ", channel,
578                        match selem.get_capture_volume(*channel) {Ok(v) => format!("{}", v), Err(_) => "n/a".to_string()},
579                        match selem.get_capture_vol_db(*channel) {Ok(v) => format!("{}", v.to_db()), Err(_) => "n/a".to_string()}
580                    );}
581                }
582                println!();
583            }
584
585            if selem.can_playback() {
586                print!("\t  Playback channels: ");
587                if selem.is_playback_mono() {
588                    print!("Mono");
589                } else {
590                    for channel in SelemChannelId::all() {
591                        if selem.has_playback_channel(*channel) { print!("{}, ", channel) };
592                    }
593                }
594                println!();
595                if selem.has_playback_volume() {
596                    print!("\t  Playback volumes: ");
597                    for channel in SelemChannelId::all() {
598                        if selem.has_playback_channel(*channel) { print!("{}: {} / {}dB, ",
599                            channel,
600                            match selem.get_playback_volume(*channel) {Ok(v) => format!("{}", v), Err(_) => "n/a".to_string()},
601                            match selem.get_playback_vol_db(*channel) {Ok(v) => format!("{}", v.to_db()), Err(_) => "n/a".to_string()}
602                        );}
603                    }
604                    println!();
605                }
606            }
607        }
608    }
609}
610
611#[test]
612#[ignore]
613fn get_and_set_playback_volume() {
614    extern crate std;
615
616    let mixer = Mixer::new("hw:1", false).unwrap();
617    let selem = mixer.find_selem(&SelemId::new("Master", 0)).unwrap();
618
619    let (rmin, rmax) = selem.get_playback_volume_range();
620    let mut channel = SelemChannelId::mono();
621    for c in SelemChannelId::all().iter() {
622        if selem.has_playback_channel(*c) { channel = *c; break }
623    }
624    std::println!("Testing on {} with limits {}-{} on channel {}", selem.get_id().get_name().unwrap(), rmin, rmax, channel);
625
626    let old: i64 = selem.get_playback_volume(channel).unwrap();
627    let new: i64 = rmax / 2;
628    assert_ne!(new, old);
629
630    std::println!("Changing volume of {} from {} to {}", channel, old, new);
631    selem.set_playback_volume(channel, new).unwrap();
632    let mut result: i64 = selem.get_playback_volume(channel).unwrap();
633    assert_eq!(new, result);
634
635    // return volume to old value
636    selem.set_playback_volume(channel, old).unwrap();
637    result = selem.get_playback_volume(channel).unwrap();
638    assert_eq!(old, result);
639}
640
641#[test]
642#[ignore]
643fn get_and_set_capture_volume() {
644    extern crate std;
645
646    let mixer = Mixer::new("hw:1", false).unwrap();
647    let selem = mixer.find_selem(&SelemId::new("Capture", 0)).unwrap();
648
649    let (rmin, rmax) = selem.get_capture_volume_range();
650    let mut channel = SelemChannelId::mono();
651    for c in SelemChannelId::all().iter() {
652        if selem.has_playback_channel(*c) { channel = *c; break }
653    }
654    std::println!("Testing on {} with limits {}-{} on channel {}", selem.get_id().get_name().unwrap(), rmin, rmax, channel);
655
656    let old: i64 = selem.get_capture_volume(channel).unwrap();
657    let new: i64 = rmax / 2;
658    assert_ne!(new, old);
659
660    std::println!("Changing volume of {} from {} to {}", channel, old, new);
661    selem.set_capture_volume(channel, new).unwrap();
662    let mut result: i64 = selem.get_capture_volume(channel).unwrap();
663    assert_eq!(new, result);
664
665    // return volume to old value
666    selem.set_capture_volume(channel, old).unwrap();
667    result = selem.get_capture_volume(channel).unwrap();
668    assert_eq!(old, result);
669}
670
671
672#[test]
673fn print_sizeof() {
674    extern crate std;
675    let selemid = unsafe { alsa::snd_mixer_selem_id_sizeof() } as usize;
676
677    assert!(selemid <= SELEM_ID_SIZE);
678    std::println!("Selem id: {}", selemid);
679}