| 1 | //! MIDI devices I/O and enumeration |
| 2 | |
| 3 | use libc::{c_int, c_uint, c_void, size_t, c_short, pollfd}; |
| 4 | use super::ctl_int::{ctl_ptr, Ctl}; |
| 5 | use super::{Direction, poll}; |
| 6 | use super::error::*; |
| 7 | use crate::alsa; |
| 8 | use std::{ptr, io}; |
| 9 | use std::ffi::{CStr, CString}; |
| 10 | |
| 11 | /// Iterator over [Rawmidi](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) devices and subdevices |
| 12 | pub struct Iter<'a> { |
| 13 | ctl: &'a Ctl, |
| 14 | device: c_int, |
| 15 | in_count: i32, |
| 16 | out_count: i32, |
| 17 | current: i32, |
| 18 | } |
| 19 | |
| 20 | /// [snd_rawmidi_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) wrapper |
| 21 | pub struct Info(*mut alsa::snd_rawmidi_info_t); |
| 22 | |
| 23 | impl Drop for Info { |
| 24 | fn drop(&mut self) { unsafe { alsa::snd_rawmidi_info_free(self.0) }; } |
| 25 | } |
| 26 | |
| 27 | impl Info { |
| 28 | fn new() -> Result<Info> { |
| 29 | let mut p = ptr::null_mut(); |
| 30 | acheck!(snd_rawmidi_info_malloc(&mut p)).map(|_| Info(p)) |
| 31 | } |
| 32 | |
| 33 | fn from_iter(c: &Ctl, device: i32, sub: i32, dir: Direction) -> Result<Info> { |
| 34 | let r = Info::new()?; |
| 35 | unsafe { alsa::snd_rawmidi_info_set_device(r.0, device as c_uint) }; |
| 36 | let d = match dir { |
| 37 | Direction::Playback => alsa::SND_RAWMIDI_STREAM_OUTPUT, |
| 38 | Direction::Capture => alsa::SND_RAWMIDI_STREAM_INPUT, |
| 39 | }; |
| 40 | unsafe { alsa::snd_rawmidi_info_set_stream(r.0, d) }; |
| 41 | unsafe { alsa::snd_rawmidi_info_set_subdevice(r.0, sub as c_uint) }; |
| 42 | acheck!(snd_ctl_rawmidi_info(ctl_ptr(c), r.0)).map(|_| r) |
| 43 | } |
| 44 | |
| 45 | fn subdev_count(c: &Ctl, device: c_int) -> Result<(i32, i32)> { |
| 46 | let i = Info::from_iter(c, device, 0, Direction::Capture)?; |
| 47 | let o = Info::from_iter(c, device, 0, Direction::Playback)?; |
| 48 | Ok((unsafe { alsa::snd_rawmidi_info_get_subdevices_count(o.0) as i32 }, |
| 49 | unsafe { alsa::snd_rawmidi_info_get_subdevices_count(i.0) as i32 })) |
| 50 | } |
| 51 | |
| 52 | pub fn get_device(&self) -> i32 { unsafe { alsa::snd_rawmidi_info_get_device(self.0) as i32 }} |
| 53 | pub fn get_subdevice(&self) -> i32 { unsafe { alsa::snd_rawmidi_info_get_subdevice(self.0) as i32 }} |
| 54 | pub fn get_stream(&self) -> super::Direction { |
| 55 | if unsafe { alsa::snd_rawmidi_info_get_stream(self.0) } == alsa::SND_RAWMIDI_STREAM_OUTPUT { super::Direction::Playback } |
| 56 | else { super::Direction::Capture } |
| 57 | } |
| 58 | |
| 59 | pub fn get_subdevice_name(&self) -> Result<String> { |
| 60 | let c = unsafe { alsa::snd_rawmidi_info_get_subdevice_name(self.0) }; |
| 61 | from_const("snd_rawmidi_info_get_subdevice_name" , c).map(|s| s.to_string()) |
| 62 | } |
| 63 | pub fn get_id(&self) -> Result<String> { |
| 64 | let c = unsafe { alsa::snd_rawmidi_info_get_id(self.0) }; |
| 65 | from_const("snd_rawmidi_info_get_id" , c).map(|s| s.to_string()) |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | /// [snd_rawmidi_info_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) wrapper |
| 70 | pub struct Status(*mut alsa::snd_rawmidi_status_t); |
| 71 | |
| 72 | impl Status { |
| 73 | fn new() -> Result<Self> { |
| 74 | let mut p: *mut _snd_rawmidi_status = ptr::null_mut(); |
| 75 | acheck!(snd_rawmidi_status_malloc(&mut p)).map(|_| Status(p)) |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | impl Status { |
| 80 | pub fn get_avail(&self) -> usize { unsafe { alsa::snd_rawmidi_status_get_avail(self.0 as *const _) } } |
| 81 | pub fn get_xruns(&self) -> usize { unsafe { alsa::snd_rawmidi_status_get_xruns(self.0 as *const _) } } |
| 82 | } |
| 83 | |
| 84 | impl Drop for Status { |
| 85 | fn drop(&mut self) { unsafe { alsa::snd_rawmidi_status_free(self.0) }; } |
| 86 | } |
| 87 | |
| 88 | |
| 89 | impl<'a> Iter<'a> { |
| 90 | pub fn new(c: &'a Ctl) -> Iter<'a> { Iter { ctl: c, device: -1, in_count: 0, out_count: 0, current: 0 }} |
| 91 | } |
| 92 | |
| 93 | impl<'a> Iterator for Iter<'a> { |
| 94 | type Item = Result<Info>; |
| 95 | fn next(&mut self) -> Option<Result<Info>> { |
| 96 | if self.current < self.in_count { |
| 97 | self.current += 1; |
| 98 | return Some(Info::from_iter(self.ctl, self.device, self.current-1, Direction::Capture)); |
| 99 | } |
| 100 | if self.current - self.in_count < self.out_count { |
| 101 | self.current += 1; |
| 102 | return Some(Info::from_iter(self.ctl, self.device, self.current-1-self.in_count, Direction::Playback)); |
| 103 | } |
| 104 | |
| 105 | let r = acheck!(snd_ctl_rawmidi_next_device(ctl_ptr(self.ctl), &mut self.device)); |
| 106 | match r { |
| 107 | Err(e) => return Some(Err(e)), |
| 108 | Ok(_) if self.device == -1 => return None, |
| 109 | _ => {}, |
| 110 | } |
| 111 | self.current = 0; |
| 112 | match Info::subdev_count(self.ctl, self.device) { |
| 113 | Err(e) => Some(Err(e)), |
| 114 | Ok((oo, ii)) => { |
| 115 | self.in_count = ii; |
| 116 | self.out_count = oo; |
| 117 | self.next() |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | /// [snd_rawmidi_t](http://www.alsa-project.org/alsa-doc/alsa-lib/group___raw_midi.html) wrapper |
| 124 | pub struct Rawmidi(*mut alsa::snd_rawmidi_t); |
| 125 | |
| 126 | unsafe impl Send for Rawmidi {} |
| 127 | |
| 128 | impl Drop for Rawmidi { |
| 129 | fn drop(&mut self) { unsafe { alsa::snd_rawmidi_close(self.0) }; } |
| 130 | } |
| 131 | |
| 132 | impl Rawmidi { |
| 133 | |
| 134 | /// Wrapper around open that takes a &str instead of a &CStr |
| 135 | pub fn new(name: &str, dir: Direction, nonblock: bool) -> Result<Self> { |
| 136 | Self::open(&CString::new(name).unwrap(), dir, nonblock) |
| 137 | } |
| 138 | |
| 139 | pub fn open(name: &CStr, dir: Direction, nonblock: bool) -> Result<Rawmidi> { |
| 140 | let mut h = ptr::null_mut(); |
| 141 | let flags = if nonblock { 2 } else { 0 }; // FIXME: alsa::SND_RAWMIDI_NONBLOCK does not exist in alsa-sys |
| 142 | acheck!(snd_rawmidi_open( |
| 143 | if dir == Direction::Capture { &mut h } else { ptr::null_mut() }, |
| 144 | if dir == Direction::Playback { &mut h } else { ptr::null_mut() }, |
| 145 | name.as_ptr(), flags)) |
| 146 | .map(|_| Rawmidi(h)) |
| 147 | } |
| 148 | |
| 149 | pub fn info(&self) -> Result<Info> { |
| 150 | Info::new().and_then(|i| acheck!(snd_rawmidi_info(self.0, i.0)).map(|_| i)) |
| 151 | } |
| 152 | |
| 153 | pub fn status(&self) -> Result<Status> { |
| 154 | Status::new().and_then(|i| acheck!(snd_rawmidi_status(self.0, i.0)).map(|_| i)) |
| 155 | } |
| 156 | |
| 157 | pub fn drop(&self) -> Result<()> { acheck!(snd_rawmidi_drop(self.0)).map(|_| ()) } |
| 158 | pub fn drain(&self) -> Result<()> { acheck!(snd_rawmidi_drain(self.0)).map(|_| ()) } |
| 159 | pub fn name(&self) -> Result<String> { |
| 160 | let c = unsafe { alsa::snd_rawmidi_name(self.0) }; |
| 161 | from_const("snd_rawmidi_name" , c).map(|s| s.to_string()) |
| 162 | } |
| 163 | |
| 164 | pub fn io(&self) -> IO { IO(self) } |
| 165 | } |
| 166 | |
| 167 | impl poll::Descriptors for Rawmidi { |
| 168 | fn count(&self) -> usize { |
| 169 | unsafe { alsa::snd_rawmidi_poll_descriptors_count(self.0) as usize } |
| 170 | } |
| 171 | fn fill(&self, p: &mut [pollfd]) -> Result<usize> { |
| 172 | let z: i32 = unsafe { alsa::snd_rawmidi_poll_descriptors(self.0, pfds:p.as_mut_ptr(), space:p.len() as c_uint) }; |
| 173 | from_code("snd_rawmidi_poll_descriptors" , z).map(|_| z as usize) |
| 174 | } |
| 175 | fn revents(&self, p: &[pollfd]) -> Result<poll::Flags> { |
| 176 | let mut r: u16 = 0; |
| 177 | let z: i32 = unsafe { alsa::snd_rawmidi_poll_descriptors_revents(self.0, pfds:p.as_ptr() as *mut pollfd, nfds:p.len() as c_uint, &mut r) }; |
| 178 | from_code("snd_rawmidi_poll_descriptors_revents" , z).map(|_| poll::Flags::from_bits_truncate(bits:r as c_short)) |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | /// Implements `std::io::Read` and `std::io::Write` for `Rawmidi` |
| 183 | pub struct IO<'a>(&'a Rawmidi); |
| 184 | |
| 185 | impl<'a> io::Read for IO<'a> { |
| 186 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
| 187 | let r: isize = unsafe { alsa::snd_rawmidi_read((self.0).0, buffer:buf.as_mut_ptr() as *mut c_void, size:buf.len() as size_t) }; |
| 188 | if r < 0 { Err(io::Error::from_raw_os_error(code:r as i32)) } |
| 189 | else { Ok(r as usize) } |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | impl<'a> io::Write for IO<'a> { |
| 194 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
| 195 | let r: isize = unsafe { alsa::snd_rawmidi_write((self.0).0, buffer:buf.as_ptr() as *const c_void, size:buf.len() as size_t) }; |
| 196 | if r < 0 { Err(io::Error::from_raw_os_error(code:r as i32)) } |
| 197 | else { Ok(r as usize) } |
| 198 | } |
| 199 | fn flush(&mut self) -> io::Result<()> { Ok(()) } |
| 200 | } |
| 201 | |
| 202 | |
| 203 | #[test ] |
| 204 | fn print_rawmidis() { |
| 205 | for a in super::card::Iter::new().map(|a| a.unwrap()) { |
| 206 | for b in Iter::new(&Ctl::from_card(&a, false).unwrap()).map(|b| b.unwrap()) { |
| 207 | println!("Rawmidi {:?} (hw:{},{},{}) {} - {}" , b.get_stream(), a.get_index(), b.get_device(), b.get_subdevice(), |
| 208 | a.get_name().unwrap(), b.get_subdevice_name().unwrap()) |
| 209 | } |
| 210 | } |
| 211 | } |
| 212 | |