Ejemplo n.º 1
0
        private void ReadThreadStart()
        {
            byte[] buf         = new byte[512];
            byte[] overflowBuf = null;

            // Compute number of pollfd structures required to poll
            int numPollStructs = AlsaNativeMethods.Snd_rawmidi_poll_descriptors_count(handle);

            // Allocate space for the poll structures
            IntPtr pollAreaPtr = Marshal.AllocHGlobal(numPollStructs * AlsaNativeMethods.POLLFDSZ);

            int st = AlsaNativeMethods.Snd_rawmidi_poll_descriptors(handle, pollAreaPtr, (uint)numPollStructs);

            while (!stop)
            {
                st = AlsaNativeMethods.Poll(pollAreaPtr, numPollStructs, 200);
                if (st < 0)
                {
                    var errno = Marshal.GetLastWin32Error();
                    if (errno == 4 /* EINTR */)
                    {
                        continue;
                    }
                    Console.WriteLine("MidiIn: Cannot poll - errno = " + errno);
                    break;
                }
                if (st == 0)
                {
                    continue;
                }

                // We got something - scan the poll struct
                ushort revent = 0;
                st = AlsaNativeMethods.Snd_rawmidi_poll_descriptors_revents(
                    handle, pollAreaPtr, (uint)numPollStructs, ref revent);

                if (st < 0)
                {
                    Console.WriteLine("MidiIn: Cannot parse poll - " + AlsaUtils.StrError(st));
                    break;
                }

                AlsaNativeMethods.EPoll evt = (AlsaNativeMethods.EPoll)revent;
                if (evt.HasFlag(AlsaNativeMethods.EPoll.POLLERR) ||
                    evt.HasFlag(AlsaNativeMethods.EPoll.POLLHUP))
                {
                    break;
                }

                if (evt.HasFlag(AlsaNativeMethods.EPoll.POLLIN))
                {
                    // We have something to read - read it
                    int nRead = (int)AlsaNativeMethods.Snd_rawmidi_read(handle, buf, (uint)buf.Length);
                    if (nRead < 0)
                    {
                        int errno = Marshal.GetLastWin32Error();
                        if (errno == 4 /* EINTR */)
                        {
                            continue;
                        }

                        Console.WriteLine("MidiIn: Cannot parse poll - " + AlsaUtils.StrError(st));
                        break;
                    }

                    // Combine with overflow buffer if we had an overflow last time
                    if (overflowBuf != null)
                    {
                        var newBuf = new byte[buf.Length + overflowBuf.Length];
                        Array.Copy(overflowBuf, 0, newBuf, 0, overflowBuf.Length);
                        Array.Copy(buf, 0, newBuf, overflowBuf.Length, buf.Length);
                        buf         = newBuf;
                        overflowBuf = null;
                    }

                    // Translate the input to midi message
                    int nParsed = 0;
                    while (true)
                    {
                        st = ParseBuffer(buf, nParsed, nRead, out MidiMessage midiMessage);
                        if (st < 0)
                        {
                            // buffer contains an incomplete message
                            overflowBuf = buf;
                            buf         = new byte[overflowBuf.Length];
                        }
                        else if (st == 0)
                        {
                            // Message not supported - drop everything.
                            break;
                        }
                        else // st > 0
                        {
                            // Send message
                            var midiEvent    = new MidiEvent(midiMessage, 0);
                            var midiEventArg = new WindowsMultiMedia.WindowsMidiEventArgs(midiEvent, IntPtr.Zero, IntPtr.Zero);
                            MidiInputReceived?.Invoke(this, midiEventArg);

                            // Update counters
                            nParsed += st;
                            nRead   -= st;

                            if (nRead <= 0)
                            {
                                // Normal end
                                break;
                            }
                        }
                    }
                }
            }

            Marshal.FreeHGlobal(pollAreaPtr);
        }
Ejemplo n.º 2
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));
                            }
                        }
                    }
                }
            }
        }