private int ParseSysex(byte[] data, int offset, int length, ref MidiMessage message) { var endOfSysexIndex = Array.FindIndex(data, 1, b => b == (byte)EMidiCommand.EndOfSystemExclusive); if (endOfSysexIndex < 0) { // Incomplete sysex return(-1); } // Build body var body = new byte[endOfSysexIndex - 1]; Array.Copy(data, 1, body, 0, body.Length); // Build message message = new MidiSysexMessage(body); return(endOfSysexIndex + 1); }
private MidiMessage[] GetMessages() { if (messages == null) { var msgList = new List <MidiMessage>(); for (int b = 0; b < data.Length; b++) { MidiMessage result = null; uint rawCommand = data[b]; uint channel = (rawCommand & 0x0f) + 1; EMidiCommand command = (EMidiCommand)(rawCommand & 0xF0); if (command != EMidiCommand.SystemMessageMask) { // Common message switch (command) { case EMidiCommand.NoteOff: case EMidiCommand.NoteOn: case EMidiCommand.KeyAfterTouch: result = new MidiNoteMessage(channel, command, data[++b], data[++b]); break; case EMidiCommand.ControlChange: result = new MidiControlChangeMessage(channel, data[++b], data[++b]); break; case EMidiCommand.PatchChange: result = new MidiPatchChangeMessage(channel, data[++b]); break; case EMidiCommand.ChannelAfterTouch: result = new MidiChannelAfterTouchMessage(channel, data[++b]); break; case EMidiCommand.PitchWheelChange: result = new MidiPitchChangeMessage(channel, data[++b] + ((uint)data[++b] << 7)); break; default: throw new InvalidOperationException("Unsupported message in sequence: " + command); } } else if (rawCommand == (uint)EMidiCommand.SystemExclusive) { var sysexBody = new List <byte>(); sysexBody.Add((byte)EMidiCommand.SystemExclusive); byte bodyByte = 0; while ((bodyByte = data[++b]) != (uint)EMidiCommand.EndOfSystemExclusive) { sysexBody.Add(bodyByte); } sysexBody.Add((byte)EMidiCommand.EndOfSystemExclusive); result = new MidiSysexMessage(sysexBody.ToArray()); } else { // System message throw new InvalidOperationException("Only sysex and common messages supported in sequence"); } msgList.Add(result); } messages = msgList.ToArray(); } return(messages); }
// 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)); } } } } } }
private void TestMidiInOut() { Console.WriteLine("=== Test midi in-out via LoopBe"); var inPorts = PortEnumerator.InputPorts; var loopIn = inPorts.FirstOrDefault(p => p.Name.StartsWith("LoopBe")); if (loopIn == null) { Console.WriteLine(" No LoopBe input port found - skipping test"); return; } Console.WriteLine(" Found LoopBe input midi port: " + loopIn.Name); var outPorts = PortEnumerator.OutputPorts; var loopOut = outPorts.First(p => p.Name.StartsWith("LoopBe")); if (loopOut == null) { Console.WriteLine(" No LoopBe output port found - skipping test"); return; } Console.WriteLine(" Found LoopBe output midi port: " + loopOut.Name); // Open the ports loopGotShortMessage = false; loopGotLongMessage = false; loopIn.MidiInputReceived += LoopInCallback; if (loopIn.Open() != null) { throw new InvalidOperationException("Cannot open loop input"); } if (loopIn.Start() != null) { throw new InvalidOperationException("Cannot start loop input"); } if (loopOut.Open() != null) { throw new InvalidOperationException("Cannot open loop output"); } // Send a short message var message = new MidiNoteMessage(2, EMidiCommand.NoteOn, 63, 100); if (loopOut.Send(message) != null) { throw new InvalidOperationException("Cannot send short message to loop output"); } // Wait until the input port gets it while (!loopGotShortMessage) { Thread.Sleep(10); } // Send a long message (sysex) var sysex = new MidiSysexMessage(new byte[] { 0xF0, 0x55, 0x15, 0x02, 0x19, 0x64, 0xF7 }); if (loopOut.Send(sysex) != null) { throw new InvalidOperationException("Cannot send short message to loop output"); } // Wait until the input port gets it while (!loopGotLongMessage) { Thread.Sleep(10); } // Close the ports if (loopIn.Close() != null) { throw new InvalidOperationException("Cannot close loop input"); } if (loopOut.Close() != null) { throw new InvalidOperationException("Cannot close loop output"); } Console.WriteLine(" Loop test completed successfully."); }
// Parse a midi message from a midi file private static MidiMessage ParseMidiMessage(MidiFileFormatReader mffr, uint runningStatus) { MidiMessage result = null; uint rawCommand; uint p1 = 0; bool alreadyReadP1 = false; // Read the command rawCommand = mffr.ReadUInt8(); if ((rawCommand & 0x80) == 0x80) { // New running status runningStatus = rawCommand; } else { // What we just read is actually p1 of a common message alreadyReadP1 = true; p1 = rawCommand; rawCommand = runningStatus; } // Get channel uint channel = (rawCommand & 0x0f) + 1; EMidiCommand command = (EMidiCommand)(rawCommand & 0xF0); if (command == EMidiCommand.SystemMessageMask) { // System message channel = 0; command = (EMidiCommand)rawCommand; switch (command) { // System messages - Exclusive case EMidiCommand.SystemExclusive: { // Determine length of body + end-of-exclusive uint length = mffr.ReadVarLengthQuantity(); byte[] sysexBody = new byte[length + 1]; // Read body sysexBody[0] = (byte)EMidiCommand.SystemExclusive; for (uint b = 0; b < length; b++) { sysexBody[b + 1] = mffr.ReadUInt8(); } // Verify end-of-exclusive if (sysexBody[length] != (byte)EMidiCommand.EndOfSystemExclusive) { throw new FormatException("Sysex of length " + length + " ending with " + sysexBody[length] + " instead of F7"); } result = new MidiSysexMessage(sysexBody); break; } // System messages - Common case EMidiCommand.SongPosition: case EMidiCommand.SongSelect: case EMidiCommand.TuneRequest: // System messages - Real time case EMidiCommand.TimingClock: case EMidiCommand.StartSequence: case EMidiCommand.ContinueSequence: case EMidiCommand.StopSequence: case EMidiCommand.ActiveSensing: throw new FormatException(string.Format("Found event type {0} - not supported!", command)); // Meta event case EMidiCommand.MetaEvent: result = ParseMidiMetaMessage(mffr); break; } } else { if (!alreadyReadP1) { p1 = mffr.ReadUInt8(); } // Channel message switch (command) { case EMidiCommand.NoteOff: case EMidiCommand.NoteOn: case EMidiCommand.KeyAfterTouch: result = new MidiNoteMessage(channel, command, p1, mffr.ReadUInt8()); break; case EMidiCommand.ControlChange: result = new MidiControlChangeMessage(channel, p1, mffr.ReadUInt8()); break; case EMidiCommand.PatchChange: result = new MidiPatchChangeMessage(channel, p1); break; case EMidiCommand.ChannelAfterTouch: result = new MidiChannelAfterTouchMessage(channel, p1); break; case EMidiCommand.PitchWheelChange: result = new MidiPitchChangeMessage(channel, p1 + ((uint)mffr.ReadUInt8() << 7)); break; } } result.RunningStatus = runningStatus; return(result); }
public SerMidiSysexMessage(MidiSysexMessage msg) : base(msg) { this.body = msg.Body; }