openMSX
Midi_w32.cc
Go to the documentation of this file.
1/*
2 * Win32 MIDI utility routines for openMSX.
3 *
4 * Copyright (c) 2003 Reikan. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#ifdef _WIN32
28
29#include "Midi_w32.hh"
30
31#include "MSXException.hh"
32
33#include "MemBuffer.hh"
34#include "cstdiop.hh"
35#include "one_of.hh"
36#include "xrange.hh"
37
38#include <array>
39#include <bit>
40#include <cstring>
41#include <cstdlib>
42#include <sstream>
43#include <cassert>
44
45#define MAXPATHLEN MAX_PATH
46
47namespace openmsx {
48
49/*
50 * MIDI I/O helper functions for Win32.
51 */
52// MIDI SYSTEM MESSAGES max length is not defined...
53// TODO: support variable length.
54static constexpr size_t OPENMSX_W32_MIDI_SYSMES_MAXLEN = 4096;
55
56
57struct vfn_midi {
58 unsigned idx;
59 unsigned devid;
60 HMIDI handle;
61 std::array<char, MAXPATHLEN + 1> vfname;
62 std::array<char, MAXPNAMELEN> devname;
63};
64
65struct outbuf {
66 MIDIHDR header;
67};
68
69static MemBuffer<vfn_midi> vfnt_midiout;
70static MemBuffer<vfn_midi> vfnt_midiin;
71static unsigned vfnt_midiout_num, vfnt_midiin_num;
72
73static MemBuffer<int> state_out;
74static MemBuffer<outbuf> buf_out;
75
76static MIDIHDR inhdr;
77static std::array<char, OPENMSX_W32_MIDI_SYSMES_MAXLEN> inlongmes;
78
79
80static void w32_midiDevNameConv(char *dst, const char *src)
81{
82 size_t len = strlen(src);
83 size_t i = 0;
84 for (; i < len; ++i) {
85 if ((src[i] < '0') || (src[i] > 'z') ||
86 ((src[i] > '9') && (src[i] < 'A')) ||
87 ((src[i] > 'Z') && (src[i] < 'a'))) {
88 dst[i] = '_';
89 } else {
90 dst[i] = src[i];
91 }
92 }
93 dst[i] = '\0';
94}
95
96
97// MIDI-OUT
98static int w32_midiOutFindDev(unsigned *idx, unsigned *dev, const char *vfn)
99{
100 for (auto i : xrange(vfnt_midiout_num)) {
101 if (!strcmp(vfnt_midiout[i].vfname.data(), vfn)) {
102 *idx = i;
103 *dev = vfnt_midiout[i].devid;
104 return 0;
105 }
106 }
107 return -1;
108}
109
110
111int w32_midiOutInit()
112{
113 vfnt_midiout_num = 0;
114 unsigned num = midiOutGetNumDevs();
115 if (!num) return 0;
116
117 state_out.resize(num + 1);
118 memset(state_out.data(), 0, (num + 1) * sizeof(int));
119
120 buf_out.resize(num + 1);
121 memset(buf_out.data(), 0, (num + 1) * sizeof(outbuf));
122
123 vfnt_midiout.resize(num + 1);
124
125 // MIDI_MAPPER is #define's as ((UINT)-1)
126 auto OPENMSX_MIDI_MAPPER = static_cast<UINT>(-1);
127 MIDIOUTCAPSA cap;
128 if (midiOutGetDevCapsA(OPENMSX_MIDI_MAPPER, &cap, sizeof(cap)) != MMSYSERR_NOERROR) {
129 return 2;
130 }
131 vfnt_midiout[0].devid = OPENMSX_MIDI_MAPPER;
132 w32_midiDevNameConv(vfnt_midiout[0].devname.data(), cap.szPname);
133 strncpy(vfnt_midiout[0].vfname.data(), "midi-out", MAXPATHLEN);
134 vfnt_midiout[0].vfname[MAXPATHLEN] = '\0';
135 vfnt_midiout_num++;
136
137 for (auto i : xrange(num)) {
138 if (midiOutGetDevCapsA(i, &cap, sizeof(cap)) != MMSYSERR_NOERROR) {
139 return 0; // atleast MIDI-MAPPER is available...
140 }
141 vfnt_midiout[i + 1].devid = i;
142 w32_midiDevNameConv(vfnt_midiout[i + 1].devname.data(), cap.szPname);
143 snprintf(vfnt_midiout[i + 1].vfname.data(), MAXPATHLEN + 1, "midi-out-%u", i);
144 vfnt_midiout_num++;
145 }
146 return 0;
147}
148
149void w32_midiOutClean()
150{
151 vfnt_midiout_num = 0;
152}
153
154
155unsigned w32_midiOutGetVFNsNum()
156{
157 return vfnt_midiout_num;
158}
159
160std::string w32_midiOutGetVFN(unsigned nmb)
161{
162 assert(nmb < vfnt_midiout_num);
163 return vfnt_midiout[nmb].vfname.data();
164}
165
166std::string w32_midiOutGetRDN(unsigned nmb)
167{
168 assert(nmb < vfnt_midiout_num);
169 return vfnt_midiout[nmb].devname.data();
170}
171
172
173unsigned w32_midiOutOpen(const char *vfn)
174{
175 unsigned idx, devid;
176 if (w32_midiOutFindDev(&idx, &devid,vfn)) {
177 return unsigned(-1);
178 }
179 if (midiOutOpen(std::bit_cast<HMIDIOUT*>(&vfnt_midiout[idx].handle), devid, 0, 0 ,0) != MMSYSERR_NOERROR) {
180 return unsigned(-1);
181 }
182 return idx;
183}
184
185int w32_midiOutClose(unsigned idx)
186{
187 midiOutReset(reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle));
188 if (midiOutClose(reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle)) == MMSYSERR_NOERROR) {
189 return 0;
190 } else {
191 return -1;
192 }
193}
194
195
196static int w32_midiOutFlushExclusiveMsg(unsigned idx)
197{
198 int i;
199 //buf_out[idx].header.lpData = buf_out[idx].longmes;
200 //buf_out[idx].header.dwBufferLength = buf_out[idx].longmes_cnt;
201 buf_out[idx].header.dwFlags = 0;
202 if ((i = midiOutPrepareHeader(reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle), &buf_out[idx].header, sizeof(buf_out[idx].header))) != MMSYSERR_NOERROR) {
203 throw FatalError("midiOutPrepareHeader() returned ", i);
204 }
205 if ((i = midiOutLongMsg(reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle), &buf_out[idx].header, sizeof(buf_out[idx].header))) != MMSYSERR_NOERROR) {
206 throw FatalError("midiOutLongMsg() returned ", i);
207 }
208 // Wait sending in driver.
209 // This may take long...
210 while (!(buf_out[idx].header.dwFlags & MHDR_DONE)) {
211 Sleep(1);
212 }
213 // Sending Exclusive done.
214 if ((i = midiOutUnprepareHeader(reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle), &buf_out[idx].header, sizeof(buf_out[idx].header))) != MMSYSERR_NOERROR) {
215 throw FatalError("midiOutUnPrepareHeader() returned ", i);
216 }
217 return 0;
218}
219
220int w32_midiOutMsg(size_t size, const uint8_t* data, unsigned idx)
221{
222 if (size == 0) return 0;
223
224 auto hMidiOut = reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle);
225 if (data[0] == one_of(0xF0, 0xF7)) { // SysEx
226 if (size > OPENMSX_W32_MIDI_SYSMES_MAXLEN) {
227 return -1;
228 }
229 auto& buf = buf_out[idx];
230 // Note: We have to be careful with the const_cast here.
231 // Even though Windows doesn't write to the buffer, it fails if you don't have
232 // write access to the respective memory page.
233 buf.header.lpData = const_cast<LPSTR>(reinterpret_cast<LPCSTR>(data));
234 buf.header.dwBufferLength = unsigned(size);
235 w32_midiOutFlushExclusiveMsg(idx);
236 } else {
237 DWORD midiMsg = 0x000000;
238 for (unsigned i = 0; i < size && i < 4; i++) {
239 midiMsg |= data[i] << (8 * i);
240 }
241 midiOutShortMsg(hMidiOut, midiMsg);
242 }
243 return 0;
244}
245
246
247// MIDI-IN
248static int w32_midiInFindDev(unsigned *idx, unsigned *dev, const char *vfn)
249{
250 for (auto i : xrange(vfnt_midiin_num)) {
251 if (!strcmp(vfnt_midiin[i].vfname.data(), vfn)) {
252 *idx = i;
253 *dev = vfnt_midiin[i].devid;
254 return 0;
255 }
256 }
257 return -1;
258}
259
260
261int w32_midiInInit()
262{
263 vfnt_midiin_num = 0;
264 unsigned num = midiInGetNumDevs();
265 if (!num) return 0;
266
267 vfnt_midiin.resize(num + 1);
268 for (auto i : xrange(num)) {
269 MIDIINCAPSA cap;
270 if (midiInGetDevCapsA(i, &cap, sizeof(cap)) != MMSYSERR_NOERROR) {
271 return 1;
272 }
273 vfnt_midiin[i].devid = i;
274 w32_midiDevNameConv(vfnt_midiin[i].devname.data(), cap.szPname);
275 snprintf(vfnt_midiin[i].vfname.data(), MAXPATHLEN + 1, "midi-in-%u", i);
276 vfnt_midiin_num++;
277 }
278 return 0;
279}
280
281void w32_midiInClean()
282{
283 vfnt_midiin_num = 0;
284}
285
286
287unsigned w32_midiInGetVFNsNum()
288{
289 return vfnt_midiin_num;
290}
291
292std::string w32_midiInGetVFN(unsigned nmb)
293{
294 assert(nmb < vfnt_midiin_num);
295 return vfnt_midiin[nmb].vfname.data();
296}
297
298std::string w32_midiInGetRDN(unsigned nmb)
299{
300 assert(nmb < vfnt_midiin_num);
301 return vfnt_midiin[nmb].devname.data();
302}
303
304unsigned w32_midiInOpen(const char *vfn, DWORD thrdid)
305{
306 unsigned idx, devid;
307 if (w32_midiInFindDev(&idx, &devid, vfn)) {
308 return unsigned(-1);
309 }
310 if (midiInOpen(std::bit_cast<HMIDIIN*>(&vfnt_midiin[idx].handle), devid, thrdid, 0, CALLBACK_THREAD) != MMSYSERR_NOERROR) {
311 return unsigned(-1);
312 }
313 memset(&inhdr, 0, sizeof(inhdr));
314 inhdr.lpData = inlongmes.data();
315 inhdr.dwBufferLength = OPENMSX_W32_MIDI_SYSMES_MAXLEN;
316 if (midiInPrepareHeader(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle),
317 &inhdr, sizeof(inhdr)) != MMSYSERR_NOERROR) {
318 return unsigned(-1);
319 }
320 if (midiInAddBuffer(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle), &inhdr, sizeof(inhdr)) != MMSYSERR_NOERROR) {
321 return unsigned(-1);
322 }
323 if (midiInStart(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle)) != MMSYSERR_NOERROR) {
324 return unsigned(-1);
325 }
326 return idx;
327}
328
329int w32_midiInClose(unsigned idx)
330{
331 midiInStop(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle));
332 midiInReset(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle));
333 midiInUnprepareHeader(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle),
334 &inhdr, sizeof(inhdr));
335 if (midiInClose(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle)) == MMSYSERR_NOERROR) {
336 return 0;
337 } else {
338 return -1;
339 }
340}
341
342} // namespace openmsx
343
344#endif // _WIN32
#define MAXPATHLEN
This file implemented 3 utility functions:
Definition Autofire.cc:11
size_t size(std::string_view utf8)
constexpr auto xrange(T e)
Definition xrange.hh:132