1 /*
2 * midi-win32-drv.c - MIDI emulation for win32.
3 *
4 * Written by
5 * Daniel Kahlin <daniel@kahlin.net>
6 *
7 * This file is part of VICE, the Versatile Commodore Emulator.
8 * See README for copyright notice.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23 * 02111-1307 USA.
24 *
25 */
26
27 #undef DEBUG
28
29 #include "vice.h"
30
31 #ifdef WIN32_COMPILE
32
33 #ifdef HAVE_MIDI
34
35 #include "types.h"
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <windows.h>
41 #include <mmsystem.h>
42
43 #include "cmdline.h"
44 #include "log.h"
45 #include "mididrv.h"
46
47 #include "resources.h"
48
49 #if 0
50 #ifndef DWORD_PTR
51 #define DWORD_PTR unsigned long
52 #endif
53 #endif
54
55 /* ------------------------------------------------------------------------- */
56
57 static log_t mididrv_log = LOG_ERR;
58
59 static HMIDIIN handle_in = 0;
60 static HMIDIOUT handle_out = 0;
61
62 static void CALLBACK midi_callback(HMIDIIN handle, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
63
64 /* ------------------------------------------------------------------------- */
65
66 #define OUT_BUF_LEN 3
67
68 static int out_index = 0;
69 static BYTE out_buf[OUT_BUF_LEN];
70
71 /* ------------------------------------------------------------------------- */
72
73 #define IN_BUF_LEN 1024
74
75 static volatile unsigned int in_wi = 0;
76 static volatile unsigned int in_ri = 0;
77 static BYTE in_buf[IN_BUF_LEN];
78
79 static int midi_in_dev = 0;
80 static int midi_out_dev = 0;
81
set_midi_in_dev(int val,void * param)82 static int set_midi_in_dev(int val, void *param)
83 {
84 midi_in_dev = val;
85 return 0;
86 }
87
set_midi_out_dev(int val,void * param)88 static int set_midi_out_dev(int val, void *param)
89 {
90 midi_out_dev = val;
91 return 0;
92 }
93
94 static const resource_int_t resources_int[] = {
95 { "MIDIInDev", 0, RES_EVENT_NO, (resource_value_t)0,
96 &midi_in_dev, set_midi_in_dev, NULL },
97 { "MIDIOutDev", 0, RES_EVENT_NO, (resource_value_t)0,
98 &midi_out_dev, set_midi_out_dev, NULL },
99 RESOURCE_INT_LIST_END
100 };
101
mididrv_resources_init(void)102 int mididrv_resources_init(void)
103 {
104 return resources_register_int(resources_int);
105 }
106
mididrv_resources_shutdown(void)107 void mididrv_resources_shutdown(void)
108 {
109 }
110
111 static const cmdline_option_t cmdline_options[] =
112 {
113 { "-midiin", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
114 NULL, NULL, "MIDIInDev", NULL,
115 "<number>", "Specify MIDI-In device" },
116 { "-midiout", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
117 NULL, NULL, "MIDIOutDev", NULL,
118 "<number>", "Specify MIDI-Out device" },
119 CMDLINE_LIST_END
120 };
121
mididrv_cmdline_options_init(void)122 int mididrv_cmdline_options_init(void)
123 {
124 return cmdline_register_options(cmdline_options);
125 }
126
reset_fifo(void)127 static void reset_fifo(void)
128 {
129 in_wi = 0;
130 in_ri = 0;
131 }
132
write_fifo(BYTE data)133 static int write_fifo(BYTE data)
134 {
135 if (((in_wi - in_ri) % IN_BUF_LEN) == (IN_BUF_LEN - 1)) {
136 return 1;
137 }
138
139 in_buf[in_wi] = data;
140 in_wi = (in_wi + 1) % IN_BUF_LEN;
141 return 0;
142 }
143
read_fifo(BYTE * data)144 static int read_fifo(BYTE *data)
145 {
146 if (((in_wi - in_ri) % IN_BUF_LEN) != 0) {
147 *data = in_buf[in_ri];
148 in_ri = (in_ri + 1) % IN_BUF_LEN;
149 return 1;
150 }
151 return 0;
152 }
153
154 /* ------------------------------------------------------------------------- */
155
message_len(BYTE msg)156 static int message_len(BYTE msg)
157 {
158 int len = 0;
159
160 switch (msg & 0xf0) {
161 case 0x80: /* Note Off */
162 case 0x90: /* Note On */
163 case 0xa0: /* Polyphonic Aftertouch */
164 case 0xb0: /* Control Change */
165 case 0xe0: /* Pitch Wheel */
166 len = 3;
167 break;
168 case 0xc0: /* Program Change */
169 case 0xd0: /* Channel Aftertouch */
170 len = 2;
171 break;
172 case 0xf0: /* Special */
173 switch (msg) {
174 case 0xf0: /* Sysex Start (shouldn't happen here) */
175 case 0xf7: /* Sysex End (shouldn't happen here) */
176 len = -1;
177 break;
178 case 0xf2: /* Song Pointer */
179 len = 3;
180 break;
181 case 0xf1: /* Quarter Frame */
182 case 0xf3: /* Song Select */
183 case 0xf9: /* Measure End */
184 len = 2;
185 break;
186 case 0xf6: /* Tuning Request */
187 case 0xf8: /* Timing Clock */
188 case 0xfa: /* Start */
189 case 0xfb: /* Continue */
190 case 0xfc: /* Stop */
191 case 0xfe: /* Active Sensing */
192 case 0xff: /* Reset */
193 len = 1;
194 break;
195 default:
196 break;
197 }
198 default: /* running status */
199 len = 2;
200 break;
201 }
202 return len;
203 }
204
205 /* ------------------------------------------------------------------------- */
206
mididrv_init(void)207 void mididrv_init(void)
208 {
209 if (mididrv_log == LOG_ERR) {
210 mididrv_log = log_open("MIDIdrv");
211 }
212 }
213
214 /* opens a MIDI-In device, returns handle */
mididrv_in_open(void)215 int mididrv_in_open(void)
216 {
217 MMRESULT ret;
218
219 log_message(mididrv_log, "Opening MIDI-In device #%d", midi_in_dev);
220 if (handle_in) {
221 mididrv_in_close();
222 }
223
224 if (midi_in_dev != -1) {
225 ret = midiInOpen(&handle_in, midi_in_dev, (DWORD_PTR)midi_callback, 0, CALLBACK_FUNCTION);
226 if (ret != MMSYSERR_NOERROR) {
227 log_error(mididrv_log, "Cannot open MIDI-In device #%d!", midi_in_dev);
228 handle_in = 0;
229 return -1;
230 }
231 } else {
232 handle_in = 0;
233 return -1;
234 }
235
236 /* reset FIFO */
237 reset_fifo();
238
239 /* can theoretically return MMSYSERR_INVALHANDLE */
240 ret = midiInStart(handle_in);
241
242 return 0;
243 }
244
245 /* opens a MIDI-Out device, returns handle */
mididrv_out_open(void)246 int mididrv_out_open(void)
247 {
248 MMRESULT ret;
249
250 log_message(mididrv_log, "Opening MIDI-Out device #%d", midi_out_dev);
251 if (handle_out) {
252 mididrv_out_close();
253 }
254
255 if (midi_out_dev != -1) {
256 ret = midiOutOpen(&handle_out, midi_out_dev, 0, 0, CALLBACK_NULL);
257 if (ret != MMSYSERR_NOERROR) {
258 log_error(mididrv_log, "Cannot open MIDI-Out device #%d!", midi_out_dev);
259 handle_out = 0;
260 return -1;
261 }
262 } else {
263 handle_out = 0;
264 return -1;
265 }
266
267 /* reset buffer */
268 out_index = 0;
269
270 return 0;
271 }
272
273 /* closes the MIDI-In device*/
mididrv_in_close(void)274 void mididrv_in_close(void)
275 {
276 MMRESULT ret;
277 #ifdef DEBUG
278 log_message(mididrv_log, "in_close");
279 #endif
280 if (!handle_in) {
281 log_error(mididrv_log, "Attempt to close MIDI-In device that wasn't open!");
282 return;
283 }
284 /* can theoretically return MMSYSERR_INVALHANDLE */
285 ret = midiInReset(handle_in);
286
287 ret = midiInClose(handle_in);
288 if (ret != MMSYSERR_NOERROR) {
289 log_error(mididrv_log, "Couldn't close MIDI-In device.");
290 }
291 handle_in = 0;
292 }
293
294 /* closes the MIDI-Out device*/
mididrv_out_close(void)295 void mididrv_out_close(void)
296 {
297 MMRESULT ret;
298 #ifdef DEBUG
299 log_message(mididrv_log, "out_close");
300 #endif
301 if (!handle_out) {
302 log_error(mididrv_log, "Attempt to close MIDI-Out device that wasn't open!");
303 return;
304 }
305
306 /* can theoretically return MMSYSERR_INVALHANDLE */
307 ret = midiOutReset(handle_out);
308
309 ret = midiOutClose(handle_out);
310 if (ret != MMSYSERR_NOERROR) {
311 log_error(mididrv_log, "Couldn't close MIDI-Out device.");
312 }
313 handle_out = 0;
314 }
315
316 /* sends a byte to MIDI-Out */
mididrv_out(uint8_t b)317 void mididrv_out(uint8_t b)
318 {
319 MMRESULT ret;
320 int thres;
321
322 #ifdef DEBUG
323 log_message(mididrv_log, "out %02x", b);
324 #endif
325
326 out_buf[out_index] = b;
327 out_index++;
328 if (out_index > OUT_BUF_LEN) {
329 out_index = 0;
330 log_error(mididrv_log, "MIDI-Out overrun.");
331 }
332
333 thres = message_len(out_buf[0]);
334
335 /* flush when enough bytes have been queued */
336 if (out_index >= thres) {
337 DWORD data;
338
339 out_index = 0;
340 data = out_buf[0] | (out_buf[1] << 8) | (out_buf[2] << 16);
341 #ifdef DEBUG
342 log_message(mididrv_log, "flushing out %06x", data);
343 #endif
344 ret = midiOutShortMsg(handle_out, data);
345 if (ret != MMSYSERR_NOERROR) {
346 log_error(mididrv_log, "Failed to output data on MIDI-Out device.");
347 }
348 }
349
350 return;
351 }
352
midi_callback(HMIDIIN handle,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2)353 static void CALLBACK midi_callback(HMIDIIN handle, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
354 {
355 int len;
356 int i;
357 switch (uMsg) {
358 case MIM_DATA:
359 #ifdef DEBUG
360 log_message(mididrv_log, "MIDI callback got %08x", dwParam1);
361 #endif
362 len = message_len((BYTE)(dwParam1 & 0xff));
363 for (i = 0; i < len; i++) {
364 write_fifo((BYTE)(dwParam1 & 0xff));
365 dwParam1 >>= 8;
366 }
367 break;
368 case MIM_LONGDATA:
369 break;
370 case MIM_OPEN:
371 case MIM_CLOSE:
372 case MIM_ERROR:
373 case MIM_LONGERROR:
374 case MIM_MOREDATA:
375 break;
376 default:
377 break;
378 }
379 }
380
381
382 /* gets a byte from MIDI-In, returns !=0 if byte received, byte in *b. */
mididrv_in(uint8_t * b)383 int mididrv_in(uint8_t *b)
384 {
385 if (!handle_in) {
386 log_error(mididrv_log, "Attempt to read from closed MIDI-In port!");
387 return -1;
388 }
389
390 if (read_fifo(b)) {
391 #ifdef DEBUG
392 log_message(mididrv_log, "in got %02x", *b);
393 #endif
394 return 1;
395 }
396 return 0;
397 }
398
399 #endif
400 #endif
401