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: Card in super::card::Iter::new().map(|a: Result| a.unwrap()) { |
206 | for b: Info in Iter::new(&Ctl::from_card(&a, nonblock:false).unwrap()).map(|b: Result| 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 | |