/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Copyright (c) 2009, Tom Lokovic // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. using System; using System.Runtime.InteropServices; using System.Text; namespace Midi { /// /// C# wrappers for the Win32 MIDI API. /// /// Because .NET does not provide MIDI support itself, in C# we must use P/Invoke to wrap the /// Win32 API. That API consists of the MMSystem.h C header and the winmm.dll library. The API /// is described in detail here: http://msdn.microsoft.com/en-us/library/ms712733(VS.85).aspx. /// The P/Invoke interop mechanism is described here: /// http://msdn.microsoft.com/en-us/library/aa288468(VS.71).aspx. /// /// This file covers the subset of the MIDI protocol needed to manage input and output devices /// and send and receive Note On/Off, Control Change, Pitch Bend and Program Change messages. /// Other portions of the MIDI protocol (such as sysex events) are supported in the Win32 API /// but are not wrapped here. /// /// Some of the C functions are not typesafe when wrapped, so those wrappers are made private /// and typesafe variants are provided. static class Win32API { #region Constants // The following constants come from MMSystem.h. /// /// Max length of a manufacturer name in the Win32 API. /// public const UInt32 MAXPNAMELEN = 32; /// /// Status type returned from most functions in the Win32 API. /// public enum MMRESULT : uint { // General return codes. MMSYSERR_BASE = 0, MMSYSERR_NOERROR = MMSYSERR_BASE + 0, MMSYSERR_ERROR = MMSYSERR_BASE + 1, MMSYSERR_BADDEVICEID = MMSYSERR_BASE + 2, MMSYSERR_NOTENABLED = MMSYSERR_BASE + 3, MMSYSERR_ALLOCATED = MMSYSERR_BASE + 4, MMSYSERR_INVALHANDLE = MMSYSERR_BASE + 5, MMSYSERR_NODRIVER = MMSYSERR_BASE + 6, MMSYSERR_NOMEM = MMSYSERR_BASE + 7, MMSYSERR_NOTSUPPORTED = MMSYSERR_BASE + 8, MMSYSERR_BADERRNUM = MMSYSERR_BASE + 9, MMSYSERR_INVALFLAG = MMSYSERR_BASE + 10, MMSYSERR_INVALPARAM = MMSYSERR_BASE + 11, MMSYSERR_HANDLEBUSY = MMSYSERR_BASE + 12, MMSYSERR_INVALIDALIAS = MMSYSERR_BASE + 13, MMSYSERR_BADDB = MMSYSERR_BASE + 14, MMSYSERR_KEYNOTFOUND = MMSYSERR_BASE + 15, MMSYSERR_READERROR = MMSYSERR_BASE + 16, MMSYSERR_WRITEERROR = MMSYSERR_BASE + 17, MMSYSERR_DELETEERROR = MMSYSERR_BASE + 18, MMSYSERR_VALNOTFOUND = MMSYSERR_BASE + 19, MMSYSERR_NODRIVERCB = MMSYSERR_BASE + 20, MMSYSERR_MOREDATA = MMSYSERR_BASE + 21, MMSYSERR_LASTERROR = MMSYSERR_BASE + 21, // MIDI-specific return codes. MIDIERR_BASE = 64, MIDIERR_UNPREPARED = MIDIERR_BASE + 0, MIDIERR_STILLPLAYING = MIDIERR_BASE + 1, MIDIERR_NOMAP = MIDIERR_BASE + 2, MIDIERR_NOTREADY = MIDIERR_BASE + 3, MIDIERR_NODEVICE = MIDIERR_BASE + 4, MIDIERR_INVALIDSETUP = MIDIERR_BASE + 5, MIDIERR_BADOPENMODE = MIDIERR_BASE + 6, MIDIERR_DONT_CONTINUE = MIDIERR_BASE + 7, MIDIERR_LASTERROR = MIDIERR_BASE + 7 } /// /// Flags passed to midiInOpen() and midiOutOpen(). /// public enum MidiOpenFlags : uint { CALLBACK_TYPEMASK = 0x70000, CALLBACK_NULL = 0x00000, CALLBACK_WINDOW = 0x10000, CALLBACK_TASK = 0x20000, CALLBACK_FUNCTION = 0x30000, CALLBACK_THREAD = CALLBACK_TASK, CALLBACK_EVENT = 0x50000, MIDI_IO_STATUS = 0x00020 } /// /// Values for wTechnology field of MIDIOUTCAPS structure. /// public enum MidiDeviceType : ushort { MOD_MIDIPORT = 1, MOD_SYNTH = 2, MOD_SQSYNTH = 3, MOD_FMSYNTH = 4, MOD_MAPPER = 5, MOD_WAVETABLE = 6, MOD_SWSYNTH = 7 } /// /// Flags for dwSupport field of MIDIOUTCAPS structure. /// public enum MidiExtraFeatures : uint { MIDICAPS_VOLUME = 0x0001, MIDICAPS_LRVOLUME = 0x0002, MIDICAPS_CACHE = 0x0004, MIDICAPS_STREAM = 0x0008 } /// /// "Midi Out Messages", passed to wMsg param of MidiOutProc. /// public enum MidiOutMessage : uint { MOM_OPEN = 0x3C7, MOM_CLOSE = 0x3C8, MOM_DONE = 0x3C9 } /// /// "Midi In Messages", passed to wMsg param of MidiInProc. /// public enum MidiInMessage : uint { MIM_OPEN = 0x3C1, MIM_CLOSE = 0x3C2, MIM_DATA = 0x3C3, MIM_LONGDATA = 0x3C4, MIM_ERROR = 0x3C5, MIM_LONGERROR = 0x3C6, MIM_MOREDATA = 0x3CC } #endregion #region Handles /// /// Win32 handle for a MIDI output device. /// [StructLayout(LayoutKind.Sequential)] public struct HMIDIOUT { public Int32 handle; } /// /// Win32 handle for a MIDI input device. /// [StructLayout(LayoutKind.Sequential)] public struct HMIDIIN { public Int32 handle; } #endregion #region Structs /// /// Struct representing the capabilities of an output device. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711619(VS.85).aspx [StructLayout(LayoutKind.Sequential)] public struct MIDIOUTCAPS { public UInt16 wMid; public UInt16 wPid; public UInt32 vDriverVersion; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)MAXPNAMELEN)] public string szPname; public MidiDeviceType wTechnology; public UInt16 wVoices; public UInt16 wNotes; public UInt16 wChannelMask; public MidiExtraFeatures dwSupport; } /// /// Struct representing the capabilities of an input device. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711596(VS.85).aspx [StructLayout(LayoutKind.Sequential)] public struct MIDIINCAPS { public UInt16 wMid; public UInt16 wPid; public UInt32 vDriverVersion; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)MAXPNAMELEN)] public string szPname; public UInt32 dwSupport; } #endregion #region Functions for MIDI Output /// /// Returns the number of MIDI output devices on this system. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711627(VS.85).aspx [DllImport("winmm.dll", SetLastError = true)] public static extern UInt32 midiOutGetNumDevs(); /// /// Fills in the capabilities struct for a specific output device. /// /// NOTE: This is adapted from the original Win32 function in order to make it typesafe. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711621(VS.85).aspx public static MMRESULT midiOutGetDevCaps(UIntPtr uDeviceID, out MIDIOUTCAPS caps) { return midiOutGetDevCaps(uDeviceID, out caps, (UInt32)Marshal.SizeOf(typeof(MIDIOUTCAPS))); } /// /// Callback invoked when a MIDI output device is opened, closed, or finished with a buffer. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711637(VS.85).aspx public delegate void MidiOutProc(HMIDIOUT hmo, MidiOutMessage wMsg, UIntPtr dwInstance, UIntPtr dwParam1, UIntPtr dwParam2); /// /// Opens a MIDI output device. /// /// NOTE: This is adapted from the original Win32 function in order to make it typesafe. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711632(VS.85).aspx public static MMRESULT midiOutOpen(out HMIDIOUT lphmo, UIntPtr uDeviceID, MidiOutProc dwCallback, UIntPtr dwCallbackInstance) { return midiOutOpen(out lphmo, uDeviceID, dwCallback, dwCallbackInstance, dwCallback == null ? MidiOpenFlags.CALLBACK_NULL : MidiOpenFlags.CALLBACK_FUNCTION); } /// /// Turns off all notes and sustains on a MIDI output device. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/dd798479(VS.85).aspx [DllImport("winmm.dll", SetLastError = true)] public static extern MMRESULT midiOutReset(HMIDIOUT hmo); /// /// Closes a MIDI output device. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711620(VS.85).aspx [DllImport("winmm.dll", SetLastError = true)] public static extern MMRESULT midiOutClose(HMIDIOUT hmo); /// /// Sends a short MIDI message (anything but sysex or stream). /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711640(VS.85).aspx [DllImport("winmm.dll", SetLastError = true)] public static extern MMRESULT midiOutShortMsg(HMIDIOUT hmo, UInt32 dwMsg); /// /// Gets the error text for a return code related to an output device. /// /// NOTE: This is adapted from the original Win32 function in order to make it typesafe. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711622(VS.85).aspx public static MMRESULT midiOutGetErrorText(MMRESULT mmrError, StringBuilder lpText) { return midiOutGetErrorText(mmrError, lpText, (UInt32)lpText.Capacity); } #endregion #region Functions for MIDI Input /// /// Returns the number of MIDI input devices on this system. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711608(VS.85).aspx [DllImport("winmm.dll", SetLastError = true)] public static extern UInt32 midiInGetNumDevs(); /// /// Fills in the capabilities struct for a specific input device. /// /// NOTE: This is adapted from the original Win32 function in order to make it typesafe. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711604(VS.85).aspx public static MMRESULT midiInGetDevCaps(UIntPtr uDeviceID, out MIDIINCAPS caps) { return midiInGetDevCaps(uDeviceID, out caps, (UInt32)Marshal.SizeOf(typeof(MIDIINCAPS))); } /// /// Callback invoked when a MIDI event is received from an input device. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711612(VS.85).aspx public delegate void MidiInProc(HMIDIIN hMidiIn, MidiInMessage wMsg, UIntPtr dwInstance, UIntPtr dwParam1, UIntPtr dwParam2); /// /// Opens a MIDI input device. /// /// NOTE: This is adapted from the original Win32 function in order to make it typesafe. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711610(VS.85).aspx public static MMRESULT midiInOpen(out HMIDIIN lphMidiIn, UIntPtr uDeviceID, MidiInProc dwCallback, UIntPtr dwCallbackInstance) { return midiInOpen(out lphMidiIn, uDeviceID, dwCallback, dwCallbackInstance, dwCallback == null ? MidiOpenFlags.CALLBACK_NULL : MidiOpenFlags.CALLBACK_FUNCTION); } /// /// Starts input on a MIDI input device. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711614(VS.85).aspx [DllImport("winmm.dll", SetLastError = true)] public static extern MMRESULT midiInStart(HMIDIIN hMidiIn); /// /// Stops input on a MIDI input device. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711615(VS.85).aspx [DllImport("winmm.dll", SetLastError = true)] public static extern MMRESULT midiInStop(HMIDIIN hMidiIn); /// /// Resets input on a MIDI input device. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711613(VS.85).aspx [DllImport("winmm.dll", SetLastError = true)] public static extern MMRESULT midiInReset(HMIDIIN hMidiIn); /// /// Closes a MIDI input device. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711602(VS.85).aspx [DllImport("winmm.dll", SetLastError = true)] public static extern MMRESULT midiInClose(HMIDIIN hMidiIn); /// /// Gets the error text for a return code related to an input device. /// /// NOTE: This is adapted from the original Win32 function in order to make it typesafe. /// /// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711605(VS.85).aspx public static MMRESULT midiInGetErrorText(MMRESULT mmrError, StringBuilder lpText) { return midiInGetErrorText(mmrError, lpText, (UInt32)lpText.Capacity); } #endregion #region Non-Typesafe Bindings // The bindings in this section are not typesafe, so we make them private and privide // typesafe variants above. [DllImport("winmm.dll", SetLastError = true)] private static extern MMRESULT midiOutGetDevCaps(UIntPtr uDeviceID, out MIDIOUTCAPS caps, UInt32 cbMidiOutCaps); [DllImport("winmm.dll", SetLastError = true)] private static extern MMRESULT midiOutOpen(out HMIDIOUT lphmo, UIntPtr uDeviceID, MidiOutProc dwCallback, UIntPtr dwCallbackInstance, MidiOpenFlags dwFlags); [DllImport("winmm.dll", SetLastError = true)] private static extern MMRESULT midiOutGetErrorText(MMRESULT mmrError, StringBuilder lpText, UInt32 cchText); [DllImport("winmm.dll", SetLastError = true)] private static extern MMRESULT midiInGetDevCaps(UIntPtr uDeviceID, out MIDIINCAPS caps, UInt32 cbMidiInCaps); [DllImport("winmm.dll", SetLastError = true)] private static extern MMRESULT midiInOpen(out HMIDIIN lphMidiIn, UIntPtr uDeviceID, MidiInProc dwCallback, UIntPtr dwCallbackInstance, MidiOpenFlags dwFlags); [DllImport("winmm.dll", SetLastError = true)] private static extern MMRESULT midiInGetErrorText(MMRESULT mmrError, StringBuilder lpText, UInt32 cchText); #endregion } }