forked from odashi/MMSystem.NET
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Midi_Controller.cs
executable file
·192 lines (166 loc) · 5.26 KB
/
Midi_Controller.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
using System;
using System.Runtime.InteropServices;
namespace MMSystem
{
namespace Midi
{
/// <summary>
/// MIDI 出力デバイスのコントローラ。
/// </summary>
public class Controller : IDisposable
{
DeviceCaps caps_;
/// <summary>
/// 一度に送出できる MIDI メッセージの最大バイト数。
/// </summary>
public const int MAX_BYTES_PER_SENDING = 1024;
/// <summary>
/// MIDI 出力デバイスの情報。
/// </summary>
public DeviceCaps DeviceCaps
{
get { return caps_; }
}
/// <summary>
/// MIDI 出力デバイスのコントローラを生成する。
/// </summary>
/// <param name="deviceId">対象となる MIDI 出力デバイスの ID。</param>
public Controller(uint deviceId)
{
Win32.MMRESULT ret;
caps_ = new DeviceCaps(deviceId);
// MIDIHDR オブジェクトのサイズ
cb_hdr_ = (uint)Marshal.SizeOf(typeof(Win32.MIDIHDR));
try
{
unsafe
{
// HMIDIOUT オブジェクトの取得
fixed (IntPtr* lphmo = &hmo_)
{
ret = Win32.Api.midiOutOpen((IntPtr)lphmo, deviceId, 0, 0, 0);
if (ret != Win32.MMRESULT.MMSYSERR_NOERROR)
throw new Win32Exception(ret);
}
// 送信バッファの作成
buffer_ = Marshal.AllocHGlobal((int)MAX_BYTES_PER_SENDING);
// MIDIHDR データの生成
hdr_ = Marshal.AllocHGlobal((int)cb_hdr_);
((Win32.MIDIHDR*)hdr_)->lpData = buffer_;
}
}
catch
{
Dispose();
throw;
}
}
/// <summary>
/// MIDI 出力デバイスのコントローラを生成する。
/// </summary>
/// <param name="deviceName">対象となる MIDI 出力デバイスの名前。DeviceCaps.Name の値を使用できる。</param>
/// <returns>
/// deviceName で指定した名前のデバイスが見つかった場合は、そのデバイスを表す Controller オブジェクト。
/// 見つからなかった場合は null。
/// </returns>
public static Controller FromName(string deviceName)
{
// 名前の一致するデバイスを探す
uint n = DeviceCaps.NumDevices;
for (uint i = 0; i < n; ++i)
{
DeviceCaps caps = new DeviceCaps(i);
if (caps.Name == deviceName)
// デバイスを発見したので初期化
return new Controller(i);
}
// 名前の一致するデバイスがなかった
return null;
}
/// <summary>
/// デストラクタ。
/// </summary>
~Controller()
{
Dispose();
}
/// <summary>
/// Controller オブジェクトを破棄する。
/// </summary>
public void Dispose()
{
// MIDIHDR オブジェクトの破棄
if (hdr_ != IntPtr.Zero)
{
Marshal.FreeHGlobal(hdr_);
hdr_ = IntPtr.Zero;
}
// 送信バッファの破棄
if (buffer_ != IntPtr.Zero)
{
Marshal.FreeHGlobal(buffer_);
buffer_ = IntPtr.Zero;
}
// HMIDIOUT オブジェクトの破棄
if (hmo_ != IntPtr.Zero)
{
Reset();
Win32.Api.midiOutClose(hmo_);
hmo_ = IntPtr.Zero;
}
// GC がデストラクタを呼び出さないようにする
GC.SuppressFinalize(this);
}
/// <summary>
/// MIDI 出力デバイスをリセットする。
/// </summary>
public void Reset()
{
Win32.Api.midiOutReset(hmo_);
}
/// <summary>
/// デバイスへ 1 ~ 3 バイトの MIDI メッセージを送出する。
/// </summary>
/// <param name="status">ステータスバイト。</param>
/// <param name="data1">データバイト 1。</param>
/// <param name="data2">データバイト 2。</param>
public void Send(byte status, byte data1 = 0, byte data2 = 0)
{
Win32.Api.midiOutShortMsg(hmo_, (uint)status | (((uint)data1) << 8) | (((uint)data2) << 16));
}
/// <summary>
/// デバイスへ指定したバイト数の MIDI メッセージを送出する。
/// システム エクスクルーシブ メッセージの送出などに使用する。
/// </summary>
/// <param name="message">メッセージのバイト列を格納した配列。</param>
public void Send(params byte[] message)
{
int len = message.Length;
for (int offset = 0; offset < message.Length; offset += MAX_BYTES_PER_SENDING)
{
int sending = Math.Min(message.Length - offset, MAX_BYTES_PER_SENDING);
// データのコピー
Marshal.Copy(message, offset, buffer_, sending);
unsafe
{
// 準備
((Win32.MIDIHDR*)hdr_)->dwBufferLength = (uint)sending;
((Win32.MIDIHDR*)hdr_)->dwFlags = 0;
Win32.Api.midiOutPrepareHeader(hmo_, hdr_, cb_hdr_);
// 送出
var ret = Win32.Api.midiOutLongMsg(hmo_, hdr_, cb_hdr_);
// 送出完了まで待機 (ブロッキング)
if (ret == Win32.MMRESULT.MMSYSERR_NOERROR)
while ((((Win32.MIDIHDR*)hdr_)->dwFlags & Win32.MIDIHDR_FLAGS.MHDR_DONE) == 0) ;
// 後始末
Win32.Api.midiOutUnprepareHeader(hmo_, hdr_, cb_hdr_);
}
}
}
private IntPtr hmo_;
private IntPtr buffer_;
private IntPtr hdr_;
private uint cb_hdr_;
}
}
}