Esempio n. 1
0
        /// <summary>
        /// Sends events directly to the event queue without buffering
        /// </summary>
        /// <param name="events">The events to send</param>
        /// <remarks>The total size of the events must be less than 64kb</remarks>
        public void SendDirect(IEnumerable <MidiEvent> events)
        {
            if (null == events)
            {
                throw new ArgumentNullException("events");
            }
            if (IntPtr.Zero == _handle)
            {
                throw new InvalidOperationException("The stream is closed.");
            }
            if (IntPtr.Zero != _sendHeader.lpData)
            {
                throw new InvalidOperationException("The stream is busy playing.");
            }
            int    baseEventSize = Marshal.SizeOf(typeof(MIDIEVENT));
            int    blockSize     = 0;
            IntPtr eventPointer  = _sendEventBuffer;
            var    ofs           = 0;
            var    ptrOfs        = 0;
            var    hasEvents     = false;

            foreach (var @event in events)
            {
                hasEvents = true;
                if (0xF0 != (@event.Message.Status & 0xF0))
                {
                    blockSize += baseEventSize;
                    if (_SendBufferSize <= blockSize)
                    {
                        throw new ArgumentException("There are too many events in the event buffer - maximum size must be 64k", "events");
                    }
                    var se = new MIDIEVENT();
                    se.dwDeltaTime = @event.Position + ofs;
                    se.dwStreamId  = 0;
                    se.dwEvent     = MidiUtility.PackMessage(@event.Message);
                    Marshal.StructureToPtr(se, new IntPtr(ptrOfs + eventPointer.ToInt64()), false);
                    ptrOfs += baseEventSize;
                    ofs     = 0;
                }
                else if (0xFF == @event.Message.Status)
                {
                    var mm = @event.Message as MidiMessageMeta;
                    if (0x51 == mm.Data1)                     // tempo
                    {
                        blockSize += baseEventSize;
                        if (_SendBufferSize <= blockSize)
                        {
                            throw new ArgumentException("There are too many events in the event buffer - maximum size must be 64k", "events");
                        }

                        var se = new MIDIEVENT();
                        se.dwDeltaTime = @event.Position + ofs;
                        se.dwStreamId  = 0;
                        se.dwEvent     = (mm.Data[0] << 16) | (mm.Data[1] << 8) | mm.Data[2] | (MEVT_TEMPO << 24);
                        Marshal.StructureToPtr(se, new IntPtr(ptrOfs + eventPointer.ToInt64()), false);
                        ptrOfs += baseEventSize;
                        ofs     = 0;
                        // TODO: This signal is sent too early. It should really wait until after the
                        // MEVT_TEMPO message is processed by the driver, but i have no easy way to
                        // do that. All we can do is hope, here
                        Interlocked.Exchange(ref _tempoSyncMessagesSentCount, 0);
                    }
                    else if (0x2f == mm.Data1)                     // end track
                    {
                        blockSize += baseEventSize;
                        if (_SendBufferSize <= blockSize)
                        {
                            throw new ArgumentException("There are too many events in the event buffer - maximum size must be 64k", "events");
                        }

                        // add a NOP message to it just to pad our output in case we're looping
                        var se = new MIDIEVENT();
                        se.dwDeltaTime = @event.Position + ofs;
                        se.dwStreamId  = 0;
                        se.dwEvent     = (MEVT_NOP << 24);
                        Marshal.StructureToPtr(se, new IntPtr(ptrOfs + eventPointer.ToInt64()), false);
                        ptrOfs += baseEventSize;
                        ofs     = 0;
                    }
                    else
                    {
                        ofs = @event.Position;
                    }
                }
                else                 // sysex
                {
                    var msx = @event.Message as MidiMessageSysex;
                    var dl  = msx.Data.Length + 1;
                    if (0 != (dl % 4))
                    {
                        dl += 4 - (dl % 4);
                    }
                    blockSize += baseEventSize + dl;
                    if (_SendBufferSize <= blockSize)
                    {
                        throw new ArgumentException("There are too many events in the event buffer - maximum size must be 64k", "events");
                    }

                    var se = new MIDIEVENT();
                    se.dwDeltaTime = @event.Position + ofs;
                    se.dwStreamId  = 0;
                    se.dwEvent     = MEVT_F_LONG | (msx.Data.Length + 1);
                    Marshal.StructureToPtr(se, new IntPtr(ptrOfs + eventPointer.ToInt64()), false);
                    ptrOfs += baseEventSize;
                    Marshal.WriteByte(new IntPtr(ptrOfs + eventPointer.ToInt64()), msx.Status);
                    Marshal.Copy(msx.Data, 0, new IntPtr(ptrOfs + eventPointer.ToInt64() + 1), msx.Data.Length);

                    ptrOfs += dl;
                    ofs     = 0;
                }
            }
            if (hasEvents)
            {
                _sendHeader = default(MIDIHDR);
                Interlocked.Exchange(ref _sendHeader.lpData, eventPointer);
                _sendHeader.dwBufferLength = _sendHeader.dwBytesRecorded = unchecked ((uint)blockSize);
                _sendEventBuffer           = eventPointer;
                int headerSize = Marshal.SizeOf(typeof(MIDIHDR));
                _CheckOutResult(midiOutPrepareHeader(_handle, ref _sendHeader, headerSize));
                _CheckOutResult(midiStreamOut(_handle, ref _sendHeader, headerSize));
            }
        }
