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};
fn current_port_number(&self) -> Option<UINT> { for i in0..Self::count() { iflet Ok(name) = Self::name(i) { if name != self.name { continue;
} iflet Ok(id) = Self::interface_id(i) { if id == self.interface_id { return Some(i);
}
}
}
}
None
}
}
struct SysexBuffer([*mut MIDIHDR; MIDIR_SYSEX_BUFFER_COUNT]); unsafeimpl Send for SysexBuffer {}
struct MidiInHandle(Mutex<HMIDIIN>); unsafeimpl 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>,
}
pub(crate) fn ports_internal(&self) -> Vec<crate::common::MidiInputPort> { let count = MidiInputPort::count(); letmut result = Vec::with_capacity(count as usize); for i in0..count { let port = match MidiInputPort::from_port_number(i) {
Ok(p) => p,
Err(_) => continue,
};
result.push(crate::common::MidiInputPort { imp: port });
}
result
}
pubfn port_count(&self) -> usize {
MidiInputPort::count() as usize
}
letmut 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,
Some(handler::handle_input::<T> 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,
));
} elseif 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 in0..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::<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,
));
}
}
// 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,
));
}
// 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 in0..MIDIR_SYSEX_BUFFER_COUNT { let result; unsafe {
result = midiInUnprepareHeader(
*in_handle_lock, self.handler_data.sysex_buffer.0[i],
mem::size_of::<MIDIHDR>() as u32,
);
dealloc(
(*self.handler_data.sysex_buffer.0[i]).lpData.0as *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<T> Drop for MidiInputConnection<T> { fn drop(&mutself) { // If user_data has been emptied, we know that we already have closed the connection ifself.handler_data.user_data.is_some() { self.close_internal()
}
}
}
pub(crate) fn ports_internal(&self) -> Vec<crate::common::MidiOutputPort> { let count = MidiOutputPort::count(); letmut result = Vec::with_capacity(count as usize); for i in0..count { let port = match MidiOutputPort::from_port_number(i) {
Ok(p) => p,
Err(_) => continue,
};
result.push(crate::common::MidiOutputPort { imp: port });
}
result
}
pubfn port_count(&self) -> usize {
MidiOutputPort::count() as usize
}
pubfn 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)),
}; letmut out_handle: MaybeUninit<HMIDIOUT> = 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,
));
} elseif 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 { pubfn close(self) -> MidiOutput { // The actual closing is done by the implementation of Drop
MidiOutput // In this API this is a noop
}
pubfn send(&mutself, 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 letmut buffer = message.to_vec();
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, &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. letmut packet: u32 = 0; let ptr = std::ptr::addr_of_mut!(packet).cast::<u8>(); 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(&mutself) { unsafe {
midiOutReset(self.out_handle);
midiOutClose(self.out_handle);
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.3 Sekunden
(vorverarbeitet am 2026-08-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.