// Start the port and setup buffers to receives Sysex public string Start() { // Allocate buffer array if (buffers == null) { const int numBuffers = 4; const int bufferSize = 4096; // ZZZ Param? buffers = new MidiBuffer[numBuffers]; // Allocate and prepare the buffers for (int b = 0; b < buffers.Length; b++) { // Create buffer buffers[b] = new MidiBuffer(bufferSize, (IntPtr)b); // Prepare the buffer and give it to windows EMMError st = buffers[b].AddMidiInBuffer(handle); if (st != EMMError.NOERROR) { return(WindowsUtil.StrError(st)); } } } // Finally, start the port return(WindowsUtil.StrError(NativeMethods.midiInStart(handle))); }
// Send a message public string Send(MidiMessage m) { var st = EMMError.NOERROR; if (m is MidiSysexMessage) { // Sysex messages are sent using a buffer. var sysex = m as MidiSysexMessage; var buffer = new MidiBuffer(sysex.Body); using (buffer) { // Prepare the buffer st = buffer.PrepareForMidiOut(handle); if (st == EMMError.NOERROR) { // Send the sysex st = NativeMethods.midiOutLongMsg(handle, buffer.Header, buffer.HeaderSize); } if (st == EMMError.NOERROR) { // Wait until it is sent // Note: this is simple but weak... If performance becomes necessary // we can declare a callback, not wait here (just queue the buffer) and // unprepare it when we receive the appropriate message in the callback while (!buffer.IsFree()) { Thread.Sleep(1); } // unprepare the buffer st = buffer.UnprepareForMidiOut(handle); } } } else { uint sm = m.GetAsShortMessage(); if (sm != uint.MaxValue) { st = NativeMethods.midiOutShortMsg(handle, sm); } } return(WindowsUtil.StrError(st)); }
// Forward callback to user-provided function private void MidiProc(IntPtr hMidiIn, EMMMidiMessages wMsg, IntPtr dwInstance, IntPtr dwParam1, IntPtr dwParam2) { if (wMsg == EMMMidiMessages.MIM_DATA) { if (MidiInputReceived != null) { // Make a midi event out of the incoming params MidiEvent e = MidiInParser.ParseMimDataMessage((uint)dwParam1, (uint)dwParam2); // Give it to the user MidiInputReceived.Invoke(this, new WindowsMidiEventArgs(e, dwParam1, dwParam2)); } } else if (wMsg == EMMMidiMessages.MIMLONG_DATA) { // The first parameter is a pointer to a buffer descriptor var header = dwParam1; // Find this buffer in our buffer set int index = (int)MidiBuffer.GetUserData(header); if (index < 0 || index >= buffers.Length) { throw new InvalidOperationException("midiInputPort: MIMLONG_DATA buffer not found - index " + index); } var buffer = buffers[index]; if (buffer != null) { // Get data from buffer var data = buffer.GetRecordedData(); if (data.Length == 0) { // This happens on Reset(), all the buffers are freed by windows and we get a MIMLONG_DATA for each of them. // Free the buffer. buffer.RemoveMidiInBuffer(handle); buffer.Dispose(); buffers[index] = null; } else { // Give the buffer back to windows, we are done with it buffer.AddMidiInBuffer(handle); // If we have a callback, either generate a sysex or // memorize the data if we don't have a full sysex if (MidiInputReceived != null) { MidiSysexMessage sysex = null; if (data[0] == (byte)EMidiCommand.SystemExclusive) { // This buffer contains the beginning of the sysex - check we don't have an ongoing sysex if (this.partialSysex != null) { throw new InvalidOperationException("midiInputPort: Beginning of new sysex received while previous one still on-going"); } if (data[data.Length - 1] == (byte)EMidiCommand.EndOfSystemExclusive) { // This buffer contains a full sysex sysex = new MidiSysexMessage(data); } else { // This buffer contains the beginning of a sysex this.partialSysex = data; } } else { // This buffer is a continuation of a sysex. Verify we have an ongoing sysex if (this.partialSysex == null) { throw new InvalidOperationException("midiInputPort: Continuation sysex without beginning"); } // Concatenate the partial data and the new data var dataSoFar = new byte[partialSysex.Length + data.Length]; partialSysex.CopyTo(dataSoFar, 0); data.CopyTo(dataSoFar, dataSoFar.Length); if (data[data.Length - 1] == (byte)EMidiCommand.EndOfSystemExclusive) { // This buffer concludes the sysex sysex = new MidiSysexMessage(dataSoFar); this.partialSysex = null; } else { // This buffer contains a partial sysex, add it to the partial sysex this.partialSysex = dataSoFar; } } // Create sysex event if we have a sysex if (sysex != null) { // Make a midi event out of the sysex var e = new MidiEvent(sysex, (uint)dwParam2); // Give it to the user MidiInputReceived.Invoke(this, new WindowsMidiEventArgs(e, dwParam1, dwParam2)); } } } } } }