1 | //! Mixer API - Simple Mixer API for mixer control |
2 | //! |
3 | use std::ffi::{CStr, CString}; |
4 | use std::{ptr, mem, fmt, ops}; |
5 | use libc::{c_long, c_int, c_uint, c_short, pollfd}; |
6 | use crate::poll; |
7 | |
8 | use crate::alsa; |
9 | use super::Round; |
10 | use super::error::*; |
11 | |
12 | const 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)] |
16 | pub struct Mixer(*mut alsa::snd_mixer_t); |
17 | |
18 | unsafe impl Send for Mixer {} |
19 | |
20 | impl 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 |
69 | impl Drop for Mixer { |
70 | fn drop(&mut self) { |
71 | unsafe { alsa::snd_mixer_close(self.0) }; |
72 | } |
73 | } |
74 | |
75 | |
76 | impl 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)] |
98 | pub struct MilliBel(pub i64); |
99 | |
100 | impl 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 | |
105 | impl ops::Deref for MilliBel { |
106 | type Target = i64; |
107 | fn deref(&self) -> &i64 { &self.0 } |
108 | } |
109 | |
110 | impl ops::Add for MilliBel { |
111 | type Output = MilliBel; |
112 | fn add(self, rhs: Self) -> Self { MilliBel(self.0 + rhs.0) } |
113 | } |
114 | |
115 | impl ops::AddAssign for MilliBel { |
116 | fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 } |
117 | } |
118 | |
119 | impl ops::Sub for MilliBel { |
120 | type Output = MilliBel; |
121 | fn sub(self, rhs: Self) -> Self { MilliBel(self.0 - rhs.0) } |
122 | } |
123 | |
124 | impl 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)] |
130 | pub 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)] |
137 | pub struct Iter<'a>{ |
138 | last_handle: *mut alsa::snd_mixer_elem_t, |
139 | mixer: &'a Mixer |
140 | } |
141 | |
142 | impl<'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)] |
165 | pub struct SelemId([u8; SELEM_ID_SIZE]); |
166 | |
167 | impl 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)] |
212 | pub struct Selem<'a>(Elem<'a>); |
213 | |
214 | impl<'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 | |
470 | impl<'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 | |
479 | pub struct IterEnum<'a>(&'a Selem<'a>, u32, u32); |
480 | |
481 | impl<'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 | |
489 | alsa_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 | |
506 | impl SelemChannelId { |
507 | pub fn mono() -> SelemChannelId { SelemChannelId::FrontLeft } |
508 | } |
509 | |
510 | impl 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 ] |
517 | fn 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 ] |
604 | fn 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 ] |
632 | fn 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 ] |
660 | fn 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 | |