use parking_lot::ReentrantMutex as Mutex; use std::alloc::{alloc, dealloc, Layout}; use std::ffi::{c_void, OsString}; use std::io::{stderr, Write}; use std::mem::MaybeUninit; use std::os::windows::ffi::OsStringExt; use std::ptr::null_mut; use std::thread::sleep; use std::time::Duration; use std::{mem, ptr, slice}; use windows::core::PSTR; use windows::Win32::Media::Audio::{ midiInAddBuffer, midiInClose, midiInGetDevCapsW, midiInGetNumDevs, midiInMessage, midiInOpen, midiInPrepareHeader, midiInReset, midiInStart, midiInStop, midiInUnprepareHeader, midiOutClose, midiOutGetDevCapsW, midiOutGetNumDevs, midiOutLongMsg, midiOutMessage, midiOutOpen, midiOutPrepareHeader, midiOutReset, midiOutShortMsg, midiOutUnprepareHeader, CALLBACK_FUNCTION, CALLBACK_NULL, HMIDIIN, HMIDIOUT, MIDIERR_NOTREADY, MIDIERR_STILLPLAYING, MIDIHDR, MIDIINCAPSW, MIDIOUTCAPSW, }; use windows::Win32::Media::Multimedia::{DRV_QUERYDEVICEINTERFACE, DRV_QUERYDEVICEINTERFACESIZE}; use windows::Win32::Media::{MMSYSERR_ALLOCATED, MMSYSERR_BADDEVICEID, MMSYSERR_NOERROR}; #[allow(non_camel_case_types)] #[allow(clippy::upper_case_acronyms)] type ULONG = u32; #[allow(non_camel_case_types)] #[allow(clippy::upper_case_acronyms)] type UINT = u32; #[allow(non_camel_case_types)] #[allow(clippy::upper_case_acronyms)] type DWORD = u32; #[allow(non_camel_case_types)] #[allow(clippy::upper_case_acronyms)] type DWORD_PTR = usize; use crate::errors::*; use crate::{Ignore, MidiMessage}; mod handler; const MIDIR_SYSEX_BUFFER_SIZE: usize = 1024; const MIDIR_SYSEX_BUFFER_COUNT: usize = 4; // helper for string conversion fn from_wide_ptr(ptr: *const u16, max_len: usize) -> OsString { unsafe { assert!(!ptr.is_null()); let len = (0..max_len as isize) .position(|i| *ptr.offset(i) == 0) .unwrap(); let slice = slice::from_raw_parts(ptr, len); OsString::from_wide(slice) } } #[derive(Debug)] pub struct MidiInput { ignore_flags: Ignore, } #[derive(Clone)] pub struct MidiInputPort { name: String, interface_id: Box<[u16]>, } impl MidiInputPort { pub fn id(&self) -> String { String::from_utf16_lossy(&self.interface_id) } } impl PartialEq for MidiInputPort { fn eq(&self, other: &Self) -> bool { self.interface_id == other.interface_id } } pub struct MidiInputConnection { handler_data: Box>, } impl MidiInputPort { fn count() -> UINT { unsafe { midiInGetNumDevs() } } fn interface_id(port_number: UINT) -> Result, PortInfoError> { let mut buffer_size: ULONG = 0; let result = unsafe { midiInMessage( Some(HMIDIIN(port_number as *mut c_void)), DRV_QUERYDEVICEINTERFACESIZE, Some(&mut buffer_size as *mut _ as DWORD_PTR), None, ) }; if result == MMSYSERR_BADDEVICEID { return Err(PortInfoError::PortNumberOutOfRange); } else if result != MMSYSERR_NOERROR { return Err(PortInfoError::CannotRetrievePortName); } let mut buffer = Vec::::with_capacity(buffer_size as usize / 2); unsafe { let result = midiInMessage( Some(HMIDIIN(port_number as *mut c_void)), DRV_QUERYDEVICEINTERFACE, Some(buffer.as_mut_ptr() as usize), Some(buffer_size as DWORD_PTR), ); if result == MMSYSERR_BADDEVICEID { return Err(PortInfoError::PortNumberOutOfRange); } else if result != MMSYSERR_NOERROR { return Err(PortInfoError::CannotRetrievePortName); } buffer.set_len(buffer_size as usize / 2); } //println!("{}", from_wide_ptr(buffer.as_ptr(), buffer.len()).to_string_lossy().into_owned()); Ok(buffer.into_boxed_slice()) } fn name(port_number: UINT) -> Result { let mut device_caps: MaybeUninit = MaybeUninit::uninit(); let result = unsafe { midiInGetDevCapsW( port_number as usize, device_caps.as_mut_ptr(), mem::size_of::() as u32, ) }; if result == MMSYSERR_BADDEVICEID { return Err(PortInfoError::PortNumberOutOfRange); } else if result != MMSYSERR_NOERROR { return Err(PortInfoError::CannotRetrievePortName); } let device_caps = unsafe { device_caps.assume_init() }; let pname_ptr: *const [u16; 32] = std::ptr::addr_of!(device_caps.szPname); let output = from_wide_ptr(pname_ptr as *const _, 32) .to_string_lossy() .into_owned(); Ok(output) } fn from_port_number(port_number: UINT) -> Result { Ok(MidiInputPort { name: Self::name(port_number)?, interface_id: Self::interface_id(port_number)?, }) } fn current_port_number(&self) -> Option { for i in 0..Self::count() { if let Ok(name) = Self::name(i) { if name != self.name { continue; } if let Ok(id) = Self::interface_id(i) { if id == self.interface_id { return Some(i); } } } } None } } struct SysexBuffer([*mut MIDIHDR; MIDIR_SYSEX_BUFFER_COUNT]); unsafe impl Send for SysexBuffer {} struct MidiInHandle(Mutex); unsafe impl Send for MidiInHandle {} /// This is all the data that is stored on the heap as long as a connection /// is opened and passed to the callback handler. /// /// It is important that `user_data` is the last field to not influence /// offsets after monomorphization. struct HandlerData { message: MidiMessage, sysex_buffer: SysexBuffer, in_handle: Option, ignore_flags: Ignore, callback: Box, user_data: Option, } impl MidiInput { pub fn new(_client_name: &str) -> Result { Ok(MidiInput { ignore_flags: Ignore::None, }) } pub fn ignore(&mut self, flags: Ignore) { self.ignore_flags = flags; } pub(crate) fn ports_internal(&self) -> Vec { let count = MidiInputPort::count(); let mut result = Vec::with_capacity(count as usize); for i in 0..count { let port = match MidiInputPort::from_port_number(i) { Ok(p) => p, Err(_) => continue, }; result.push(crate::common::MidiInputPort { imp: port }); } result } pub fn port_count(&self) -> usize { MidiInputPort::count() as usize } pub fn port_name(&self, port: &MidiInputPort) -> Result { Ok(port.name.clone()) } pub fn connect( self, port: &MidiInputPort, _port_name: &str, callback: F, data: T, ) -> Result, ConnectError> where F: FnMut(u64, &[u8], &mut T) + Send + 'static, { let port_number = match port.current_port_number() { Some(p) => p, None => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)), }; let mut handler_data = Box::new(HandlerData { message: MidiMessage::new(), sysex_buffer: SysexBuffer([null_mut(); MIDIR_SYSEX_BUFFER_COUNT]), in_handle: None, ignore_flags: self.ignore_flags, callback: Box::new(callback), user_data: Some(data), }); let mut in_handle: MaybeUninit = MaybeUninit::uninit(); let handler_data_ptr: *mut HandlerData = &mut *handler_data; let result = unsafe { midiInOpen( in_handle.as_mut_ptr(), port_number as UINT, Some(handler::handle_input:: as DWORD_PTR), Some(handler_data_ptr as DWORD_PTR), CALLBACK_FUNCTION, ) }; if result == MMSYSERR_ALLOCATED { return Err(ConnectError::other( "could not create Windows MM MIDI input port (MMSYSERR_ALLOCATED)", self, )); } else if result != MMSYSERR_NOERROR { return Err(ConnectError::other( "could not create Windows MM MIDI input port", self, )); } let in_handle = unsafe { in_handle.assume_init() }; // Allocate and init the sysex buffers. for i in 0..MIDIR_SYSEX_BUFFER_COUNT { handler_data.sysex_buffer.0[i] = Box::into_raw(Box::new(MIDIHDR { lpData: PSTR(unsafe { alloc(Layout::from_size_align_unchecked( MIDIR_SYSEX_BUFFER_SIZE, 1, )) }), dwBufferLength: MIDIR_SYSEX_BUFFER_SIZE as u32, dwBytesRecorded: 0, dwUser: i as DWORD_PTR, // We use the dwUser parameter as buffer indicator dwFlags: 0, lpNext: ptr::null_mut(), reserved: 0, dwOffset: 0, dwReserved: unsafe { mem::zeroed() }, })); // TODO: are those buffers ever freed if an error occurs here (altough these calls probably only fail with out-of-memory)? // TODO: close port in case of error? let result = unsafe { midiInPrepareHeader( in_handle, handler_data.sysex_buffer.0[i], mem::size_of::() as u32, ) }; if result != MMSYSERR_NOERROR { return Err(ConnectError::other( "could not initialize Windows MM MIDI input port (PrepareHeader)", self, )); } // Register the buffer. let result = unsafe { midiInAddBuffer( in_handle, handler_data.sysex_buffer.0[i], mem::size_of::() as u32, ) }; if result != MMSYSERR_NOERROR { return Err(ConnectError::other( "could not initialize Windows MM MIDI input port (AddBuffer)", self, )); } } handler_data.in_handle = Some(MidiInHandle(Mutex::new(in_handle))); // We can safely access (a copy of) `in_handle` here, although // it has been copied into the Mutex already, because the callback // has not been called yet. let result = unsafe { midiInStart(in_handle) }; if result != MMSYSERR_NOERROR { unsafe { midiInClose(in_handle) }; return Err(ConnectError::other( "could not start Windows MM MIDI input port", self, )); } Ok(MidiInputConnection { handler_data }) } } impl MidiInputConnection { pub fn close(mut self) -> (MidiInput, T) { self.close_internal(); ( MidiInput { ignore_flags: self.handler_data.ignore_flags, }, self.handler_data.user_data.take().unwrap(), ) } fn close_internal(&mut self) { // for information about this lock, see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo let in_handle_lock = self.handler_data.in_handle.as_ref().unwrap().0.lock(); // TODO: Call both reset and stop here? The difference seems to be that // reset "returns all pending input buffers to the callback function" unsafe { midiInReset(*in_handle_lock); midiInStop(*in_handle_lock); } for i in 0..MIDIR_SYSEX_BUFFER_COUNT { let result; unsafe { result = midiInUnprepareHeader( *in_handle_lock, self.handler_data.sysex_buffer.0[i], mem::size_of::() as u32, ); dealloc( (*self.handler_data.sysex_buffer.0[i]).lpData.0 as *mut _, Layout::from_size_align_unchecked(MIDIR_SYSEX_BUFFER_SIZE, 1), ); // recreate the Box so that it will be dropped/deallocated at the end of this scope let _ = Box::from_raw(self.handler_data.sysex_buffer.0[i]); } if result != MMSYSERR_NOERROR { let _ = writeln!(stderr(), "Warning: Ignoring error shutting down Windows MM input port (UnprepareHeader)."); } } unsafe { midiInClose(*in_handle_lock) }; } } impl Drop for MidiInputConnection { fn drop(&mut self) { // If user_data has been emptied, we know that we already have closed the connection if self.handler_data.user_data.is_some() { self.close_internal() } } } #[derive(Debug)] pub struct MidiOutput; #[derive(Clone)] pub struct MidiOutputPort { name: String, interface_id: Box<[u16]>, } impl MidiOutputPort { pub fn id(&self) -> String { String::from_utf16_lossy(&self.interface_id) } } impl PartialEq for MidiOutputPort { fn eq(&self, other: &Self) -> bool { self.interface_id == other.interface_id } } pub struct MidiOutputConnection { out_handle: HMIDIOUT, } unsafe impl Send for MidiOutputConnection {} impl MidiOutputPort { fn count() -> UINT { unsafe { midiOutGetNumDevs() } } fn interface_id(port_number: UINT) -> Result, PortInfoError> { let mut buffer_size: ULONG = 0; let result = unsafe { midiOutMessage( Some(HMIDIOUT(port_number as *mut c_void)), DRV_QUERYDEVICEINTERFACESIZE, Some(&mut buffer_size as *mut _ as DWORD_PTR), None, ) }; if result == MMSYSERR_BADDEVICEID { return Err(PortInfoError::PortNumberOutOfRange); } else if result != MMSYSERR_NOERROR { return Err(PortInfoError::CannotRetrievePortName); } let mut buffer = Vec::::with_capacity(buffer_size as usize / 2); unsafe { let result = midiOutMessage( Some(HMIDIOUT(port_number as *mut c_void)), DRV_QUERYDEVICEINTERFACE, Some(buffer.as_mut_ptr() as DWORD_PTR), Some(buffer_size as DWORD_PTR), ); if result == MMSYSERR_BADDEVICEID { return Err(PortInfoError::PortNumberOutOfRange); } else if result != MMSYSERR_NOERROR { return Err(PortInfoError::CannotRetrievePortName); } buffer.set_len(buffer_size as usize / 2); } //println!("{}", from_wide_ptr(buffer.as_ptr(), buffer.len()).to_string_lossy().into_owned()); Ok(buffer.into_boxed_slice()) } fn name(port_number: UINT) -> Result { let mut device_caps: MaybeUninit = MaybeUninit::uninit(); let result = unsafe { midiOutGetDevCapsW( port_number as usize, device_caps.as_mut_ptr(), mem::size_of::() as u32, ) }; if result == MMSYSERR_BADDEVICEID { return Err(PortInfoError::PortNumberOutOfRange); } else if result != MMSYSERR_NOERROR { return Err(PortInfoError::CannotRetrievePortName); } let device_caps = unsafe { device_caps.assume_init() }; let pname_ptr: *const [u16; 32] = std::ptr::addr_of!(device_caps.szPname); let output = from_wide_ptr(pname_ptr as *const _, 32) .to_string_lossy() .into_owned(); Ok(output) } fn from_port_number(port_number: UINT) -> Result { Ok(MidiOutputPort { name: Self::name(port_number)?, interface_id: Self::interface_id(port_number)?, }) } fn current_port_number(&self) -> Option { for i in 0..Self::count() { if let Ok(name) = Self::name(i) { if name != self.name { continue; } if let Ok(id) = Self::interface_id(i) { if id == self.interface_id { return Some(i); } } } } None } } impl MidiOutput { pub fn new(_client_name: &str) -> Result { Ok(MidiOutput) } pub(crate) fn ports_internal(&self) -> Vec { let count = MidiOutputPort::count(); let mut result = Vec::with_capacity(count as usize); for i in 0..count { let port = match MidiOutputPort::from_port_number(i) { Ok(p) => p, Err(_) => continue, }; result.push(crate::common::MidiOutputPort { imp: port }); } result } pub fn port_count(&self) -> usize { MidiOutputPort::count() as usize } pub fn port_name(&self, port: &MidiOutputPort) -> Result { Ok(port.name.clone()) } pub fn connect( self, port: &MidiOutputPort, _port_name: &str, ) -> Result> { let port_number = match port.current_port_number() { Some(p) => p, None => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self)), }; let mut out_handle: MaybeUninit = MaybeUninit::uninit(); let result = unsafe { midiOutOpen( out_handle.as_mut_ptr(), port_number as UINT, None, None, CALLBACK_NULL, ) }; if result == MMSYSERR_ALLOCATED { return Err(ConnectError::other( "could not create Windows MM MIDI output port (MMSYSERR_ALLOCATED)", self, )); } else if result != MMSYSERR_NOERROR { return Err(ConnectError::other( "could not create Windows MM MIDI output port", self, )); } Ok(MidiOutputConnection { out_handle: unsafe { out_handle.assume_init() }, }) } } impl MidiOutputConnection { pub fn close(self) -> MidiOutput { // The actual closing is done by the implementation of Drop MidiOutput // In this API this is a noop } pub fn send(&mut self, message: &[u8]) -> Result<(), SendError> { let nbytes = message.len(); if nbytes == 0 { return Err(SendError::InvalidData( "message to be sent must not be empty", )); } if message[0] == 0xF0 { // Sysex message // Allocate buffer for sysex data and copy message let mut buffer = message.to_vec(); // Create and prepare MIDIHDR structure. let mut sysex = MIDIHDR { lpData: PSTR(buffer.as_mut_ptr()), dwBufferLength: nbytes as u32, dwBytesRecorded: 0, dwUser: 0, dwFlags: 0, lpNext: ptr::null_mut(), reserved: 0, dwOffset: 0, dwReserved: unsafe { mem::zeroed() }, }; let result = unsafe { midiOutPrepareHeader( self.out_handle, &mut sysex, mem::size_of::() as u32, ) }; if result != MMSYSERR_NOERROR { return Err(SendError::Other( "preparation for sending sysex message failed (OutPrepareHeader)", )); } // Send the message. loop { let result = unsafe { midiOutLongMsg(self.out_handle, &sysex, mem::size_of::() as u32) }; if result == MIDIERR_NOTREADY { sleep(Duration::from_millis(1)); continue; } else { if result != MMSYSERR_NOERROR { return Err(SendError::Other("sending sysex message failed")); } break; } } loop { let result = unsafe { midiOutUnprepareHeader( self.out_handle, &mut sysex, mem::size_of::() as u32, ) }; if result == MIDIERR_STILLPLAYING { sleep(Duration::from_millis(1)); continue; } else { break; } } } else { // Channel or system message. // Make sure the message size isn't too big. if nbytes > 3 { return Err(SendError::InvalidData( "non-sysex message must not be longer than 3 bytes", )); } // Pack MIDI bytes into double word. let mut packet: u32 = 0; let ptr = std::ptr::addr_of_mut!(packet).cast::(); for (i, item) in message.iter().enumerate().take(nbytes) { unsafe { *ptr.add(i) = *item }; } // Send the message immediately. loop { let result = unsafe { midiOutShortMsg(self.out_handle, packet) }; if result == MIDIERR_NOTREADY { sleep(Duration::from_millis(1)); continue; } else { if result != MMSYSERR_NOERROR { return Err(SendError::Other("sending non-sysex message failed")); } break; } } } Ok(()) } } impl Drop for MidiOutputConnection { fn drop(&mut self) { unsafe { midiOutReset(self.out_handle); midiOutClose(self.out_handle); } } }