forked from jstnryan/midi-dot-net
/
Win32API.cs
496 lines (432 loc) · 19.8 KB
/
Win32API.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
// 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
{
/// <summary>
/// C# wrappers for the Win32 MIDI API.
/// </summary>
/// 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.
/// <summary>
/// Max length of a manufacturer name in the Win32 API.
/// </summary>
public const UInt32 MAXPNAMELEN = 32;
/// <summary>
/// Status type returned from most functions in the Win32 API.
/// </summary>
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
}
/// <summary>
/// Flags passed to midiInOpen() and midiOutOpen().
/// </summary>
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
}
/// <summary>
/// Values for wTechnology field of MIDIOUTCAPS structure.
/// </summary>
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
}
/// <summary>
/// Flags for dwSupport field of MIDIOUTCAPS structure.
/// </summary>
public enum MidiExtraFeatures : uint
{
MIDICAPS_VOLUME = 0x0001,
MIDICAPS_LRVOLUME = 0x0002,
MIDICAPS_CACHE = 0x0004,
MIDICAPS_STREAM = 0x0008
}
/// <summary>
/// "Midi Out Messages", passed to wMsg param of MidiOutProc.
/// </summary>
public enum MidiOutMessage : uint
{
MOM_OPEN = 0x3C7,
MOM_CLOSE = 0x3C8,
MOM_DONE = 0x3C9
}
/// <summary>
/// "Midi In Messages", passed to wMsg param of MidiInProc.
/// </summary>
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
/// <summary>
/// Win32 handle for a MIDI output device.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct HMIDIOUT
{
public Int32 handle;
}
/// <summary>
/// Win32 handle for a MIDI input device.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct HMIDIIN
{
public Int32 handle;
}
#endregion
#region Structs
/// <summary>
/// Struct representing the capabilities of an output device.
/// </summary>
/// 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;
}
/// <summary>
/// Struct representing the capabilities of an input device.
/// </summary>
/// 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;
}
#region SysEx
/// <summary>
/// Strut to hold outgoing long messages (sysex)
/// </summary>
/// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711592(v=VS.85).aspx
[StructLayout(LayoutKind.Sequential)]
public struct MIDIHDR
{
public IntPtr lpData;
public int dwBufferLength;
public int dwBytesRecorded;
public IntPtr dwUser;
public int dwFlags;
public IntPtr lpNext;
public IntPtr reserved;
public int dwOffset;
//public IntPtr dwReserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public int[] reservedArray;
}
#endregion
#endregion
#region Functions for MIDI Output
/// <summary>
/// Returns the number of MIDI output devices on this system.
/// </summary>
/// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711627(VS.85).aspx
[DllImport("winmm.dll", SetLastError = true)]
public static extern UInt32 midiOutGetNumDevs();
/// <summary>
/// Fills in the capabilities struct for a specific output device.
/// </summary>
/// 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)));
}
/// <summary>
/// Callback invoked when a MIDI output device is opened, closed, or finished with a buffer.
/// </summary>
/// 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);
/// <summary>
/// Opens a MIDI output device.
/// </summary>
/// 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);
#region SysEx
return midiOutOpen(out lphmo, uDeviceID, dwCallback, dwCallbackInstance,
dwCallback == null ? MidiOpenFlags.CALLBACK_NULL : MidiOpenFlags.CALLBACK_FUNCTION & MidiOpenFlags.MIDI_IO_STATUS);
#endregion
}
/// <summary>
/// Turns off all notes and sustains on a MIDI output device.
/// </summary>
/// 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);
/// <summary>
/// Closes a MIDI output device.
/// </summary>
/// 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);
/// <summary>
/// Sends a short MIDI message (anything but sysex or stream).
/// </summary>
/// 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);
#region SysEx
/// <summary>
/// Sends a long MIDI message (sysex).
/// </summary>
/// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711629(VS.85).aspx
[DllImport("winmm.dll", SetLastError = true)]
public static extern MMRESULT midiOutLongMsg(HMIDIOUT hmo, IntPtr lpMidiOutHdr, UInt32 cbMidiOutHdr);
// MMRESULT midiOutLongMsg(HMIDIOUT hmo, LPMIDIHDR lpMidiOutHdr, UINT cbMidiOutHdr);
/// <summary>
/// Prepares a long message for sending
/// </summary>
/// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711634(v=VS.85).aspx
[DllImport("winmm.dll", SetLastError = true)]
public static extern MMRESULT midiOutPrepareHeader(HMIDIOUT hmo, IntPtr lpMidiOutHdr, UInt32 cbMidiOutHdr);
/// <summary>
/// Frees header space after sending long message
/// </summary>
/// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711641(v=VS.85).aspx
[DllImport("winmm.dll", SetLastError = true)]
public static extern MMRESULT midiOutUnprepareHeader(HMIDIOUT hmo, IntPtr lpMidiOutHdr, UInt32 cbMidiOutHdr);
#endregion
/// <summary>
/// Gets the error text for a return code related to an output device.
/// </summary>
/// 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
/// <summary>
/// Returns the number of MIDI input devices on this system.
/// </summary>
/// Win32 docs: http://msdn.microsoft.com/en-us/library/ms711608(VS.85).aspx
[DllImport("winmm.dll", SetLastError = true)]
public static extern UInt32 midiInGetNumDevs();
/// <summary>
/// Fills in the capabilities struct for a specific input device.
/// </summary>
/// 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)));
}
/// <summary>
/// Callback invoked when a MIDI event is received from an input device.
/// </summary>
/// 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);
/// <summary>
/// Opens a MIDI input device.
/// </summary>
/// 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);
}
/// <summary>
/// Starts input on a MIDI input device.
/// </summary>
/// 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);
/// <summary>
/// Stops input on a MIDI input device.
/// </summary>
/// 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);
/// <summary>
/// Resets input on a MIDI input device.
/// </summary>
/// 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);
/// <summary>
/// Closes a MIDI input device.
/// </summary>
/// 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);
/// <summary>
/// Gets the error text for a return code related to an input device.
/// </summary>
/// 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);
}
#region SysEx
/// <summary>
/// Send a buffer to and input device in order to receive SysEx messages.
/// </summary>
/// Win32 docs: http://msdn.microsoft.com/en-us/library/dd798450(VS.85).aspx
[DllImport("winmm.dll", SetLastError = true)]
public static extern MMRESULT midiInAddBuffer(HMIDIIN hMidiIn, IntPtr lpMidiInHdr, UInt32 cbMidiInHdr);
/// <summary>
/// Prepare an input buffer before passing to midiInAddBuffer.
/// </summary>
/// Win32 docs: http://msdn.microsoft.com/en-us/library/dd798459(VS.85).aspx
[DllImport("winmm.dll", SetLastError = true)]
public static extern MMRESULT midiInPrepareHeader(HMIDIIN hMidiIn, IntPtr headerPtr, UInt32 cbMidiInHdr);
/// <summary>
/// Clean up preparation performed by midiInPrepareHeader.
/// </summary>
/// Win32 docs: http://msdn.microsoft.com/en-us/library/dd798464(VS.85).aspx
[DllImport("winmm.dll", SetLastError = true)]
public static extern MMRESULT midiInUnprepareHeader(HMIDIIN hMidiIn, IntPtr headerPtr, UInt32 cbMidiInHdr);
#endregion
#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
}
}