예제 #1
0
        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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        // 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));
                            }
                        }
                    }
                }
            }
        }
예제 #4
0
        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.");
        }
예제 #5
0
        // 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);
        }
예제 #6
0
 public SerMidiSysexMessage(MidiSysexMessage msg)
     : base(msg)
 {
     this.body = msg.Body;
 }