midir/common.rs
1#![deny(missing_docs)]
2
3use backend::{
4 MidiInput as MidiInputImpl, MidiInputConnection as MidiInputConnectionImpl,
5 MidiInputPort as MidiInputPortImpl, MidiOutput as MidiOutputImpl,
6 MidiOutputConnection as MidiOutputConnectionImpl, MidiOutputPort as MidiOutputPortImpl,
7};
8use errors::*;
9
10use crate::{backend, errors, Ignore, InitError};
11
12/// Trait that abstracts over input and output ports.
13pub trait MidiIO {
14 /// Type of an input or output port structure.
15 type Port: Clone;
16
17 /// Get a collection of all MIDI input or output ports.
18 /// The resulting vector contains one object per port, which you can use to
19 /// query metadata about the port or connect to it.
20 fn ports(&self) -> Vec<Self::Port>;
21
22 /// Get the number of available MIDI input or output ports.
23 fn port_count(&self) -> usize;
24
25 /// Get the name of a specified MIDI input or output port.
26 ///
27 /// An error will be returned when the port is no longer valid
28 /// (e.g. the respective device has been disconnected).
29 fn port_name(&self, port: &Self::Port) -> Result<String, PortInfoError>;
30}
31
32/// An object representing a single input port.
33/// How the port is identified internally is backend-dependent.
34/// If the backend allows it, port objects remain valid when
35/// other ports in the system change (i.e. it is not just an index).
36///
37/// Use the `ports` method of a `MidiInput` instance to obtain
38/// available ports.
39#[derive(Clone, PartialEq)]
40pub struct MidiInputPort {
41 pub(crate) imp: MidiInputPortImpl,
42}
43
44impl MidiInputPort {
45 /// Get a unique stable identifier for this port.
46 /// This identifier must be treated as an opaque string.
47 pub fn id(&self) -> String {
48 self.imp.id()
49 }
50}
51
52/// A collection of input ports.
53pub type MidiInputPorts = Vec<MidiInputPort>;
54
55/// An instance of `MidiInput` is required for anything related to MIDI input.
56/// Create one with `MidiInput::new`.
57pub struct MidiInput {
58 //ignore_flags: Ignore
59 imp: MidiInputImpl,
60}
61
62impl MidiInput {
63 /// Creates a new `MidiInput` object that is required for any MIDI input functionality.
64 pub fn new(client_name: &str) -> Result<Self, InitError> {
65 MidiInputImpl::new(client_name).map(|imp| MidiInput { imp })
66 }
67
68 /// Set flags to decide what kind of messages should be ignored (i.e., filtered out)
69 /// by this `MidiInput`. By default, no messages are ignored.
70 pub fn ignore(&mut self, flags: Ignore) {
71 self.imp.ignore(flags);
72 }
73
74 /// Get a collection of all MIDI input ports that *midir* can connect to.
75 /// The resulting vector contains one object per port, which you can use to
76 /// query metadata about the port or connect to it in order to receive
77 /// MIDI messages.
78 pub fn ports(&self) -> MidiInputPorts {
79 self.imp.ports_internal()
80 }
81
82 /// Get the number of available MIDI input ports that *midir* can connect to.
83 pub fn port_count(&self) -> usize {
84 self.imp.port_count()
85 }
86
87 /// Get the name of a specified MIDI input port.
88 ///
89 /// An error will be returned when the port is no longer valid
90 /// (e.g. the respective device has been disconnected).
91 pub fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> {
92 self.imp.port_name(&port.imp)
93 }
94
95 /// Get a MIDI input port by its unique identifier.
96 pub fn find_port_by_id(&self, id: String) -> Option<MidiInputPort> {
97 self.ports().into_iter().find(|port| port.id() == id)
98 }
99
100 /// Connect to a specified MIDI input port in order to receive messages.
101 /// For each incoming MIDI message, the provided `callback` function will
102 /// be called. The first parameter of the callback function is a timestamp
103 /// (in microseconds) designating the time since some unspecified point in
104 /// the past (which will not change during the lifetime of a
105 /// `MidiInputConnection`). The second parameter contains the actual bytes
106 /// of the MIDI message.
107 ///
108 /// Additional data that should be passed whenever the callback is
109 /// invoked can be specified by `data`. Use the empty tuple `()` if
110 /// you do not want to pass any additional data.
111 ///
112 /// The connection will be kept open as long as the returned
113 /// `MidiInputConnection` is kept alive.
114 ///
115 /// The `port_name` is an additional name that will be assigned to the
116 /// connection. It is only used by some backends.
117 ///
118 /// An error will be returned when the port is no longer valid
119 /// (e.g. the respective device has been disconnected).
120 pub fn connect<F, T: Send>(
121 self,
122 port: &MidiInputPort,
123 port_name: &str,
124 callback: F,
125 data: T,
126 ) -> Result<MidiInputConnection<T>, ConnectError<MidiInput>>
127 where
128 F: FnMut(u64, &[u8], &mut T) + Send + 'static,
129 {
130 match self.imp.connect(&port.imp, port_name, callback, data) {
131 Ok(imp) => Ok(MidiInputConnection { imp }),
132 Err(imp) => {
133 let kind = imp.kind();
134 Err(ConnectError::new(
135 kind,
136 MidiInput {
137 imp: imp.into_inner(),
138 },
139 ))
140 }
141 }
142 }
143}
144
145impl MidiIO for MidiInput {
146 type Port = MidiInputPort;
147
148 fn ports(&self) -> MidiInputPorts {
149 self.imp.ports_internal()
150 }
151
152 fn port_count(&self) -> usize {
153 self.imp.port_count()
154 }
155
156 fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> {
157 self.imp.port_name(&port.imp)
158 }
159}
160
161#[cfg(unix)]
162impl<T: Send> crate::os::unix::VirtualInput<T> for MidiInput {
163 fn create_virtual<F>(
164 self,
165 port_name: &str,
166 callback: F,
167 data: T,
168 ) -> Result<MidiInputConnection<T>, ConnectError<Self>>
169 where
170 F: FnMut(u64, &[u8], &mut T) + Send + 'static,
171 {
172 match self.imp.create_virtual(port_name, callback, data) {
173 Ok(imp) => Ok(MidiInputConnection { imp }),
174 Err(imp) => {
175 let kind = imp.kind();
176 Err(ConnectError::new(
177 kind,
178 MidiInput {
179 imp: imp.into_inner(),
180 },
181 ))
182 }
183 }
184 }
185}
186
187/// Represents an open connection to a MIDI input port.
188pub struct MidiInputConnection<T: 'static> {
189 imp: MidiInputConnectionImpl<T>,
190}
191
192impl<T> MidiInputConnection<T> {
193 /// Closes the connection. The returned values allow you to
194 /// inspect the additional data passed to the callback (the `data`
195 /// parameter of `connect`), or to reuse the `MidiInput` object,
196 /// but they can be safely ignored.
197 pub fn close(self) -> (MidiInput, T) {
198 let (imp, data) = self.imp.close();
199 (MidiInput { imp }, data)
200 }
201}
202
203/// An object representing a single output port.
204/// How the port is identified internally is backend-dependent.
205/// If the backend allows it, port objects remain valid when
206/// other ports in the system change (i.e. it is not just an index).
207///
208/// Use the `ports` method of a `MidiOutput` instance to obtain
209/// available ports.
210#[derive(Clone, PartialEq)]
211pub struct MidiOutputPort {
212 pub(crate) imp: MidiOutputPortImpl,
213}
214
215impl MidiOutputPort {
216 /// Get a unique stable identifier for this port.
217 /// This identifier must be treated as an opaque string.
218 pub fn id(&self) -> String {
219 self.imp.id()
220 }
221}
222
223/// A collection of output ports.
224pub type MidiOutputPorts = Vec<MidiOutputPort>;
225
226/// An instance of `MidiOutput` is required for anything related to MIDI output.
227/// Create one with `MidiOutput::new`.
228pub struct MidiOutput {
229 imp: MidiOutputImpl,
230}
231
232impl MidiOutput {
233 /// Creates a new `MidiOutput` object that is required for any MIDI output functionality.
234 pub fn new(client_name: &str) -> Result<Self, InitError> {
235 MidiOutputImpl::new(client_name).map(|imp| MidiOutput { imp })
236 }
237
238 /// Get a collection of all MIDI output ports that *midir* can connect to.
239 /// The resulting vector contains one object per port, which you can use to
240 /// query metadata about the port or connect to it in order to send
241 /// MIDI messages.
242 pub fn ports(&self) -> MidiOutputPorts {
243 self.imp.ports_internal()
244 }
245
246 /// Get the number of available MIDI output ports that *midir* can connect to.
247 pub fn port_count(&self) -> usize {
248 self.imp.port_count()
249 }
250
251 /// Get the name of a specified MIDI output port.
252 ///
253 /// An error will be returned when the port is no longer valid
254 /// (e.g. the respective device has been disconnected).
255 pub fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> {
256 self.imp.port_name(&port.imp)
257 }
258
259 /// Get a MIDI output port by its unique identifier.
260 pub fn find_port_by_id(&self, id: String) -> Option<MidiOutputPort> {
261 self.ports().into_iter().find(|port| port.id() == id)
262 }
263
264 /// Connect to a specified MIDI output port in order to send messages.
265 /// The connection will be kept open as long as the returned
266 /// `MidiOutputConnection` is kept alive.
267 ///
268 /// The `port_name` is an additional name that will be assigned to the
269 /// connection. It is only used by some backends.
270 ///
271 /// An error will be returned when the port is no longer valid
272 /// (e.g. the respective device has been disconnected).
273 pub fn connect(
274 self,
275 port: &MidiOutputPort,
276 port_name: &str,
277 ) -> Result<MidiOutputConnection, ConnectError<MidiOutput>> {
278 match self.imp.connect(&port.imp, port_name) {
279 Ok(imp) => Ok(MidiOutputConnection { imp }),
280 Err(imp) => {
281 let kind = imp.kind();
282 Err(ConnectError::new(
283 kind,
284 MidiOutput {
285 imp: imp.into_inner(),
286 },
287 ))
288 }
289 }
290 }
291}
292
293impl MidiIO for MidiOutput {
294 type Port = MidiOutputPort;
295
296 fn ports(&self) -> MidiOutputPorts {
297 self.imp.ports_internal()
298 }
299
300 fn port_count(&self) -> usize {
301 self.imp.port_count()
302 }
303
304 fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> {
305 self.imp.port_name(&port.imp)
306 }
307}
308
309#[cfg(unix)]
310impl crate::os::unix::VirtualOutput for MidiOutput {
311 fn create_virtual(
312 self,
313 port_name: &str,
314 ) -> Result<MidiOutputConnection, ConnectError<MidiOutput>> {
315 match self.imp.create_virtual(port_name) {
316 Ok(imp) => Ok(MidiOutputConnection { imp }),
317 Err(imp) => {
318 let kind = imp.kind();
319 Err(ConnectError::new(
320 kind,
321 MidiOutput {
322 imp: imp.into_inner(),
323 },
324 ))
325 }
326 }
327 }
328}
329
330/// Represents an open connection to a MIDI output port.
331pub struct MidiOutputConnection {
332 imp: MidiOutputConnectionImpl,
333}
334
335impl MidiOutputConnection {
336 /// Closes the connection. The returned value allows you to
337 /// reuse the `MidiOutput` object, but it can be safely ignored.
338 pub fn close(self) -> MidiOutput {
339 MidiOutput {
340 imp: self.imp.close(),
341 }
342 }
343
344 /// Send a message to the port that this output connection is connected to.
345 /// The message must be a valid MIDI message (see https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message).
346 pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> {
347 self.imp.send(message)
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[test]
356 fn test_trait_impls() {
357 // make sure that all the structs implement `Send`
358 fn is_send<T: Send>() {}
359 is_send::<MidiInput>();
360 is_send::<MidiOutput>();
361 #[cfg(not(target_arch = "wasm32"))]
362 {
363 // The story around threading and `Send` on WASM is not clear yet
364 // Tracking issue: https://github.com/Boddlnagg/midir/issues/49
365 // Prev. discussion: https://github.com/Boddlnagg/midir/pull/47
366 is_send::<MidiInputPort>();
367 is_send::<MidiInputConnection<()>>();
368 is_send::<MidiOutputPort>();
369 is_send::<MidiOutputConnection>();
370 }
371
372 // make sure that Midi port structs implement `PartialEq`
373 fn is_partial_eq<T: PartialEq>() {}
374 is_partial_eq::<MidiInputPortImpl>();
375 is_partial_eq::<MidiOutputPortImpl>();
376
377 is_partial_eq::<MidiInputPort>();
378 is_partial_eq::<MidiOutputPort>();
379 }
380}