winebus.sys: Implement IRP_MN_QUERY_ID.
[wine.git] / dlls / winealsa.drv / midi.c
blob6fc75eeaab063c78bec58a3186fbfbda24549912
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
4 * Sample MIDI Wine Driver for ALSA (basically Linux)
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
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.
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.
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
30 * TODO: Finish midi record
34 #include "config.h"
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>
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"
55 #include <alsa/asoundlib.h>
57 WINE_DEFAULT_DEBUG_CHANNEL(midi);
59 #ifndef SND_SEQ_PORT_TYPE_PORT
60 #define SND_SEQ_PORT_TYPE_PORT (1<<19) /* Appears in version 1.0.12rc1 */
61 #endif
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;
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;
90 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
91 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
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;
98 static CRITICAL_SECTION midiSeqLock;
99 static CRITICAL_SECTION_DEBUG midiSeqLockDebug =
101 0, 0, &midiSeqLock,
102 { &midiSeqLockDebug.ProcessLocksList, &midiSeqLockDebug.ProcessLocksList },
103 0, 0, { (DWORD_PTR)(__FILE__ ": midiSeqLock") }
105 static CRITICAL_SECTION midiSeqLock = { &midiSeqLockDebug, -1, 0, 0, 0, 0 };
106 static snd_seq_t* midiSeq = NULL;
107 static int numOpenMidiSeq = 0;
108 static int numStartedMidiIn = 0;
110 static int port_in;
111 static int port_out;
113 static CRITICAL_SECTION crit_sect; /* protects all MidiIn buffer queues */
114 static CRITICAL_SECTION_DEBUG critsect_debug =
116 0, 0, &crit_sect,
117 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
118 0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
120 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
122 static int end_thread;
123 static HANDLE hThread;
125 /*======================================================================*
126 * Low level MIDI implementation *
127 *======================================================================*/
129 #if 0 /* Debug Purpose */
130 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
132 va_list arg;
133 if (err == ENOENT)
134 return;
135 va_start(arg, fmt);
136 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
137 vfprintf(stderr, fmt, arg);
138 if (err)
139 fprintf(stderr, ": %s", snd_strerror(err));
140 putc('\n', stderr);
141 va_end(arg);
143 #endif
145 /**************************************************************************
146 * MIDI_unixToWindowsDeviceType [internal]
148 * return the Windows equivalent to a Unix Device Type
151 static int MIDI_AlsaToWindowsDeviceType(unsigned int type)
153 /* MOD_MIDIPORT output port
154 * MOD_SYNTH generic internal synth
155 * MOD_SQSYNTH square wave internal synth
156 * MOD_FMSYNTH FM internal synth
157 * MOD_MAPPER MIDI mapper
158 * MOD_WAVETABLE hardware wavetable internal synth
159 * MOD_SWSYNTH software internal synth
162 /* FIXME Is this really the correct equivalence from ALSA to
163 Windows Sound type? */
165 if (type & SND_SEQ_PORT_TYPE_SYNTH)
166 return MOD_FMSYNTH;
168 if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
169 return MOD_SYNTH;
171 if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION))
172 return MOD_MIDIPORT;
174 ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type);
175 return MOD_FMSYNTH;
178 /**************************************************************************
179 * MIDI_NotifyClient [internal]
181 static void MIDI_NotifyClient(UINT wDevID, WORD wMsg,
182 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
184 DWORD_PTR dwCallBack;
185 UINT uFlags;
186 HANDLE hDev;
187 DWORD_PTR dwInstance;
189 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
190 wDevID, wMsg, dwParam1, dwParam2);
192 switch (wMsg) {
193 case MOM_OPEN:
194 case MOM_CLOSE:
195 case MOM_DONE:
196 case MOM_POSITIONCB:
197 if (wDevID > MODM_NumDevs) return;
199 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
200 uFlags = MidiOutDev[wDevID].wFlags;
201 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
202 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
203 break;
205 case MIM_OPEN:
206 case MIM_CLOSE:
207 case MIM_DATA:
208 case MIM_LONGDATA:
209 case MIM_ERROR:
210 case MIM_LONGERROR:
211 case MIM_MOREDATA:
212 if (wDevID > MIDM_NumDevs) return;
214 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
215 uFlags = MidiInDev[wDevID].wFlags;
216 hDev = MidiInDev[wDevID].midiDesc.hMidi;
217 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
218 break;
219 default:
220 ERR("Unsupported MSW-MIDI message %u\n", wMsg);
221 return;
224 DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
227 static BOOL midi_warn = TRUE;
228 /**************************************************************************
229 * midiOpenSeq [internal]
231 static int midiOpenSeq(BOOL create_client)
233 EnterCriticalSection(&midiSeqLock);
234 if (numOpenMidiSeq == 0) {
235 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
237 if (midi_warn)
239 WARN("Error opening ALSA sequencer.\n");
241 midi_warn = FALSE;
242 LeaveCriticalSection(&midiSeqLock);
243 return -1;
246 if (create_client) {
247 /* Setting the client name is the only init to do */
248 snd_seq_set_client_name(midiSeq, "WINE midi driver");
250 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
251 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
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 %d created successfully\n", port_out);
257 #else
258 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
259 SND_SEQ_PORT_TYPE_APPLICATION);
260 if (port_out < 0)
261 TRACE("Unable to create output port\n");
262 else
263 TRACE("Outport port %d created successfully\n", port_out);
265 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
266 SND_SEQ_PORT_TYPE_APPLICATION);
267 if (port_in < 0)
268 TRACE("Unable to create input port\n");
269 else
270 TRACE("Input port %d created successfully\n", port_in);
271 #endif
274 numOpenMidiSeq++;
275 LeaveCriticalSection(&midiSeqLock);
276 return 0;
279 /**************************************************************************
280 * midiCloseSeq [internal]
282 static int midiCloseSeq(void)
284 EnterCriticalSection(&midiSeqLock);
285 if (--numOpenMidiSeq == 0) {
286 snd_seq_delete_simple_port(midiSeq, port_out);
287 snd_seq_delete_simple_port(midiSeq, port_in);
288 snd_seq_close(midiSeq);
289 midiSeq = NULL;
291 LeaveCriticalSection(&midiSeqLock);
292 return 0;
295 static DWORD WINAPI midRecThread(LPVOID arg)
297 int npfd;
298 struct pollfd *pfd;
299 int ret;
301 TRACE("Thread startup\n");
303 while(!end_thread) {
304 TRACE("Thread loop\n");
305 EnterCriticalSection(&midiSeqLock);
306 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
307 pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
308 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
309 LeaveCriticalSection(&midiSeqLock);
311 /* Check if an event is present */
312 if (poll(pfd, npfd, 250) <= 0) {
313 HeapFree(GetProcessHeap(), 0, pfd);
314 continue;
317 /* Note: This definitely does not work.
318 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
319 snd_seq_event_t* ev;
320 snd_seq_event_input(midiSeq, &ev);
321 ....................
322 snd_seq_free_event(ev);
325 do {
326 WORD wDevID;
327 snd_seq_event_t* ev;
328 EnterCriticalSection(&midiSeqLock);
329 snd_seq_event_input(midiSeq, &ev);
330 LeaveCriticalSection(&midiSeqLock);
331 /* Find the target device */
332 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
333 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
334 break;
335 if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
336 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
337 else {
338 DWORD dwTime, toSend = 0;
339 int value = 0;
340 /* FIXME: Should use ev->time instead for better accuracy */
341 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
342 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
343 switch(ev->type)
345 case SND_SEQ_EVENT_NOTEOFF:
346 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
347 break;
348 case SND_SEQ_EVENT_NOTEON:
349 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
350 break;
351 case SND_SEQ_EVENT_KEYPRESS:
352 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
353 break;
354 case SND_SEQ_EVENT_CONTROLLER:
355 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
356 break;
357 case SND_SEQ_EVENT_PITCHBEND:
358 value = ev->data.control.value + 0x2000;
359 toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
360 break;
361 case SND_SEQ_EVENT_PGMCHANGE:
362 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
363 break;
364 case SND_SEQ_EVENT_CHANPRESS:
365 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
366 break;
367 case SND_SEQ_EVENT_CLOCK:
368 toSend = 0xF8;
369 break;
370 case SND_SEQ_EVENT_START:
371 toSend = 0xFA;
372 break;
373 case SND_SEQ_EVENT_CONTINUE:
374 toSend = 0xFB;
375 break;
376 case SND_SEQ_EVENT_STOP:
377 toSend = 0xFC;
378 break;
379 case SND_SEQ_EVENT_SONGPOS:
380 toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_POS;
381 break;
382 case SND_SEQ_EVENT_SONGSEL:
383 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_SELECT;
384 break;
385 case SND_SEQ_EVENT_RESET:
386 toSend = 0xFF;
387 break;
388 case SND_SEQ_EVENT_QFRAME:
389 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_MTC_QUARTER;
390 break;
391 case SND_SEQ_EVENT_SYSEX:
393 int pos = 0;
394 int len = ev->data.ext.len;
395 LPBYTE ptr = ev->data.ext.ptr;
396 LPMIDIHDR lpMidiHdr;
398 EnterCriticalSection(&crit_sect);
399 while (len) {
400 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
401 int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
402 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
403 lpMidiHdr->dwBytesRecorded += copylen;
404 len -= copylen;
405 pos += copylen;
406 /* We check if we reach the end of buffer or the end of sysex before notifying
407 * to handle the case where ALSA split the sysex into several events */
408 if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
409 (*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
410 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
411 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
412 lpMidiHdr->dwFlags |= MHDR_DONE;
413 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
415 } else {
416 FIXME("Sysex data received but no buffer to store it!\n");
417 break;
420 LeaveCriticalSection(&crit_sect);
422 break;
423 case SND_SEQ_EVENT_SENSING:
424 /* Noting to do */
425 break;
426 default:
427 FIXME("Unhandled event received, type = %x\n", ev->type);
428 break;
430 if (toSend != 0) {
431 TRACE("Received event %08x from %d:%d\n", toSend, ev->source.client, ev->source.port);
432 MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
435 snd_seq_free_event(ev);
437 EnterCriticalSection(&midiSeqLock);
438 ret = snd_seq_event_input_pending(midiSeq, 0);
439 LeaveCriticalSection(&midiSeqLock);
440 } while(ret > 0);
442 HeapFree(GetProcessHeap(), 0, pfd);
444 return 0;
447 /**************************************************************************
448 * midGetDevCaps [internal]
450 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
452 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
454 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
455 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
457 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
459 return MMSYSERR_NOERROR;
463 /**************************************************************************
464 * midOpen [internal]
466 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
468 int ret = 0;
470 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
472 if (lpDesc == NULL) {
473 WARN("Invalid Parameter !\n");
474 return MMSYSERR_INVALPARAM;
477 /* FIXME :
478 * how to check that content of lpDesc is correct ?
480 if (wDevID >= MIDM_NumDevs) {
481 WARN("wDevID too large (%u) !\n", wDevID);
482 return MMSYSERR_BADDEVICEID;
484 if (MidiInDev[wDevID].state == -1) {
485 WARN("device disabled\n");
486 return MIDIERR_NODEVICE;
488 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
489 WARN("device already open !\n");
490 return MMSYSERR_ALLOCATED;
492 if ((dwFlags & MIDI_IO_STATUS) != 0) {
493 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
494 dwFlags &= ~MIDI_IO_STATUS;
496 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
497 FIXME("Bad dwFlags\n");
498 return MMSYSERR_INVALFLAG;
501 if (midiOpenSeq(TRUE) < 0) {
502 return MMSYSERR_ERROR;
505 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
507 MidiInDev[wDevID].lpQueueHdr = NULL;
508 MidiInDev[wDevID].dwTotalPlayed = 0;
509 MidiInDev[wDevID].bufsize = 0x3FFF;
510 MidiInDev[wDevID].midiDesc = *lpDesc;
511 MidiInDev[wDevID].state = 0;
512 MidiInDev[wDevID].incLen = 0;
513 MidiInDev[wDevID].startTime = 0;
515 /* Connect our app port to the device port */
516 EnterCriticalSection(&midiSeqLock);
517 ret = snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client,
518 MidiInDev[wDevID].addr.port);
519 LeaveCriticalSection(&midiSeqLock);
520 if (ret < 0)
521 return MMSYSERR_NOTENABLED;
523 TRACE("Input port :%d connected %d:%d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
525 if (numStartedMidiIn++ == 0) {
526 end_thread = 0;
527 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
528 if (!hThread) {
529 numStartedMidiIn = 0;
530 WARN("Couldn't create thread for midi-in\n");
531 midiCloseSeq();
532 return MMSYSERR_ERROR;
534 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
535 TRACE("Created thread for midi-in\n");
538 MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
539 return MMSYSERR_NOERROR;
542 /**************************************************************************
543 * midClose [internal]
545 static DWORD midClose(WORD wDevID)
547 int ret = MMSYSERR_NOERROR;
549 TRACE("(%04X);\n", wDevID);
551 if (wDevID >= MIDM_NumDevs) {
552 WARN("wDevID too big (%u) !\n", wDevID);
553 return MMSYSERR_BADDEVICEID;
555 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
556 WARN("device not opened !\n");
557 return MMSYSERR_ERROR;
559 if (MidiInDev[wDevID].lpQueueHdr != 0) {
560 return MIDIERR_STILLPLAYING;
563 if (midiSeq == NULL) {
564 WARN("ooops !\n");
565 return MMSYSERR_ERROR;
567 if (--numStartedMidiIn == 0) {
568 TRACE("Stopping thread for midi-in\n");
569 end_thread = 1;
570 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
571 WARN("Thread end not signaled, force termination\n");
572 TerminateThread(hThread, 0);
574 TRACE("Stopped thread for midi-in\n");
577 EnterCriticalSection(&midiSeqLock);
578 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
579 LeaveCriticalSection(&midiSeqLock);
580 midiCloseSeq();
582 MidiInDev[wDevID].bufsize = 0;
583 MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
584 MidiInDev[wDevID].midiDesc.hMidi = 0;
586 return ret;
590 /**************************************************************************
591 * midAddBuffer [internal]
593 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
595 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
597 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
598 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
600 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
601 if (dwSize < offsetof(MIDIHDR,dwOffset)) return MMSYSERR_INVALPARAM;
602 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
603 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
604 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
606 EnterCriticalSection(&crit_sect);
607 lpMidiHdr->dwFlags &= ~WHDR_DONE;
608 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
609 lpMidiHdr->dwBytesRecorded = 0;
610 lpMidiHdr->lpNext = 0;
611 if (MidiInDev[wDevID].lpQueueHdr == 0) {
612 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
613 } else {
614 LPMIDIHDR ptr;
616 for (ptr = MidiInDev[wDevID].lpQueueHdr; ptr->lpNext != 0;
617 ptr = ptr->lpNext);
618 ptr->lpNext = lpMidiHdr;
620 LeaveCriticalSection(&crit_sect);
622 return MMSYSERR_NOERROR;
625 /**************************************************************************
626 * midPrepare [internal]
628 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
630 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
632 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
633 return MMSYSERR_INVALPARAM;
634 if (lpMidiHdr->dwFlags & MHDR_PREPARED)
635 return MMSYSERR_NOERROR;
637 lpMidiHdr->lpNext = 0;
638 lpMidiHdr->dwFlags |= MHDR_PREPARED;
639 lpMidiHdr->dwFlags &= ~(MHDR_DONE|MHDR_INQUEUE); /* flags cleared since w2k */
641 return MMSYSERR_NOERROR;
644 /**************************************************************************
645 * midUnprepare [internal]
647 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
649 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
651 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
652 return MMSYSERR_INVALPARAM;
653 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
654 return MMSYSERR_NOERROR;
655 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
656 return MIDIERR_STILLPLAYING;
658 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
660 return MMSYSERR_NOERROR;
663 /**************************************************************************
664 * midReset [internal]
666 static DWORD midReset(WORD wDevID)
668 DWORD dwTime = GetTickCount();
670 TRACE("(%04X);\n", wDevID);
672 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
673 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
675 EnterCriticalSection(&crit_sect);
676 while (MidiInDev[wDevID].lpQueueHdr) {
677 LPMIDIHDR lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
678 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
679 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
680 lpMidiHdr->dwFlags |= MHDR_DONE;
681 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
683 LeaveCriticalSection(&crit_sect);
685 return MMSYSERR_NOERROR;
688 /**************************************************************************
689 * midStart [internal]
691 static DWORD midStart(WORD wDevID)
693 TRACE("(%04X);\n", wDevID);
695 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
696 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
698 MidiInDev[wDevID].state = 1;
699 MidiInDev[wDevID].startTime = GetTickCount();
700 return MMSYSERR_NOERROR;
703 /**************************************************************************
704 * midStop [internal]
706 static DWORD midStop(WORD wDevID)
708 TRACE("(%04X);\n", wDevID);
710 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
711 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
713 MidiInDev[wDevID].state = 0;
714 return MMSYSERR_NOERROR;
717 /**************************************************************************
718 * modGetDevCaps [internal]
720 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
722 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
724 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
725 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
727 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
729 return MMSYSERR_NOERROR;
732 /**************************************************************************
733 * modOpen [internal]
735 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
737 int ret;
739 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
740 if (lpDesc == NULL) {
741 WARN("Invalid Parameter !\n");
742 return MMSYSERR_INVALPARAM;
744 if (wDevID >= MODM_NumDevs) {
745 TRACE("MAX_MIDIOUTDRV reached !\n");
746 return MMSYSERR_BADDEVICEID;
748 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
749 WARN("device already open !\n");
750 return MMSYSERR_ALLOCATED;
752 if (!MidiOutDev[wDevID].bEnabled) {
753 WARN("device disabled !\n");
754 return MIDIERR_NODEVICE;
756 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
757 WARN("bad dwFlags\n");
758 return MMSYSERR_INVALFLAG;
761 MidiOutDev[wDevID].lpExtra = 0;
763 switch (MidiOutDev[wDevID].caps.wTechnology) {
764 case MOD_FMSYNTH:
765 case MOD_MIDIPORT:
766 case MOD_SYNTH:
767 if (midiOpenSeq(TRUE) < 0) {
768 return MMSYSERR_ALLOCATED;
770 break;
771 default:
772 WARN("Technology not supported (yet) %d !\n",
773 MidiOutDev[wDevID].caps.wTechnology);
774 return MMSYSERR_NOTENABLED;
777 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
779 MidiOutDev[wDevID].lpQueueHdr = NULL;
780 MidiOutDev[wDevID].dwTotalPlayed = 0;
781 MidiOutDev[wDevID].bufsize = 0x3FFF;
782 MidiOutDev[wDevID].midiDesc = *lpDesc;
784 /* Connect our app port to the device port */
785 EnterCriticalSection(&midiSeqLock);
786 ret = snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client,
787 MidiOutDev[wDevID].addr.port);
788 LeaveCriticalSection(&midiSeqLock);
789 if (ret < 0)
790 return MMSYSERR_NOTENABLED;
792 TRACE("Output port :%d connected %d:%d\n",port_out,MidiOutDev[wDevID].addr.client,MidiOutDev[wDevID].addr.port);
794 MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
795 return MMSYSERR_NOERROR;
799 /**************************************************************************
800 * modClose [internal]
802 static DWORD modClose(WORD wDevID)
804 int ret = MMSYSERR_NOERROR;
806 TRACE("(%04X);\n", wDevID);
808 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
809 WARN("device not opened !\n");
810 return MMSYSERR_ERROR;
812 /* FIXME: should test that no pending buffer is still in the queue for
813 * playing */
815 if (midiSeq == NULL) {
816 WARN("can't close !\n");
817 return MMSYSERR_ERROR;
820 switch (MidiOutDev[wDevID].caps.wTechnology) {
821 case MOD_FMSYNTH:
822 case MOD_MIDIPORT:
823 case MOD_SYNTH:
824 EnterCriticalSection(&midiSeqLock);
825 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
826 LeaveCriticalSection(&midiSeqLock);
827 midiCloseSeq();
828 break;
829 default:
830 WARN("Technology not supported (yet) %d !\n",
831 MidiOutDev[wDevID].caps.wTechnology);
832 return MMSYSERR_NOTENABLED;
835 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
836 MidiOutDev[wDevID].lpExtra = 0;
838 MidiOutDev[wDevID].bufsize = 0;
839 MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
840 MidiOutDev[wDevID].midiDesc.hMidi = 0;
841 return ret;
844 /**************************************************************************
845 * modData [internal]
847 static DWORD modData(WORD wDevID, DWORD dwParam)
849 BYTE evt = LOBYTE(LOWORD(dwParam));
850 BYTE d1 = HIBYTE(LOWORD(dwParam));
851 BYTE d2 = LOBYTE(HIWORD(dwParam));
853 TRACE("(%04X, %08X);\n", wDevID, dwParam);
855 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
856 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
858 if (midiSeq == NULL) {
859 WARN("can't play !\n");
860 return MIDIERR_NODEVICE;
862 switch (MidiOutDev[wDevID].caps.wTechnology) {
863 case MOD_SYNTH:
864 case MOD_MIDIPORT:
866 int handled = 1; /* Assume event is handled */
867 snd_seq_event_t event;
868 snd_seq_ev_clear(&event);
869 snd_seq_ev_set_direct(&event);
870 snd_seq_ev_set_source(&event, port_out);
871 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
873 switch (evt & 0xF0) {
874 case MIDI_CMD_NOTE_OFF:
875 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
876 break;
877 case MIDI_CMD_NOTE_ON:
878 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
879 break;
880 case MIDI_CMD_NOTE_PRESSURE:
881 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
882 break;
883 case MIDI_CMD_CONTROL:
884 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
885 break;
886 case MIDI_CMD_BENDER:
887 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
888 break;
889 case MIDI_CMD_PGM_CHANGE:
890 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
891 break;
892 case MIDI_CMD_CHANNEL_PRESSURE:
893 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
894 break;
895 case MIDI_CMD_COMMON_SYSEX:
896 switch (evt & 0x0F) {
897 case 0x00: /* System Exclusive, don't do it on modData,
898 * should require modLongData*/
899 case 0x04: /* Undefined. */
900 case 0x05: /* Undefined. */
901 case 0x07: /* End of Exclusive. */
902 case 0x09: /* Undefined. */
903 case 0x0D: /* Undefined. */
904 handled = 0;
905 break;
906 case 0x06: /* Tune Request */
907 case 0x08: /* Timing Clock. */
908 case 0x0A: /* Start. */
909 case 0x0B: /* Continue */
910 case 0x0C: /* Stop */
911 case 0x0E: /* Active Sensing. */
912 /* FIXME: Is this function suitable for these purposes
913 (and also Song Select and Song Position Pointer) */
914 snd_seq_ev_set_sysex(&event, 1, &evt);
915 break;
916 case 0x0F: /* Reset */
917 /* snd_seq_ev_set_sysex(&event, 1, &evt);
918 this other way may be better */
920 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
921 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
923 break;
924 case 0x01: /* MTC Quarter frame */
925 case 0x03: /* Song Select. */
927 BYTE buf[2];
928 buf[0] = evt;
929 buf[1] = d1;
930 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
932 break;
933 case 0x02: /* Song Position Pointer. */
935 BYTE buf[3];
936 buf[0] = evt;
937 buf[1] = d1;
938 buf[2] = d2;
939 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
941 break;
943 break;
945 if (handled) {
946 EnterCriticalSection(&midiSeqLock);
947 snd_seq_event_output_direct(midiSeq, &event);
948 LeaveCriticalSection(&midiSeqLock);
951 break;
952 default:
953 WARN("Technology not supported (yet) %d !\n",
954 MidiOutDev[wDevID].caps.wTechnology);
955 return MMSYSERR_NOTENABLED;
958 return MMSYSERR_NOERROR;
961 /**************************************************************************
962 * modLongData [internal]
964 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
966 int len_add = 0;
967 BYTE *lpData, *lpNewData = NULL;
968 snd_seq_event_t event;
970 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
972 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
973 * but it seems to be used only for midi input.
974 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
977 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
978 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
980 if (midiSeq == NULL) {
981 WARN("can't play !\n");
982 return MIDIERR_NODEVICE;
985 lpData = (BYTE*)lpMidiHdr->lpData;
987 if (lpData == NULL)
988 return MIDIERR_UNPREPARED;
989 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
990 return MIDIERR_UNPREPARED;
991 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
992 return MIDIERR_STILLPLAYING;
993 lpMidiHdr->dwFlags &= ~MHDR_DONE;
994 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
996 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
997 * data, or can it also contain raw MIDI data, to be split up and sent to
998 * modShortData() ?
999 * If the latest is true, then the following WARNing will fire up
1001 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
1002 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
1003 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
1006 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
1007 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
1008 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
1009 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
1011 switch (MidiOutDev[wDevID].caps.wTechnology) {
1012 case MOD_FMSYNTH:
1013 /* FIXME: I don't think there is much to do here */
1014 HeapFree(GetProcessHeap(), 0, lpNewData);
1015 break;
1016 case MOD_MIDIPORT:
1017 if (lpData[0] != 0xF0) {
1018 /* Send start of System Exclusive */
1019 len_add = 1;
1020 lpNewData[0] = 0xF0;
1021 memcpy(lpNewData + 1, lpData, lpMidiHdr->dwBufferLength);
1022 WARN("Adding missing 0xF0 marker at the beginning of system exclusive byte stream\n");
1024 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
1025 /* Send end of System Exclusive */
1026 if (!len_add)
1027 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
1028 lpNewData[lpMidiHdr->dwBufferLength + len_add] = 0xF7;
1029 len_add++;
1030 WARN("Adding missing 0xF7 marker at the end of system exclusive byte stream\n");
1032 snd_seq_ev_clear(&event);
1033 snd_seq_ev_set_direct(&event);
1034 snd_seq_ev_set_source(&event, port_out);
1035 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1036 TRACE("destination %d:%d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1037 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1038 EnterCriticalSection(&midiSeqLock);
1039 snd_seq_event_output_direct(midiSeq, &event);
1040 LeaveCriticalSection(&midiSeqLock);
1041 HeapFree(GetProcessHeap(), 0, lpNewData);
1042 break;
1043 default:
1044 WARN("Technology not supported (yet) %d !\n",
1045 MidiOutDev[wDevID].caps.wTechnology);
1046 HeapFree(GetProcessHeap(), 0, lpNewData);
1047 return MMSYSERR_NOTENABLED;
1050 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1051 lpMidiHdr->dwFlags |= MHDR_DONE;
1052 MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
1053 return MMSYSERR_NOERROR;
1056 /**************************************************************************
1057 * modPrepare [internal]
1059 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1061 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
1063 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
1064 return MMSYSERR_INVALPARAM;
1065 if (lpMidiHdr->dwFlags & MHDR_PREPARED)
1066 return MMSYSERR_NOERROR;
1068 lpMidiHdr->lpNext = 0;
1069 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1070 lpMidiHdr->dwFlags &= ~(MHDR_DONE|MHDR_INQUEUE); /* flags cleared since w2k */
1071 return MMSYSERR_NOERROR;
1074 /**************************************************************************
1075 * modUnprepare [internal]
1077 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1079 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
1081 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
1082 return MMSYSERR_INVALPARAM;
1083 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1084 return MMSYSERR_NOERROR;
1085 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1086 return MIDIERR_STILLPLAYING;
1087 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1088 return MMSYSERR_NOERROR;
1091 /**************************************************************************
1092 * modGetVolume [internal]
1094 static DWORD modGetVolume(WORD wDevID, DWORD* lpdwVolume)
1096 if (!lpdwVolume) return MMSYSERR_INVALPARAM;
1097 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1098 *lpdwVolume = 0xFFFFFFFF;
1099 return (MidiOutDev[wDevID].caps.dwSupport & MIDICAPS_VOLUME) ? 0 : MMSYSERR_NOTSUPPORTED;
1102 /**************************************************************************
1103 * modReset [internal]
1105 static DWORD modReset(WORD wDevID)
1107 unsigned chn;
1109 TRACE("(%04X);\n", wDevID);
1111 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1112 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1114 /* stop all notes */
1115 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1116 * it's channel dependent...
1118 for (chn = 0; chn < 16; chn++) {
1119 /* turn off every note */
1120 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1121 /* remove sustain on all channels */
1122 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1124 /* FIXME: the LongData buffers must also be returned to the app */
1125 return MMSYSERR_NOERROR;
1129 /**************************************************************************
1130 * ALSA_AddMidiPort [internal]
1132 * Helper for ALSA_MidiInit
1134 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1136 char midiPortName[MAXPNAMELEN];
1138 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1139 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1140 snd_seq_client_info_get_name(cinfo),
1141 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1142 snd_seq_port_info_get_port(pinfo),
1143 snd_seq_port_info_get_name(pinfo),
1144 type);
1146 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1147 return;
1148 if (!type)
1149 return;
1151 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1153 /* Manufac ID. We do not have access to this with soundcard.h
1154 * Does not seem to be a problem, because in mmsystem.h only
1155 * Microsoft's ID is listed.
1157 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1158 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1159 /* Product Version. We simply say "1" */
1160 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1161 /* The following are mandatory for MOD_MIDIPORT */
1162 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1163 MidiOutDev[MODM_NumDevs].caps.wVoices = 0;
1164 MidiOutDev[MODM_NumDevs].caps.wNotes = 0;
1165 MidiOutDev[MODM_NumDevs].caps.dwSupport = 0;
1167 /* Try to use both client and port names, if this is too long take the port name only.
1168 In the second case the port name should be explicit enough due to its big size.
1170 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1171 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1172 } else {
1173 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1175 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1176 MidiOutDev[MODM_NumDevs].caps.szPname,
1177 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1179 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1181 if (MOD_MIDIPORT != MidiOutDev[MODM_NumDevs].caps.wTechnology) {
1182 /* FIXME Do we have this information?
1183 * Assuming the soundcards can handle
1184 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1185 * not MIDICAPS_CACHE.
1187 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1188 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1190 /* FIXME Is it possible to know the maximum
1191 * number of simultaneous notes of a soundcard ?
1192 * I believe we don't have this information, but
1193 * it's probably equal or more than wVoices
1195 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1197 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1199 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1200 "\tALSA info: midi dev-type=%x, capa=0\n",
1201 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1202 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1203 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1204 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1205 type);
1207 MODM_NumDevs++;
1209 if (cap & SND_SEQ_PORT_CAP_READ) {
1210 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1211 snd_seq_client_info_get_name(cinfo),
1212 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1213 snd_seq_port_info_get_port(pinfo),
1214 snd_seq_port_info_get_name(pinfo),
1215 type);
1217 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1218 return;
1219 if (!type)
1220 return;
1222 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1224 /* Manufac ID. We do not have access to this with soundcard.h
1225 * Does not seem to be a problem, because in mmsystem.h only
1226 * Microsoft's ID is listed.
1228 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1229 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1230 /* Product Version. We simply say "1" */
1231 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1232 MidiInDev[MIDM_NumDevs].caps.dwSupport = 0; /* mandatory with MIDIINCAPS */
1234 /* Try to use both client and port names, if this is too long take the port name only.
1235 In the second case the port name should be explicit enough due to its big size.
1237 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1238 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1239 } else {
1240 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1242 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1243 MidiInDev[MIDM_NumDevs].caps.szPname,
1244 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1245 MidiInDev[MIDM_NumDevs].state = 0;
1247 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1248 "\tALSA info: midi dev-type=%x, capa=0\n",
1249 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1250 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1251 type);
1253 MIDM_NumDevs++;
1258 /*======================================================================*
1259 * MIDI entry points *
1260 *======================================================================*/
1262 /**************************************************************************
1263 * ALSA_MidiInit [internal]
1265 * Initializes the MIDI devices information variables
1267 static BOOL ALSA_MidiInit(void)
1269 static BOOL bInitDone = FALSE;
1270 snd_seq_client_info_t *cinfo;
1271 snd_seq_port_info_t *pinfo;
1273 if (bInitDone)
1274 return TRUE;
1276 TRACE("Initializing the MIDI variables.\n");
1277 bInitDone = TRUE;
1279 /* try to open device */
1280 if (midiOpenSeq(FALSE) == -1) {
1281 return TRUE;
1284 #if 0 /* Debug purpose */
1285 snd_lib_error_set_handler(error_handler);
1286 #endif
1287 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1288 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1290 /* First, search for all internal midi devices */
1291 snd_seq_client_info_set_client(cinfo, -1);
1292 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1293 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1294 snd_seq_port_info_set_port(pinfo, -1);
1295 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1296 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1297 unsigned int type = snd_seq_port_info_get_type(pinfo);
1298 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1299 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1303 /* Second, search for all external ports */
1304 snd_seq_client_info_set_client(cinfo, -1);
1305 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1306 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1307 snd_seq_port_info_set_port(pinfo, -1);
1308 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1309 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1310 unsigned int type = snd_seq_port_info_get_type(pinfo);
1311 if (type & SND_SEQ_PORT_TYPE_PORT)
1312 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1316 /* close file and exit */
1317 midiCloseSeq();
1318 HeapFree( GetProcessHeap(), 0, cinfo );
1319 HeapFree( GetProcessHeap(), 0, pinfo );
1321 TRACE("End\n");
1322 return TRUE;
1325 /**************************************************************************
1326 * midMessage (WINEALSA.@)
1328 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1329 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1331 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1332 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1333 switch (wMsg) {
1334 case DRVM_INIT:
1335 ALSA_MidiInit();
1336 return 0;
1337 case DRVM_EXIT:
1338 case DRVM_ENABLE:
1339 case DRVM_DISABLE:
1340 /* FIXME: Pretend this is supported */
1341 return 0;
1342 case MIDM_OPEN:
1343 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1344 case MIDM_CLOSE:
1345 return midClose(wDevID);
1346 case MIDM_ADDBUFFER:
1347 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1348 case MIDM_PREPARE:
1349 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1350 case MIDM_UNPREPARE:
1351 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1352 case MIDM_GETDEVCAPS:
1353 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1354 case MIDM_GETNUMDEVS:
1355 return MIDM_NumDevs;
1356 case MIDM_RESET:
1357 return midReset(wDevID);
1358 case MIDM_START:
1359 return midStart(wDevID);
1360 case MIDM_STOP:
1361 return midStop(wDevID);
1362 default:
1363 TRACE("Unsupported message\n");
1365 return MMSYSERR_NOTSUPPORTED;
1368 /**************************************************************************
1369 * modMessage (WINEALSA.@)
1371 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1372 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1374 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1375 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1377 switch (wMsg) {
1378 case DRVM_INIT:
1379 ALSA_MidiInit();
1380 return 0;
1381 case DRVM_EXIT:
1382 case DRVM_ENABLE:
1383 case DRVM_DISABLE:
1384 /* FIXME: Pretend this is supported */
1385 return 0;
1386 case MODM_OPEN:
1387 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1388 case MODM_CLOSE:
1389 return modClose(wDevID);
1390 case MODM_DATA:
1391 return modData(wDevID, dwParam1);
1392 case MODM_LONGDATA:
1393 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1394 case MODM_PREPARE:
1395 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1396 case MODM_UNPREPARE:
1397 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1398 case MODM_GETDEVCAPS:
1399 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1400 case MODM_GETNUMDEVS:
1401 return MODM_NumDevs;
1402 case MODM_GETVOLUME:
1403 return modGetVolume(wDevID, (DWORD*)dwParam1);
1404 case MODM_SETVOLUME:
1405 return 0;
1406 case MODM_RESET:
1407 return modReset(wDevID);
1408 default:
1409 TRACE("Unsupported message\n");
1411 return MMSYSERR_NOTSUPPORTED;
1414 /**************************************************************************
1415 * DriverProc (WINEALSA.@)
1417 LRESULT CALLBACK ALSA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1418 LPARAM dwParam1, LPARAM dwParam2)
1420 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
1421 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
1423 switch(wMsg) {
1424 case DRV_LOAD:
1425 case DRV_FREE:
1426 case DRV_OPEN:
1427 case DRV_CLOSE:
1428 case DRV_ENABLE:
1429 case DRV_DISABLE:
1430 case DRV_QUERYCONFIGURE:
1431 case DRV_CONFIGURE:
1432 return 1;
1433 case DRV_INSTALL:
1434 case DRV_REMOVE:
1435 return DRV_SUCCESS;
1436 default:
1437 return 0;