1//! MIDI devices I/O and enumeration
2
3use libc::{c_int, c_uint, c_void, size_t, c_short, pollfd};
4use super::ctl_int::{ctl_ptr, Ctl};
5use super::{Direction, poll};
6use super::error::*;
7use crate::alsa;
8use std::{ptr, io};
9use 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
12pub 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
21pub struct Info(*mut alsa::snd_rawmidi_info_t);
22
23impl Drop for Info {
24 fn drop(&mut self) { unsafe { alsa::snd_rawmidi_info_free(self.0) }; }
25}
26
27impl 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
70pub struct Status(*mut alsa::snd_rawmidi_status_t);
71
72impl 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
79impl 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
84impl Drop for Status {
85 fn drop(&mut self) { unsafe { alsa::snd_rawmidi_status_free(self.0) }; }
86}
87
88
89impl<'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
93impl<'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
124pub struct Rawmidi(*mut alsa::snd_rawmidi_t);
125
126unsafe impl Send for Rawmidi {}
127
128impl Drop for Rawmidi {
129 fn drop(&mut self) { unsafe { alsa::snd_rawmidi_close(self.0) }; }
130}
131
132impl 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
167impl 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`
183pub struct IO<'a>(&'a Rawmidi);
184
185impl<'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
193impl<'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]
204fn 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