alsa/
mixer.rs

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