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 #include "MSXException.hh"
31 #include "MemBuffer.hh"
32 #include "cstdiop.hh"
33 #include "one_of.hh"
34 #include "xrange.hh"
35 
36 #include <cstring>
37 #include <cstdlib>
38 #include <sstream>
39 #include <cassert>
40 
41 #define MAXPATHLEN MAX_PATH
42 
43 using std::string;
44 
45 namespace openmsx {
46 
47 /*
48  * MIDI I/O helper functions for Win32.
49  */
50 // MIDI SYSTEM MESSAGES max length is not defined...
51 // TODO: support variable length.
52 #define OPENMSX_W32_MIDI_SYSMES_MAXLEN 4096
53 
54 
55 struct vfn_midi {
56  unsigned idx;
57  unsigned devid;
58  HMIDI handle;
59  char vfname[MAXPATHLEN + 1];
60  char devname[MAXPNAMELEN];
61 };
62 
63 struct outbuf {
64  MIDIHDR header;
65 };
66 
67 static MemBuffer<vfn_midi> vfnt_midiout;
68 static MemBuffer<vfn_midi> vfnt_midiin;
69 static unsigned vfnt_midiout_num, vfnt_midiin_num;
70 
71 static MemBuffer<int> state_out;
72 static MemBuffer<outbuf> buf_out;
73 
74 static MIDIHDR inhdr;
75 static char inlongmes[OPENMSX_W32_MIDI_SYSMES_MAXLEN];
76 
77 
78 static void w32_midiDevNameConv(char *dst, char *src)
79 {
80  size_t len = strlen(src);
81  size_t i = 0;
82  for (; i < len; ++i) {
83  if ((src[i] < '0') || (src[i] > 'z') ||
84  ((src[i] > '9') && (src[i] < 'A')) ||
85  ((src[i] > 'Z') && (src[i] < 'a'))) {
86  dst[i] = '_';
87  } else {
88  dst[i] = src[i];
89  }
90  }
91  dst[i] = '\0';
92 }
93 
94 
95 // MIDI-OUT
96 static int w32_midiOutFindDev(unsigned *idx, unsigned *dev, const char *vfn)
97 {
98  for (auto i : xrange(vfnt_midiout_num)) {
99  if (!strcmp(vfnt_midiout[i].vfname, vfn)) {
100  *idx = i;
101  *dev = vfnt_midiout[i].devid;
102  return 0;
103  }
104  }
105  return -1;
106 }
107 
108 
109 int w32_midiOutInit()
110 {
111  vfnt_midiout_num = 0;
112  unsigned num = midiOutGetNumDevs();
113  if (!num) return 0;
114 
115  state_out.resize(num + 1);
116  memset(state_out.data(), 0, (num + 1) * sizeof(int));
117 
118  buf_out.resize(num + 1);
119  memset(buf_out.data(), 0, (num + 1) * sizeof(outbuf));
120 
121  vfnt_midiout.resize(num + 1);
122 
123  // MIDI_MAPPER is #define's as ((UINT)-1)
124  UINT OPENMSX_MIDI_MAPPER = static_cast<UINT>(-1);
125  MIDIOUTCAPSA cap;
126  if (midiOutGetDevCapsA(OPENMSX_MIDI_MAPPER, &cap, sizeof(cap)) != MMSYSERR_NOERROR) {
127  return 2;
128  }
129  vfnt_midiout[0].devid = OPENMSX_MIDI_MAPPER;
130  w32_midiDevNameConv(vfnt_midiout[0].devname, cap.szPname);
131  strncpy(vfnt_midiout[0].vfname, "midi-out", MAXPATHLEN + 1);
132  vfnt_midiout_num ++;
133 
134  for (auto i : xrange(num)) {
135  if (midiOutGetDevCapsA(i, &cap, sizeof(cap)) != MMSYSERR_NOERROR) {
136  return 0; // atleast MIDI-MAPPER is available...
137  }
138  vfnt_midiout[i + 1].devid = i;
139  w32_midiDevNameConv(vfnt_midiout[i + 1].devname, cap.szPname);
140  snprintf(vfnt_midiout[i + 1].vfname, MAXPATHLEN + 1, "midi-out-%u", i);
141  vfnt_midiout_num++;
142  }
143  return 0;
144 }
145 
146 void w32_midiOutClean()
147 {
148  vfnt_midiout_num = 0;
149 }
150 
151 
152 unsigned w32_midiOutGetVFNsNum()
153 {
154  return vfnt_midiout_num;
155 }
156 
157 string w32_midiOutGetVFN(unsigned nmb)
158 {
159  assert(nmb < vfnt_midiout_num);
160  return vfnt_midiout[nmb].vfname;
161 }
162 
163 string w32_midiOutGetRDN(unsigned nmb)
164 {
165  assert(nmb < vfnt_midiout_num);
166  return vfnt_midiout[nmb].devname;
167 }
168 
169 
170 unsigned w32_midiOutOpen(const char *vfn)
171 {
172  unsigned idx, devid;
173  if (w32_midiOutFindDev(&idx, &devid,vfn)) {
174  return unsigned(-1);
175  }
176  if (midiOutOpen(reinterpret_cast<HMIDIOUT*>(&vfnt_midiout[idx].handle), devid, 0, 0 ,0) != MMSYSERR_NOERROR) {
177  return unsigned(-1);
178  }
179  return idx;
180 }
181 
182 int w32_midiOutClose(unsigned idx)
183 {
184  midiOutReset(reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle));
185  if (midiOutClose(reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle)) == MMSYSERR_NOERROR) {
186  return 0;
187  } else {
188  return -1;
189  }
190 }
191 
192 
193 static int w32_midiOutFlushExclusiveMsg(unsigned idx)
194 {
195  int i;
196  //buf_out[idx].header.lpData = buf_out[idx].longmes;
197  //buf_out[idx].header.dwBufferLength = buf_out[idx].longmes_cnt;
198  buf_out[idx].header.dwFlags = 0;
199  if ((i = midiOutPrepareHeader(reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle), &buf_out[idx].header, sizeof(buf_out[idx].header))) != MMSYSERR_NOERROR) {
200  throw FatalError("midiOutPrepareHeader() returned ", i);
201  }
202  if ((i = midiOutLongMsg(reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle), &buf_out[idx].header, sizeof(buf_out[idx].header))) != MMSYSERR_NOERROR) {
203  throw FatalError("midiOutLongMsg() returned ", i);
204  }
205  // Wait sending in driver.
206  // This may take long...
207  while (!(buf_out[idx].header.dwFlags & MHDR_DONE)) {
208  Sleep(1);
209  }
210  // Sending Exclusive done.
211  if ((i = midiOutUnprepareHeader(reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle), &buf_out[idx].header, sizeof(buf_out[idx].header))) != MMSYSERR_NOERROR) {
212  throw FatalError("midiOutUnPrepareHeader() returned ", i);
213  }
214  return 0;
215 }
216 
217 int w32_midiOutMsg(size_t size, const uint8_t* data, unsigned idx)
218 {
219  if (size == 0) return 0;
220 
221  HMIDIOUT hMidiOut = reinterpret_cast<HMIDIOUT>(vfnt_midiout[idx].handle);
222  if (data[0] == one_of(0xF0, 0xF7)) { // SysEx
223  if (size > OPENMSX_W32_MIDI_SYSMES_MAXLEN) {
224  return -1;
225  }
226  auto& buf = buf_out[idx];
227  // Note: We have to be careful with the const_cast here.
228  // Even though Windows doesn't write to the buffer, it fails if you don't have
229  // write access to the respective memory page.
230  buf.header.lpData = const_cast<LPSTR>(reinterpret_cast<LPCSTR>(data));
231  buf.header.dwBufferLength = unsigned(size);
232  w32_midiOutFlushExclusiveMsg(idx);
233  } else {
234  DWORD midiMsg = 0x000000;
235  for (unsigned i = 0; i < size && i < 4; i++) {
236  midiMsg |= data[i] << (8 * i);
237  }
238  midiOutShortMsg(hMidiOut, midiMsg);
239  }
240  return 0;
241 }
242 
243 
244 // MIDI-IN
245 static int w32_midiInFindDev(unsigned *idx, unsigned *dev, const char *vfn)
246 {
247  for (auto i : xrange(vfnt_midiin_num)) {
248  if (!strcmp(vfnt_midiin[i].vfname, vfn)) {
249  *idx = i;
250  *dev = vfnt_midiin[i].devid;
251  return 0;
252  }
253  }
254  return -1;
255 }
256 
257 
258 int w32_midiInInit()
259 {
260  vfnt_midiin_num = 0;
261  unsigned num = midiInGetNumDevs();
262  if (!num) return 0;
263 
264  vfnt_midiin.resize(num + 1);
265  for (auto i : xrange(num)) {
266  MIDIINCAPSA cap;
267  if (midiInGetDevCapsA(i, &cap, sizeof(cap)) != MMSYSERR_NOERROR) {
268  return 1;
269  }
270  vfnt_midiin[i].devid = i;
271  w32_midiDevNameConv(vfnt_midiin[i].devname, cap.szPname);
272  snprintf(vfnt_midiin[i].vfname, MAXPATHLEN + 1, "midi-in-%u", i);
273  vfnt_midiin_num++;
274  }
275  return 0;
276 }
277 
278 void w32_midiInClean()
279 {
280  vfnt_midiin_num = 0;
281 }
282 
283 
284 unsigned w32_midiInGetVFNsNum()
285 {
286  return vfnt_midiin_num;
287 }
288 
289 string w32_midiInGetVFN(unsigned nmb)
290 {
291  assert(nmb < vfnt_midiin_num);
292  return vfnt_midiin[nmb].vfname;
293 }
294 
295 string w32_midiInGetRDN(unsigned nmb)
296 {
297  assert(nmb < vfnt_midiin_num);
298  return vfnt_midiin[nmb].devname;
299 }
300 
301 unsigned w32_midiInOpen(const char *vfn, DWORD thrdid)
302 {
303  unsigned idx, devid;
304  if (w32_midiInFindDev(&idx, &devid, vfn)) {
305  return unsigned(-1);
306  }
307  if (midiInOpen(reinterpret_cast<HMIDIIN*>(&vfnt_midiin[idx].handle), devid, thrdid, 0, CALLBACK_THREAD) != MMSYSERR_NOERROR) {
308  return unsigned(-1);
309  }
310  memset(&inhdr, 0, sizeof(inhdr));
311  inhdr.lpData = inlongmes;
312  inhdr.dwBufferLength = OPENMSX_W32_MIDI_SYSMES_MAXLEN;
313  if (midiInPrepareHeader(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle),
314  static_cast<LPMIDIHDR>(&inhdr), sizeof(inhdr)) != MMSYSERR_NOERROR) {
315  return unsigned(-1);
316  }
317  if (midiInAddBuffer(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle), static_cast<LPMIDIHDR>(&inhdr), sizeof(inhdr)) != MMSYSERR_NOERROR) {
318  return unsigned(-1);
319  }
320  if (midiInStart(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle)) != MMSYSERR_NOERROR) {
321  return unsigned(-1);
322  }
323  return idx;
324 }
325 
326 int w32_midiInClose(unsigned idx)
327 {
328  midiInStop(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle));
329  midiInReset(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle));
330  midiInUnprepareHeader(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle),
331  static_cast<LPMIDIHDR>(&inhdr), sizeof(inhdr));
332  if (midiInClose(reinterpret_cast<HMIDIIN>(vfnt_midiin[idx].handle)) == MMSYSERR_NOERROR) {
333  return 0;
334  } else {
335  return -1;
336  }
337 }
338 
339 } // namespace openmsx
340 
341 #endif // _WIN32
#define MAXPATHLEN
Definition: one_of.hh:7
This file implemented 3 utility functions:
Definition: Autofire.cc:5
size_t size(std::string_view utf8)
constexpr auto xrange(T e)
Definition: xrange.hh:155