1 2 use crate::alsa; 3 use std::ffi::{CStr, CString}; 4 use super::error::*; 5 use super::mixer::MilliBel; 6 use super::Round; 7 use std::{ptr, mem, fmt, cmp}; 8 use crate::{Card, poll}; 9 use std::cell::UnsafeCell; 10 use libc::{c_uint, c_void, size_t, c_long, c_int, pollfd, c_short}; 11 12 /// We prefer not to allocate for every ElemId, ElemInfo or ElemValue. 13 /// But we don't know if these will increase in the future or on other platforms. 14 /// Unfortunately, Rust does not support alloca, so hard-code the sizes for now. 15 16 const ELEM_ID_SIZE: usize = 64; 17 // const ELEM_VALUE_SIZE: usize = 1224; 18 // const ELEM_INFO_SIZE: usize = 272; 19 20 /// [snd_ctl_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper 21 pub struct Ctl(*mut alsa::snd_ctl_t); 22 23 unsafe impl Send for Ctl {} 24 25 impl Ctl { 26 /// Wrapper around open that takes a &str instead of a &CStr 27 pub fn new(c: &str, nonblock: bool) -> Result<Self> { 28 Self::open(&CString::new(c).unwrap(), nonblock) 29 } 30 31 /// Open does not support async mode (it's not very Rustic anyway) 32 pub fn open(c: &CStr, nonblock: bool) -> Result<Ctl> { 33 let mut r = ptr::null_mut(); 34 let flags = if nonblock { 1 } else { 0 }; // FIXME: alsa::SND_CTL_NONBLOCK does not exist in alsa-sys 35 acheck!(snd_ctl_open(&mut r, c.as_ptr(), flags)).map(|_| Ctl(r)) 36 } 37 38 pub fn from_card(c: &Card, nonblock: bool) -> Result<Ctl> { 39 let s = format!("hw:{}", c.get_index()); 40 Ctl::open(&CString::new(s).unwrap(), nonblock) 41 } 42 43 pub fn card_info(&self) -> Result<CardInfo> { CardInfo::new().and_then(|c| 44 acheck!(snd_ctl_card_info(self.0, c.0)).map(|_| c)) } 45 46 pub fn wait(&self, timeout_ms: Option<u32>) -> Result<bool> { 47 acheck!(snd_ctl_wait(self.0, timeout_ms.map(|x| x as c_int).unwrap_or(-1))).map(|i| i == 1) } 48 49 pub fn get_db_range(&self, id: &ElemId) -> Result<(MilliBel, MilliBel)> { 50 let mut min: c_long = 0; 51 let mut max: c_long = 0; 52 acheck!(snd_ctl_get_dB_range(self.0, elem_id_ptr(id), &mut min, &mut max)) 53 .map(|_| (MilliBel(min as i64), MilliBel(max as i64))) 54 } 55 56 pub fn convert_to_db(&self, id: &ElemId, volume: i64) -> Result<MilliBel> { 57 let mut m: c_long = 0; 58 acheck!(snd_ctl_convert_to_dB(self.0, elem_id_ptr(id), volume as c_long, &mut m)) 59 .map(|_| (MilliBel(m as i64))) 60 } 61 62 pub fn convert_from_db(&self, id: &ElemId, mb: MilliBel, dir: Round) -> Result<i64> { 63 let mut m: c_long = 0; 64 acheck!(snd_ctl_convert_from_dB(self.0, elem_id_ptr(id), mb.0 as c_long, &mut m, dir as c_int)) 65 .map(|_| m as i64) 66 } 67 68 /// Note: According to alsa-lib documentation, you're also supposed to have functionality for 69 /// returning whether or not you are subscribed. This does not work in practice, so I'm not 70 /// including that here. 71 pub fn subscribe_events(&self, subscribe: bool) -> Result<()> { 72 acheck!(snd_ctl_subscribe_events(self.0, if subscribe { 1 } else { 0 })).map(|_| ()) 73 } 74 75 pub fn read(&self) -> Result<Option<Event>> { 76 let e = event_new()?; 77 acheck!(snd_ctl_read(self.0, e.0)).map(|r| if r == 1 { Some(e) } else { None }) 78 } 79 } 80 81 impl Drop for Ctl { 82 fn drop(&mut self) { unsafe { alsa::snd_ctl_close(self.0) }; } 83 } 84 85 impl poll::PollDescriptors for Ctl { 86 fn count(&self) -> usize { 87 unsafe { alsa::snd_ctl_poll_descriptors_count(self.0) as usize } 88 } 89 fn fill(&self, p: &mut [pollfd]) -> Result<usize> { 90 let z = unsafe { alsa::snd_ctl_poll_descriptors(self.0, p.as_mut_ptr(), p.len() as c_uint) }; 91 from_code("snd_ctl_poll_descriptors", z).map(|_| z as usize) 92 } 93 fn revents(&self, p: &[pollfd]) -> Result<poll::PollFlags> { 94 let mut r = 0; 95 let z = unsafe { alsa::snd_ctl_poll_descriptors_revents(self.0, p.as_ptr() as *mut pollfd, p.len() as c_uint, &mut r) }; 96 from_code("snd_ctl_poll_descriptors_revents", z).map(|_| poll::PollFlags::from_bits_truncate(r as c_short)) 97 } 98 } 99 100 101 pub fn ctl_ptr(a: &Ctl) -> *mut alsa::snd_ctl_t { a.0 } 102 103 /// [snd_ctl_card_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper 104 pub struct CardInfo(*mut alsa::snd_ctl_card_info_t); 105 106 impl Drop for CardInfo { 107 fn drop(&mut self) { unsafe { alsa::snd_ctl_card_info_free(self.0) }} 108 } 109 110 impl CardInfo { 111 fn new() -> Result<CardInfo> { 112 let mut p = ptr::null_mut(); 113 acheck!(snd_ctl_card_info_malloc(&mut p)).map(|_| CardInfo(p)) 114 } 115 116 pub fn get_id(&self) -> Result<&str> { 117 from_const("snd_ctl_card_info_get_id", unsafe { alsa::snd_ctl_card_info_get_id(self.0) })} 118 pub fn get_driver(&self) -> Result<&str> { 119 from_const("snd_ctl_card_info_get_driver", unsafe { alsa::snd_ctl_card_info_get_driver(self.0) })} 120 pub fn get_components(&self) -> Result<&str> { 121 from_const("snd_ctl_card_info_get_components", unsafe { alsa::snd_ctl_card_info_get_components(self.0) })} 122 pub fn get_longname(&self) -> Result<&str> { 123 from_const("snd_ctl_card_info_get_longname", unsafe { alsa::snd_ctl_card_info_get_longname(self.0) })} 124 pub fn get_name(&self) -> Result<&str> { 125 from_const("snd_ctl_card_info_get_name", unsafe { alsa::snd_ctl_card_info_get_name(self.0) })} 126 pub fn get_mixername(&self) -> Result<&str> { 127 from_const("snd_ctl_card_info_get_mixername", unsafe { alsa::snd_ctl_card_info_get_mixername(self.0) })} 128 pub fn get_card(&self) -> Card { Card::new(unsafe { alsa::snd_ctl_card_info_get_card(self.0) })} 129 } 130 131 alsa_enum!( 132 /// [SND_CTL_ELEM_IFACE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) constants 133 ElemIface, ALL_ELEMIFACE[7], 134 135 Card = SND_CTL_ELEM_IFACE_CARD, 136 Hwdep = SND_CTL_ELEM_IFACE_HWDEP, 137 Mixer = SND_CTL_ELEM_IFACE_MIXER, 138 PCM = SND_CTL_ELEM_IFACE_PCM, 139 Rawmidi = SND_CTL_ELEM_IFACE_RAWMIDI, 140 Timer = SND_CTL_ELEM_IFACE_TIMER, 141 Sequencer = SND_CTL_ELEM_IFACE_SEQUENCER, 142 ); 143 144 alsa_enum!( 145 /// [SND_CTL_ELEM_TYPE_xxx](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) constants 146 ElemType, ALL_ELEMTYPE[7], 147 148 None = SND_CTL_ELEM_TYPE_NONE, 149 Boolean = SND_CTL_ELEM_TYPE_BOOLEAN, 150 Integer = SND_CTL_ELEM_TYPE_INTEGER, 151 Enumerated = SND_CTL_ELEM_TYPE_ENUMERATED, 152 Bytes = SND_CTL_ELEM_TYPE_BYTES, 153 IEC958 = SND_CTL_ELEM_TYPE_IEC958, 154 Integer64 = SND_CTL_ELEM_TYPE_INTEGER64, 155 ); 156 157 /// [snd_ctl_elem_value_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper 158 pub struct ElemValue { 159 ptr: *mut alsa::snd_ctl_elem_value_t, 160 etype: ElemType, 161 count: u32, 162 } 163 164 impl Drop for ElemValue { 165 fn drop(&mut self) { unsafe { alsa::snd_ctl_elem_value_free(self.ptr) }; } 166 } 167 168 pub fn elem_value_ptr(a: &ElemValue) -> *mut alsa::snd_ctl_elem_value_t { a.ptr } 169 170 pub fn elem_value_new(t: ElemType, count: u32) -> Result<ElemValue> { 171 let mut p = ptr::null_mut(); 172 acheck!(snd_ctl_elem_value_malloc(&mut p)) 173 .map(|_| ElemValue { ptr: p, etype: t, count: count }) 174 } 175 176 impl ElemValue { 177 178 // Note: The get_bytes hands out a reference to inside the object. Therefore, we can't treat 179 // the content as "cell"ed, but must take a "&mut self" (to make sure the reference 180 // from get_bytes has been dropped when calling a set_* function). 181 182 pub fn get_boolean(&self, idx: u32) -> Option<bool> { 183 if self.etype != ElemType::Boolean || idx >= self.count { None } 184 else { Some( unsafe { alsa::snd_ctl_elem_value_get_boolean(self.ptr, idx as c_uint) } != 0) } 185 } 186 187 pub fn set_boolean(&mut self, idx: u32, val: bool) -> Option<()> { 188 if self.etype != ElemType::Boolean || idx >= self.count { None } 189 else { unsafe { alsa::snd_ctl_elem_value_set_boolean(self.ptr, idx as c_uint, if val {1} else {0}) }; Some(()) } 190 } 191 192 pub fn get_integer(&self, idx: u32) -> Option<i32> { 193 if self.etype != ElemType::Integer || idx >= self.count { None } 194 else { Some( unsafe { alsa::snd_ctl_elem_value_get_integer(self.ptr, idx as c_uint) } as i32) } 195 } 196 197 pub fn set_integer(&mut self, idx: u32, val: i32) -> Option<()> { 198 if self.etype != ElemType::Integer || idx >= self.count { None } 199 else { unsafe { alsa::snd_ctl_elem_value_set_integer(self.ptr, idx as c_uint, val as c_long) }; Some(()) } 200 } 201 202 pub fn get_integer64(&self, idx: u32) -> Option<i64> { 203 if self.etype != ElemType::Integer64 || idx >= self.count { None } 204 else { Some( unsafe { alsa::snd_ctl_elem_value_get_integer64(self.ptr, idx as c_uint) } as i64) } 205 } 206 207 pub fn set_integer64(&mut self, idx: u32, val: i64) -> Option<()> { 208 if self.etype != ElemType::Integer || idx >= self.count { None } 209 else { unsafe { alsa::snd_ctl_elem_value_set_integer64(self.ptr, idx as c_uint, val) }; Some(()) } 210 } 211 212 pub fn get_enumerated(&self, idx: u32) -> Option<u32> { 213 if self.etype != ElemType::Enumerated || idx >= self.count { None } 214 else { Some( unsafe { alsa::snd_ctl_elem_value_get_enumerated(self.ptr, idx as c_uint) } as u32) } 215 } 216 217 pub fn set_enumerated(&mut self, idx: u32, val: u32) -> Option<()> { 218 if self.etype != ElemType::Enumerated || idx >= self.count { None } 219 else { unsafe { alsa::snd_ctl_elem_value_set_enumerated(self.ptr, idx as c_uint, val as c_uint) }; Some(()) } 220 } 221 222 pub fn get_byte(&self, idx: u32) -> Option<u8> { 223 if self.etype != ElemType::Bytes || idx >= self.count { None } 224 else { Some( unsafe { alsa::snd_ctl_elem_value_get_byte(self.ptr, idx as c_uint) } as u8) } 225 } 226 227 pub fn set_byte(&mut self, idx: u32, val: u8) -> Option<()> { 228 if self.etype != ElemType::Bytes || idx >= self.count { None } 229 else { unsafe { alsa::snd_ctl_elem_value_set_byte(self.ptr, idx as c_uint, val) }; Some(()) } 230 } 231 232 pub fn get_bytes(&self) -> Option<&[u8]> { 233 if self.etype != ElemType::Bytes { None } 234 else { Some( unsafe { ::std::slice::from_raw_parts( 235 alsa::snd_ctl_elem_value_get_bytes(self.ptr) as *const u8, self.count as usize) } ) } 236 } 237 238 pub fn set_bytes(&mut self, val: &[u8]) -> Option<()> { 239 if self.etype != ElemType::Bytes || val.len() != self.count as usize { None } 240 241 // Note: the alsa-lib function definition is broken. First, the pointer is declared as mut even 242 // though it's const, and second, there is a "value" missing between "elem" and "set_bytes". 243 else { unsafe { alsa::snd_ctl_elem_set_bytes(self.ptr, val.as_ptr() as *mut c_void, val.len() as size_t) }; Some(()) } 244 } 245 246 /// Creates a new ElemValue. 247 pub fn new(t: ElemType) -> Result<ElemValue> { 248 // See max length in include/uapi/sound/asound.h in linux kernel for these values 249 let count = match t { 250 ElemType::None => 1, 251 ElemType::Boolean => 128, 252 ElemType::Integer => 128, 253 ElemType::Enumerated => 128, 254 ElemType::Bytes => 512, 255 ElemType::IEC958 => 1, 256 ElemType::Integer64 => 64, 257 }; 258 // if count > maxcount { return Err(Error::new(Some("ElemValue::new - count too large".into()), 1)) } 259 let ev = elem_value_new(t, count)?; 260 unsafe { alsa::snd_ctl_elem_value_clear(elem_value_ptr(&ev)) }; 261 Ok(ev) 262 } 263 264 } 265 266 impl fmt::Debug for ElemValue { 267 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 268 use self::ElemType::*; 269 write!(f, "ElemValue({:?}", self.etype)?; 270 for a in 0..self.count { match self.etype { 271 Boolean => write!(f, ",{:?}", self.get_boolean(a).unwrap()), 272 Integer => write!(f, ",{:?}", self.get_integer(a).unwrap()), 273 Integer64 => write!(f, ",{:?}", self.get_integer64(a).unwrap()), 274 Enumerated => write!(f, ",{:?}", self.get_enumerated(a).unwrap()), 275 Bytes => write!(f, ",{:?}", self.get_byte(a).unwrap()), 276 _ => Ok(()), 277 }?}; 278 write!(f, ")") 279 } 280 } 281 282 /// [snd_ctl_elem_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper 283 pub struct ElemInfo(*mut alsa::snd_ctl_elem_info_t); 284 285 pub fn elem_info_ptr(a: &ElemInfo) -> *mut alsa::snd_ctl_elem_info_t { a.0 } 286 287 impl Drop for ElemInfo { 288 fn drop(&mut self) { unsafe { alsa::snd_ctl_elem_info_free(self.0) }; } 289 } 290 291 pub fn elem_info_new() -> Result<ElemInfo> { 292 let mut p = ptr::null_mut(); 293 acheck!(snd_ctl_elem_info_malloc(&mut p)).map(|_| ElemInfo(p)) 294 } 295 296 impl ElemInfo { 297 pub fn get_type(&self) -> ElemType { ElemType::from_c_int( 298 unsafe { alsa::snd_ctl_elem_info_get_type(self.0) } as c_int, "snd_ctl_elem_info_get_type").unwrap() } 299 pub fn get_count(&self) -> u32 { unsafe { alsa::snd_ctl_elem_info_get_count(self.0) as u32 } } 300 } 301 302 // 303 // Non-allocating version of ElemId 304 // 305 306 /// [snd_ctl_elem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper 307 pub struct ElemId(UnsafeCell<[u8; ELEM_ID_SIZE]>); 308 309 pub fn elem_id_new() -> Result<ElemId> { 310 assert!(unsafe { alsa::snd_ctl_elem_id_sizeof() } as usize <= ELEM_ID_SIZE); 311 Ok(ElemId(UnsafeCell::new(unsafe { mem::zeroed() }))) 312 } 313 314 #[inline] 315 pub fn elem_id_ptr(a: &ElemId) -> *mut alsa::snd_ctl_elem_id_t { a.0.get() as *mut _ as *mut alsa::snd_ctl_elem_id_t } 316 317 unsafe impl Send for ElemId {} 318 319 impl Clone for ElemId { 320 fn clone(&self) -> Self { 321 ElemId(UnsafeCell::new(unsafe { *self.0.get() })) 322 } 323 } 324 325 // 326 // Allocating version of ElemId 327 // 328 329 /* 330 331 /// [snd_ctl_elem_id_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper 332 pub struct ElemId(*mut alsa::snd_ctl_elem_id_t); 333 334 impl Drop for ElemId { 335 fn drop(&mut self) { unsafe { alsa::snd_ctl_elem_id_free(self.0) }; } 336 } 337 338 pub fn elem_id_new() -> Result<ElemId> { 339 let mut p = ptr::null_mut(); 340 acheck!(snd_ctl_elem_id_malloc(&mut p)).map(|_| ElemId(p)) 341 } 342 343 pub fn elem_id_ptr(a: &ElemId) -> *mut alsa::snd_ctl_elem_id_t { a.0 } 344 345 */ 346 347 impl ElemId { 348 pub fn get_name(&self) -> Result<&str> { 349 from_const("snd_hctl_elem_id_get_name", unsafe { alsa::snd_ctl_elem_id_get_name(elem_id_ptr(&self)) })} 350 pub fn get_device(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_device(elem_id_ptr(&self)) as u32 }} 351 pub fn get_subdevice(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_subdevice(elem_id_ptr(&self)) as u32 }} 352 pub fn get_numid(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_numid(elem_id_ptr(&self)) as u32 }} 353 pub fn get_index(&self) -> u32 { unsafe { alsa::snd_ctl_elem_id_get_index(elem_id_ptr(&self)) as u32 }} 354 pub fn get_interface(&self) -> ElemIface { ElemIface::from_c_int( 355 unsafe { alsa::snd_ctl_elem_id_get_interface(elem_id_ptr(&self)) } as c_int, "snd_ctl_elem_id_get_interface").unwrap() } 356 357 pub fn set_device(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_device(elem_id_ptr(&self), v) }} 358 pub fn set_subdevice(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_subdevice(elem_id_ptr(&self), v) }} 359 pub fn set_numid(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_numid(elem_id_ptr(&self), v) }} 360 pub fn set_index(&mut self, v: u32) { unsafe { alsa::snd_ctl_elem_id_set_index(elem_id_ptr(&self), v) }} 361 pub fn set_interface(&mut self, v: ElemIface) { unsafe { alsa::snd_ctl_elem_id_set_interface(elem_id_ptr(&self), v as u32) }} 362 pub fn set_name(&mut self, v: &CStr) { unsafe { alsa::snd_ctl_elem_id_set_name(elem_id_ptr(&self), v.as_ptr()) }} 363 364 /// Creates a new ElemId. 365 /// 366 /// To ensure safety (i e make sure we never have an invalid interface enum), we need to supply it to the "new" function. 367 pub fn new(iface: ElemIface) -> Self { 368 let mut r = elem_id_new().unwrap(); 369 r.set_interface(iface); 370 r 371 } 372 } 373 374 impl cmp::Eq for ElemId {} 375 376 impl cmp::PartialEq for ElemId { 377 fn eq(&self, a: &ElemId) -> bool { 378 self.get_numid() == a.get_numid() && self.get_interface() == a.get_interface() && 379 self.get_index() == a.get_index() && self.get_device() == a.get_device() && 380 self.get_subdevice() == a.get_subdevice() && self.get_name().ok() == a.get_name().ok() 381 } 382 } 383 384 impl fmt::Debug for ElemId { 385 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 386 let index = self.get_index(); 387 let device = self.get_device(); 388 let subdevice = self.get_subdevice(); 389 390 write!(f, "ElemId(#{}, {:?}, {:?}", self.get_numid(), self.get_interface(), self.get_name())?; 391 if index > 0 { write!(f, ", index={}", index)? }; 392 if device > 0 || subdevice > 0 { write!(f, ", device={}", device)? }; 393 if subdevice > 0 { write!(f, ", subdevice={}", device)? }; 394 write!(f, ")") 395 } 396 } 397 398 /// [snd_ctl_event_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) wrapper 399 pub struct Event(*mut alsa::snd_ctl_event_t); 400 401 impl Drop for Event { 402 fn drop(&mut self) { unsafe { alsa::snd_ctl_event_free(self.0) }; } 403 } 404 405 pub fn event_new() -> Result<Event> { 406 let mut p = ptr::null_mut(); 407 acheck!(snd_ctl_event_malloc(&mut p)).map(|_| Event(p)) 408 } 409 410 impl Event { 411 pub fn get_mask(&self) -> EventMask { EventMask(unsafe { alsa::snd_ctl_event_elem_get_mask(self.0) as u32 })} 412 pub fn get_id(&self) -> ElemId { 413 let r = elem_id_new().unwrap(); 414 unsafe { alsa::snd_ctl_event_elem_get_id(self.0, elem_id_ptr(&r)) }; 415 r 416 } 417 } 418 419 420 /// [SND_CTL_EVENT_MASK_XXX](http://www.alsa-project.org/alsa-doc/alsa-lib/group___control.html) bitmask 421 #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] 422 pub struct EventMask(pub u32); 423 424 impl EventMask { 425 pub fn remove(&self) -> bool { return self.0 & 0xffffffff == 0xffffffff } 426 pub fn value(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 0) != 0); } 427 pub fn info(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 1) != 0); } 428 pub fn add(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 2) != 0); } 429 pub fn tlv(&self) -> bool { return (!self.remove()) && (self.0 & (1 << 3) != 0); } 430 } 431 432 #[test] 433 fn print_sizeof() { 434 let elemid = unsafe { alsa::snd_ctl_elem_id_sizeof() } as usize; 435 let elemvalue = unsafe { alsa::snd_ctl_elem_value_sizeof() } as usize; 436 let eleminfo = unsafe { alsa::snd_ctl_elem_info_sizeof() } as usize; 437 438 assert!(elemid <= ELEM_ID_SIZE); 439 // assert!(elemvalue <= ELEM_VALUE_SIZE); 440 // assert!(eleminfo <= ELEM_INFO_SIZE); 441 442 println!("Elem id: {}, Elem value: {}, Elem info: {}", elemid, elemvalue, eleminfo); 443 } 444 445