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