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: i32 = unsafe { alsa::snd_mixer_poll_descriptors(self.0, pfds:p.as_mut_ptr(), space: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: u16 = 0;
86 let z: i32 = unsafe { alsa::snd_mixer_poll_descriptors_revents(self.0, pfds:p.as_ptr() as *mut pollfd, nfds:p.len() as c_uint, &mut r) };
87 from_code("snd_mixer_poll_descriptors_revents", z).map(|_| poll::Flags::from_bits_truncate(bits: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: *mut _snd_mixer_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: usize = unsafe { alsa::snd_mixer_selem_id_sizeof() } as usize;
662
663 assert!(selemid <= SELEM_ID_SIZE);
664 println!("Selem id: {}", selemid);
665}
666