Esempio n. 2
0
        void _SendBlock()
        {
            if (null == _sendQueue)
            {
                return;
            }
            if (IntPtr.Zero == _handle)
            {
                throw new InvalidOperationException("The stream is closed.");
            }

            if (IntPtr.Zero != Interlocked.CompareExchange(ref _sendHeader.lpData, _sendEventBuffer, IntPtr.Zero))
            {
                throw new InvalidOperationException("The stream is busy playing.");
            }

            int    baseEventSize = Marshal.SizeOf(typeof(MIDIEVENT));
            int    blockSize     = 0;
            IntPtr eventPointer  = _sendEventBuffer;
            var    ofs           = 0;
            var    ptrOfs        = 0;

            for (; _sendQueuePosition < _sendQueue.Count; Interlocked.Exchange(ref _sendQueuePosition, _sendQueuePosition + 1))
            {
                var @event = _sendQueue[_sendQueuePosition];
                if (0x00 != @event.Message.Status && 0xF0 != (@event.Message.Status & 0xF0))
                {
                    if (_SendBufferSize < blockSize + baseEventSize)
                    {
                        break;
                    }
                    blockSize += baseEventSize;
                    var se = new MIDIEVENT();
                    se.dwDeltaTime = @event.Position + ofs;
                    se.dwStreamId  = 0;
                    se.dwEvent     = MidiUtility.PackMessage(@event.Message);
                    var gch = GCHandle.Alloc(se, GCHandleType.Pinned);
                    CopyMemory(new IntPtr(ptrOfs + eventPointer.ToInt64()), gch.AddrOfPinnedObject(), Marshal.SizeOf(typeof(MIDIEVENT)));
                    gch.Free();
                    ptrOfs += baseEventSize;
                    ofs     = 0;
                }
                else if (0xFF == @event.Message.Status)
                {
                    var mm = @event.Message as MidiMessageMeta;
                    if (0x51 == mm.Data1)                     // tempo
                    {
                        if (_SendBufferSize < blockSize + baseEventSize)
                        {
                            break;
                        }
                        blockSize += baseEventSize;
                        var se = new MIDIEVENT();
                        se.dwDeltaTime = @event.Position + ofs;
                        se.dwStreamId  = 0;
                        se.dwEvent     = (mm.Data[0] << 16) | (mm.Data[1] << 8) | mm.Data[2] | (MEVT_TEMPO << 24);
                        var gch = GCHandle.Alloc(se, GCHandleType.Pinned);
                        CopyMemory(new IntPtr(ptrOfs + eventPointer.ToInt64()), gch.AddrOfPinnedObject(), Marshal.SizeOf(typeof(MIDIEVENT)));
                        gch.Free();
                        ptrOfs += baseEventSize;
                        ofs     = 0;
                    }
                    else if (0x2f == mm.Data1)                     // end track
                    {
                        if (_SendBufferSize < blockSize + baseEventSize)
                        {
                            break;
                        }
                        blockSize += baseEventSize;

                        // add a NOP message to it just to pad our output in case we're looping
                        var se = new MIDIEVENT();
                        se.dwDeltaTime = @event.Position + ofs;
                        se.dwStreamId  = 0;
                        se.dwEvent     = (MEVT_NOP << 24);
                        var gch = GCHandle.Alloc(se, GCHandleType.Pinned);
                        CopyMemory(new IntPtr(ptrOfs + eventPointer.ToInt64()), gch.AddrOfPinnedObject(), Marshal.SizeOf(typeof(MIDIEVENT)));
                        gch.Free();
                        ptrOfs += baseEventSize;
                        ofs     = 0;
                    }
                    else
                    {
                        ofs = @event.Position;
                    }
                }
                else                 // sysex or sysex part
                {
                    byte[] data;
                    if (0 == @event.Message.Status)
                    {
                        data = (@event.Message as MidiMessageSysexPart).Data;
                    }
                    else
                    {
                        data = MidiUtility.ToMessageBytes(@event.Message);
                    }


                    var dl = data.Length;
                    if (0 != (dl % 4))
                    {
                        dl += 4 - (dl % 4);
                    }
                    if (_SendBufferSize < blockSize + baseEventSize + dl)
                    {
                        break;
                    }

                    blockSize += baseEventSize + dl;

                    var se = new MIDIEVENT();
                    se.dwDeltaTime = @event.Position + ofs;
                    se.dwStreamId  = 0;
                    se.dwEvent     = MEVT_F_LONG | data.Length;
                    var gch = GCHandle.Alloc(se, GCHandleType.Pinned);
                    CopyMemory(new IntPtr(ptrOfs + eventPointer.ToInt64()), gch.AddrOfPinnedObject(), Marshal.SizeOf(typeof(MIDIEVENT)));
                    gch.Free();
                    ptrOfs += baseEventSize;
                    Marshal.Copy(data, 0, new IntPtr(ptrOfs + eventPointer.ToInt64()), data.Length);

                    ptrOfs += dl;
                    ofs     = 0;
                }
            }
            _sendHeader = default(MIDIHDR);
            _sendHeader.dwBufferLength = _sendHeader.dwBytesRecorded = unchecked ((uint)blockSize);
            _sendHeader.lpData         = _sendEventBuffer;
            int headerSize = Marshal.SizeOf(typeof(MIDIHDR));

            _CheckOutResult(midiOutPrepareHeader(_handle, ref _sendHeader, headerSize));
            _CheckOutResult(midiStreamOut(_handle, ref _sendHeader, headerSize));
        }