using System; using System.Collections.Concurrent; using System.Threading; using static RushMidiLibrary.MidiApiWindows; namespace RushMidiLibrary { public class MidiInputDevice : MidiDevice { private MidiApiWindows.MidiInProc _inputCallback; private UIntPtr _deviceId; private MidiApiWindows.HMIDIIN _deviceHandle; private Thread _messageProcessingThread; private bool _messageProcessingThreadExitRequest = false; readonly BlockingCollection _messageQueue = new BlockingCollection(); private struct Message { public MidiApiWindows.HMIDIIN hMidiIn; public MidiApiWindows.MidiInMessage wMsg; public UIntPtr dwInstance; public UIntPtr dwParam1; public UIntPtr dwParam2; } public MidiInputDevice(UIntPtr deviceId, MidiApiWindows.MIDIINCAPS caps) { _deviceId = deviceId; } public static MidiInputDevice[] Enumerate() { var devs = MidiApiWindows.midiInGetNumDevs(); MidiInputDevice[] result = new MidiInputDevice[devs]; for (uint deviceId = 0; deviceId < devs; deviceId++) { MidiApiWindows.MIDIINCAPS caps; MidiApiWindows.midiInGetDevCaps((UIntPtr)deviceId, out caps); result[deviceId] = new MidiInputDevice((UIntPtr)deviceId, caps); } return result; } public void Open() { _inputCallback = this.InputCallback; BeginMessageProcessing(); try { CheckMMResult(MidiApiWindows.midiInOpen(out _deviceHandle, _deviceId, _inputCallback, (UIntPtr)0)); } catch (Exception) { EndMessageProcessing(); throw; } } public void Start() { CheckMMResult(MidiApiWindows.midiInStart(_deviceHandle)); } private void InputCallback(MidiApiWindows.HMIDIIN hMidiIn, MidiApiWindows.MidiInMessage wMsg, UIntPtr dwInstance, UIntPtr dwParam1, UIntPtr dwParam2) { Message message = new Message(); message.hMidiIn = hMidiIn; message.wMsg = wMsg; message.dwInstance = dwInstance; message.dwParam1 = dwParam1; message.dwParam2 = dwParam2; _messageQueue.Add(message); } private void BeginMessageProcessing() { _messageProcessingThreadExitRequest = false; _messageProcessingThread = new Thread(MessageProcessingThreadHandler); _messageProcessingThread.Start(); } private void MessageProcessingThreadHandler() { while (!_messageProcessingThreadExitRequest) { Message message; if (_messageQueue.TryTake(out message, 100)) { HandleMessage(message); } } } private void EndMessageProcessing() { _messageProcessingThreadExitRequest = true; _messageProcessingThread.Join(); } private void HandleMessage(Message message) { switch (message.wMsg) { case MidiInMessage.MIM_DATA: HandleMessageData(new MidiShortMessage(message.dwParam1, message.dwParam2)); break; case MidiInMessage.MIM_LONGDATA: HandleMessageLongData(message.dwParam1, message.dwParam2); break; default: break; } } private void HandleMessageData(MidiShortMessage message) { if (message.IsNoteOn) { MidiChannel channel; int noteId; int velocity; uint timestamp; message.GetNoteOn(out channel, out noteId, out velocity, out timestamp); var note = new MidiNote(noteId); Console.WriteLine($"Received short message NoteOn: Name={note.Name} Octave={note.OctaveName} Channel={channel.ToString()}, NoteId={noteId}, Velocity={velocity}, Timestapm={timestamp}"); } else if (message.IsNoteOff) { MidiChannel channel; int noteId; int velocity; uint timestamp; message.GetNoteOff(out channel, out noteId, out velocity, out timestamp); var note = new MidiNote(noteId); Console.WriteLine($"Received short message NoteOff: Name={note.Name} Octave={note.OctaveName} Channel={channel.ToString()}, NoteId={noteId}, Velocity={velocity}, Timestapm={timestamp}"); } else { Console.WriteLine($"Received short message"); } } private void HandleMessageLongData(UIntPtr dwParam1, UIntPtr dwParam2) { Console.WriteLine($"Received long message"); } } }