Exemple #1
0
 /// <summary>
 /// Sends a message immediately to the device
 /// </summary>
 /// <param name="message">The message to send</param>
 /// <remarks>The message is not queued. Tempo change messages are not honored.</remarks>
 public void Send(MidiMessage message)
 {
     if (IntPtr.Zero == _handle)
     {
         throw new InvalidOperationException("The device is closed.");
     }
     if (null == message)
     {
         throw new ArgumentNullException("message");
     }
     if (0xF0 == (message.Status & 0xF0))
     {
         if (0xF != message.Channel)
         {
             var data = MidiUtility.ToMessageBytes(message);
             if (null == data)
             {
                 return;
             }
             if (254 < data.Length)
             {
                 var len = 254;
                 for (var i = 0; i < data.Length; i += len)
                 {
                     if (data.Length <= i + len)
                     {
                         len = data.Length - i;
                     }
                     _SendRaw(data, i, len);
                 }
             }
             else
             {
                 _SendRaw(data, 0, data.Length);
             }
         }
     }
     else
     {
         _CheckOutResult(midiOutShortMsg(_handle, MidiUtility.PackMessage(message)));
     }
 }
Exemple #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));
        }
Exemple #3
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));
            }
        }
Exemple #4
0
        void _SendBlock()
        {
            if (null == _sendQueue)
            {
                return;
            }
            if (IntPtr.Zero == Handle)
            {
                throw new InvalidOperationException("The stream is closed.");
            }

            int    blockSize     = 0;
            IntPtr headerPointer = Marshal.AllocHGlobal(MIDIHDR_SIZE + MAX_EVENTBLOCK_SIZE);

            try
            {
                IntPtr eventPointer = new IntPtr(headerPointer.ToInt64() + MIDIHDR_SIZE);
                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 (MAX_EVENTBLOCK_SIZE < blockSize + MIDIEVENT_SIZE)
                        {
                            break;
                        }
                        blockSize += MIDIEVENT_SIZE;
                        var se = default(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 += MIDIEVENT_SIZE;
                        ofs     = 0;
                    }
                    else if (0xFF == @event.Message.Status)
                    {
                        var mm = @event.Message as MidiMessageMeta;
                        if (0x51 == mm.Data1)                         // tempo
                        {
                            if (MAX_EVENTBLOCK_SIZE < blockSize + MIDIEVENT_SIZE)
                            {
                                break;
                            }
                            blockSize += MIDIEVENT_SIZE;
                            var se = default(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 += MIDIEVENT_SIZE;
                            ofs     = 0;
                        }
                        else if (0x2f == mm.Data1)                         // end track
                        {
                            if (MAX_EVENTBLOCK_SIZE < blockSize + MIDIEVENT_SIZE)
                            {
                                break;
                            }
                            blockSize += MIDIEVENT_SIZE;

                            // add a NOP message to it just to pad our output in case we're looping
                            var se = default(MIDIEVENT);
                            se.dwDeltaTime = @event.Position + ofs;
                            se.dwStreamId  = 0;
                            se.dwEvent     = (MEVT_NOP << 24);
                            Marshal.StructureToPtr(se, new IntPtr(ptrOfs + eventPointer.ToInt64()), false);
                            ptrOfs += MIDIEVENT_SIZE;
                            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 (MAX_EVENTBLOCK_SIZE < blockSize + MIDIEVENT_SIZE + dl)
                        {
                            break;
                        }

                        blockSize += MIDIEVENT_SIZE + dl;

                        var se = default(MIDIEVENT);
                        se.dwDeltaTime = @event.Position + ofs;
                        se.dwStreamId  = 0;
                        se.dwEvent     = MEVT_F_LONG | data.Length;
                        Marshal.StructureToPtr(se, new IntPtr(ptrOfs + eventPointer.ToInt64()), false);
                        ptrOfs += MIDIEVENT_SIZE;
                        Marshal.Copy(data, 0, new IntPtr(ptrOfs + eventPointer.ToInt64()), data.Length);

                        ptrOfs += dl;
                        ofs     = 0;
                    }
                }
                var header = default(MIDIHDR);
                header.dwBufferLength = header.dwBytesRecorded = unchecked ((uint)blockSize);
                header.lpData         = eventPointer;
                Marshal.StructureToPtr(header, headerPointer, false);
                _CheckOutResult(midiOutPrepareHeader(Handle, headerPointer, MIDIHDR_SIZE));
                _CheckOutResult(midiStreamOut(Handle, headerPointer, MIDIHDR_SIZE));
                headerPointer = IntPtr.Zero;
            }
            finally
            {
                if (IntPtr.Zero != headerPointer)
                {
                    Marshal.FreeHGlobal(headerPointer);
                }
            }
        }