use std::io::{stderr, Write}; use std::{mem, slice}; use windows::Win32::Media::Audio::{midiInAddBuffer, HMIDIIN, MIDIHDR}; use windows::Win32::Media::{MMSYSERR_NOERROR, MM_MIM_DATA, MM_MIM_LONGDATA, MM_MIM_LONGERROR}; use crate::Ignore; use super::{DWORD, DWORD_PTR, UINT}; use super::HandlerData; pub extern "system" fn handle_input( _: 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 = unsafe { &mut *(instance_ptr as *mut HandlerData) }; // 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.0, sysex.dwBytesRecorded as usize) }; data.message.bytes.extend_from_slice(bytes); // TODO: If sysex messages are longer than MIDIR_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] }).dwBytesRecorded > 0 { //if ( sysex->dwBytesRecorded > 0 ) { let in_handle = data.in_handle.as_ref().unwrap().0.lock(); let result = unsafe { midiInAddBuffer( *in_handle, data.sysex_buffer.0[sysex.dwUser], mem::size_of::() 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(); }