1use parking_lot::ReentrantMutex as Mutex;
2use std::alloc::{alloc, dealloc, Layout};
3use std::ffi::OsString;
4use std::io::{stderr, Write};
5use std::mem::MaybeUninit;
6use std::os::windows::ffi::OsStringExt;
7use std::ptr::null_mut;
8use std::thread::sleep;
9use std::time::Duration;
10use std::{mem, ptr, slice};
11
12use windows::core::PSTR;
13use windows::Win32::Media::Audio::{
14 midiInAddBuffer, midiInClose, midiInGetDevCapsW, midiInGetNumDevs, midiInMessage, midiInOpen,
15 midiInPrepareHeader, midiInReset, midiInStart, midiInStop, midiInUnprepareHeader, midiOutClose,
16 midiOutGetDevCapsW, midiOutGetNumDevs, midiOutLongMsg, midiOutMessage, midiOutOpen,
17 midiOutPrepareHeader, midiOutReset, midiOutShortMsg, midiOutUnprepareHeader, CALLBACK_FUNCTION,
18 CALLBACK_NULL, HMIDIIN, HMIDIOUT, MIDIERR_NOTREADY, MIDIERR_STILLPLAYING, MIDIHDR, MIDIINCAPSW,
19 MIDIOUTCAPSW,
20};
21use windows::Win32::Media::Multimedia::{DRV_QUERYDEVICEINTERFACE, DRV_QUERYDEVICEINTERFACESIZE};
22use windows::Win32::Media::{MMSYSERR_ALLOCATED, MMSYSERR_BADDEVICEID, MMSYSERR_NOERROR};
23
24#[allow(non_camel_case_types)]
25#[allow(clippy::upper_case_acronyms)]
26type ULONG = u32;
27#[allow(non_camel_case_types)]
28#[allow(clippy::upper_case_acronyms)]
29type UINT = u32;
30#[allow(non_camel_case_types)]
31#[allow(clippy::upper_case_acronyms)]
32type DWORD = u32;
33#[allow(non_camel_case_types)]
34#[allow(clippy::upper_case_acronyms)]
35type DWORD_PTR = usize;
36
37use crate::errors::*;
38use crate::{Ignore, MidiMessage};
39
40mod handler;
41
42const MIDIR_SYSEX_BUFFER_SIZE: usize = 1024;
43const MIDIR_SYSEX_BUFFER_COUNT: usize = 4;
44
45fn from_wide_ptr(ptr: *const u16, max_len: usize) -> OsString {
47 unsafe {
48 assert!(!ptr.is_null());
49 let len = (0..max_len as isize)
50 .position(|i| *ptr.offset(i) == 0)
51 .unwrap();
52 let slice = slice::from_raw_parts(ptr, len);
53 OsString::from_wide(slice)
54 }
55}
56
57#[derive(Debug)]
58pub struct MidiInput {
59 ignore_flags: Ignore,
60}
61
62#[derive(Clone)]
63pub struct MidiInputPort {
64 name: String,
65 interface_id: Box<[u16]>,
66}
67
68impl MidiInputPort {
69 pub fn id(&self) -> String {
70 String::from_utf16_lossy(&self.interface_id)
71 }
72}
73
74impl PartialEq for MidiInputPort {
75 fn eq(&self, other: &Self) -> bool {
76 self.interface_id == other.interface_id
77 }
78}
79
80pub struct MidiInputConnection<T> {
81 handler_data: Box<HandlerData<T>>,
82}
83
84impl MidiInputPort {
85 fn count() -> UINT {
86 unsafe { midiInGetNumDevs() }
87 }
88
89 fn interface_id(port_number: UINT) -> Result<Box<[u16]>, PortInfoError> {
90 let mut buffer_size: ULONG = 0;
91 let result = unsafe {
92 midiInMessage(
93 HMIDIIN(port_number as isize),
94 DRV_QUERYDEVICEINTERFACESIZE,
95 &mut buffer_size as *mut _ as DWORD_PTR,
96 0,
97 )
98 };
99 if result == MMSYSERR_BADDEVICEID {
100 return Err(PortInfoError::PortNumberOutOfRange);
101 } else if result != MMSYSERR_NOERROR {
102 return Err(PortInfoError::CannotRetrievePortName);
103 }
104 let mut buffer = Vec::<u16>::with_capacity(buffer_size as usize / 2);
105 unsafe {
106 let result = midiInMessage(
107 HMIDIIN(port_number as isize),
108 DRV_QUERYDEVICEINTERFACE,
109 buffer.as_mut_ptr() as usize,
110 buffer_size as DWORD_PTR,
111 );
112 if result == MMSYSERR_BADDEVICEID {
113 return Err(PortInfoError::PortNumberOutOfRange);
114 } else if result != MMSYSERR_NOERROR {
115 return Err(PortInfoError::CannotRetrievePortName);
116 }
117 buffer.set_len(buffer_size as usize / 2);
118 }
119 Ok(buffer.into_boxed_slice())
121 }
122
123 fn name(port_number: UINT) -> Result<String, PortInfoError> {
124 let mut device_caps: MaybeUninit<MIDIINCAPSW> = MaybeUninit::uninit();
125 let result = unsafe {
126 midiInGetDevCapsW(
127 port_number as usize,
128 device_caps.as_mut_ptr(),
129 mem::size_of::<MIDIINCAPSW>() as u32,
130 )
131 };
132 if result == MMSYSERR_BADDEVICEID {
133 return Err(PortInfoError::PortNumberOutOfRange);
134 } else if result != MMSYSERR_NOERROR {
135 return Err(PortInfoError::CannotRetrievePortName);
136 }
137 let device_caps = unsafe { device_caps.assume_init() };
138 let pname_ptr: *const [u16; 32] = std::ptr::addr_of!(device_caps.szPname);
139 let output = from_wide_ptr(pname_ptr as *const _, 32)
140 .to_string_lossy()
141 .into_owned();
142 Ok(output)
143 }
144
145 fn from_port_number(port_number: UINT) -> Result<Self, PortInfoError> {
146 Ok(MidiInputPort {
147 name: Self::name(port_number)?,
148 interface_id: Self::interface_id(port_number)?,
149 })
150 }
151
152 fn current_port_number(&self) -> Option<UINT> {
153 for i in 0..Self::count() {
154 if let Ok(name) = Self::name(i) {
155 if name != self.name {
156 continue;
157 }
158 if let Ok(id) = Self::interface_id(i) {
159 if id == self.interface_id {
160 return Some(i);
161 }
162 }
163 }
164 }
165 None
166 }
167}
168
169struct SysexBuffer([*mut MIDIHDR; MIDIR_SYSEX_BUFFER_COUNT]);
170unsafe impl Send for SysexBuffer {}
171
172struct MidiInHandle(Mutex<HMIDIIN>);
173unsafe impl Send for MidiInHandle {}
174
175struct HandlerData<T> {
181 message: MidiMessage,
182 sysex_buffer: SysexBuffer,
183 in_handle: Option<MidiInHandle>,
184 ignore_flags: Ignore,
185 callback: Box<dyn FnMut(u64, &[u8], &mut T) + Send + 'static>,
186 user_data: Option<T>,
187}
188
189impl MidiInput {
190 pub fn new(_client_name: &str) -> Result<Self, InitError> {
191 Ok(MidiInput {
192 ignore_flags: Ignore::None,
193 })
194 }
195
196 pub fn ignore(&mut self, flags: Ignore) {
197 self.ignore_flags = flags;
198 }
199
200 pub(crate) fn ports_internal(&self) -> Vec<crate::common::MidiInputPort> {
201 let count = MidiInputPort::count();
202 let mut result = Vec::with_capacity(count as usize);
203 for i in 0..count {
204 let port = match MidiInputPort::from_port_number(i) {
205 Ok(p) => p,
206 Err(_) => continue,
207 };
208 result.push(crate::common::MidiInputPort { imp: port });
209 }
210 result
211 }
212
213 pub fn port_count(&self) -> usize {
214 MidiInputPort::count() as usize
215 }
216
217 pub fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> {
218 Ok(port.name.clone())
219 }
220
221 pub fn connect<F, T: Send>(
222 self,
223 port: &MidiInputPort,
224 _port_name: &str,
225 callback: F,
226 data: T,
227 ) -> Result<MidiInputConnection<T>, ConnectError<MidiInput>>
228 where
229 F: FnMut(u64, &[u8], &mut T) + Send + 'static,
230 {
231 let port_number = match port.current_port_number() {
232 Some(p) => p,
233 None => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)),
234 };
235
236 let mut handler_data = Box::new(HandlerData {
237 message: MidiMessage::new(),
238 sysex_buffer: SysexBuffer([null_mut(); MIDIR_SYSEX_BUFFER_COUNT]),
239 in_handle: None,
240 ignore_flags: self.ignore_flags,
241 callback: Box::new(callback),
242 user_data: Some(data),
243 });
244
245 let mut in_handle: MaybeUninit<HMIDIIN> = MaybeUninit::uninit();
246 let handler_data_ptr: *mut HandlerData<T> = &mut *handler_data;
247 let result = unsafe {
248 midiInOpen(
249 in_handle.as_mut_ptr(),
250 port_number as UINT,
251 handler::handle_input::<T> as DWORD_PTR,
252 handler_data_ptr as DWORD_PTR,
253 CALLBACK_FUNCTION,
254 )
255 };
256 if result == MMSYSERR_ALLOCATED {
257 return Err(ConnectError::other(
258 "could not create Windows MM MIDI input port (MMSYSERR_ALLOCATED)",
259 self,
260 ));
261 } else if result != MMSYSERR_NOERROR {
262 return Err(ConnectError::other(
263 "could not create Windows MM MIDI input port",
264 self,
265 ));
266 }
267 let in_handle = unsafe { in_handle.assume_init() };
268
269 for i in 0..MIDIR_SYSEX_BUFFER_COUNT {
271 handler_data.sysex_buffer.0[i] = Box::into_raw(Box::new(MIDIHDR {
272 lpData: PSTR(unsafe {
273 alloc(Layout::from_size_align_unchecked(
274 MIDIR_SYSEX_BUFFER_SIZE,
275 1,
276 ))
277 }),
278 dwBufferLength: MIDIR_SYSEX_BUFFER_SIZE as u32,
279 dwBytesRecorded: 0,
280 dwUser: i as DWORD_PTR, dwFlags: 0,
282 lpNext: ptr::null_mut(),
283 reserved: 0,
284 dwOffset: 0,
285 dwReserved: unsafe { mem::zeroed() },
286 }));
287
288 let result = unsafe {
292 midiInPrepareHeader(
293 in_handle,
294 handler_data.sysex_buffer.0[i],
295 mem::size_of::<MIDIHDR>() as u32,
296 )
297 };
298 if result != MMSYSERR_NOERROR {
299 return Err(ConnectError::other(
300 "could not initialize Windows MM MIDI input port (PrepareHeader)",
301 self,
302 ));
303 }
304
305 let result = unsafe {
307 midiInAddBuffer(
308 in_handle,
309 handler_data.sysex_buffer.0[i],
310 mem::size_of::<MIDIHDR>() as u32,
311 )
312 };
313 if result != MMSYSERR_NOERROR {
314 return Err(ConnectError::other(
315 "could not initialize Windows MM MIDI input port (AddBuffer)",
316 self,
317 ));
318 }
319 }
320
321 handler_data.in_handle = Some(MidiInHandle(Mutex::new(in_handle)));
322
323 let result = unsafe { midiInStart(in_handle) };
327 if result != MMSYSERR_NOERROR {
328 unsafe { midiInClose(in_handle) };
329 return Err(ConnectError::other(
330 "could not start Windows MM MIDI input port",
331 self,
332 ));
333 }
334
335 Ok(MidiInputConnection { handler_data })
336 }
337}
338
339impl<T> MidiInputConnection<T> {
340 pub fn close(mut self) -> (MidiInput, T) {
341 self.close_internal();
342
343 (
344 MidiInput {
345 ignore_flags: self.handler_data.ignore_flags,
346 },
347 self.handler_data.user_data.take().unwrap(),
348 )
349 }
350
351 fn close_internal(&mut self) {
352 let in_handle_lock = self.handler_data.in_handle.as_ref().unwrap().0.lock();
354
355 unsafe {
358 midiInReset(*in_handle_lock);
359 midiInStop(*in_handle_lock);
360 }
361
362 for i in 0..MIDIR_SYSEX_BUFFER_COUNT {
363 let result;
364 unsafe {
365 result = midiInUnprepareHeader(
366 *in_handle_lock,
367 self.handler_data.sysex_buffer.0[i],
368 mem::size_of::<MIDIHDR>() as u32,
369 );
370 dealloc(
371 (*self.handler_data.sysex_buffer.0[i]).lpData.0 as *mut _,
372 Layout::from_size_align_unchecked(MIDIR_SYSEX_BUFFER_SIZE, 1),
373 );
374 let _ = Box::from_raw(self.handler_data.sysex_buffer.0[i]);
376 }
377
378 if result != MMSYSERR_NOERROR {
379 let _ = writeln!(stderr(), "Warning: Ignoring error shutting down Windows MM input port (UnprepareHeader).");
380 }
381 }
382
383 unsafe { midiInClose(*in_handle_lock) };
384 }
385}
386
387impl<T> Drop for MidiInputConnection<T> {
388 fn drop(&mut self) {
389 if self.handler_data.user_data.is_some() {
391 self.close_internal()
392 }
393 }
394}
395
396#[derive(Debug)]
397pub struct MidiOutput;
398
399#[derive(Clone)]
400pub struct MidiOutputPort {
401 name: String,
402 interface_id: Box<[u16]>,
403}
404
405impl MidiOutputPort {
406 pub fn id(&self) -> String {
407 String::from_utf16_lossy(&self.interface_id)
408 }
409}
410
411impl PartialEq for MidiOutputPort {
412 fn eq(&self, other: &Self) -> bool {
413 self.interface_id == other.interface_id
414 }
415}
416
417pub struct MidiOutputConnection {
418 out_handle: HMIDIOUT,
419}
420
421unsafe impl Send for MidiOutputConnection {}
422
423impl MidiOutputPort {
424 fn count() -> UINT {
425 unsafe { midiOutGetNumDevs() }
426 }
427
428 fn interface_id(port_number: UINT) -> Result<Box<[u16]>, PortInfoError> {
429 let mut buffer_size: ULONG = 0;
430 let result = unsafe {
431 midiOutMessage(
432 HMIDIOUT(port_number as isize),
433 DRV_QUERYDEVICEINTERFACESIZE,
434 &mut buffer_size as *mut _ as DWORD_PTR,
435 0,
436 )
437 };
438 if result == MMSYSERR_BADDEVICEID {
439 return Err(PortInfoError::PortNumberOutOfRange);
440 } else if result != MMSYSERR_NOERROR {
441 return Err(PortInfoError::CannotRetrievePortName);
442 }
443 let mut buffer = Vec::<u16>::with_capacity(buffer_size as usize / 2);
444 unsafe {
445 let result = midiOutMessage(
446 HMIDIOUT(port_number as isize),
447 DRV_QUERYDEVICEINTERFACE,
448 buffer.as_mut_ptr() as DWORD_PTR,
449 buffer_size as DWORD_PTR,
450 );
451 if result == MMSYSERR_BADDEVICEID {
452 return Err(PortInfoError::PortNumberOutOfRange);
453 } else if result != MMSYSERR_NOERROR {
454 return Err(PortInfoError::CannotRetrievePortName);
455 }
456 buffer.set_len(buffer_size as usize / 2);
457 }
458 Ok(buffer.into_boxed_slice())
460 }
461
462 fn name(port_number: UINT) -> Result<String, PortInfoError> {
463 let mut device_caps: MaybeUninit<MIDIOUTCAPSW> = MaybeUninit::uninit();
464 let result = unsafe {
465 midiOutGetDevCapsW(
466 port_number as usize,
467 device_caps.as_mut_ptr(),
468 mem::size_of::<MIDIOUTCAPSW>() as u32,
469 )
470 };
471 if result == MMSYSERR_BADDEVICEID {
472 return Err(PortInfoError::PortNumberOutOfRange);
473 } else if result != MMSYSERR_NOERROR {
474 return Err(PortInfoError::CannotRetrievePortName);
475 }
476 let device_caps = unsafe { device_caps.assume_init() };
477 let pname_ptr: *const [u16; 32] = std::ptr::addr_of!(device_caps.szPname);
478 let output = from_wide_ptr(pname_ptr as *const _, 32)
479 .to_string_lossy()
480 .into_owned();
481 Ok(output)
482 }
483
484 fn from_port_number(port_number: UINT) -> Result<Self, PortInfoError> {
485 Ok(MidiOutputPort {
486 name: Self::name(port_number)?,
487 interface_id: Self::interface_id(port_number)?,
488 })
489 }
490
491 fn current_port_number(&self) -> Option<UINT> {
492 for i in 0..Self::count() {
493 if let Ok(name) = Self::name(i) {
494 if name != self.name {
495 continue;
496 }
497 if let Ok(id) = Self::interface_id(i) {
498 if id == self.interface_id {
499 return Some(i);
500 }
501 }
502 }
503 }
504 None
505 }
506}
507
508impl MidiOutput {
509 pub fn new(_client_name: &str) -> Result<Self, InitError> {
510 Ok(MidiOutput)
511 }
512
513 pub(crate) fn ports_internal(&self) -> Vec<crate::common::MidiOutputPort> {
514 let count = MidiOutputPort::count();
515 let mut result = Vec::with_capacity(count as usize);
516 for i in 0..count {
517 let port = match MidiOutputPort::from_port_number(i) {
518 Ok(p) => p,
519 Err(_) => continue,
520 };
521 result.push(crate::common::MidiOutputPort { imp: port });
522 }
523 result
524 }
525
526 pub fn port_count(&self) -> usize {
527 MidiOutputPort::count() as usize
528 }
529
530 pub fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> {
531 Ok(port.name.clone())
532 }
533
534 pub fn connect(
535 self,
536 port: &MidiOutputPort,
537 _port_name: &str,
538 ) -> Result<MidiOutputConnection, ConnectError<MidiOutput>> {
539 let port_number = match port.current_port_number() {
540 Some(p) => p,
541 None => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)),
542 };
543 let mut out_handle: MaybeUninit<HMIDIOUT> = MaybeUninit::uninit();
544 let result = unsafe {
545 midiOutOpen(
546 out_handle.as_mut_ptr(),
547 port_number as UINT,
548 0,
549 0,
550 CALLBACK_NULL,
551 )
552 };
553 if result == MMSYSERR_ALLOCATED {
554 return Err(ConnectError::other(
555 "could not create Windows MM MIDI output port (MMSYSERR_ALLOCATED)",
556 self,
557 ));
558 } else if result != MMSYSERR_NOERROR {
559 return Err(ConnectError::other(
560 "could not create Windows MM MIDI output port",
561 self,
562 ));
563 }
564 Ok(MidiOutputConnection {
565 out_handle: unsafe { out_handle.assume_init() },
566 })
567 }
568}
569
570impl MidiOutputConnection {
571 pub fn close(self) -> MidiOutput {
572 MidiOutput }
575
576 pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> {
577 let nbytes = message.len();
578 if nbytes == 0 {
579 return Err(SendError::InvalidData(
580 "message to be sent must not be empty",
581 ));
582 }
583
584 if message[0] == 0xF0 {
585 let mut buffer = message.to_vec();
588
589 let mut sysex = MIDIHDR {
591 lpData: PSTR(buffer.as_mut_ptr()),
592 dwBufferLength: nbytes as u32,
593 dwBytesRecorded: 0,
594 dwUser: 0,
595 dwFlags: 0,
596 lpNext: ptr::null_mut(),
597 reserved: 0,
598 dwOffset: 0,
599 dwReserved: unsafe { mem::zeroed() },
600 };
601
602 let result = unsafe {
603 midiOutPrepareHeader(
604 self.out_handle,
605 &mut sysex,
606 mem::size_of::<MIDIHDR>() as u32,
607 )
608 };
609
610 if result != MMSYSERR_NOERROR {
611 return Err(SendError::Other(
612 "preparation for sending sysex message failed (OutPrepareHeader)",
613 ));
614 }
615
616 loop {
618 let result = unsafe {
619 midiOutLongMsg(self.out_handle, &sysex, mem::size_of::<MIDIHDR>() as u32)
620 };
621 if result == MIDIERR_NOTREADY {
622 sleep(Duration::from_millis(1));
623 continue;
624 } else {
625 if result != MMSYSERR_NOERROR {
626 return Err(SendError::Other("sending sysex message failed"));
627 }
628 break;
629 }
630 }
631
632 loop {
633 let result = unsafe {
634 midiOutUnprepareHeader(
635 self.out_handle,
636 &mut sysex,
637 mem::size_of::<MIDIHDR>() as u32,
638 )
639 };
640 if result == MIDIERR_STILLPLAYING {
641 sleep(Duration::from_millis(1));
642 continue;
643 } else {
644 break;
645 }
646 }
647 } else {
648 if nbytes > 3 {
651 return Err(SendError::InvalidData(
652 "non-sysex message must not be longer than 3 bytes",
653 ));
654 }
655
656 let mut packet: u32 = 0;
658 let ptr = std::ptr::addr_of_mut!(packet).cast::<u8>();
659 for (i, item) in message.iter().enumerate().take(nbytes) {
660 unsafe { *ptr.add(i) = *item };
661 }
662
663 loop {
665 let result = unsafe { midiOutShortMsg(self.out_handle, packet) };
666 if result == MIDIERR_NOTREADY {
667 sleep(Duration::from_millis(1));
668 continue;
669 } else {
670 if result != MMSYSERR_NOERROR {
671 return Err(SendError::Other("sending non-sysex message failed"));
672 }
673 break;
674 }
675 }
676 }
677
678 Ok(())
679 }
680}
681
682impl Drop for MidiOutputConnection {
683 fn drop(&mut self) {
684 unsafe {
685 midiOutReset(self.out_handle);
686 midiOutClose(self.out_handle);
687 }
688 }
689}