push 87b6981010d7405c33b14cddcceec21b47729eba
[wine/hacks.git] / dlls / winealsa.drv / midi.c
blobe792c655ba204574f8063d9e34c9689ce985b37b
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(unsigned 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 void 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) return;
193 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
194 uFlags = MidiOutDev[wDevID].wFlags;
195 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
196 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
197 break;
199 case MIM_OPEN:
200 case MIM_CLOSE:
201 case MIM_DATA:
202 case MIM_LONGDATA:
203 case MIM_ERROR:
204 case MIM_LONGERROR:
205 case MIM_MOREDATA:
206 if (wDevID > MIDM_NumDevs) return;
208 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
209 uFlags = MidiInDev[wDevID].wFlags;
210 hDev = MidiInDev[wDevID].midiDesc.hMidi;
211 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
212 break;
213 default:
214 ERR("Unsupported MSW-MIDI message %u\n", wMsg);
215 return;
218 DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
221 static int midi_warn = 1;
222 /**************************************************************************
223 * midiOpenSeq [internal]
225 static int midiOpenSeq(int create_client)
227 if (numOpenMidiSeq == 0) {
228 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
230 if (midi_warn)
232 WARN("Error opening ALSA sequencer.\n");
234 midi_warn = 0;
235 return -1;
238 if (create_client) {
239 /* Setting the client name is the only init to do */
240 snd_seq_set_client_name(midiSeq, "WINE midi driver");
242 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
243 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
244 SND_SEQ_PORT_TYPE_APPLICATION);
245 if (port_out < 0)
246 TRACE("Unable to create output port\n");
247 else
248 TRACE("Outport port created successfully (%d)\n", port_out);
249 #else
250 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
251 SND_SEQ_PORT_TYPE_APPLICATION);
252 if (port_out < 0)
253 TRACE("Unable to create output port\n");
254 else
255 TRACE("Outport port created successfully (%d)\n", port_out);
257 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
258 SND_SEQ_PORT_TYPE_APPLICATION);
259 if (port_in < 0)
260 TRACE("Unable to create input port\n");
261 else
262 TRACE("Input port created successfully (%d)\n", port_in);
263 #endif
266 numOpenMidiSeq++;
267 return 0;
270 /**************************************************************************
271 * midiCloseSeq [internal]
273 static int midiCloseSeq(void)
275 if (--numOpenMidiSeq == 0) {
276 snd_seq_delete_simple_port(midiSeq, port_out);
277 snd_seq_delete_simple_port(midiSeq, port_in);
278 snd_seq_close(midiSeq);
279 midiSeq = NULL;
281 return 0;
284 static DWORD WINAPI midRecThread(LPVOID arg)
286 int npfd;
287 struct pollfd *pfd;
289 TRACE("Thread startup\n");
291 while(!end_thread) {
292 TRACE("Thread loop\n");
293 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
294 pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
295 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
297 /* Check if an event is present */
298 if (poll(pfd, npfd, 250) < 0) {
299 HeapFree(GetProcessHeap(), 0, pfd);
300 continue;
303 /* Note: This definitely does not work.
304 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
305 snd_seq_event_t* ev;
306 snd_seq_event_input(midiSeq, &ev);
307 ....................
308 snd_seq_free_event(ev);
311 do {
312 WORD wDevID;
313 snd_seq_event_t* ev;
314 snd_seq_event_input(midiSeq, &ev);
315 /* Find the target device */
316 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
317 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
318 break;
319 if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
320 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
321 else {
322 DWORD dwTime, toSend = 0;
323 int value = 0;
324 /* FIXME: Should use ev->time instead for better accuracy */
325 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
326 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
327 switch(ev->type)
329 case SND_SEQ_EVENT_NOTEOFF:
330 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
331 break;
332 case SND_SEQ_EVENT_NOTEON:
333 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
334 break;
335 case SND_SEQ_EVENT_KEYPRESS:
336 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
337 break;
338 case SND_SEQ_EVENT_CONTROLLER:
339 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
340 break;
341 case SND_SEQ_EVENT_PITCHBEND:
342 value = ev->data.control.value + 0x2000;
343 toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
344 break;
345 case SND_SEQ_EVENT_PGMCHANGE:
346 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
347 break;
348 case SND_SEQ_EVENT_CHANPRESS:
349 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
350 break;
351 case SND_SEQ_EVENT_CLOCK:
352 toSend = 0xF8;
353 break;
354 case SND_SEQ_EVENT_START:
355 toSend = 0xFA;
356 break;
357 case SND_SEQ_EVENT_CONTINUE:
358 toSend = 0xFB;
359 break;
360 case SND_SEQ_EVENT_STOP:
361 toSend = 0xFC;
362 break;
363 case SND_SEQ_EVENT_SONGPOS:
364 toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | 0xF2;
365 break;
366 case SND_SEQ_EVENT_SONGSEL:
367 toSend = ((ev->data.control.value & 0x7f) << 8) | 0xF3;
368 break;
369 case SND_SEQ_EVENT_RESET:
370 toSend = 0xFF;
371 break;
372 case SND_SEQ_EVENT_SYSEX:
374 int pos = 0;
375 int len = ev->data.ext.len;
376 LPBYTE ptr = ev->data.ext.ptr;
377 LPMIDIHDR lpMidiHdr;
379 EnterCriticalSection(&crit_sect);
380 while (len) {
381 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
382 int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
383 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
384 lpMidiHdr->dwBytesRecorded += copylen;
385 len -= copylen;
386 pos += copylen;
387 /* We check if we reach the end of buffer or the end of sysex before notifying
388 * to handle the case where ALSA split the sysex into several events */
389 if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
390 (*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
391 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
392 lpMidiHdr->dwFlags |= MHDR_DONE;
393 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
394 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
396 } else {
397 FIXME("Sysex data received but no buffer to store it!\n");
398 break;
401 LeaveCriticalSection(&crit_sect);
403 break;
404 case SND_SEQ_EVENT_SENSING:
405 /* Noting to do */
406 break;
407 default:
408 FIXME("Unhandled event received, type = %x\n", ev->type);
409 break;
411 if (toSend != 0) {
412 TRACE("Sending event %08x (from %d %d)\n", toSend, ev->source.client, ev->source.port);
413 MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
416 snd_seq_free_event(ev);
417 } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
419 HeapFree(GetProcessHeap(), 0, pfd);
421 return 0;
424 /**************************************************************************
425 * midGetDevCaps [internal]
427 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
429 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
431 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
432 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
434 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
436 return MMSYSERR_NOERROR;
440 /**************************************************************************
441 * midOpen [internal]
443 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
445 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
447 if (lpDesc == NULL) {
448 WARN("Invalid Parameter !\n");
449 return MMSYSERR_INVALPARAM;
452 /* FIXME :
453 * how to check that content of lpDesc is correct ?
455 if (wDevID >= MIDM_NumDevs) {
456 WARN("wDevID too large (%u) !\n", wDevID);
457 return MMSYSERR_BADDEVICEID;
459 if (MidiInDev[wDevID].state == -1) {
460 WARN("device disabled\n");
461 return MIDIERR_NODEVICE;
463 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
464 WARN("device already open !\n");
465 return MMSYSERR_ALLOCATED;
467 if ((dwFlags & MIDI_IO_STATUS) != 0) {
468 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
469 dwFlags &= ~MIDI_IO_STATUS;
471 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
472 FIXME("Bad dwFlags\n");
473 return MMSYSERR_INVALFLAG;
476 if (midiOpenSeq(1) < 0) {
477 return MMSYSERR_ERROR;
480 /* Connect our app port to the device port */
481 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
482 return MMSYSERR_NOTENABLED;
484 TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
486 if (numStartedMidiIn++ == 0) {
487 end_thread = 0;
488 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
489 if (!hThread) {
490 numStartedMidiIn = 0;
491 WARN("Couldn't create thread for midi-in\n");
492 midiCloseSeq();
493 return MMSYSERR_ERROR;
495 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
496 TRACE("Created thread for midi-in\n");
499 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
501 MidiInDev[wDevID].lpQueueHdr = NULL;
502 MidiInDev[wDevID].dwTotalPlayed = 0;
503 MidiInDev[wDevID].bufsize = 0x3FFF;
504 MidiInDev[wDevID].midiDesc = *lpDesc;
505 MidiInDev[wDevID].state = 0;
506 MidiInDev[wDevID].incLen = 0;
507 MidiInDev[wDevID].startTime = 0;
509 MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
510 return MMSYSERR_NOERROR;
513 /**************************************************************************
514 * midClose [internal]
516 static DWORD midClose(WORD wDevID)
518 int ret = MMSYSERR_NOERROR;
520 TRACE("(%04X);\n", wDevID);
522 if (wDevID >= MIDM_NumDevs) {
523 WARN("wDevID too big (%u) !\n", wDevID);
524 return MMSYSERR_BADDEVICEID;
526 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
527 WARN("device not opened !\n");
528 return MMSYSERR_ERROR;
530 if (MidiInDev[wDevID].lpQueueHdr != 0) {
531 return MIDIERR_STILLPLAYING;
534 if (midiSeq == NULL) {
535 WARN("ooops !\n");
536 return MMSYSERR_ERROR;
538 if (--numStartedMidiIn == 0) {
539 TRACE("Stopping thread for midi-in\n");
540 end_thread = 1;
541 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
542 WARN("Thread end not signaled, force termination\n");
543 TerminateThread(hThread, 0);
545 TRACE("Stopped thread for midi-in\n");
548 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
549 midiCloseSeq();
551 MidiInDev[wDevID].bufsize = 0;
552 MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
553 MidiInDev[wDevID].midiDesc.hMidi = 0;
555 return ret;
559 /**************************************************************************
560 * midAddBuffer [internal]
562 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
564 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
566 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
567 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
569 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
570 if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
571 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
572 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
573 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
575 EnterCriticalSection(&crit_sect);
576 lpMidiHdr->dwFlags &= ~WHDR_DONE;
577 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
578 lpMidiHdr->dwBytesRecorded = 0;
579 lpMidiHdr->lpNext = 0;
580 if (MidiInDev[wDevID].lpQueueHdr == 0) {
581 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
582 } else {
583 LPMIDIHDR ptr;
585 for (ptr = MidiInDev[wDevID].lpQueueHdr; ptr->lpNext != 0;
586 ptr = ptr->lpNext);
587 ptr->lpNext = lpMidiHdr;
589 LeaveCriticalSection(&crit_sect);
591 return MMSYSERR_NOERROR;
594 /**************************************************************************
595 * midPrepare [internal]
597 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
599 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
601 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
602 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0)
603 return MMSYSERR_INVALPARAM;
605 lpMidiHdr->lpNext = 0;
606 lpMidiHdr->dwFlags |= MHDR_PREPARED;
607 lpMidiHdr->dwBytesRecorded = 0;
609 return MMSYSERR_NOERROR;
612 /**************************************************************************
613 * midUnprepare [internal]
615 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
617 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
619 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
620 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
622 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
623 lpMidiHdr->lpData == 0)
624 return MMSYSERR_INVALPARAM;
626 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
627 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
629 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
631 return MMSYSERR_NOERROR;
634 /**************************************************************************
635 * midReset [internal]
637 static DWORD midReset(WORD wDevID)
639 DWORD dwTime = GetTickCount();
641 TRACE("(%04X);\n", wDevID);
643 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
644 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
646 EnterCriticalSection(&crit_sect);
647 while (MidiInDev[wDevID].lpQueueHdr) {
648 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
649 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
650 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
651 MIDI_NotifyClient(wDevID, MIM_LONGDATA,
652 (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime);
653 MidiInDev[wDevID].lpQueueHdr = MidiInDev[wDevID].lpQueueHdr->lpNext;
655 LeaveCriticalSection(&crit_sect);
657 return MMSYSERR_NOERROR;
660 /**************************************************************************
661 * midStart [internal]
663 static DWORD midStart(WORD wDevID)
665 TRACE("(%04X);\n", wDevID);
667 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
668 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
670 MidiInDev[wDevID].state = 1;
671 MidiInDev[wDevID].startTime = GetTickCount();
672 return MMSYSERR_NOERROR;
675 /**************************************************************************
676 * midStop [internal]
678 static DWORD midStop(WORD wDevID)
680 TRACE("(%04X);\n", wDevID);
682 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
683 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
685 MidiInDev[wDevID].state = 0;
686 return MMSYSERR_NOERROR;
689 /**************************************************************************
690 * modGetDevCaps [internal]
692 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
694 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
696 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
697 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
699 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
701 return MMSYSERR_NOERROR;
704 /**************************************************************************
705 * modOpen [internal]
707 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
709 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
710 if (lpDesc == NULL) {
711 WARN("Invalid Parameter !\n");
712 return MMSYSERR_INVALPARAM;
714 if (wDevID >= MODM_NumDevs) {
715 TRACE("MAX_MIDIOUTDRV reached !\n");
716 return MMSYSERR_BADDEVICEID;
718 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
719 WARN("device already open !\n");
720 return MMSYSERR_ALLOCATED;
722 if (!MidiOutDev[wDevID].bEnabled) {
723 WARN("device disabled !\n");
724 return MIDIERR_NODEVICE;
726 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
727 WARN("bad dwFlags\n");
728 return MMSYSERR_INVALFLAG;
730 if (!MidiOutDev[wDevID].bEnabled) {
731 TRACE("disabled wDevID\n");
732 return MMSYSERR_NOTENABLED;
735 MidiOutDev[wDevID].lpExtra = 0;
737 switch (MidiOutDev[wDevID].caps.wTechnology) {
738 case MOD_FMSYNTH:
739 case MOD_MIDIPORT:
740 case MOD_SYNTH:
741 if (midiOpenSeq(1) < 0) {
742 return MMSYSERR_ALLOCATED;
744 break;
745 default:
746 WARN("Technology not supported (yet) %d !\n",
747 MidiOutDev[wDevID].caps.wTechnology);
748 return MMSYSERR_NOTENABLED;
751 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
753 MidiOutDev[wDevID].lpQueueHdr = NULL;
754 MidiOutDev[wDevID].dwTotalPlayed = 0;
755 MidiOutDev[wDevID].bufsize = 0x3FFF;
756 MidiOutDev[wDevID].midiDesc = *lpDesc;
758 /* Connect our app port to the device port */
759 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
760 return MMSYSERR_NOTENABLED;
762 MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
763 TRACE("Successful !\n");
764 return MMSYSERR_NOERROR;
768 /**************************************************************************
769 * modClose [internal]
771 static DWORD modClose(WORD wDevID)
773 int ret = MMSYSERR_NOERROR;
775 TRACE("(%04X);\n", wDevID);
777 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
778 WARN("device not opened !\n");
779 return MMSYSERR_ERROR;
781 /* FIXME: should test that no pending buffer is still in the queue for
782 * playing */
784 if (midiSeq == NULL) {
785 WARN("can't close !\n");
786 return MMSYSERR_ERROR;
789 switch (MidiOutDev[wDevID].caps.wTechnology) {
790 case MOD_FMSYNTH:
791 case MOD_MIDIPORT:
792 case MOD_SYNTH:
793 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
794 midiCloseSeq();
795 break;
796 default:
797 WARN("Technology not supported (yet) %d !\n",
798 MidiOutDev[wDevID].caps.wTechnology);
799 return MMSYSERR_NOTENABLED;
802 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
803 MidiOutDev[wDevID].lpExtra = 0;
805 MidiOutDev[wDevID].bufsize = 0;
806 MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
807 MidiOutDev[wDevID].midiDesc.hMidi = 0;
808 return ret;
811 /**************************************************************************
812 * modData [internal]
814 static DWORD modData(WORD wDevID, DWORD dwParam)
816 BYTE evt = LOBYTE(LOWORD(dwParam));
817 BYTE d1 = HIBYTE(LOWORD(dwParam));
818 BYTE d2 = LOBYTE(HIWORD(dwParam));
820 TRACE("(%04X, %08X);\n", wDevID, dwParam);
822 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
823 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
825 if (midiSeq == NULL) {
826 WARN("can't play !\n");
827 return MIDIERR_NODEVICE;
829 switch (MidiOutDev[wDevID].caps.wTechnology) {
830 case MOD_SYNTH:
831 case MOD_MIDIPORT:
833 int handled = 1; /* Assume event is handled */
834 snd_seq_event_t event;
835 snd_seq_ev_clear(&event);
836 snd_seq_ev_set_direct(&event);
837 snd_seq_ev_set_source(&event, port_out);
838 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
840 switch (evt & 0xF0) {
841 case MIDI_CMD_NOTE_OFF:
842 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
843 break;
844 case MIDI_CMD_NOTE_ON:
845 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
846 break;
847 case MIDI_CMD_NOTE_PRESSURE:
848 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
849 break;
850 case MIDI_CMD_CONTROL:
851 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
852 break;
853 case MIDI_CMD_BENDER:
854 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
855 break;
856 case MIDI_CMD_PGM_CHANGE:
857 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
858 break;
859 case MIDI_CMD_CHANNEL_PRESSURE:
860 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
861 break;
862 case MIDI_CMD_COMMON_SYSEX:
863 switch (evt & 0x0F) {
864 case 0x00: /* System Exclusive, don't do it on modData,
865 * should require modLongData*/
866 case 0x01: /* Undefined */
867 case 0x04: /* Undefined. */
868 case 0x05: /* Undefined. */
869 case 0x07: /* End of Exclusive. */
870 case 0x09: /* Undefined. */
871 case 0x0D: /* Undefined. */
872 handled = 0;
873 break;
874 case 0x06: /* Tune Request */
875 case 0x08: /* Timing Clock. */
876 case 0x0A: /* Start. */
877 case 0x0B: /* Continue */
878 case 0x0C: /* Stop */
879 case 0x0E: /* Active Sensing. */
880 /* FIXME: Is this function suitable for these purposes
881 (and also Song Select and Song Position Pointer) */
882 snd_seq_ev_set_sysex(&event, 1, &evt);
883 break;
884 case 0x0F: /* Reset */
885 /* snd_seq_ev_set_sysex(&event, 1, &evt);
886 this other way may be better */
888 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
889 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
891 break;
892 case 0x03: /* Song Select. */
894 BYTE buf[2];
895 buf[0] = evt;
896 buf[1] = d1;
897 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
899 break;
900 case 0x02: /* Song Position Pointer. */
902 BYTE buf[3];
903 buf[0] = evt;
904 buf[1] = d1;
905 buf[2] = d2;
906 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
908 break;
910 break;
912 if (handled)
913 snd_seq_event_output_direct(midiSeq, &event);
915 break;
916 default:
917 WARN("Technology not supported (yet) %d !\n",
918 MidiOutDev[wDevID].caps.wTechnology);
919 return MMSYSERR_NOTENABLED;
922 return MMSYSERR_NOERROR;
925 /**************************************************************************
926 * modLongData [internal]
928 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
930 int len_add = 0;
931 LPBYTE lpData, lpNewData = NULL;
932 snd_seq_event_t event;
934 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
936 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
937 * but it seems to be used only for midi input.
938 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
941 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
942 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
944 if (midiSeq == NULL) {
945 WARN("can't play !\n");
946 return MIDIERR_NODEVICE;
949 lpData = (LPBYTE) lpMidiHdr->lpData;
951 if (lpData == NULL)
952 return MIDIERR_UNPREPARED;
953 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
954 return MIDIERR_UNPREPARED;
955 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
956 return MIDIERR_STILLPLAYING;
957 lpMidiHdr->dwFlags &= ~MHDR_DONE;
958 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
960 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
961 * data, or can it also contain raw MIDI data, to be split up and sent to
962 * modShortData() ?
963 * If the latest is true, then the following WARNing will fire up
965 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
966 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
967 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
970 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
971 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
972 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
973 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
975 switch (MidiOutDev[wDevID].caps.wTechnology) {
976 case MOD_FMSYNTH:
977 /* FIXME: I don't think there is much to do here */
978 break;
979 case MOD_MIDIPORT:
980 if (lpData[0] != 0xF0) {
981 /* Send start of System Exclusive */
982 len_add = 1;
983 lpData[0] = 0xF0;
984 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
985 WARN("Adding missing 0xF0 marker at the beginning of "
986 "system exclusive byte stream\n");
988 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
989 /* Send end of System Exclusive */
990 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
991 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
992 len_add++;
993 WARN("Adding missing 0xF7 marker at the end of "
994 "system exclusive byte stream\n");
996 snd_seq_ev_clear(&event);
997 snd_seq_ev_set_direct(&event);
998 snd_seq_ev_set_source(&event, port_out);
999 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1000 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1001 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1002 snd_seq_event_output_direct(midiSeq, &event);
1003 if (lpNewData)
1004 HeapFree(GetProcessHeap(), 0, lpData);
1005 break;
1006 default:
1007 WARN("Technology not supported (yet) %d !\n",
1008 MidiOutDev[wDevID].caps.wTechnology);
1009 HeapFree(GetProcessHeap(), 0, lpNewData);
1010 return MMSYSERR_NOTENABLED;
1013 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1014 lpMidiHdr->dwFlags |= MHDR_DONE;
1015 MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
1016 return MMSYSERR_NOERROR;
1019 /**************************************************************************
1020 * modPrepare [internal]
1022 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1024 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1026 if (midiSeq == NULL) {
1027 WARN("can't prepare !\n");
1028 return MMSYSERR_NOTENABLED;
1031 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1032 * asks to prepare MIDIHDR which dwFlags != 0.
1033 * So at least check for the inqueue flag
1035 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1036 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0) {
1037 WARN("%p %p %08x %d\n", lpMidiHdr, lpMidiHdr ? lpMidiHdr->lpData : NULL,
1038 lpMidiHdr ? lpMidiHdr->dwFlags : 0, dwSize);
1039 return MMSYSERR_INVALPARAM;
1042 lpMidiHdr->lpNext = 0;
1043 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1044 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1045 return MMSYSERR_NOERROR;
1048 /**************************************************************************
1049 * modUnprepare [internal]
1051 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1053 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1055 if (midiSeq == NULL) {
1056 WARN("can't unprepare !\n");
1057 return MMSYSERR_NOTENABLED;
1060 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1061 return MMSYSERR_INVALPARAM;
1062 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1063 return MIDIERR_STILLPLAYING;
1064 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1065 return MMSYSERR_NOERROR;
1068 /**************************************************************************
1069 * modReset [internal]
1071 static DWORD modReset(WORD wDevID)
1073 unsigned chn;
1075 TRACE("(%04X);\n", wDevID);
1077 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1078 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1080 /* stop all notes */
1081 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1082 * it's channel dependent...
1084 for (chn = 0; chn < 16; chn++) {
1085 /* turn off every note */
1086 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1087 /* remove sustain on all channels */
1088 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1090 /* FIXME: the LongData buffers must also be returned to the app */
1091 return MMSYSERR_NOERROR;
1095 /**************************************************************************
1096 * ALSA_AddMidiPort [internal]
1098 * Helper for ALSA_MidiInit
1100 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1102 char midiPortName[MAXPNAMELEN];
1104 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1105 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1106 snd_seq_client_info_get_name(cinfo),
1107 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1108 snd_seq_port_info_get_port(pinfo),
1109 snd_seq_port_info_get_name(pinfo),
1110 type);
1112 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1113 return;
1114 if (!type)
1115 return;
1117 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1119 /* Manufac ID. We do not have access to this with soundcard.h
1120 * Does not seem to be a problem, because in mmsystem.h only
1121 * Microsoft's ID is listed.
1123 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1124 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1125 /* Product Version. We simply say "1" */
1126 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1127 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1129 /* FIXME Do we have this information?
1130 * Assuming the soundcards can handle
1131 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1132 * not MIDICAPS_CACHE.
1134 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1136 /* Try to use both client and port names, if this is too long take the port name only.
1137 In the second case the port name should be explicit enough due to its big size.
1139 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1140 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1141 } else {
1142 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1143 midiPortName[MAXPNAMELEN-1] = 0;
1145 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1146 MidiOutDev[MODM_NumDevs].caps.szPname,
1147 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1149 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1150 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1152 /* FIXME Is it possible to know the maximum
1153 * number of simultaneous notes of a soundcard ?
1154 * I believe we don't have this information, but
1155 * it's probably equal or more than wVoices
1157 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1158 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1160 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1161 "\tALSA info: midi dev-type=%x, capa=0\n",
1162 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1163 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1164 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1165 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1166 type);
1168 MODM_NumDevs++;
1170 if (cap & SND_SEQ_PORT_CAP_READ) {
1171 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1172 snd_seq_client_info_get_name(cinfo),
1173 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1174 snd_seq_port_info_get_port(pinfo),
1175 snd_seq_port_info_get_name(pinfo),
1176 type);
1178 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1179 return;
1180 if (!type)
1181 return;
1183 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1185 /* Manufac ID. We do not have access to this with soundcard.h
1186 * Does not seem to be a problem, because in mmsystem.h only
1187 * Microsoft's ID is listed.
1189 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1190 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1191 /* Product Version. We simply say "1" */
1192 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1194 /* FIXME Do we have this information?
1195 * Assuming the soundcards can handle
1196 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1197 * not MIDICAPS_CACHE.
1199 MidiInDev[MIDM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1201 /* Try to use both client and port names, if this is too long take the port name only.
1202 In the second case the port name should be explicit enough due to its big size.
1204 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1205 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1206 } else {
1207 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1208 midiPortName[MAXPNAMELEN-1] = 0;
1210 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1211 MidiInDev[MIDM_NumDevs].caps.szPname,
1212 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1213 MidiInDev[MIDM_NumDevs].state = 0;
1215 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1216 "\tALSA info: midi dev-type=%x, capa=0\n",
1217 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1218 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1219 type);
1221 MIDM_NumDevs++;
1225 #endif /* HAVE_ALSA */
1228 /*======================================================================*
1229 * MIDI entry points *
1230 *======================================================================*/
1232 /**************************************************************************
1233 * ALSA_MidiInit [internal]
1235 * Initializes the MIDI devices information variables
1237 LONG ALSA_MidiInit(void)
1239 #ifdef HAVE_ALSA
1240 static BOOL bInitDone = FALSE;
1241 snd_seq_client_info_t *cinfo;
1242 snd_seq_port_info_t *pinfo;
1244 if (bInitDone)
1245 return TRUE;
1247 TRACE("Initializing the MIDI variables.\n");
1248 bInitDone = TRUE;
1250 /* try to open device */
1251 if (midiOpenSeq(0) == -1) {
1252 return TRUE;
1255 #if 0 /* Debug purpose */
1256 snd_lib_error_set_handler(error_handler);
1257 #endif
1258 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1259 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1261 /* First, search for all internal midi devices */
1262 snd_seq_client_info_set_client(cinfo, -1);
1263 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1264 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1265 snd_seq_port_info_set_port(pinfo, -1);
1266 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1267 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1268 unsigned int type = snd_seq_port_info_get_type(pinfo);
1269 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1270 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1274 /* Second, search for all external ports */
1275 snd_seq_client_info_set_client(cinfo, -1);
1276 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1277 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1278 snd_seq_port_info_set_port(pinfo, -1);
1279 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1280 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1281 unsigned int type = snd_seq_port_info_get_type(pinfo);
1282 if (type & SND_SEQ_PORT_TYPE_PORT)
1283 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1287 /* close file and exit */
1288 midiCloseSeq();
1289 HeapFree( GetProcessHeap(), 0, cinfo );
1290 HeapFree( GetProcessHeap(), 0, pinfo );
1292 TRACE("End\n");
1293 #endif
1294 return TRUE;
1297 /**************************************************************************
1298 * midMessage (WINEALSA.@)
1300 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1301 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1303 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1304 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1305 switch (wMsg) {
1306 #ifdef HAVE_ALSA
1307 case DRVM_INIT:
1308 case DRVM_EXIT:
1309 case DRVM_ENABLE:
1310 case DRVM_DISABLE:
1311 /* FIXME: Pretend this is supported */
1312 return 0;
1313 case MIDM_OPEN:
1314 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1315 case MIDM_CLOSE:
1316 return midClose(wDevID);
1317 case MIDM_ADDBUFFER:
1318 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1319 case MIDM_PREPARE:
1320 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1321 case MIDM_UNPREPARE:
1322 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1323 case MIDM_GETDEVCAPS:
1324 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1325 case MIDM_GETNUMDEVS:
1326 return MIDM_NumDevs;
1327 case MIDM_RESET:
1328 return midReset(wDevID);
1329 case MIDM_START:
1330 return midStart(wDevID);
1331 case MIDM_STOP:
1332 return midStop(wDevID);
1333 #endif
1334 default:
1335 TRACE("Unsupported message\n");
1337 return MMSYSERR_NOTSUPPORTED;
1340 /**************************************************************************
1341 * modMessage (WINEALSA.@)
1343 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1344 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1346 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1347 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1349 switch (wMsg) {
1350 #ifdef HAVE_ALSA
1351 case DRVM_INIT:
1352 case DRVM_EXIT:
1353 case DRVM_ENABLE:
1354 case DRVM_DISABLE:
1355 /* FIXME: Pretend this is supported */
1356 return 0;
1357 case MODM_OPEN:
1358 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1359 case MODM_CLOSE:
1360 return modClose(wDevID);
1361 case MODM_DATA:
1362 return modData(wDevID, dwParam1);
1363 case MODM_LONGDATA:
1364 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1365 case MODM_PREPARE:
1366 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1367 case MODM_UNPREPARE:
1368 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1369 case MODM_GETDEVCAPS:
1370 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1371 case MODM_GETNUMDEVS:
1372 return MODM_NumDevs;
1373 case MODM_GETVOLUME:
1374 return 0;
1375 case MODM_SETVOLUME:
1376 return 0;
1377 case MODM_RESET:
1378 return modReset(wDevID);
1379 #endif
1380 default:
1381 TRACE("Unsupported message\n");
1383 return MMSYSERR_NOTSUPPORTED;
1386 /*-----------------------------------------------------------------------*/