Hi Jeff,
Two versions, one simple and one more complex. Any questions, just ask
This is using a console app and procedurally rather than using OOP as it's simpler to understand. This is as simple as I can make it!
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace SimpleSysExSender
{
class Program
{
const int MMSYSERR_NOERROR = 0;
const int MIDIERR_STILLPLAYING = 65;
static readonly int MidiHdrSize = Marshal.SizeOf(typeof(MIDIHDR));
static void Main(string[] args)
{
int id = 0;
byte[] data = new byte[] { 0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7 };
if (id >= 0 && id < midiOutGetNumDevs())
{
IntPtr handle;
if (midiOutOpen(out handle, id, IntPtr.Zero, IntPtr.Zero, 0) == MMSYSERR_NOERROR)
{
IntPtr dataHandle = Marshal.AllocHGlobal(data.Length);
Marshal.Copy(data, 0, dataHandle, data.Length);
MIDIHDR buffer = new MIDIHDR(dataHandle, data.Length);
GCHandle gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr address = gcHandle.AddrOfPinnedObject();
if (midiOutPrepareHeader(handle, address, MidiHdrSize) == MMSYSERR_NOERROR)
{
midiOutLongMsg(handle, address, MidiHdrSize);
while (midiOutUnprepareHeader(handle, address, MidiHdrSize) == MIDIERR_STILLPLAYING)
Thread.Sleep(1);
}
gcHandle.Free();
Marshal.FreeHGlobal(dataHandle);
midiOutClose(handle);
}
}
}
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutClose(IntPtr hmo);
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutGetNumDevs();
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutLongMsg(IntPtr hmo, IntPtr lpMidiOutHdr, int cbMidiOutHdr);
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutOpen(out IntPtr lphmo, int uDeviceID, IntPtr dwCallback, IntPtr dwCallbackInstance, int dwFlags);
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutPrepareHeader(IntPtr hmo, IntPtr lpMidiOutHdr, int cbMidiOutHdr);
public delegate void MidiOutProc(IntPtr hmo, int wMsg, IntPtr dwInstance, IntPtr dwParam1, IntPtr dwParam2);
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutUnprepareHeader(IntPtr hmo, IntPtr lpMidiOutHdr, int cbMidiOutHdr);
}
[StructLayout(LayoutKind.Sequential)]
internal struct MIDIHDR
{
private IntPtr lpData;
private int dwBufferLength;
private int dwBytesRecorded;
private IntPtr dwUser;
private int dwFlags;
private IntPtr lpNext;
private IntPtr reserved;
private int dwOffset;
private IntPtr dwReserved;
public MIDIHDR(IntPtr lpData, int dwBufferLength)
{
this.lpData = lpData;
this.dwBufferLength = dwBufferLength;
dwBytesRecorded = dwBufferLength;
dwUser = IntPtr.Zero;
dwFlags = 0;
lpNext = IntPtr.Zero;
reserved = IntPtr.Zero;
dwOffset = 0;
dwReserved = IntPtr.Zero;
}
}
}
This is a more OOP version of the same thing (sorry for the length!). More robust and more comments, but may be overkill for your requirements:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Threading;
namespace MidiSysExSender
{
class Program
{
static void Main(string[] args)
{
if (MidiOutputCollection.Outputs.Count > 0)
{
MidiOutput output = MidiOutputCollection.Outputs[0];
output.Open();
output.SendSysEx(MidiSystemExclusiveMessage.GeneralMidiOn);
output.Close();
Console.WriteLine("Done");
}
else
Console.WriteLine("No MIDI Outs found");
Console.ReadKey();
}
}
public class MidiOutput
{
private NativeMethods.MidiOutProc callback;
private MIDIOUTCAPS caps;
private IntPtr handle;
private int id;
internal MidiOutput(int id)
{
MIDIOUTCAPS caps;
int nativeResult = NativeMethods.midiOutGetDevCaps(id, out caps, MIDIOUTCAPS.Size);
if (nativeResult == NativeMethods.MMSYSERR_NOERROR)
{
callback = Callback;
this.caps = caps;
this.id = id;
}
else
throw new InvalidOperationException();
}
public int ChannelMask
{
get { return caps.ChannelMask; }
}
public Version DriverVersion
{
get { return caps.DriverVersion; }
}
public int Id
{
get { return id; }
}
public bool IsOpen
{
get { return handle != IntPtr.Zero; }
}
public int ManufacturerId
{
get { return caps.ManufacturerId; }
}
public string Name
{
get { return caps.Name; }
}
public int Notes
{
get { return caps.Notes; }
}
public int ProductId
{
get { return caps.ProductId; }
}
public MidiOutputTechnology Technology
{
get { return caps.Technology; }
}
public int Voices
{
get { return caps.Voices; }
}
public MidiOutputSupport Support
{
get { return caps.Support; }
}
private void Callback(IntPtr hmo, int wMsg, IntPtr dwInstance, IntPtr dwParam1, IntPtr dwParam2)
{
}
public void Close()
{
if (IsOpen)
{
int nativeResult = NativeMethods.midiOutClose(handle);
if (nativeResult == NativeMethods.MMSYSERR_NOERROR)
handle = IntPtr.Zero;
else
throw new InvalidOperationException();
}
}
public void Open()
{
if (!IsOpen)
{
IntPtr handle;
int nativeResult = NativeMethods.midiOutOpen(out handle, id, callback, IntPtr.Zero, NativeMethods.CALLBACK_FUNCTION);
if (nativeResult == NativeMethods.MMSYSERR_NOERROR)
this.handle = handle;
else
throw new InvalidOperationException();
}
}
public void SendSysEx(MidiSystemExclusiveMessage sysEx)
{
if (!IsOpen)
Open();
MIDIHDR buffer = new MIDIHDR(sysEx);
GCHandle gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr address = gcHandle.AddrOfPinnedObject();
int nativeResult = NativeMethods.midiOutPrepareHeader(handle, address, MIDIHDR.Size);
if (nativeResult == NativeMethods.MMSYSERR_NOERROR)
{
NativeMethods.midiOutLongMsg(handle, address, MIDIHDR.Size);
while (NativeMethods.midiOutUnprepareHeader(handle, address, MIDIHDR.Size) == NativeMethods.MIDIERR_STILLPLAYING)
Thread.Sleep(1);
}
gcHandle.Free();
buffer.Free();
}
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "MIDI Output {0}: {1}", id, Name);
}
}
public class MidiOutputCollection : IDisposable, IEnumerable<MidiOutput>
{
private List<MidiOutput> list;
private MidiOutputCollection()
{
int count = NativeMethods.midiOutGetNumDevs();
list = new List<MidiOutput>(count);
if (count > 0)
{
for (int id = 0; id < count; id++)
list.Add(new MidiOutput(id));
}
}
~MidiOutputCollection()
{
Dispose(false);
}
public MidiOutput this[int index]
{
get { return list[index]; }
}
public int Count
{
get { return list.Count; }
}
public static MidiOutputCollection Outputs
{
get { return InstanceHolder.Instance; }
}
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{ }
foreach (MidiOutput midiOutput in this)
midiOutput.Close();
}
public IEnumerator<MidiOutput> GetEnumerator()
{
return list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return list.GetEnumerator();
}
public int IndexOf(MidiOutput midiOutput)
{
return list.IndexOf(midiOutput);
}
private class InstanceHolder
{
static InstanceHolder()
{ }
private static readonly MidiOutputCollection instance = new MidiOutputCollection();
internal static MidiOutputCollection Instance
{
get { return instance; }
}
}
}
public enum MidiOutputTechnology
{
Undefined,
Port = NativeMethods.MOD_MIDIPORT,
Synthesizer = NativeMethods.MOD_SYNTH,
SquareWaveSynthesizer = NativeMethods.MOD_SQSYNTH,
FMSynthesizer = NativeMethods.MOD_FMSYNTH,
MidiMapper = NativeMethods.MOD_MAPPER,
WavetableSynthesizer = NativeMethods.MOD_WAVETABLE,
SoftwareSynthesizer = NativeMethods.MOD_SWSYNTH
}
[Flags]
public enum MidiOutputSupport
{
None = 0,
PatchCaching = NativeMethods.MIDICAPS_CACHE,
StereoVolume = NativeMethods.MIDICAPS_LRVOLUME,
StreamOut = NativeMethods.MIDICAPS_STREAM,
Volume = NativeMethods.MIDICAPS_VOLUME,
}
public class MidiSystemExclusiveMessage : IEnumerable<byte>
{
public const byte SOX = 0xF0;
public const byte EOX = 0xF7;
public const byte MaxByte = 0x7F;
public static MidiSystemExclusiveMessage GeneralMidiOn = new MidiSystemExclusiveMessage(0x7E, 0x7F, 0x09, 0x01);
public static MidiSystemExclusiveMessage GeneralMidiOff = new MidiSystemExclusiveMessage(0x7E, 0x7F, 0x09, 0x02);
private List<byte> list;
public MidiSystemExclusiveMessage(params byte[] data)
{
if (data == null)
throw new ArgumentNullException("data");
list = new List<byte>(data.Length + 2);
list.Add(SOX);
foreach (byte b in data)
if (b <= MaxByte)
list.Add(b);
list.Add(EOX);
list.TrimExcess();
}
public byte this[int index]
{
get { return list[index]; }
}
public int Count
{
get { return list.Count; }
}
public IEnumerator<byte> GetEnumerator()
{
return list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return list.GetEnumerator();
}
public byte[] ToArray()
{
return list.ToArray();
}
}
#region Interop
internal static class NativeMethods
{
public const int MAXPNAMELEN = 32;
public const int MOD_MIDIPORT = 1;
public const int MOD_SYNTH = 2;
public const int MOD_SQSYNTH = 3;
public const int MOD_FMSYNTH = 4;
public const int MOD_MAPPER = 5;
public const int MOD_WAVETABLE = 6;
public const int MOD_SWSYNTH = 7;
public const int MIDICAPS_VOLUME = 0x0001;
public const int MIDICAPS_LRVOLUME = 0x0002;
public const int MIDICAPS_CACHE = 0x0004;
public const int MIDICAPS_STREAM = 0x0008;
private const int MIDIERR_BASE = 64;
public const int MMSYSERR_NOERROR = 0;
public const int MIDIERR_STILLPLAYING = (MIDIERR_BASE + 1);
public const int CALLBACK_FUNCTION = 0x00030000;
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutClose(IntPtr hmo);
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutGetDevCaps(int uDeviceID, out MIDIOUTCAPS lpMidiOutCaps, int cbMidiOutCaps);
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutGetNumDevs();
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutLongMsg(IntPtr hmo, IntPtr lpMidiOutHdr, int cbMidiOutHdr);
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutOpen(out IntPtr lphmo, int uDeviceID, MidiOutProc dwCallback, IntPtr dwCallbackInstance, int dwFlags);
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutPrepareHeader(IntPtr hmo, IntPtr lpMidiOutHdr, int cbMidiOutHdr);
public delegate void MidiOutProc(IntPtr hmo, int wMsg, IntPtr dwInstance, IntPtr dwParam1, IntPtr dwParam2);
[DllImport("Winmm.dll", SetLastError = true)]
public static extern int midiOutUnprepareHeader(IntPtr hmo, IntPtr lpMidiOutHdr, int cbMidiOutHdr);
}
[StructLayout(LayoutKind.Sequential)]
internal struct MIDIOUTCAPS
{
public static readonly int Size = Marshal.SizeOf(typeof(MIDIOUTCAPS));
private short wMid;
private short wPid;
private int vDriverVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NativeMethods.MAXPNAMELEN)]
private string szPname;
private short wTechnology;
private short wVoices;
private short wNotes;
private short wChannelMask;
private int dwSupport;
public int ManufacturerId
{
get { return wMid; }
}
public int ProductId
{
get { return wPid; }
}
public Version DriverVersion
{
get { return new Version((vDriverVersion >> 8) & 0xFF, vDriverVersion & 0xFF, 0, 0); }
}
public string Name
{
get { return szPname; }
}
public MidiOutputTechnology Technology
{
get { return (MidiOutputTechnology)wTechnology; }
}
public int Voices
{
get { return wVoices; }
}
public int Notes
{
get { return wNotes; }
}
public int ChannelMask
{
get { return wChannelMask & 0xFFFF; }
}
public MidiOutputSupport Support
{
get { return (MidiOutputSupport)dwSupport; }
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct MIDIHDR
{
public static readonly int Size = Marshal.SizeOf(typeof(MIDIHDR));
private IntPtr lpData;
private int dwBufferLength;
private int dwBytesRecorded;
private IntPtr dwUser;
private int dwFlags;
private IntPtr lpNext;
private IntPtr reserved;
private int dwOffset;
private IntPtr dwReserved;
public MIDIHDR(MidiSystemExclusiveMessage data)
{
IntPtr dataHandle = Marshal.AllocHGlobal(data.Count);
Marshal.Copy(data.ToArray(), 0, dataHandle, data.Count);
lpData = dataHandle;
dwBufferLength = data.Count;
dwBytesRecorded = data.Count;
dwUser = IntPtr.Zero;
dwFlags = 0;
lpNext = IntPtr.Zero;
reserved = IntPtr.Zero;
dwOffset = 0;
dwReserved = IntPtr.Zero;
}
public void Free()
{
Marshal.FreeHGlobal(lpData);
}
}
#endregion Interop
}
|