push 337eb2e2d902d84a5d689451984c5832d7e04fc4
[wine/hacks.git] / dlls / winealsa.drv / midi.c
blob9c004ae8dc2631872992e7a7ed2f106b986de654
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 "alsa.h"
52 #include "wine/debug.h"
54 WINE_DEFAULT_DEBUG_CHANNEL(midi);
56 #ifdef HAVE_ALSA
58 #ifndef SND_SEQ_PORT_TYPE_PORT
59 #define SND_SEQ_PORT_TYPE_PORT (1<<19) /* Appears in version 1.0.12rc1 */
60 #endif
62 typedef struct {
63 int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
64 DWORD bufsize;
65 MIDIOPENDESC midiDesc;
66 WORD wFlags;
67 LPMIDIHDR lpQueueHdr;
68 DWORD dwTotalPlayed;
69 unsigned char incoming[3];
70 unsigned char incPrev;
71 char incLen;
72 DWORD startTime;
73 MIDIINCAPSW caps;
74 snd_seq_addr_t addr;
75 } WINE_MIDIIN;
77 typedef struct {
78 BOOL bEnabled;
79 DWORD bufsize;
80 MIDIOPENDESC midiDesc;
81 WORD wFlags;
82 LPMIDIHDR lpQueueHdr;
83 DWORD dwTotalPlayed;
84 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
85 MIDIOUTCAPSW caps;
86 snd_seq_addr_t addr;
87 } WINE_MIDIOUT;
89 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
90 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
92 /* this is the total number of MIDI out devices found (synth and port) */
93 static int MODM_NumDevs = 0;
94 /* this is the total number of MIDI out devices found */
95 static int MIDM_NumDevs = 0;
97 static snd_seq_t* midiSeq = NULL;
98 static int numOpenMidiSeq = 0;
99 static int numStartedMidiIn = 0;
101 static int port_in;
102 static int port_out;
104 static CRITICAL_SECTION crit_sect; /* protects all MidiIn buffer queues */
105 static CRITICAL_SECTION_DEBUG critsect_debug =
107 0, 0, &crit_sect,
108 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
109 0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
111 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
113 static int end_thread;
114 static HANDLE hThread;
116 /*======================================================================*
117 * Low level MIDI implementation *
118 *======================================================================*/
120 static int midiOpenSeq(int);
121 static int midiCloseSeq(void);
123 #if 0 /* Debug Purpose */
124 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
126 va_list arg;
127 if (err == ENOENT)
128 return;
129 va_start(arg, fmt);
130 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
131 vfprintf(stderr, fmt, arg);
132 if (err)
133 fprintf(stderr, ": %s", snd_strerror(err));
134 putc('\n', stderr);
135 va_end(arg);
137 #endif
139 /**************************************************************************
140 * MIDI_unixToWindowsDeviceType [internal]
142 * return the Windows equivalent to a Unix Device Type
145 static int MIDI_AlsaToWindowsDeviceType(int type)
147 /* MOD_MIDIPORT output port
148 * MOD_SYNTH generic internal synth
149 * MOD_SQSYNTH square wave internal synth
150 * MOD_FMSYNTH FM internal synth
151 * MOD_MAPPER MIDI mapper
152 * MOD_WAVETABLE hardware watetable internal synth
153 * MOD_SWSYNTH software internal synth
156 /* FIXME Is this really the correct equivalence from ALSA to
157 Windows Sound type */
159 if (type & SND_SEQ_PORT_TYPE_SYNTH)
160 return MOD_FMSYNTH;
162 if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
163 return MOD_SYNTH;
165 if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION))
166 return MOD_MIDIPORT;
168 ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type);
169 return MOD_FMSYNTH;
172 /**************************************************************************
173 * MIDI_NotifyClient [internal]
175 static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg,
176 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
178 DWORD dwCallBack;
179 UINT uFlags;
180 HANDLE hDev;
181 DWORD dwInstance;
183 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
184 wDevID, wMsg, dwParam1, dwParam2);
186 switch (wMsg) {
187 case MOM_OPEN:
188 case MOM_CLOSE:
189 case MOM_DONE:
190 case MOM_POSITIONCB:
191 if (wDevID > MODM_NumDevs)
192 return MMSYSERR_BADDEVICEID;
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;
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)
208 return MMSYSERR_BADDEVICEID;
210 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
211 uFlags = MidiInDev[wDevID].wFlags;
212 hDev = MidiInDev[wDevID].midiDesc.hMidi;
213 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
214 break;
215 default:
216 WARN("Unsupported MSW-MIDI message %u\n", wMsg);
217 return MMSYSERR_ERROR;
220 return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
221 0 : MMSYSERR_ERROR;
224 static int midi_warn = 1;
225 /**************************************************************************
226 * midiOpenSeq [internal]
228 static int midiOpenSeq(int create_client)
230 if (numOpenMidiSeq == 0) {
231 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
233 if (midi_warn)
235 WARN("Error opening ALSA sequencer.\n");
237 midi_warn = 0;
238 return -1;
241 if (create_client) {
242 /* Setting the client name is the only init to do */
243 snd_seq_set_client_name(midiSeq, "WINE midi driver");
245 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
246 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
247 SND_SEQ_PORT_TYPE_APPLICATION);
248 if (port_out < 0)
249 TRACE("Unable to create output port\n");
250 else
251 TRACE("Outport port created successfully (%d)\n", port_out);
252 #else
253 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
254 SND_SEQ_PORT_TYPE_APPLICATION);
255 if (port_out < 0)
256 TRACE("Unable to create output port\n");
257 else
258 TRACE("Outport port created successfully (%d)\n", port_out);
260 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
261 SND_SEQ_PORT_TYPE_APPLICATION);
262 if (port_in < 0)
263 TRACE("Unable to create input port\n");
264 else
265 TRACE("Input port created successfully (%d)\n", port_in);
266 #endif
269 numOpenMidiSeq++;
270 return 0;
273 /**************************************************************************
274 * midiCloseSeq [internal]
276 static int midiCloseSeq(void)
278 if (--numOpenMidiSeq == 0) {
279 snd_seq_delete_simple_port(midiSeq, port_out);
280 snd_seq_delete_simple_port(midiSeq, port_in);
281 snd_seq_close(midiSeq);
282 midiSeq = NULL;
284 return 0;
287 static DWORD WINAPI midRecThread(LPVOID arg)
289 int npfd;
290 struct pollfd *pfd;
292 TRACE("Thread startup\n");
294 while(!end_thread) {
295 TRACE("Thread loop\n");
296 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
297 pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
298 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
300 /* Check if an event is present */
301 if (poll(pfd, npfd, 250) < 0) {
302 HeapFree(GetProcessHeap(), 0, pfd);
303 continue;
306 /* Note: This definitely does not work.
307 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
308 snd_seq_event_t* ev;
309 snd_seq_event_input(midiSeq, &ev);
310 ....................
311 snd_seq_free_event(ev);
314 do {
315 WORD wDevID;
316 snd_seq_event_t* ev;
317 snd_seq_event_input(midiSeq, &ev);
318 /* Find the target device */
319 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
320 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
321 break;
322 if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
323 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
324 else {
325 DWORD dwTime, toSend = 0;
326 int value = 0;
327 /* FIXME: Should use ev->time instead for better accuracy */
328 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
329 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
330 switch(ev->type)
332 case SND_SEQ_EVENT_NOTEOFF:
333 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
334 break;
335 case SND_SEQ_EVENT_NOTEON:
336 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
337 break;
338 case SND_SEQ_EVENT_KEYPRESS:
339 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
340 break;
341 case SND_SEQ_EVENT_CONTROLLER:
342 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
343 break;
344 case SND_SEQ_EVENT_PITCHBEND:
345 value = ev->data.control.value + 0x2000;
346 toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
347 break;
348 case SND_SEQ_EVENT_PGMCHANGE:
349 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
350 break;
351 case SND_SEQ_EVENT_CHANPRESS:
352 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
353 break;
354 case SND_SEQ_EVENT_CLOCK:
355 toSend = 0xF8;
356 break;
357 case SND_SEQ_EVENT_START:
358 toSend = 0xFA;
359 break;
360 case SND_SEQ_EVENT_CONTINUE:
361 toSend = 0xFB;
362 break;
363 case SND_SEQ_EVENT_STOP:
364 toSend = 0xFC;
365 break;
366 case SND_SEQ_EVENT_SONGPOS:
367 toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | 0xF2;
368 break;
369 case SND_SEQ_EVENT_SONGSEL:
370 toSend = ((ev->data.control.value & 0x7f) << 8) | 0xF3;
371 break;
372 case SND_SEQ_EVENT_RESET:
373 toSend = 0xFF;
374 break;
375 case SND_SEQ_EVENT_SYSEX:
377 int len = ev->data.ext.len;
378 LPBYTE ptr = (BYTE*) ev->data.ext.ptr;
379 LPMIDIHDR lpMidiHdr;
381 /* FIXME: Should handle sysex greater than lpMidiHdr->dwBufferLength */
382 EnterCriticalSection(&crit_sect);
383 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
384 if (lpMidiHdr->dwBytesRecorded + len <= lpMidiHdr->dwBufferLength) {
385 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr, len);
386 lpMidiHdr->dwBytesRecorded += len;
387 if (*(ptr + (len-1)) == 0xF7) {
388 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
389 lpMidiHdr->dwFlags |= MHDR_DONE;
390 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
391 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime) != MMSYSERR_NOERROR)
392 WARN("Couldn't notify client\n");
394 } else
395 FIXME("No enough space in the buffer to store sysex!\n");
396 } else
397 FIXME("Sysex received but no buffer to store it!\n");
398 LeaveCriticalSection(&crit_sect);
400 break;
401 case SND_SEQ_EVENT_SENSING:
402 /* Noting to do */
403 break;
404 default:
405 FIXME("Unhandled event received, type = %x\n", ev->type);
406 break;
408 if (toSend != 0) {
409 TRACE("Sending event %08x (from %d %d)\n", toSend, ev->source.client, ev->source.port);
410 if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
411 WARN("Couldn't notify client\n");
415 snd_seq_free_event(ev);
416 } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
418 HeapFree(GetProcessHeap(), 0, pfd);
420 return 0;
423 /**************************************************************************
424 * midGetDevCaps [internal]
426 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
428 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
430 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
431 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
433 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
435 return MMSYSERR_NOERROR;
439 /**************************************************************************
440 * midOpen [internal]
442 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
444 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
446 if (lpDesc == NULL) {
447 WARN("Invalid Parameter !\n");
448 return MMSYSERR_INVALPARAM;
451 /* FIXME :
452 * how to check that content of lpDesc is correct ?
454 if (wDevID >= MIDM_NumDevs) {
455 WARN("wDevID too large (%u) !\n", wDevID);
456 return MMSYSERR_BADDEVICEID;
458 if (MidiInDev[wDevID].state == -1) {
459 WARN("device disabled\n");
460 return MIDIERR_NODEVICE;
462 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
463 WARN("device already open !\n");
464 return MMSYSERR_ALLOCATED;
466 if ((dwFlags & MIDI_IO_STATUS) != 0) {
467 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
468 dwFlags &= ~MIDI_IO_STATUS;
470 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
471 FIXME("Bad dwFlags\n");
472 return MMSYSERR_INVALFLAG;
475 if (midiOpenSeq(1) < 0) {
476 return MMSYSERR_ERROR;
479 /* Connect our app port to the device port */
480 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
481 return MMSYSERR_NOTENABLED;
483 TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
485 if (numStartedMidiIn++ == 0) {
486 end_thread = 0;
487 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
488 if (!hThread) {
489 numStartedMidiIn = 0;
490 WARN("Couldn't create thread for midi-in\n");
491 midiCloseSeq();
492 return MMSYSERR_ERROR;
494 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
495 TRACE("Created thread for midi-in\n");
498 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
500 MidiInDev[wDevID].lpQueueHdr = NULL;
501 MidiInDev[wDevID].dwTotalPlayed = 0;
502 MidiInDev[wDevID].bufsize = 0x3FFF;
503 MidiInDev[wDevID].midiDesc = *lpDesc;
504 MidiInDev[wDevID].state = 0;
505 MidiInDev[wDevID].incLen = 0;
506 MidiInDev[wDevID].startTime = 0;
508 if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
509 WARN("can't notify client !\n");
510 return MMSYSERR_INVALPARAM;
512 return MMSYSERR_NOERROR;
515 /**************************************************************************
516 * midClose [internal]
518 static DWORD midClose(WORD wDevID)
520 int ret = MMSYSERR_NOERROR;
522 TRACE("(%04X);\n", wDevID);
524 if (wDevID >= MIDM_NumDevs) {
525 WARN("wDevID too big (%u) !\n", wDevID);
526 return MMSYSERR_BADDEVICEID;
528 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
529 WARN("device not opened !\n");
530 return MMSYSERR_ERROR;
532 if (MidiInDev[wDevID].lpQueueHdr != 0) {
533 return MIDIERR_STILLPLAYING;
536 if (midiSeq == NULL) {
537 WARN("ooops !\n");
538 return MMSYSERR_ERROR;
540 if (--numStartedMidiIn == 0) {
541 TRACE("Stopping thread for midi-in\n");
542 end_thread = 1;
543 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
544 WARN("Thread end not signaled, force termination\n");
545 TerminateThread(hThread, 0);
547 TRACE("Stopped thread for midi-in\n");
550 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
551 midiCloseSeq();
553 MidiInDev[wDevID].bufsize = 0;
554 if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
555 WARN("can't notify client !\n");
556 ret = MMSYSERR_INVALPARAM;
558 MidiInDev[wDevID].midiDesc.hMidi = 0;
560 return ret;
564 /**************************************************************************
565 * midAddBuffer [internal]
567 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
569 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
571 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
572 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
574 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
575 if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
576 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
577 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
578 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
580 EnterCriticalSection(&crit_sect);
581 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
582 if (MidiInDev[wDevID].lpQueueHdr == 0) {
583 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
584 } else {
585 LPMIDIHDR ptr;
587 for (ptr = MidiInDev[wDevID].lpQueueHdr;
588 ptr->lpNext != 0;
589 ptr = (LPMIDIHDR)ptr->lpNext);
590 ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
592 LeaveCriticalSection(&crit_sect);
594 return MMSYSERR_NOERROR;
597 /**************************************************************************
598 * midPrepare [internal]
600 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
602 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
604 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
605 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
606 lpMidiHdr->dwBufferLength >= 0x10000ul)
607 return MMSYSERR_INVALPARAM;
609 lpMidiHdr->lpNext = 0;
610 lpMidiHdr->dwFlags |= MHDR_PREPARED;
611 lpMidiHdr->dwBytesRecorded = 0;
613 return MMSYSERR_NOERROR;
616 /**************************************************************************
617 * midUnprepare [internal]
619 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
621 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
623 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
624 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
626 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
627 lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
628 return MMSYSERR_INVALPARAM;
630 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
631 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
633 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
635 return MMSYSERR_NOERROR;
638 /**************************************************************************
639 * midReset [internal]
641 static DWORD midReset(WORD wDevID)
643 DWORD dwTime = GetTickCount();
645 TRACE("(%04X);\n", wDevID);
647 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
648 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
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 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
656 (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
657 WARN("Couldn't notify client\n");
659 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
661 LeaveCriticalSection(&crit_sect);
663 return MMSYSERR_NOERROR;
666 /**************************************************************************
667 * midStart [internal]
669 static DWORD midStart(WORD wDevID)
671 TRACE("(%04X);\n", wDevID);
673 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
674 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
676 MidiInDev[wDevID].state = 1;
677 MidiInDev[wDevID].startTime = GetTickCount();
678 return MMSYSERR_NOERROR;
681 /**************************************************************************
682 * midStop [internal]
684 static DWORD midStop(WORD wDevID)
686 TRACE("(%04X);\n", wDevID);
688 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
689 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
691 MidiInDev[wDevID].state = 0;
692 return MMSYSERR_NOERROR;
695 /**************************************************************************
696 * modGetDevCaps [internal]
698 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
700 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
702 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
703 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
705 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
707 return MMSYSERR_NOERROR;
710 /**************************************************************************
711 * modOpen [internal]
713 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
715 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
716 if (lpDesc == NULL) {
717 WARN("Invalid Parameter !\n");
718 return MMSYSERR_INVALPARAM;
720 if (wDevID >= MODM_NumDevs) {
721 TRACE("MAX_MIDIOUTDRV reached !\n");
722 return MMSYSERR_BADDEVICEID;
724 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
725 WARN("device already open !\n");
726 return MMSYSERR_ALLOCATED;
728 if (!MidiOutDev[wDevID].bEnabled) {
729 WARN("device disabled !\n");
730 return MIDIERR_NODEVICE;
732 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
733 WARN("bad dwFlags\n");
734 return MMSYSERR_INVALFLAG;
736 if (!MidiOutDev[wDevID].bEnabled) {
737 TRACE("disabled wDevID\n");
738 return MMSYSERR_NOTENABLED;
741 MidiOutDev[wDevID].lpExtra = 0;
743 switch (MidiOutDev[wDevID].caps.wTechnology) {
744 case MOD_FMSYNTH:
745 case MOD_MIDIPORT:
746 case MOD_SYNTH:
747 if (midiOpenSeq(1) < 0) {
748 return MMSYSERR_ALLOCATED;
750 break;
751 default:
752 WARN("Technology not supported (yet) %d !\n",
753 MidiOutDev[wDevID].caps.wTechnology);
754 return MMSYSERR_NOTENABLED;
757 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
759 MidiOutDev[wDevID].lpQueueHdr = NULL;
760 MidiOutDev[wDevID].dwTotalPlayed = 0;
761 MidiOutDev[wDevID].bufsize = 0x3FFF;
762 MidiOutDev[wDevID].midiDesc = *lpDesc;
764 /* Connect our app port to the device port */
765 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
766 return MMSYSERR_NOTENABLED;
768 if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
769 WARN("can't notify client !\n");
770 return MMSYSERR_INVALPARAM;
772 TRACE("Successful !\n");
773 return MMSYSERR_NOERROR;
777 /**************************************************************************
778 * modClose [internal]
780 static DWORD modClose(WORD wDevID)
782 int ret = MMSYSERR_NOERROR;
784 TRACE("(%04X);\n", wDevID);
786 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
787 WARN("device not opened !\n");
788 return MMSYSERR_ERROR;
790 /* FIXME: should test that no pending buffer is still in the queue for
791 * playing */
793 if (midiSeq == NULL) {
794 WARN("can't close !\n");
795 return MMSYSERR_ERROR;
798 switch (MidiOutDev[wDevID].caps.wTechnology) {
799 case MOD_FMSYNTH:
800 case MOD_MIDIPORT:
801 case MOD_SYNTH:
802 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
803 midiCloseSeq();
804 break;
805 default:
806 WARN("Technology not supported (yet) %d !\n",
807 MidiOutDev[wDevID].caps.wTechnology);
808 return MMSYSERR_NOTENABLED;
811 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
812 MidiOutDev[wDevID].lpExtra = 0;
814 MidiOutDev[wDevID].bufsize = 0;
815 if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
816 WARN("can't notify client !\n");
817 ret = MMSYSERR_INVALPARAM;
819 MidiOutDev[wDevID].midiDesc.hMidi = 0;
820 return ret;
823 /**************************************************************************
824 * modData [internal]
826 static DWORD modData(WORD wDevID, DWORD dwParam)
828 BYTE evt = LOBYTE(LOWORD(dwParam));
829 BYTE d1 = HIBYTE(LOWORD(dwParam));
830 BYTE d2 = LOBYTE(HIWORD(dwParam));
832 TRACE("(%04X, %08X);\n", wDevID, dwParam);
834 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
835 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
837 if (midiSeq == NULL) {
838 WARN("can't play !\n");
839 return MIDIERR_NODEVICE;
841 switch (MidiOutDev[wDevID].caps.wTechnology) {
842 case MOD_SYNTH:
843 case MOD_MIDIPORT:
845 int handled = 1; /* Assume event is handled */
846 snd_seq_event_t event;
847 snd_seq_ev_clear(&event);
848 snd_seq_ev_set_direct(&event);
849 snd_seq_ev_set_source(&event, port_out);
850 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
852 switch (evt & 0xF0) {
853 case MIDI_CMD_NOTE_OFF:
854 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
855 break;
856 case MIDI_CMD_NOTE_ON:
857 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
858 break;
859 case MIDI_CMD_NOTE_PRESSURE:
860 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
861 break;
862 case MIDI_CMD_CONTROL:
863 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
864 break;
865 case MIDI_CMD_BENDER:
866 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
867 break;
868 case MIDI_CMD_PGM_CHANGE:
869 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
870 break;
871 case MIDI_CMD_CHANNEL_PRESSURE:
872 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
873 break;
874 case MIDI_CMD_COMMON_SYSEX:
875 switch (evt & 0x0F) {
876 case 0x00: /* System Exclusive, don't do it on modData,
877 * should require modLongData*/
878 case 0x01: /* Undefined */
879 case 0x04: /* Undefined. */
880 case 0x05: /* Undefined. */
881 case 0x07: /* End of Exclusive. */
882 case 0x09: /* Undefined. */
883 case 0x0D: /* Undefined. */
884 handled = 0;
885 break;
886 case 0x06: /* Tune Request */
887 case 0x08: /* Timing Clock. */
888 case 0x0A: /* Start. */
889 case 0x0B: /* Continue */
890 case 0x0C: /* Stop */
891 case 0x0E: /* Active Sensing. */
892 /* FIXME: Is this function suitable for these purposes
893 (and also Song Select and Song Position Pointer) */
894 snd_seq_ev_set_sysex(&event, 1, &evt);
895 break;
896 case 0x0F: /* Reset */
897 /* snd_seq_ev_set_sysex(&event, 1, &evt);
898 this other way may be better */
900 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
901 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
903 break;
904 case 0x03: /* Song Select. */
906 BYTE buf[2];
907 buf[0] = evt;
908 buf[1] = d1;
909 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
911 break;
912 case 0x02: /* Song Position Pointer. */
914 BYTE buf[3];
915 buf[0] = evt;
916 buf[1] = d1;
917 buf[2] = d2;
918 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
920 break;
922 break;
924 if (handled)
925 snd_seq_event_output_direct(midiSeq, &event);
927 break;
928 default:
929 WARN("Technology not supported (yet) %d !\n",
930 MidiOutDev[wDevID].caps.wTechnology);
931 return MMSYSERR_NOTENABLED;
934 return MMSYSERR_NOERROR;
937 /**************************************************************************
938 * modLongData [internal]
940 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
942 int len_add = 0;
943 LPBYTE lpData, lpNewData = NULL;
944 snd_seq_event_t event;
946 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
948 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
949 * but it seems to be used only for midi input.
950 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
953 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
954 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
956 if (midiSeq == NULL) {
957 WARN("can't play !\n");
958 return MIDIERR_NODEVICE;
961 lpData = (LPBYTE) lpMidiHdr->lpData;
963 if (lpData == NULL)
964 return MIDIERR_UNPREPARED;
965 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
966 return MIDIERR_UNPREPARED;
967 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
968 return MIDIERR_STILLPLAYING;
969 lpMidiHdr->dwFlags &= ~MHDR_DONE;
970 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
972 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
973 * data, or can it also contain raw MIDI data, to be split up and sent to
974 * modShortData() ?
975 * If the latest is true, then the following WARNing will fire up
977 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
978 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
979 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
982 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
983 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
984 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
985 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
987 switch (MidiOutDev[wDevID].caps.wTechnology) {
988 case MOD_FMSYNTH:
989 /* FIXME: I don't think there is much to do here */
990 break;
991 case MOD_MIDIPORT:
992 if (lpData[0] != 0xF0) {
993 /* Send start of System Exclusive */
994 len_add = 1;
995 lpData[0] = 0xF0;
996 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
997 WARN("Adding missing 0xF0 marker at the beginning of "
998 "system exclusive byte stream\n");
1000 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
1001 /* Send end of System Exclusive */
1002 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
1003 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
1004 len_add++;
1005 WARN("Adding missing 0xF7 marker at the end of "
1006 "system exclusive byte stream\n");
1008 snd_seq_ev_clear(&event);
1009 snd_seq_ev_set_direct(&event);
1010 snd_seq_ev_set_source(&event, port_out);
1011 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1012 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1013 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1014 snd_seq_event_output_direct(midiSeq, &event);
1015 if (lpNewData)
1016 HeapFree(GetProcessHeap(), 0, lpData);
1017 break;
1018 default:
1019 WARN("Technology not supported (yet) %d !\n",
1020 MidiOutDev[wDevID].caps.wTechnology);
1021 HeapFree(GetProcessHeap(), 0, lpNewData);
1022 return MMSYSERR_NOTENABLED;
1025 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1026 lpMidiHdr->dwFlags |= MHDR_DONE;
1027 if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1028 WARN("can't notify client !\n");
1029 return MMSYSERR_INVALPARAM;
1031 return MMSYSERR_NOERROR;
1034 /**************************************************************************
1035 * modPrepare [internal]
1037 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1039 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1041 if (midiSeq == NULL) {
1042 WARN("can't prepare !\n");
1043 return MMSYSERR_NOTENABLED;
1046 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1047 * asks to prepare MIDIHDR which dwFlags != 0.
1048 * So at least check for the inqueue flag
1050 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1051 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
1052 lpMidiHdr->dwBufferLength >= 0x10000ul) {
1053 WARN("%p %p %08x %d\n", lpMidiHdr, lpMidiHdr ? lpMidiHdr->lpData : NULL,
1054 lpMidiHdr ? lpMidiHdr->dwFlags : 0, dwSize);
1055 return MMSYSERR_INVALPARAM;
1058 lpMidiHdr->lpNext = 0;
1059 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1060 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1061 return MMSYSERR_NOERROR;
1064 /**************************************************************************
1065 * modUnprepare [internal]
1067 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1069 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1071 if (midiSeq == NULL) {
1072 WARN("can't unprepare !\n");
1073 return MMSYSERR_NOTENABLED;
1076 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1077 return MMSYSERR_INVALPARAM;
1078 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1079 return MIDIERR_STILLPLAYING;
1080 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1081 return MMSYSERR_NOERROR;
1084 /**************************************************************************
1085 * modReset [internal]
1087 static DWORD modReset(WORD wDevID)
1089 unsigned chn;
1091 TRACE("(%04X);\n", wDevID);
1093 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1094 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1096 /* stop all notes */
1097 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1098 * it's channel dependent...
1100 for (chn = 0; chn < 16; chn++) {
1101 /* turn off every note */
1102 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1103 /* remove sustain on all channels */
1104 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1106 /* FIXME: the LongData buffers must also be returned to the app */
1107 return MMSYSERR_NOERROR;
1111 /**************************************************************************
1112 * ALSA_AddMidiPort [internal]
1114 * Helper for ALSA_MidiInit
1116 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, int cap, int type)
1118 char midiPortName[MAXPNAMELEN];
1120 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1121 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1122 snd_seq_client_info_get_name(cinfo),
1123 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1124 snd_seq_port_info_get_port(pinfo),
1125 snd_seq_port_info_get_name(pinfo),
1126 type);
1128 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1129 return;
1130 if (!type)
1131 return;
1133 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1135 /* Manufac ID. We do not have access to this with soundcard.h
1136 * Does not seem to be a problem, because in mmsystem.h only
1137 * Microsoft's ID is listed.
1139 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1140 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1141 /* Product Version. We simply say "1" */
1142 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1143 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1145 /* FIXME Do we have this information?
1146 * Assuming the soundcards can handle
1147 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1148 * not MIDICAPS_CACHE.
1150 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1152 /* Try to use both client and port names, if this is too long take the port name only.
1153 In the second case the port name should be explicit enough due to its big size.
1155 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1156 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1157 } else {
1158 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1159 midiPortName[MAXPNAMELEN-1] = 0;
1161 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1162 MidiOutDev[MODM_NumDevs].caps.szPname,
1163 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1165 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1166 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1168 /* FIXME Is it possible to know the maximum
1169 * number of simultaneous notes of a soundcard ?
1170 * I believe we don't have this information, but
1171 * it's probably equal or more than wVoices
1173 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1174 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1176 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1177 "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1178 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1179 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1180 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1181 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1182 (long)type, (long)0);
1184 MODM_NumDevs++;
1186 if (cap & SND_SEQ_PORT_CAP_READ) {
1187 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1188 snd_seq_client_info_get_name(cinfo),
1189 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1190 snd_seq_port_info_get_port(pinfo),
1191 snd_seq_port_info_get_name(pinfo),
1192 type);
1194 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1195 return;
1196 if (!type)
1197 return;
1199 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1201 /* Manufac ID. We do not have access to this with soundcard.h
1202 * Does not seem to be a problem, because in mmsystem.h only
1203 * Microsoft's ID is listed.
1205 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1206 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1207 /* Product Version. We simply say "1" */
1208 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1210 /* FIXME Do we have this information?
1211 * Assuming the soundcards can handle
1212 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1213 * not MIDICAPS_CACHE.
1215 MidiInDev[MIDM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1217 /* Try to use both client and port names, if this is too long take the port name only.
1218 In the second case the port name should be explicit enough due to its big size.
1220 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1221 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1222 } else {
1223 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1224 midiPortName[MAXPNAMELEN-1] = 0;
1226 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1227 MidiInDev[MIDM_NumDevs].caps.szPname,
1228 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1229 MidiInDev[MIDM_NumDevs].state = 0;
1231 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1232 "\tALSA info: midi dev-type=%lx, capa=%lx\n",
1233 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1234 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1235 (long)type, (long)0);
1237 MIDM_NumDevs++;
1241 #endif /* HAVE_ALSA */
1244 /*======================================================================*
1245 * MIDI entry points *
1246 *======================================================================*/
1248 /**************************************************************************
1249 * ALSA_MidiInit [internal]
1251 * Initializes the MIDI devices information variables
1253 LONG ALSA_MidiInit(void)
1255 #ifdef HAVE_ALSA
1256 static BOOL bInitDone = FALSE;
1257 snd_seq_client_info_t *cinfo;
1258 snd_seq_port_info_t *pinfo;
1260 if (bInitDone)
1261 return TRUE;
1263 TRACE("Initializing the MIDI variables.\n");
1264 bInitDone = TRUE;
1266 /* try to open device */
1267 if (midiOpenSeq(0) == -1) {
1268 return TRUE;
1271 #if 0 /* Debug purpose */
1272 snd_lib_error_set_handler(error_handler);
1273 #endif
1274 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1275 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1277 /* First, search for all internal midi devices */
1278 snd_seq_client_info_set_client(cinfo, -1);
1279 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1280 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1281 snd_seq_port_info_set_port(pinfo, -1);
1282 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1283 int cap = snd_seq_port_info_get_capability(pinfo);
1284 int type = snd_seq_port_info_get_type(pinfo);
1285 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1286 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1290 /* Second, search for all external ports */
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 int cap = snd_seq_port_info_get_capability(pinfo);
1297 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 /* close file and exit */
1304 midiCloseSeq();
1305 HeapFree( GetProcessHeap(), 0, cinfo );
1306 HeapFree( GetProcessHeap(), 0, pinfo );
1308 TRACE("End\n");
1309 #endif
1310 return TRUE;
1313 /**************************************************************************
1314 * midMessage (WINEOSS.4)
1316 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1317 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1319 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1320 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1321 switch (wMsg) {
1322 #ifdef HAVE_ALSA
1323 case DRVM_INIT:
1324 case DRVM_EXIT:
1325 case DRVM_ENABLE:
1326 case DRVM_DISABLE:
1327 /* FIXME: Pretend this is supported */
1328 return 0;
1329 case MIDM_OPEN:
1330 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1331 case MIDM_CLOSE:
1332 return midClose(wDevID);
1333 case MIDM_ADDBUFFER:
1334 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1335 case MIDM_PREPARE:
1336 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1337 case MIDM_UNPREPARE:
1338 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1339 case MIDM_GETDEVCAPS:
1340 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1341 case MIDM_GETNUMDEVS:
1342 return MIDM_NumDevs;
1343 case MIDM_RESET:
1344 return midReset(wDevID);
1345 case MIDM_START:
1346 return midStart(wDevID);
1347 case MIDM_STOP:
1348 return midStop(wDevID);
1349 #endif
1350 default:
1351 TRACE("Unsupported message\n");
1353 return MMSYSERR_NOTSUPPORTED;
1356 /**************************************************************************
1357 * modMessage (WINEOSS.5)
1359 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1360 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1362 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1363 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1365 switch (wMsg) {
1366 #ifdef HAVE_ALSA
1367 case DRVM_INIT:
1368 case DRVM_EXIT:
1369 case DRVM_ENABLE:
1370 case DRVM_DISABLE:
1371 /* FIXME: Pretend this is supported */
1372 return 0;
1373 case MODM_OPEN:
1374 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1375 case MODM_CLOSE:
1376 return modClose(wDevID);
1377 case MODM_DATA:
1378 return modData(wDevID, dwParam1);
1379 case MODM_LONGDATA:
1380 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1381 case MODM_PREPARE:
1382 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1383 case MODM_UNPREPARE:
1384 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1385 case MODM_GETDEVCAPS:
1386 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1387 case MODM_GETNUMDEVS:
1388 return MODM_NumDevs;
1389 case MODM_GETVOLUME:
1390 return 0;
1391 case MODM_SETVOLUME:
1392 return 0;
1393 case MODM_RESET:
1394 return modReset(wDevID);
1395 #endif
1396 default:
1397 TRACE("Unsupported message\n");
1399 return MMSYSERR_NOTSUPPORTED;
1402 /*-----------------------------------------------------------------------*/