Path: blob/master/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/OutputDeviceBase.cs
180 views
#region License /* Copyright (c) 2006 Leslie Sanford * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #endregion #region Contact /* * Leslie Sanford * Email: [email protected] */ #endregion using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; using Sanford.Threading; namespace Sanford.Multimedia.Midi { public abstract class OutputDeviceBase : MidiDevice { [DllImport("winmm.dll")] protected static extern int midiOutReset(int handle); [DllImport("winmm.dll")] protected static extern int midiOutShortMsg(int handle, int message); [DllImport("winmm.dll")] protected static extern int midiOutPrepareHeader(int handle, IntPtr headerPtr, int sizeOfMidiHeader); [DllImport("winmm.dll")] protected static extern int midiOutUnprepareHeader(int handle, IntPtr headerPtr, int sizeOfMidiHeader); [DllImport("winmm.dll")] protected static extern int midiOutLongMsg(int handle, IntPtr headerPtr, int sizeOfMidiHeader); [DllImport("winmm.dll")] protected static extern int midiOutGetDevCaps(int deviceID, ref MidiOutCaps caps, int sizeOfMidiOutCaps); [DllImport("winmm.dll")] protected static extern int midiOutGetNumDevs(); protected const int MOM_OPEN = 0x3C7; protected const int MOM_CLOSE = 0x3C8; protected const int MOM_DONE = 0x3C9; protected delegate void GenericDelegate<T>(T args); // Represents the method that handles messages from Windows. protected delegate void MidiOutProc(int handle, int msg, int instance, int param1, int param2); // For releasing buffers. protected DelegateQueue delegateQueue = new DelegateQueue(); protected readonly object lockObject = new object(); // The number of buffers still in the queue. protected int bufferCount = 0; // Builds MidiHeader structures for sending system exclusive messages. private MidiHeaderBuilder headerBuilder = new MidiHeaderBuilder(); // The device handle. protected int hndle = 0; public OutputDeviceBase(int deviceID) : base(deviceID) { } ~OutputDeviceBase() { Dispose(false); } protected override void Dispose(bool disposing) { if(disposing) { delegateQueue.Dispose(); } base.Dispose(disposing); } public virtual void Send(ChannelMessage message) { #region Require if(IsDisposed) { throw new ObjectDisposedException(this.GetType().Name); } #endregion Send(message.Message); } public virtual void Send(SysExMessage message) { #region Require if(IsDisposed) { throw new ObjectDisposedException(this.GetType().Name); } #endregion lock(lockObject) { headerBuilder.InitializeBuffer(message); headerBuilder.Build(); // Prepare system exclusive buffer. int result = midiOutPrepareHeader(Handle, headerBuilder.Result, SizeOfMidiHeader); // If the system exclusive buffer was prepared successfully. if(result == MidiDeviceException.MMSYSERR_NOERROR) { bufferCount++; // Send system exclusive message. result = midiOutLongMsg(Handle, headerBuilder.Result, SizeOfMidiHeader); // If the system exclusive message could not be sent. if(result != MidiDeviceException.MMSYSERR_NOERROR) { midiOutUnprepareHeader(Handle, headerBuilder.Result, SizeOfMidiHeader); bufferCount--; headerBuilder.Destroy(); // Throw an exception. throw new OutputDeviceException(result); } } // Else the system exclusive buffer could not be prepared. else { // Destroy system exclusive buffer. headerBuilder.Destroy(); // Throw an exception. throw new OutputDeviceException(result); } } } public virtual void Send(SysCommonMessage message) { #region Require if(IsDisposed) { throw new ObjectDisposedException(this.GetType().Name); } #endregion Send(message.Message); } public virtual void Send(SysRealtimeMessage message) { #region Require if(IsDisposed) { throw new ObjectDisposedException(this.GetType().Name); } #endregion Send(message.Message); } public override void Reset() { #region Require if(IsDisposed) { throw new ObjectDisposedException(this.GetType().Name); } #endregion lock(lockObject) { // Reset the OutputDevice. int result = midiOutReset(Handle); if(result == MidiDeviceException.MMSYSERR_NOERROR) { while(bufferCount > 0) { Monitor.Wait(lockObject); } } else { // Throw an exception. throw new OutputDeviceException(result); } } } protected void Send(int message) { lock(lockObject) { int result = midiOutShortMsg(Handle, message); if(result != MidiDeviceException.MMSYSERR_NOERROR) { throw new OutputDeviceException(result); } } } public static MidiOutCaps GetDeviceCapabilities(int deviceID) { MidiOutCaps caps = new MidiOutCaps(); // Get the device's capabilities. int result = midiOutGetDevCaps(deviceID, ref caps, Marshal.SizeOf(caps)); // If the capabilities could not be retrieved. if(result != MidiDeviceException.MMSYSERR_NOERROR) { // Throw an exception. throw new OutputDeviceException(result); } return caps; } // Handles Windows messages. protected virtual void HandleMessage(int handle, int msg, int instance, int param1, int param2) { if(msg == MOM_OPEN) { } else if(msg == MOM_CLOSE) { } else if(msg == MOM_DONE) { delegateQueue.Post(ReleaseBuffer, new IntPtr(param1)); } } // Releases buffers. private void ReleaseBuffer(object state) { lock(lockObject) { IntPtr headerPtr = (IntPtr)state; // Unprepare the buffer. int result = midiOutUnprepareHeader(Handle, headerPtr, SizeOfMidiHeader); if(result != MidiDeviceException.MMSYSERR_NOERROR) { Exception ex = new OutputDeviceException(result); OnError(new ErrorEventArgs(ex)); } // Release the buffer resources. headerBuilder.Destroy(headerPtr); bufferCount--; Monitor.Pulse(lockObject); Debug.Assert(bufferCount >= 0); } } public override void Dispose() { #region Guard if(IsDisposed) { return; } #endregion lock(lockObject) { Close(); } } public override int Handle { get { return hndle; } } public static int DeviceCount { get { return midiOutGetNumDevs(); } } } }