summaryrefslogtreecommitdiffstats
path: root/third_party/rust/midir/src/backend/winmm
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/midir/src/backend/winmm')
-rw-r--r--third_party/rust/midir/src/backend/winmm/handler.rs85
-rw-r--r--third_party/rust/midir/src/backend/winmm/mod.rs536
2 files changed, 621 insertions, 0 deletions
diff --git a/third_party/rust/midir/src/backend/winmm/handler.rs b/third_party/rust/midir/src/backend/winmm/handler.rs
new file mode 100644
index 0000000000..b04d2ee908
--- /dev/null
+++ b/third_party/rust/midir/src/backend/winmm/handler.rs
@@ -0,0 +1,85 @@
+use std::{mem, slice};
+use std::io::{Write, stderr};
+
+use super::winapi::shared::basetsd::DWORD_PTR;
+use super::winapi::shared::minwindef::{DWORD, UINT};
+use super::winapi::um::mmeapi::midiInAddBuffer;
+use super::winapi::um::mmsystem::{HMIDIIN, MIDIHDR, MMSYSERR_NOERROR, MM_MIM_DATA,
+ MM_MIM_LONGDATA, MM_MIM_LONGERROR};
+use super::HandlerData;
+use Ignore;
+
+pub extern "system" fn handle_input<T>(_: HMIDIIN,
+ input_status: UINT,
+ instance_ptr: DWORD_PTR,
+ midi_message: DWORD_PTR,
+ timestamp: DWORD) {
+ if input_status != MM_MIM_DATA && input_status != MM_MIM_LONGDATA && input_status != MM_MIM_LONGERROR { return; }
+
+ let data: &mut HandlerData<T> = unsafe { &mut *(instance_ptr as *mut HandlerData<T>) };
+
+ // Calculate time stamp.
+ data.message.timestamp = timestamp as u64 * 1000; // milliseconds -> microseconds
+
+ if input_status == MM_MIM_DATA { // Channel or system message
+ // Make sure the first byte is a status byte.
+ let status: u8 = (midi_message & 0x000000FF) as u8;
+ if !(status & 0x80 != 0) { return; }
+
+ // Determine the number of bytes in the MIDI message.
+ let nbytes: u16 = if status < 0xC0 { 3 }
+ else if status < 0xE0 { 2 }
+ else if status < 0xF0 { 3 }
+ else if status == 0xF1 {
+ if data.ignore_flags.contains(Ignore::Time) { return; }
+ else { 2 }
+ } else if status == 0xF2 { 3 }
+ else if status == 0xF3 { 2 }
+ else if status == 0xF8 && (data.ignore_flags.contains(Ignore::Time)) {
+ // A MIDI timing tick message and we're ignoring it.
+ return;
+ } else if status == 0xFE && (data.ignore_flags.contains(Ignore::ActiveSense)) {
+ // A MIDI active sensing message and we're ignoring it.
+ return;
+ } else { 1 };
+
+ // Copy bytes to our MIDI message.
+ let ptr = (&midi_message) as *const DWORD_PTR as *const u8;
+ let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr, nbytes as usize) };
+ data.message.bytes.extend_from_slice(bytes);
+ } else { // Sysex message (MIM_LONGDATA or MIM_LONGERROR)
+ let sysex = unsafe { &*(midi_message as *const MIDIHDR) };
+ if !data.ignore_flags.contains(Ignore::Sysex) && input_status != MM_MIM_LONGERROR {
+ // Sysex message and we're not ignoring it
+ let bytes: &[u8] = unsafe { slice::from_raw_parts(sysex.lpData as *const u8, sysex.dwBytesRecorded as usize) };
+ data.message.bytes.extend_from_slice(bytes);
+ // TODO: If sysex messages are longer than RT_SYSEX_BUFFER_SIZE, they
+ // are split in chunks. We could reassemble a single message.
+ }
+
+ // The WinMM API requires that the sysex buffer be requeued after
+ // input of each sysex message. Even if we are ignoring sysex
+ // messages, we still need to requeue the buffer in case the user
+ // decides to not ignore sysex messages in the future. However,
+ // it seems that WinMM calls this function with an empty sysex
+ // buffer when an application closes and in this case, we should
+ // avoid requeueing it, else the computer suddenly reboots after
+ // one or two minutes.
+ if (unsafe {*data.sysex_buffer.0[sysex.dwUser as usize]}).dwBytesRecorded > 0 {
+ //if ( sysex->dwBytesRecorded > 0 ) {
+ let in_handle = data.in_handle.as_ref().unwrap().0.lock().unwrap();
+ let result = unsafe { midiInAddBuffer(*in_handle, data.sysex_buffer.0[sysex.dwUser as usize], mem::size_of::<MIDIHDR>() as u32) };
+ drop(in_handle);
+ if result != MMSYSERR_NOERROR {
+ let _ = writeln!(stderr(), "\nError in handle_input: Requeuing WinMM input sysex buffer failed.\n");
+ }
+
+ if data.ignore_flags.contains(Ignore::Sysex) { return; }
+ } else { return; }
+ }
+
+ (data.callback)(data.message.timestamp, &data.message.bytes, data.user_data.as_mut().unwrap());
+
+ // Clear the vector for the next input message.
+ data.message.bytes.clear();
+} \ No newline at end of file
diff --git a/third_party/rust/midir/src/backend/winmm/mod.rs b/third_party/rust/midir/src/backend/winmm/mod.rs
new file mode 100644
index 0000000000..2d20004ece
--- /dev/null
+++ b/third_party/rust/midir/src/backend/winmm/mod.rs
@@ -0,0 +1,536 @@
+extern crate winapi;
+
+use std::{mem, ptr, slice};
+use std::ffi::OsString;
+use std::os::windows::ffi::OsStringExt;
+use std::sync::Mutex;
+use std::io::{Write, stderr};
+use std::thread::sleep;
+use std::time::Duration;
+use memalloc::{allocate, deallocate};
+use std::mem::MaybeUninit;
+use std::ptr::null_mut;
+
+use self::winapi::shared::basetsd::{DWORD_PTR, UINT_PTR};
+use self::winapi::shared::minwindef::{DWORD, UINT};
+
+use self::winapi::um::mmeapi::{midiInAddBuffer, midiInClose, midiInGetDevCapsW, midiInGetNumDevs,
+ midiInOpen, midiInPrepareHeader, midiInReset, midiInStart,
+ midiInStop, midiInUnprepareHeader, midiOutClose,
+ midiOutGetDevCapsW, midiOutGetNumDevs, midiOutLongMsg, midiOutOpen,
+ midiOutPrepareHeader, midiOutReset, midiOutShortMsg,
+ midiOutUnprepareHeader};
+
+use self::winapi::um::mmsystem::{CALLBACK_FUNCTION, CALLBACK_NULL, HMIDIIN, HMIDIOUT, LPMIDIHDR,
+ MIDIERR_NOTREADY, MIDIERR_STILLPLAYING, MIDIHDR, MIDIINCAPSW,
+ MIDIOUTCAPSW, MMSYSERR_BADDEVICEID, MMSYSERR_NOERROR, MMSYSERR_ALLOCATED};
+
+use {Ignore, MidiMessage};
+use errors::*;
+
+mod handler;
+
+const DRV_QUERYDEVICEINTERFACE: UINT = 0x80c;
+const DRV_QUERYDEVICEINTERFACESIZE: UINT = 0x80d;
+
+const RT_SYSEX_BUFFER_SIZE: usize = 1024;
+const RT_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 PartialEq for MidiInputPort {
+ fn eq(&self, other: &Self) -> bool {
+ self.interface_id == other.interface_id
+ }
+}
+
+pub struct MidiInputConnection<T> {
+ handler_data: Box<HandlerData<T>>,
+}
+
+impl MidiInputPort {
+ pub fn count() -> UINT {
+ unsafe { midiInGetNumDevs() }
+ }
+
+ fn interface_id(port_number: UINT) -> Result<Box<[u16]>, PortInfoError> {
+ let mut buffer_size: winapi::shared::minwindef::ULONG = 0;
+ let result = unsafe { winapi::um::mmeapi::midiInMessage(port_number as HMIDIIN, DRV_QUERYDEVICEINTERFACESIZE, &mut buffer_size as *mut _ as DWORD_PTR, 0) };
+ if result == MMSYSERR_BADDEVICEID {
+ return Err(PortInfoError::PortNumberOutOfRange)
+ } else if result != MMSYSERR_NOERROR {
+ return Err(PortInfoError::CannotRetrievePortName)
+ }
+ let mut buffer = Vec::<u16>::with_capacity(buffer_size as usize / 2);
+ unsafe {
+ let result = winapi::um::mmeapi::midiInMessage(port_number as HMIDIIN, DRV_QUERYDEVICEINTERFACE, buffer.as_mut_ptr() as DWORD_PTR, 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<String, PortInfoError> {
+ let mut device_caps: MaybeUninit<MIDIINCAPSW> = MaybeUninit::uninit();
+ let result = unsafe { midiInGetDevCapsW(port_number as UINT_PTR, device_caps.as_mut_ptr(), mem::size_of::<MIDIINCAPSW>() 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 = device_caps.szPname;
+ let output = from_wide_ptr(pname.as_ptr(), pname.len()).to_string_lossy().into_owned();
+ Ok(output)
+ }
+
+ fn from_port_number(port_number: UINT) -> Result<Self, PortInfoError> {
+ Ok(MidiInputPort {
+ name: Self::name(port_number)?,
+ interface_id: Self::interface_id(port_number)?
+ })
+ }
+
+ fn current_port_number(&self) -> Option<UINT> {
+ 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([LPMIDIHDR; RT_SYSEX_BUFFER_COUNT]);
+unsafe impl Send for SysexBuffer {}
+
+struct MidiInHandle(Mutex<HMIDIIN>);
+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<T> {
+ message: MidiMessage,
+ sysex_buffer: SysexBuffer,
+ in_handle: Option<MidiInHandle>,
+ ignore_flags: Ignore,
+ callback: Box<dyn FnMut(u64, &[u8], &mut T) + Send + 'static>,
+ user_data: Option<T>
+}
+
+impl MidiInput {
+ pub fn new(_client_name: &str) -> Result<Self, InitError> {
+ Ok(MidiInput { ignore_flags: Ignore::None })
+ }
+
+ pub fn ignore(&mut self, flags: Ignore) {
+ self.ignore_flags = flags;
+ }
+
+ pub(crate) fn ports_internal(&self) -> Vec<::common::MidiInputPort> {
+ 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(::common::MidiInputPort {
+ imp: port
+ });
+ }
+ result
+ }
+
+ pub fn port_count(&self) -> usize {
+ MidiInputPort::count() as usize
+ }
+
+ pub fn port_name(&self, port: &MidiInputPort) -> Result<String, PortInfoError> {
+ Ok(port.name.clone())
+ }
+
+ pub fn connect<F, T: Send>(
+ self, port: &MidiInputPort, _port_name: &str, callback: F, data: T
+ ) -> Result<MidiInputConnection<T>, ConnectError<MidiInput>>
+ 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(); RT_SYSEX_BUFFER_COUNT]),
+ in_handle: None,
+ ignore_flags: self.ignore_flags,
+ callback: Box::new(callback),
+ user_data: Some(data)
+ });
+
+ let mut in_handle: MaybeUninit<HMIDIIN> = MaybeUninit::uninit();
+ let handler_data_ptr: *mut HandlerData<T> = &mut *handler_data;
+ let result = unsafe { midiInOpen(in_handle.as_mut_ptr(),
+ port_number as UINT,
+ handler::handle_input::<T> as DWORD_PTR,
+ 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..RT_SYSEX_BUFFER_COUNT {
+ handler_data.sysex_buffer.0[i] = Box::into_raw(Box::new(MIDIHDR {
+ lpData: unsafe { allocate(RT_SYSEX_BUFFER_SIZE/*, mem::align_of::<u8>()*/) } as *mut i8,
+ dwBufferLength: RT_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::<MIDIHDR>() 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::<MIDIHDR>() 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: handler_data
+ })
+ }
+}
+
+impl<T> MidiInputConnection<T> {
+ 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 his lock, see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo
+ let in_handle_lock = self.handler_data.in_handle.as_ref().unwrap().0.lock().unwrap();
+
+ // 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..RT_SYSEX_BUFFER_COUNT {
+ let result;
+ unsafe {
+ result = midiInUnprepareHeader(*in_handle_lock, self.handler_data.sysex_buffer.0[i], mem::size_of::<MIDIHDR>() as u32);
+ deallocate((*self.handler_data.sysex_buffer.0[i]).lpData as *mut u8, RT_SYSEX_BUFFER_SIZE/*, mem::align_of::<u8>()*/);
+ // 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<T> Drop for MidiInputConnection<T> {
+ 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 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 {
+ pub fn count() -> UINT {
+ unsafe { midiOutGetNumDevs() }
+ }
+
+ fn interface_id(port_number: UINT) -> Result<Box<[u16]>, PortInfoError> {
+ let mut buffer_size: winapi::shared::minwindef::ULONG = 0;
+ let result = unsafe { winapi::um::mmeapi::midiOutMessage(port_number as HMIDIOUT, DRV_QUERYDEVICEINTERFACESIZE, &mut buffer_size as *mut _ as DWORD_PTR, 0) };
+ if result == MMSYSERR_BADDEVICEID {
+ return Err(PortInfoError::PortNumberOutOfRange)
+ } else if result != MMSYSERR_NOERROR {
+ return Err(PortInfoError::CannotRetrievePortName)
+ }
+ let mut buffer = Vec::<u16>::with_capacity(buffer_size as usize / 2);
+ unsafe {
+ let result = winapi::um::mmeapi::midiOutMessage(port_number as HMIDIOUT, DRV_QUERYDEVICEINTERFACE, buffer.as_mut_ptr() as DWORD_PTR, 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<String, PortInfoError> {
+ let mut device_caps: MaybeUninit<MIDIOUTCAPSW> = MaybeUninit::uninit();
+ let result = unsafe { midiOutGetDevCapsW(port_number as UINT_PTR, device_caps.as_mut_ptr(), mem::size_of::<MIDIOUTCAPSW>() 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 = device_caps.szPname;
+ let output = from_wide_ptr(pname.as_ptr(), pname.len()).to_string_lossy().into_owned();
+ Ok(output)
+ }
+
+ fn from_port_number(port_number: UINT) -> Result<Self, PortInfoError> {
+ Ok(MidiOutputPort {
+ name: Self::name(port_number)?,
+ interface_id: Self::interface_id(port_number)?
+ })
+ }
+
+ fn current_port_number(&self) -> Option<UINT> {
+ 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<Self, InitError> {
+ Ok(MidiOutput)
+ }
+
+ pub(crate) fn ports_internal(&self) -> Vec<::common::MidiOutputPort> {
+ 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(::common::MidiOutputPort {
+ imp: port
+ });
+ }
+ result
+ }
+
+ pub fn port_count(&self) -> usize {
+ MidiOutputPort::count() as usize
+ }
+
+ pub fn port_name(&self, port: &MidiOutputPort) -> Result<String, PortInfoError> {
+ Ok(port.name.clone())
+ }
+
+ pub fn connect(self, port: &MidiOutputPort, _port_name: &str) -> Result<MidiOutputConnection, ConnectError<MidiOutput>> {
+ let port_number = match port.current_port_number() {
+ Some(p) => p,
+ None => return Err(ConnectError::new(ConnectErrorKind::InvalidPort, self))
+ };
+ let mut out_handle: MaybeUninit<HMIDIOUT> = MaybeUninit::uninit();
+ let result = unsafe { midiOutOpen(out_handle.as_mut_ptr(), port_number as UINT, 0, 0, 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: buffer.as_mut_ptr() as *mut i8,
+ 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::<MIDIHDR>() 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, &mut sysex, mem::size_of::<MIDIHDR>() 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::<MIDIHDR>() 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 packet: DWORD = 0;
+ let ptr = &packet as *const u32 as *mut u8;
+ for i in 0..nbytes {
+ unsafe { *ptr.offset(i as isize) = message[i] };
+ }
+
+ // 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);
+ }
+ }
+}