1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4 * Sample MIDI Wine Driver for ALSA (basically Linux)
5 *
6 * Copyright 1994 Martin Ayotte
7 * Copyright 1998 Luiz Otavio L. Zorzella (init procedures)
8 * Copyright 1998/1999 Eric POUECH :
9 * 98/7 changes for making this MIDI driver work on OSS
10 * current support is limited to MIDI ports of OSS systems
11 * 98/9 rewriting MCI code for MIDI
12 * 98/11 split in midi.c and mcimidi.c
13 * Copyright 2003 Christian Costa :
14 * ALSA port
15 *
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
20 *
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
25 *
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 *
30 * TODO: Finish midi record
31 *
32 */
33
34 #include "config.h"
35
36 #include <string.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
42 #include <fcntl.h>
43 #include <errno.h>
44
45 #include "windef.h"
46 #include "winbase.h"
47 #include "wingdi.h"
48 #include "winuser.h"
49 #include "winnls.h"
50 #include "mmddk.h"
51 #include "mmreg.h"
52 #include "dsound.h"
53 #include "wine/debug.h"
54
55 #include <alsa/asoundlib.h>
56
57 WINE_DEFAULT_DEBUG_CHANNEL(midi);
58
59 #ifndef SND_SEQ_PORT_TYPE_PORT
60 #define SND_SEQ_PORT_TYPE_PORT (1<<19) /* Appears in version 1.0.12rc1 */
61 #endif
62
63 typedef struct {
64 int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
65 DWORD bufsize;
66 MIDIOPENDESC midiDesc;
67 WORD wFlags;
68 LPMIDIHDR lpQueueHdr;
69 DWORD dwTotalPlayed;
70 unsigned char incoming[3];
71 unsigned char incPrev;
72 char incLen;
73 DWORD startTime;
74 MIDIINCAPSW caps;
75 snd_seq_addr_t addr;
76 } WINE_MIDIIN;
77
78 typedef struct {
79 BOOL bEnabled;
80 DWORD bufsize;
81 MIDIOPENDESC midiDesc;
82 WORD wFlags;
83 LPMIDIHDR lpQueueHdr;
84 DWORD dwTotalPlayed;
85 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
86 MIDIOUTCAPSW caps;
87 snd_seq_addr_t addr;
88 } WINE_MIDIOUT;
89
90 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
91 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
92
93 /* this is the total number of MIDI out devices found (synth and port) */
94 static int MODM_NumDevs = 0;
95 /* this is the total number of MIDI out devices found */
96 static int MIDM_NumDevs = 0;
97
98 static snd_seq_t* midiSeq = NULL;
99 static int numOpenMidiSeq = 0;
100 static int numStartedMidiIn = 0;
101
102 static int port_in;
103 static int port_out;
104
105 static CRITICAL_SECTION crit_sect; /* protects all MidiIn buffer queues */
106 static CRITICAL_SECTION_DEBUG critsect_debug =
107 {
108 0, 0, &crit_sect,
109 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
110 0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
111 };
112 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
113
114 static int end_thread;
115 static HANDLE hThread;
116
117 /*======================================================================*
118 * Low level MIDI implementation *
119 *======================================================================*/
120
121 static int midiOpenSeq(int);
122 static int midiCloseSeq(void);
123
124 #if 0 /* Debug Purpose */
125 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
126 {
127 va_list arg;
128 if (err == ENOENT)
129 return;
130 va_start(arg, fmt);
131 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
132 vfprintf(stderr, fmt, arg);
133 if (err)
134 fprintf(stderr, ": %s", snd_strerror(err));
135 putc('\n', stderr);
136 va_end(arg);
137 }
138 #endif
139
140 /**************************************************************************
141 * MIDI_unixToWindowsDeviceType [internal]
142 *
143 * return the Windows equivalent to a Unix Device Type
144 *
145 */
146 static int MIDI_AlsaToWindowsDeviceType(unsigned int type)
147 {
148 /* MOD_MIDIPORT output port
149 * MOD_SYNTH generic internal synth
150 * MOD_SQSYNTH square wave internal synth
151 * MOD_FMSYNTH FM internal synth
152 * MOD_MAPPER MIDI mapper
153 * MOD_WAVETABLE hardware wavetable internal synth
154 * MOD_SWSYNTH software internal synth
155 */
156
157 /* FIXME Is this really the correct equivalence from ALSA to
158 Windows Sound type? */
159
160 if (type & SND_SEQ_PORT_TYPE_SYNTH)
161 return MOD_FMSYNTH;
162
163 if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
164 return MOD_SYNTH;
165
166 if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION))
167 return MOD_MIDIPORT;
168
169 ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type);
170 return MOD_FMSYNTH;
171 }
172
173 /**************************************************************************
174 * MIDI_NotifyClient [internal]
175 */
176 static void MIDI_NotifyClient(UINT wDevID, WORD wMsg,
177 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
178 {
179 DWORD dwCallBack;
180 UINT uFlags;
181 HANDLE hDev;
182 DWORD dwInstance;
183
184 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
185 wDevID, wMsg, dwParam1, dwParam2);
186
187 switch (wMsg) {
188 case MOM_OPEN:
189 case MOM_CLOSE:
190 case MOM_DONE:
191 case MOM_POSITIONCB:
192 if (wDevID > MODM_NumDevs) return;
193
194 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
195 uFlags = MidiOutDev[wDevID].wFlags;
196 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
197 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
198 break;
199
200 case MIM_OPEN:
201 case MIM_CLOSE:
202 case MIM_DATA:
203 case MIM_LONGDATA:
204 case MIM_ERROR:
205 case MIM_LONGERROR:
206 case MIM_MOREDATA:
207 if (wDevID > MIDM_NumDevs) return;
208
209 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
210 uFlags = MidiInDev[wDevID].wFlags;
211 hDev = MidiInDev[wDevID].midiDesc.hMidi;
212 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
213 break;
214 default:
215 ERR("Unsupported MSW-MIDI message %u\n", wMsg);
216 return;
217 }
218
219 DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
220 }
221
222 static int midi_warn = 1;
223 /**************************************************************************
224 * midiOpenSeq [internal]
225 */
226 static int midiOpenSeq(int create_client)
227 {
228 if (numOpenMidiSeq == 0) {
229 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
230 {
231 if (midi_warn)
232 {
233 WARN("Error opening ALSA sequencer.\n");
234 }
235 midi_warn = 0;
236 return -1;
237 }
238
239 if (create_client) {
240 /* Setting the client name is the only init to do */
241 snd_seq_set_client_name(midiSeq, "WINE midi driver");
242
243 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
244 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
245 SND_SEQ_PORT_TYPE_APPLICATION);
246 if (port_out < 0)
247 TRACE("Unable to create output port\n");
248 else
249 TRACE("Outport port created successfully (%d)\n", port_out);
250 #else
251 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
252 SND_SEQ_PORT_TYPE_APPLICATION);
253 if (port_out < 0)
254 TRACE("Unable to create output port\n");
255 else
256 TRACE("Outport port created successfully (%d)\n", port_out);
257
258 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
259 SND_SEQ_PORT_TYPE_APPLICATION);
260 if (port_in < 0)
261 TRACE("Unable to create input port\n");
262 else
263 TRACE("Input port created successfully (%d)\n", port_in);
264 #endif
265 }
266 }
267 numOpenMidiSeq++;
268 return 0;
269 }
270
271 /**************************************************************************
272 * midiCloseSeq [internal]
273 */
274 static int midiCloseSeq(void)
275 {
276 if (--numOpenMidiSeq == 0) {
277 snd_seq_delete_simple_port(midiSeq, port_out);
278 snd_seq_delete_simple_port(midiSeq, port_in);
279 snd_seq_close(midiSeq);
280 midiSeq = NULL;
281 }
282 return 0;
283 }
284
285 static DWORD WINAPI midRecThread(LPVOID arg)
286 {
287 int npfd;
288 struct pollfd *pfd;
289
290 TRACE("Thread startup\n");
291
292 while(!end_thread) {
293 TRACE("Thread loop\n");
294 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
295 pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
296 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
297
298 /* Check if an event is present */
299 if (poll(pfd, npfd, 250) < 0) {
300 HeapFree(GetProcessHeap(), 0, pfd);
301 continue;
302 }
303
304 /* Note: This definitely does not work.
305 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
306 snd_seq_event_t* ev;
307 snd_seq_event_input(midiSeq, &ev);
308 ....................
309 snd_seq_free_event(ev);
310 }*/
311
312 do {
313 WORD wDevID;
314 snd_seq_event_t* ev;
315 snd_seq_event_input(midiSeq, &ev);
316 /* Find the target device */
317 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
318 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
319 break;
320 if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
321 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
322 else {
323 DWORD dwTime, toSend = 0;
324 int value = 0;
325 /* FIXME: Should use ev->time instead for better accuracy */
326 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
327 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
328 switch(ev->type)
329 {
330 case SND_SEQ_EVENT_NOTEOFF:
331 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
332 break;
333 case SND_SEQ_EVENT_NOTEON:
334 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
335 break;
336 case SND_SEQ_EVENT_KEYPRESS:
337 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
338 break;
339 case SND_SEQ_EVENT_CONTROLLER:
340 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
341 break;
342 case SND_SEQ_EVENT_PITCHBEND:
343 value = ev->data.control.value + 0x2000;
344 toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
345 break;
346 case SND_SEQ_EVENT_PGMCHANGE:
347 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
348 break;
349 case SND_SEQ_EVENT_CHANPRESS:
350 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
351 break;
352 case SND_SEQ_EVENT_CLOCK:
353 toSend = 0xF8;
354 break;
355 case SND_SEQ_EVENT_START:
356 toSend = 0xFA;
357 break;
358 case SND_SEQ_EVENT_CONTINUE:
359 toSend = 0xFB;
360 break;
361 case SND_SEQ_EVENT_STOP:
362 toSend = 0xFC;
363 break;
364 case SND_SEQ_EVENT_SONGPOS:
365 toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_POS;
366 break;
367 case SND_SEQ_EVENT_SONGSEL:
368 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_SELECT;
369 break;
370 case SND_SEQ_EVENT_RESET:
371 toSend = 0xFF;
372 break;
373 case SND_SEQ_EVENT_QFRAME:
374 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_MTC_QUARTER;
375 break;
376 case SND_SEQ_EVENT_SYSEX:
377 {
378 int pos = 0;
379 int len = ev->data.ext.len;
380 LPBYTE ptr = ev->data.ext.ptr;
381 LPMIDIHDR lpMidiHdr;
382
383 EnterCriticalSection(&crit_sect);
384 while (len) {
385 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
386 int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
387 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
388 lpMidiHdr->dwBytesRecorded += copylen;
389 len -= copylen;
390 pos += copylen;
391 /* We check if we reach the end of buffer or the end of sysex before notifying
392 * to handle the case where ALSA split the sysex into several events */
393 if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
394 (*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
395 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
396 lpMidiHdr->dwFlags |= MHDR_DONE;
397 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
398 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
399 }
400 } else {
401 FIXME("Sysex data received but no buffer to store it!\n");
402 break;
403 }
404 }
405 LeaveCriticalSection(&crit_sect);
406 }
407 break;
408 case SND_SEQ_EVENT_SENSING:
409 /* Noting to do */
410 break;
411 default:
412 FIXME("Unhandled event received, type = %x\n", ev->type);
413 break;
414 }
415 if (toSend != 0) {
416 TRACE("Sending event %08x (from %d %d)\n", toSend, ev->source.client, ev->source.port);
417 MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
418 }
419 }
420 snd_seq_free_event(ev);
421 } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
422
423 HeapFree(GetProcessHeap(), 0, pfd);
424 }
425 return 0;
426 }
427
428 /**************************************************************************
429 * midGetDevCaps [internal]
430 */
431 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
432 {
433 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
434
435 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
436 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
437
438 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
439
440 return MMSYSERR_NOERROR;
441 }
442
443
444 /**************************************************************************
445 * midOpen [internal]
446 */
447 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
448 {
449 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
450
451 if (lpDesc == NULL) {
452 WARN("Invalid Parameter !\n");
453 return MMSYSERR_INVALPARAM;
454 }
455
456 /* FIXME :
457 * how to check that content of lpDesc is correct ?
458 */
459 if (wDevID >= MIDM_NumDevs) {
460 WARN("wDevID too large (%u) !\n", wDevID);
461 return MMSYSERR_BADDEVICEID;
462 }
463 if (MidiInDev[wDevID].state == -1) {
464 WARN("device disabled\n");
465 return MIDIERR_NODEVICE;
466 }
467 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
468 WARN("device already open !\n");
469 return MMSYSERR_ALLOCATED;
470 }
471 if ((dwFlags & MIDI_IO_STATUS) != 0) {
472 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
473 dwFlags &= ~MIDI_IO_STATUS;
474 }
475 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
476 FIXME("Bad dwFlags\n");
477 return MMSYSERR_INVALFLAG;
478 }
479
480 if (midiOpenSeq(1) < 0) {
481 return MMSYSERR_ERROR;
482 }
483
484 /* Connect our app port to the device port */
485 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
486 return MMSYSERR_NOTENABLED;
487
488 TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
489
490 if (numStartedMidiIn++ == 0) {
491 end_thread = 0;
492 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
493 if (!hThread) {
494 numStartedMidiIn = 0;
495 WARN("Couldn't create thread for midi-in\n");
496 midiCloseSeq();
497 return MMSYSERR_ERROR;
498 }
499 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
500 TRACE("Created thread for midi-in\n");
501 }
502
503 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
504
505 MidiInDev[wDevID].lpQueueHdr = NULL;
506 MidiInDev[wDevID].dwTotalPlayed = 0;
507 MidiInDev[wDevID].bufsize = 0x3FFF;
508 MidiInDev[wDevID].midiDesc = *lpDesc;
509 MidiInDev[wDevID].state = 0;
510 MidiInDev[wDevID].incLen = 0;
511 MidiInDev[wDevID].startTime = 0;
512
513 MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
514 return MMSYSERR_NOERROR;
515 }
516
517 /**************************************************************************
518 * midClose [internal]
519 */
520 static DWORD midClose(WORD wDevID)
521 {
522 int ret = MMSYSERR_NOERROR;
523
524 TRACE("(%04X);\n", wDevID);
525
526 if (wDevID >= MIDM_NumDevs) {
527 WARN("wDevID too big (%u) !\n", wDevID);
528 return MMSYSERR_BADDEVICEID;
529 }
530 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
531 WARN("device not opened !\n");
532 return MMSYSERR_ERROR;
533 }
534 if (MidiInDev[wDevID].lpQueueHdr != 0) {
535 return MIDIERR_STILLPLAYING;
536 }
537
538 if (midiSeq == NULL) {
539 WARN("ooops !\n");
540 return MMSYSERR_ERROR;
541 }
542 if (--numStartedMidiIn == 0) {
543 TRACE("Stopping thread for midi-in\n");
544 end_thread = 1;
545 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
546 WARN("Thread end not signaled, force termination\n");
547 TerminateThread(hThread, 0);
548 }
549 TRACE("Stopped thread for midi-in\n");
550 }
551
552 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
553 midiCloseSeq();
554
555 MidiInDev[wDevID].bufsize = 0;
556 MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
557 MidiInDev[wDevID].midiDesc.hMidi = 0;
558
559 return ret;
560 }
561
562
563 /**************************************************************************
564 * midAddBuffer [internal]
565 */
566 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
567 {
568 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
569
570 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
571 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
572
573 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
574 if (dwSize < offsetof(MIDIHDR,dwOffset)) return MMSYSERR_INVALPARAM;
575 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
576 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
577 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
578
579 EnterCriticalSection(&crit_sect);
580 lpMidiHdr->dwFlags &= ~WHDR_DONE;
581 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
582 lpMidiHdr->dwBytesRecorded = 0;
583 lpMidiHdr->lpNext = 0;
584 if (MidiInDev[wDevID].lpQueueHdr == 0) {
585 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
586 } else {
587 LPMIDIHDR ptr;
588
589 for (ptr = MidiInDev[wDevID].lpQueueHdr; ptr->lpNext != 0;
590 ptr = ptr->lpNext);
591 ptr->lpNext = lpMidiHdr;
592 }
593 LeaveCriticalSection(&crit_sect);
594
595 return MMSYSERR_NOERROR;
596 }
597
598 /**************************************************************************
599 * midPrepare [internal]
600 */
601 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
602 {
603 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
604
605 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
606 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0)
607 return MMSYSERR_INVALPARAM;
608
609 lpMidiHdr->lpNext = 0;
610 lpMidiHdr->dwFlags |= MHDR_PREPARED;
611 lpMidiHdr->dwBytesRecorded = 0;
612
613 return MMSYSERR_NOERROR;
614 }
615
616 /**************************************************************************
617 * midUnprepare [internal]
618 */
619 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
620 {
621 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
622
623 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
624 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
625
626 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
627 lpMidiHdr->lpData == 0)
628 return MMSYSERR_INVALPARAM;
629
630 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
631 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
632
633 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
634
635 return MMSYSERR_NOERROR;
636 }
637
638 /**************************************************************************
639 * midReset [internal]
640 */
641 static DWORD midReset(WORD wDevID)
642 {
643 DWORD dwTime = GetTickCount();
644
645 TRACE("(%04X);\n", wDevID);
646
647 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
648 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
649
650 EnterCriticalSection(&crit_sect);
651 while (MidiInDev[wDevID].lpQueueHdr) {
652 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
653 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
654 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
655 MIDI_NotifyClient(wDevID, MIM_LONGDATA,
656 (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime);
657 MidiInDev[wDevID].lpQueueHdr = MidiInDev[wDevID].lpQueueHdr->lpNext;
658 }
659 LeaveCriticalSection(&crit_sect);
660
661 return MMSYSERR_NOERROR;
662 }
663
664 /**************************************************************************
665 * midStart [internal]
666 */
667 static DWORD midStart(WORD wDevID)
668 {
669 TRACE("(%04X);\n", wDevID);
670
671 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
672 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
673
674 MidiInDev[wDevID].state = 1;
675 MidiInDev[wDevID].startTime = GetTickCount();
676 return MMSYSERR_NOERROR;
677 }
678
679 /**************************************************************************
680 * midStop [internal]
681 */
682 static DWORD midStop(WORD wDevID)
683 {
684 TRACE("(%04X);\n", wDevID);
685
686 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
687 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
688
689 MidiInDev[wDevID].state = 0;
690 return MMSYSERR_NOERROR;
691 }
692
693 /**************************************************************************
694 * modGetDevCaps [internal]
695 */
696 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
697 {
698 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
699
700 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
701 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
702
703 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
704
705 return MMSYSERR_NOERROR;
706 }
707
708 /**************************************************************************
709 * modOpen [internal]
710 */
711 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
712 {
713 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
714 if (lpDesc == NULL) {
715 WARN("Invalid Parameter !\n");
716 return MMSYSERR_INVALPARAM;
717 }
718 if (wDevID >= MODM_NumDevs) {
719 TRACE("MAX_MIDIOUTDRV reached !\n");
720 return MMSYSERR_BADDEVICEID;
721 }
722 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
723 WARN("device already open !\n");
724 return MMSYSERR_ALLOCATED;
725 }
726 if (!MidiOutDev[wDevID].bEnabled) {
727 WARN("device disabled !\n");
728 return MIDIERR_NODEVICE;
729 }
730 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
731 WARN("bad dwFlags\n");
732 return MMSYSERR_INVALFLAG;
733 }
734 if (!MidiOutDev[wDevID].bEnabled) {
735 TRACE("disabled wDevID\n");
736 return MMSYSERR_NOTENABLED;
737 }
738
739 MidiOutDev[wDevID].lpExtra = 0;
740
741 switch (MidiOutDev[wDevID].caps.wTechnology) {
742 case MOD_FMSYNTH:
743 case MOD_MIDIPORT:
744 case MOD_SYNTH:
745 if (midiOpenSeq(1) < 0) {
746 return MMSYSERR_ALLOCATED;
747 }
748 break;
749 default:
750 WARN("Technology not supported (yet) %d !\n",
751 MidiOutDev[wDevID].caps.wTechnology);
752 return MMSYSERR_NOTENABLED;
753 }
754
755 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
756
757 MidiOutDev[wDevID].lpQueueHdr = NULL;
758 MidiOutDev[wDevID].dwTotalPlayed = 0;
759 MidiOutDev[wDevID].bufsize = 0x3FFF;
760 MidiOutDev[wDevID].midiDesc = *lpDesc;
761
762 /* Connect our app port to the device port */
763 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
764 return MMSYSERR_NOTENABLED;
765
766 MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
767 TRACE("Successful !\n");
768 return MMSYSERR_NOERROR;
769 }
770
771
772 /**************************************************************************
773 * modClose [internal]
774 */
775 static DWORD modClose(WORD wDevID)
776 {
777 int ret = MMSYSERR_NOERROR;
778
779 TRACE("(%04X);\n", wDevID);
780
781 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
782 WARN("device not opened !\n");
783 return MMSYSERR_ERROR;
784 }
785 /* FIXME: should test that no pending buffer is still in the queue for
786 * playing */
787
788 if (midiSeq == NULL) {
789 WARN("can't close !\n");
790 return MMSYSERR_ERROR;
791 }
792
793 switch (MidiOutDev[wDevID].caps.wTechnology) {
794 case MOD_FMSYNTH:
795 case MOD_MIDIPORT:
796 case MOD_SYNTH:
797 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
798 midiCloseSeq();
799 break;
800 default:
801 WARN("Technology not supported (yet) %d !\n",
802 MidiOutDev[wDevID].caps.wTechnology);
803 return MMSYSERR_NOTENABLED;
804 }
805
806 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
807 MidiOutDev[wDevID].lpExtra = 0;
808
809 MidiOutDev[wDevID].bufsize = 0;
810 MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
811 MidiOutDev[wDevID].midiDesc.hMidi = 0;
812 return ret;
813 }
814
815 /**************************************************************************
816 * modData [internal]
817 */
818 static DWORD modData(WORD wDevID, DWORD dwParam)
819 {
820 BYTE evt = LOBYTE(LOWORD(dwParam));
821 BYTE d1 = HIBYTE(LOWORD(dwParam));
822 BYTE d2 = LOBYTE(HIWORD(dwParam));
823
824 TRACE("(%04X, %08X);\n", wDevID, dwParam);
825
826 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
827 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
828
829 if (midiSeq == NULL) {
830 WARN("can't play !\n");
831 return MIDIERR_NODEVICE;
832 }
833 switch (MidiOutDev[wDevID].caps.wTechnology) {
834 case MOD_SYNTH:
835 case MOD_MIDIPORT:
836 {
837 int handled = 1; /* Assume event is handled */
838 snd_seq_event_t event;
839 snd_seq_ev_clear(&event);
840 snd_seq_ev_set_direct(&event);
841 snd_seq_ev_set_source(&event, port_out);
842 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
843
844 switch (evt & 0xF0) {
845 case MIDI_CMD_NOTE_OFF:
846 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
847 break;
848 case MIDI_CMD_NOTE_ON:
849 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
850 break;
851 case MIDI_CMD_NOTE_PRESSURE:
852 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
853 break;
854 case MIDI_CMD_CONTROL:
855 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
856 break;
857 case MIDI_CMD_BENDER:
858 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
859 break;
860 case MIDI_CMD_PGM_CHANGE:
861 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
862 break;
863 case MIDI_CMD_CHANNEL_PRESSURE:
864 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
865 break;
866 case MIDI_CMD_COMMON_SYSEX:
867 switch (evt & 0x0F) {
868 case 0x00: /* System Exclusive, don't do it on modData,
869 * should require modLongData*/
870 case 0x04: /* Undefined. */
871 case 0x05: /* Undefined. */
872 case 0x07: /* End of Exclusive. */
873 case 0x09: /* Undefined. */
874 case 0x0D: /* Undefined. */
875 handled = 0;
876 break;
877 case 0x06: /* Tune Request */
878 case 0x08: /* Timing Clock. */
879 case 0x0A: /* Start. */
880 case 0x0B: /* Continue */
881 case 0x0C: /* Stop */
882 case 0x0E: /* Active Sensing. */
883 /* FIXME: Is this function suitable for these purposes
884 (and also Song Select and Song Position Pointer) */
885 snd_seq_ev_set_sysex(&event, 1, &evt);
886 break;
887 case 0x0F: /* Reset */
888 /* snd_seq_ev_set_sysex(&event, 1, &evt);
889 this other way may be better */
890 {
891 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
892 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
893 }
894 break;
895 case 0x01: /* MTC Quarter frame */
896 case 0x03: /* Song Select. */
897 {
898 BYTE buf[2];
899 buf[0] = evt;
900 buf[1] = d1;
901 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
902 }
903 break;
904 case 0x02: /* Song Position Pointer. */
905 {
906 BYTE buf[3];
907 buf[0] = evt;
908 buf[1] = d1;
909 buf[2] = d2;
910 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
911 }
912 break;
913 }
914 break;
915 }
916 if (handled)
917 snd_seq_event_output_direct(midiSeq, &event);
918 }
919 break;
920 default:
921 WARN("Technology not supported (yet) %d !\n",
922 MidiOutDev[wDevID].caps.wTechnology);
923 return MMSYSERR_NOTENABLED;
924 }
925
926 return MMSYSERR_NOERROR;
927 }
928
929 /**************************************************************************
930 * modLongData [internal]
931 */
932 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
933 {
934 int len_add = 0;
935 LPBYTE lpData, lpNewData = NULL;
936 snd_seq_event_t event;
937
938 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
939
940 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
941 * but it seems to be used only for midi input.
942 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
943 */
944
945 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
946 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
947
948 if (midiSeq == NULL) {
949 WARN("can't play !\n");
950 return MIDIERR_NODEVICE;
951 }
952
953 lpData = (LPBYTE) lpMidiHdr->lpData;
954
955 if (lpData == NULL)
956 return MIDIERR_UNPREPARED;
957 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
958 return MIDIERR_UNPREPARED;
959 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
960 return MIDIERR_STILLPLAYING;
961 lpMidiHdr->dwFlags &= ~MHDR_DONE;
962 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
963
964 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
965 * data, or can it also contain raw MIDI data, to be split up and sent to
966 * modShortData() ?
967 * If the latest is true, then the following WARNing will fire up
968 */
969 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
970 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
971 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
972 }
973
974 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
975 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
976 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
977 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
978
979 switch (MidiOutDev[wDevID].caps.wTechnology) {
980 case MOD_FMSYNTH:
981 /* FIXME: I don't think there is much to do here */
982 break;
983 case MOD_MIDIPORT:
984 if (lpData[0] != 0xF0) {
985 /* Send start of System Exclusive */
986 len_add = 1;
987 lpData[0] = 0xF0;
988 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
989 WARN("Adding missing 0xF0 marker at the beginning of "
990 "system exclusive byte stream\n");
991 }
992 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
993 /* Send end of System Exclusive */
994 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
995 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
996 len_add++;
997 WARN("Adding missing 0xF7 marker at the end of "
998 "system exclusive byte stream\n");
999 }
1000 snd_seq_ev_clear(&event);
1001 snd_seq_ev_set_direct(&event);
1002 snd_seq_ev_set_source(&event, port_out);
1003 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1004 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1005 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1006 snd_seq_event_output_direct(midiSeq, &event);
1007 if (lpNewData)
1008 HeapFree(GetProcessHeap(), 0, lpData);
1009 break;
1010 default:
1011 WARN("Technology not supported (yet) %d !\n",
1012 MidiOutDev[wDevID].caps.wTechnology);
1013 HeapFree(GetProcessHeap(), 0, lpNewData);
1014 return MMSYSERR_NOTENABLED;
1015 }
1016
1017 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1018 lpMidiHdr->dwFlags |= MHDR_DONE;
1019 MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
1020 return MMSYSERR_NOERROR;
1021 }
1022
1023 /**************************************************************************
1024 * modPrepare [internal]
1025 */
1026 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1027 {
1028 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1029
1030 if (midiSeq == NULL) {
1031 WARN("can't prepare !\n");
1032 return MMSYSERR_NOTENABLED;
1033 }
1034
1035 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1036 * asks to prepare MIDIHDR which dwFlags != 0.
1037 * So at least check for the inqueue flag
1038 */
1039 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
1040 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0) {
1041 WARN("%p %p %08x %d\n", lpMidiHdr, lpMidiHdr ? lpMidiHdr->lpData : NULL,
1042 lpMidiHdr ? lpMidiHdr->dwFlags : 0, dwSize);
1043 return MMSYSERR_INVALPARAM;
1044 }
1045
1046 lpMidiHdr->lpNext = 0;
1047 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1048 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1049 return MMSYSERR_NOERROR;
1050 }
1051
1052 /**************************************************************************
1053 * modUnprepare [internal]
1054 */
1055 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1056 {
1057 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1058
1059 if (midiSeq == NULL) {
1060 WARN("can't unprepare !\n");
1061 return MMSYSERR_NOTENABLED;
1062 }
1063
1064 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0)
1065 return MMSYSERR_INVALPARAM;
1066 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1067 return MIDIERR_STILLPLAYING;
1068 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1069 return MMSYSERR_NOERROR;
1070 }
1071
1072 /**************************************************************************
1073 * modGetVolume [internal]
1074 */
1075 static DWORD modGetVolume(WORD wDevID, DWORD* lpdwVolume)
1076 {
1077 if (!lpdwVolume) return MMSYSERR_INVALPARAM;
1078 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1079 *lpdwVolume = 0xFFFFFFFF;
1080 return (MidiOutDev[wDevID].caps.dwSupport & MIDICAPS_VOLUME) ? 0 : MMSYSERR_NOTSUPPORTED;
1081 }
1082
1083 /**************************************************************************
1084 * modReset [internal]
1085 */
1086 static DWORD modReset(WORD wDevID)
1087 {
1088 unsigned chn;
1089
1090 TRACE("(%04X);\n", wDevID);
1091
1092 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1093 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1094
1095 /* stop all notes */
1096 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1097 * it's channel dependent...
1098 */
1099 for (chn = 0; chn < 16; chn++) {
1100 /* turn off every note */
1101 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1102 /* remove sustain on all channels */
1103 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1104 }
1105 /* FIXME: the LongData buffers must also be returned to the app */
1106 return MMSYSERR_NOERROR;
1107 }
1108
1109
1110 /**************************************************************************
1111 * ALSA_AddMidiPort [internal]
1112 *
1113 * Helper for ALSA_MidiInit
1114 */
1115 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1116 {
1117 char midiPortName[MAXPNAMELEN];
1118
1119 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1120 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1121 snd_seq_client_info_get_name(cinfo),
1122 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1123 snd_seq_port_info_get_port(pinfo),
1124 snd_seq_port_info_get_name(pinfo),
1125 type);
1126
1127 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1128 return;
1129 if (!type)
1130 return;
1131
1132 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1133
1134 /* Manufac ID. We do not have access to this with soundcard.h
1135 * Does not seem to be a problem, because in mmsystem.h only
1136 * Microsoft's ID is listed.
1137 */
1138 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1139 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1140 /* Product Version. We simply say "1" */
1141 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1142 /* The following are mandatory for MOD_MIDIPORT */
1143 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1144 MidiOutDev[MODM_NumDevs].caps.wVoices = 0;
1145 MidiOutDev[MODM_NumDevs].caps.wNotes = 0;
1146 MidiOutDev[MODM_NumDevs].caps.dwSupport = 0;
1147
1148 /* Try to use both client and port names, if this is too long take the port name only.
1149 In the second case the port name should be explicit enough due to its big size.
1150 */
1151 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1152 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1153 } else {
1154 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1155 }
1156 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1157 MidiOutDev[MODM_NumDevs].caps.szPname,
1158 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1159
1160 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1161
1162 if (MOD_MIDIPORT != MidiOutDev[MODM_NumDevs].caps.wTechnology) {
1163 /* FIXME Do we have this information?
1164 * Assuming the soundcards can handle
1165 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1166 * not MIDICAPS_CACHE.
1167 */
1168 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1169 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1170
1171 /* FIXME Is it possible to know the maximum
1172 * number of simultaneous notes of a soundcard ?
1173 * I believe we don't have this information, but
1174 * it's probably equal or more than wVoices
1175 */
1176 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1177 }
1178 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1179
1180 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1181 "\tALSA info: midi dev-type=%x, capa=0\n",
1182 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1183 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1184 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1185 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1186 type);
1187
1188 MODM_NumDevs++;
1189 }
1190 if (cap & SND_SEQ_PORT_CAP_READ) {
1191 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1192 snd_seq_client_info_get_name(cinfo),
1193 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1194 snd_seq_port_info_get_port(pinfo),
1195 snd_seq_port_info_get_name(pinfo),
1196 type);
1197
1198 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1199 return;
1200 if (!type)
1201 return;
1202
1203 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1204
1205 /* Manufac ID. We do not have access to this with soundcard.h
1206 * Does not seem to be a problem, because in mmsystem.h only
1207 * Microsoft's ID is listed.
1208 */
1209 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1210 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1211 /* Product Version. We simply say "1" */
1212 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1213 MidiInDev[MIDM_NumDevs].caps.dwSupport = 0; /* mandatory with MIDIINCAPS */
1214
1215 /* Try to use both client and port names, if this is too long take the port name only.
1216 In the second case the port name should be explicit enough due to its big size.
1217 */
1218 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1219 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1220 } else {
1221 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1222 }
1223 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1224 MidiInDev[MIDM_NumDevs].caps.szPname,
1225 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1226 MidiInDev[MIDM_NumDevs].state = 0;
1227
1228 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1229 "\tALSA info: midi dev-type=%x, capa=0\n",
1230 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1231 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1232 type);
1233
1234 MIDM_NumDevs++;
1235 }
1236 }
1237
1238
1239 /*======================================================================*
1240 * MIDI entry points *
1241 *======================================================================*/
1242
1243 /**************************************************************************
1244 * ALSA_MidiInit [internal]
1245 *
1246 * Initializes the MIDI devices information variables
1247 */
1248 static LONG ALSA_MidiInit(void)
1249 {
1250 static BOOL bInitDone = FALSE;
1251 snd_seq_client_info_t *cinfo;
1252 snd_seq_port_info_t *pinfo;
1253
1254 if (bInitDone)
1255 return TRUE;
1256
1257 TRACE("Initializing the MIDI variables.\n");
1258 bInitDone = TRUE;
1259
1260 /* try to open device */
1261 if (midiOpenSeq(0) == -1) {
1262 return TRUE;
1263 }
1264
1265 #if 0 /* Debug purpose */
1266 snd_lib_error_set_handler(error_handler);
1267 #endif
1268 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1269 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1270
1271 /* First, search for all internal midi devices */
1272 snd_seq_client_info_set_client(cinfo, -1);
1273 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1274 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1275 snd_seq_port_info_set_port(pinfo, -1);
1276 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1277 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1278 unsigned int type = snd_seq_port_info_get_type(pinfo);
1279 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1280 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1281 }
1282 }
1283
1284 /* Second, search for all external ports */
1285 snd_seq_client_info_set_client(cinfo, -1);
1286 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1287 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1288 snd_seq_port_info_set_port(pinfo, -1);
1289 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1290 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1291 unsigned int type = snd_seq_port_info_get_type(pinfo);
1292 if (type & SND_SEQ_PORT_TYPE_PORT)
1293 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1294 }
1295 }
1296
1297 /* close file and exit */
1298 midiCloseSeq();
1299 HeapFree( GetProcessHeap(), 0, cinfo );
1300 HeapFree( GetProcessHeap(), 0, pinfo );
1301
1302 TRACE("End\n");
1303 return TRUE;
1304 }
1305
1306 /**************************************************************************
1307 * midMessage (WINEALSA.@)
1308 */
1309 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1310 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1311 {
1312 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1313 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1314 switch (wMsg) {
1315 case DRVM_INIT:
1316 ALSA_MidiInit();
1317 return 0;
1318 case DRVM_EXIT:
1319 case DRVM_ENABLE:
1320 case DRVM_DISABLE:
1321 /* FIXME: Pretend this is supported */
1322 return 0;
1323 case MIDM_OPEN:
1324 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1325 case MIDM_CLOSE:
1326 return midClose(wDevID);
1327 case MIDM_ADDBUFFER:
1328 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1329 case MIDM_PREPARE:
1330 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1331 case MIDM_UNPREPARE:
1332 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1333 case MIDM_GETDEVCAPS:
1334 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1335 case MIDM_GETNUMDEVS:
1336 return MIDM_NumDevs;
1337 case MIDM_RESET:
1338 return midReset(wDevID);
1339 case MIDM_START:
1340 return midStart(wDevID);
1341 case MIDM_STOP:
1342 return midStop(wDevID);
1343 default:
1344 TRACE("Unsupported message\n");
1345 }
1346 return MMSYSERR_NOTSUPPORTED;
1347 }
1348
1349 /**************************************************************************
1350 * modMessage (WINEALSA.@)
1351 */
1352 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1353 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1354 {
1355 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1356 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1357
1358 switch (wMsg) {
1359 case DRVM_INIT:
1360 ALSA_MidiInit();
1361 return 0;
1362 case DRVM_EXIT:
1363 case DRVM_ENABLE:
1364 case DRVM_DISABLE:
1365 /* FIXME: Pretend this is supported */
1366 return 0;
1367 case MODM_OPEN:
1368 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1369 case MODM_CLOSE:
1370 return modClose(wDevID);
1371 case MODM_DATA:
1372 return modData(wDevID, dwParam1);
1373 case MODM_LONGDATA:
1374 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1375 case MODM_PREPARE:
1376 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1377 case MODM_UNPREPARE:
1378 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1379 case MODM_GETDEVCAPS:
1380 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1381 case MODM_GETNUMDEVS:
1382 return MODM_NumDevs;
1383 case MODM_GETVOLUME:
1384 return modGetVolume(wDevID, (DWORD*)dwParam1);
1385 case MODM_SETVOLUME:
1386 return 0;
1387 case MODM_RESET:
1388 return modReset(wDevID);
1389 default:
1390 TRACE("Unsupported message\n");
1391 }
1392 return MMSYSERR_NOTSUPPORTED;
1393 }
1394
1395 /**************************************************************************
1396 * DriverProc (WINEALSA.@)
1397 */
1398 LRESULT CALLBACK ALSA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1399 LPARAM dwParam1, LPARAM dwParam2)
1400 {
1401 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
1402 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
1403
1404 switch(wMsg) {
1405 case DRV_LOAD:
1406 case DRV_FREE:
1407 case DRV_OPEN:
1408 case DRV_CLOSE:
1409 case DRV_ENABLE:
1410 case DRV_DISABLE:
1411 case DRV_QUERYCONFIGURE:
1412 case DRV_CONFIGURE:
1413 return 1;
1414 case DRV_INSTALL:
1415 case DRV_REMOVE:
1416 return DRV_SUCCESS;
1417 default:
1418 return 0;
1419 }
1420 }
1421
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.