kernel32: Change all functions to use CDECL.
[wine/wine64.git] / dlls / winealsa.drv / midi.c
blobc1bcb1067a7b864d47a23fee05de393f8facfb32
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 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 pos = 0;
378 int len = ev->data.ext.len;
379 LPBYTE ptr = (BYTE*) ev->data.ext.ptr;
380 LPMIDIHDR lpMidiHdr;
382 EnterCriticalSection(&crit_sect);
383 while (len) {
384 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
385 int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
386 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
387 lpMidiHdr->dwBytesRecorded += copylen;
388 len -= copylen;
389 pos += copylen;
390 /* We check if we reach the end of buffer or the end of sysex before notifying
391 * to handle the case where ALSA splitted the sysex into several events */
392 if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
393 (*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
394 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
395 lpMidiHdr->dwFlags |= MHDR_DONE;
396 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
397 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime) != MMSYSERR_NOERROR)
398 WARN("Couldn't notify client\n");
400 } else {
401 FIXME("Sysex data received but no buffer to store it!\n");
402 break;
405 LeaveCriticalSection(&crit_sect);
407 break;
408 case SND_SEQ_EVENT_SENSING:
409 /* Noting to do */
410 break;
411 default:
412 FIXME("Unhandled event received, type = %x\n", ev->type);
413 break;
415 if (toSend != 0) {
416 TRACE("Sending event %08x (from %d %d)\n", toSend, ev->source.client, ev->source.port);
417 if (MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime) != MMSYSERR_NOERROR) {
418 WARN("Couldn't notify client\n");
422 snd_seq_free_event(ev);
423 } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
425 HeapFree(GetProcessHeap(), 0, pfd);
427 return 0;
430 /**************************************************************************
431 * midGetDevCaps [internal]
433 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
435 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
437 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
438 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
440 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
442 return MMSYSERR_NOERROR;
446 /**************************************************************************
447 * midOpen [internal]
449 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
451 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
453 if (lpDesc == NULL) {
454 WARN("Invalid Parameter !\n");
455 return MMSYSERR_INVALPARAM;
458 /* FIXME :
459 * how to check that content of lpDesc is correct ?
461 if (wDevID >= MIDM_NumDevs) {
462 WARN("wDevID too large (%u) !\n", wDevID);
463 return MMSYSERR_BADDEVICEID;
465 if (MidiInDev[wDevID].state == -1) {
466 WARN("device disabled\n");
467 return MIDIERR_NODEVICE;
469 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
470 WARN("device already open !\n");
471 return MMSYSERR_ALLOCATED;
473 if ((dwFlags & MIDI_IO_STATUS) != 0) {
474 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
475 dwFlags &= ~MIDI_IO_STATUS;
477 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
478 FIXME("Bad dwFlags\n");
479 return MMSYSERR_INVALFLAG;
482 if (midiOpenSeq(1) < 0) {
483 return MMSYSERR_ERROR;
486 /* Connect our app port to the device port */
487 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
488 return MMSYSERR_NOTENABLED;
490 TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
492 if (numStartedMidiIn++ == 0) {
493 end_thread = 0;
494 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
495 if (!hThread) {
496 numStartedMidiIn = 0;
497 WARN("Couldn't create thread for midi-in\n");
498 midiCloseSeq();
499 return MMSYSERR_ERROR;
501 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
502 TRACE("Created thread for midi-in\n");
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 if (MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
516 WARN("can't notify client !\n");
517 return MMSYSERR_INVALPARAM;
519 return MMSYSERR_NOERROR;
522 /**************************************************************************
523 * midClose [internal]
525 static DWORD midClose(WORD wDevID)
527 int ret = MMSYSERR_NOERROR;
529 TRACE("(%04X);\n", wDevID);
531 if (wDevID >= MIDM_NumDevs) {
532 WARN("wDevID too big (%u) !\n", wDevID);
533 return MMSYSERR_BADDEVICEID;
535 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
536 WARN("device not opened !\n");
537 return MMSYSERR_ERROR;
539 if (MidiInDev[wDevID].lpQueueHdr != 0) {
540 return MIDIERR_STILLPLAYING;
543 if (midiSeq == NULL) {
544 WARN("ooops !\n");
545 return MMSYSERR_ERROR;
547 if (--numStartedMidiIn == 0) {
548 TRACE("Stopping thread for midi-in\n");
549 end_thread = 1;
550 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
551 WARN("Thread end not signaled, force termination\n");
552 TerminateThread(hThread, 0);
554 TRACE("Stopped thread for midi-in\n");
557 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
558 midiCloseSeq();
560 MidiInDev[wDevID].bufsize = 0;
561 if (MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
562 WARN("can't notify client !\n");
563 ret = MMSYSERR_INVALPARAM;
565 MidiInDev[wDevID].midiDesc.hMidi = 0;
567 return ret;
571 /**************************************************************************
572 * midAddBuffer [internal]
574 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
576 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
578 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
579 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
581 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
582 if (sizeof(MIDIHDR) > dwSize) return MMSYSERR_INVALPARAM;
583 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
584 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
585 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
587 EnterCriticalSection(&crit_sect);
588 lpMidiHdr->dwFlags &= ~WHDR_DONE;
589 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
590 lpMidiHdr->dwBytesRecorded = 0;
591 lpMidiHdr->lpNext = 0;
592 if (MidiInDev[wDevID].lpQueueHdr == 0) {
593 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
594 } else {
595 LPMIDIHDR ptr;
597 for (ptr = MidiInDev[wDevID].lpQueueHdr;
598 ptr->lpNext != 0;
599 ptr = (LPMIDIHDR)ptr->lpNext);
600 ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
602 LeaveCriticalSection(&crit_sect);
604 return MMSYSERR_NOERROR;
607 /**************************************************************************
608 * midPrepare [internal]
610 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
612 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
614 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
615 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
616 lpMidiHdr->dwBufferLength >= 0x10000ul)
617 return MMSYSERR_INVALPARAM;
619 lpMidiHdr->lpNext = 0;
620 lpMidiHdr->dwFlags |= MHDR_PREPARED;
621 lpMidiHdr->dwBytesRecorded = 0;
623 return MMSYSERR_NOERROR;
626 /**************************************************************************
627 * midUnprepare [internal]
629 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
631 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
633 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
634 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
636 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
637 lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
638 return MMSYSERR_INVALPARAM;
640 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
641 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
643 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
645 return MMSYSERR_NOERROR;
648 /**************************************************************************
649 * midReset [internal]
651 static DWORD midReset(WORD wDevID)
653 DWORD dwTime = GetTickCount();
655 TRACE("(%04X);\n", wDevID);
657 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
658 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
660 EnterCriticalSection(&crit_sect);
661 while (MidiInDev[wDevID].lpQueueHdr) {
662 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
663 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
664 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
665 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
666 (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
667 WARN("Couldn't notify client\n");
669 MidiInDev[wDevID].lpQueueHdr = (LPMIDIHDR)MidiInDev[wDevID].lpQueueHdr->lpNext;
671 LeaveCriticalSection(&crit_sect);
673 return MMSYSERR_NOERROR;
676 /**************************************************************************
677 * midStart [internal]
679 static DWORD midStart(WORD wDevID)
681 TRACE("(%04X);\n", wDevID);
683 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
684 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
686 MidiInDev[wDevID].state = 1;
687 MidiInDev[wDevID].startTime = GetTickCount();
688 return MMSYSERR_NOERROR;
691 /**************************************************************************
692 * midStop [internal]
694 static DWORD midStop(WORD wDevID)
696 TRACE("(%04X);\n", wDevID);
698 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
699 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
701 MidiInDev[wDevID].state = 0;
702 return MMSYSERR_NOERROR;
705 /**************************************************************************
706 * modGetDevCaps [internal]
708 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
710 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
712 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
713 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
715 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
717 return MMSYSERR_NOERROR;
720 /**************************************************************************
721 * modOpen [internal]
723 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
725 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
726 if (lpDesc == NULL) {
727 WARN("Invalid Parameter !\n");
728 return MMSYSERR_INVALPARAM;
730 if (wDevID >= MODM_NumDevs) {
731 TRACE("MAX_MIDIOUTDRV reached !\n");
732 return MMSYSERR_BADDEVICEID;
734 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
735 WARN("device already open !\n");
736 return MMSYSERR_ALLOCATED;
738 if (!MidiOutDev[wDevID].bEnabled) {
739 WARN("device disabled !\n");
740 return MIDIERR_NODEVICE;
742 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
743 WARN("bad dwFlags\n");
744 return MMSYSERR_INVALFLAG;
746 if (!MidiOutDev[wDevID].bEnabled) {
747 TRACE("disabled wDevID\n");
748 return MMSYSERR_NOTENABLED;
751 MidiOutDev[wDevID].lpExtra = 0;
753 switch (MidiOutDev[wDevID].caps.wTechnology) {
754 case MOD_FMSYNTH:
755 case MOD_MIDIPORT:
756 case MOD_SYNTH:
757 if (midiOpenSeq(1) < 0) {
758 return MMSYSERR_ALLOCATED;
760 break;
761 default:
762 WARN("Technology not supported (yet) %d !\n",
763 MidiOutDev[wDevID].caps.wTechnology);
764 return MMSYSERR_NOTENABLED;
767 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
769 MidiOutDev[wDevID].lpQueueHdr = NULL;
770 MidiOutDev[wDevID].dwTotalPlayed = 0;
771 MidiOutDev[wDevID].bufsize = 0x3FFF;
772 MidiOutDev[wDevID].midiDesc = *lpDesc;
774 /* Connect our app port to the device port */
775 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
776 return MMSYSERR_NOTENABLED;
778 if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
779 WARN("can't notify client !\n");
780 return MMSYSERR_INVALPARAM;
782 TRACE("Successful !\n");
783 return MMSYSERR_NOERROR;
787 /**************************************************************************
788 * modClose [internal]
790 static DWORD modClose(WORD wDevID)
792 int ret = MMSYSERR_NOERROR;
794 TRACE("(%04X);\n", wDevID);
796 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
797 WARN("device not opened !\n");
798 return MMSYSERR_ERROR;
800 /* FIXME: should test that no pending buffer is still in the queue for
801 * playing */
803 if (midiSeq == NULL) {
804 WARN("can't close !\n");
805 return MMSYSERR_ERROR;
808 switch (MidiOutDev[wDevID].caps.wTechnology) {
809 case MOD_FMSYNTH:
810 case MOD_MIDIPORT:
811 case MOD_SYNTH:
812 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
813 midiCloseSeq();
814 break;
815 default:
816 WARN("Technology not supported (yet) %d !\n",
817 MidiOutDev[wDevID].caps.wTechnology);
818 return MMSYSERR_NOTENABLED;
821 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
822 MidiOutDev[wDevID].lpExtra = 0;
824 MidiOutDev[wDevID].bufsize = 0;
825 if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
826 WARN("can't notify client !\n");
827 ret = MMSYSERR_INVALPARAM;
829 MidiOutDev[wDevID].midiDesc.hMidi = 0;
830 return ret;
833 /**************************************************************************
834 * modData [internal]
836 static DWORD modData(WORD wDevID, DWORD dwParam)
838 BYTE evt = LOBYTE(LOWORD(dwParam));
839 BYTE d1 = HIBYTE(LOWORD(dwParam));
840 BYTE d2 = LOBYTE(HIWORD(dwParam));
842 TRACE("(%04X, %08X);\n", wDevID, dwParam);
844 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
845 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
847 if (midiSeq == NULL) {
848 WARN("can't play !\n");
849 return MIDIERR_NODEVICE;
851 switch (MidiOutDev[wDevID].caps.wTechnology) {
852 case MOD_SYNTH:
853 case MOD_MIDIPORT:
855 int handled = 1; /* Assume event is handled */
856 snd_seq_event_t event;
857 snd_seq_ev_clear(&event);
858 snd_seq_ev_set_direct(&event);
859 snd_seq_ev_set_source(&event, port_out);
860 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
862 switch (evt & 0xF0) {
863 case MIDI_CMD_NOTE_OFF:
864 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
865 break;
866 case MIDI_CMD_NOTE_ON:
867 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
868 break;
869 case MIDI_CMD_NOTE_PRESSURE:
870 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
871 break;
872 case MIDI_CMD_CONTROL:
873 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
874 break;
875 case MIDI_CMD_BENDER:
876 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
877 break;
878 case MIDI_CMD_PGM_CHANGE:
879 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
880 break;
881 case MIDI_CMD_CHANNEL_PRESSURE:
882 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
883 break;
884 case MIDI_CMD_COMMON_SYSEX:
885 switch (evt & 0x0F) {
886 case 0x00: /* System Exclusive, don't do it on modData,
887 * should require modLongData*/
888 case 0x01: /* Undefined */
889 case 0x04: /* Undefined. */
890 case 0x05: /* Undefined. */
891 case 0x07: /* End of Exclusive. */
892 case 0x09: /* Undefined. */
893 case 0x0D: /* Undefined. */
894 handled = 0;
895 break;
896 case 0x06: /* Tune Request */
897 case 0x08: /* Timing Clock. */
898 case 0x0A: /* Start. */
899 case 0x0B: /* Continue */
900 case 0x0C: /* Stop */
901 case 0x0E: /* Active Sensing. */
902 /* FIXME: Is this function suitable for these purposes
903 (and also Song Select and Song Position Pointer) */
904 snd_seq_ev_set_sysex(&event, 1, &evt);
905 break;
906 case 0x0F: /* Reset */
907 /* snd_seq_ev_set_sysex(&event, 1, &evt);
908 this other way may be better */
910 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
911 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
913 break;
914 case 0x03: /* Song Select. */
916 BYTE buf[2];
917 buf[0] = evt;
918 buf[1] = d1;
919 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
921 break;
922 case 0x02: /* Song Position Pointer. */
924 BYTE buf[3];
925 buf[0] = evt;
926 buf[1] = d1;
927 buf[2] = d2;
928 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
930 break;
932 break;
934 if (handled)
935 snd_seq_event_output_direct(midiSeq, &event);
937 break;
938 default:
939 WARN("Technology not supported (yet) %d !\n",
940 MidiOutDev[wDevID].caps.wTechnology);
941 return MMSYSERR_NOTENABLED;
944 return MMSYSERR_NOERROR;
947 /**************************************************************************
948 * modLongData [internal]
950 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
952 int len_add = 0;
953 LPBYTE lpData, lpNewData = NULL;
954 snd_seq_event_t event;
956 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
958 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
959 * but it seems to be used only for midi input.
960 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
963 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
964 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
966 if (midiSeq == NULL) {
967 WARN("can't play !\n");
968 return MIDIERR_NODEVICE;
971 lpData = (LPBYTE) lpMidiHdr->lpData;
973 if (lpData == NULL)
974 return MIDIERR_UNPREPARED;
975 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
976 return MIDIERR_UNPREPARED;
977 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
978 return MIDIERR_STILLPLAYING;
979 lpMidiHdr->dwFlags &= ~MHDR_DONE;
980 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
982 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
983 * data, or can it also contain raw MIDI data, to be split up and sent to
984 * modShortData() ?
985 * If the latest is true, then the following WARNing will fire up
987 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
988 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
989 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
992 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
993 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
994 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
995 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
997 switch (MidiOutDev[wDevID].caps.wTechnology) {
998 case MOD_FMSYNTH:
999 /* FIXME: I don't think there is much to do here */
1000 break;
1001 case MOD_MIDIPORT:
1002 if (lpData[0] != 0xF0) {
1003 /* Send start of System Exclusive */
1004 len_add = 1;
1005 lpData[0] = 0xF0;
1006 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
1007 WARN("Adding missing 0xF0 marker at the beginning of "
1008 "system exclusive byte stream\n");
1010 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
1011 /* Send end of System Exclusive */
1012 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
1013 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
1014 len_add++;
1015 WARN("Adding missing 0xF7 marker at the end of "
1016 "system exclusive byte stream\n");
1018 snd_seq_ev_clear(&event);
1019 snd_seq_ev_set_direct(&event);
1020 snd_seq_ev_set_source(&event, port_out);
1021 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1022 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1023 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1024 snd_seq_event_output_direct(midiSeq, &event);
1025 if (lpNewData)
1026 HeapFree(GetProcessHeap(), 0, lpData);
1027 break;
1028 default:
1029 WARN("Technology not supported (yet) %d !\n",
1030 MidiOutDev[wDevID].caps.wTechnology);
1031 HeapFree(GetProcessHeap(), 0, lpNewData);
1032 return MMSYSERR_NOTENABLED;
1035 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1036 lpMidiHdr->dwFlags |= MHDR_DONE;
1037 if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1038 WARN("can't notify client !\n");
1039 return MMSYSERR_INVALPARAM;
1041 return MMSYSERR_NOERROR;
1044 /**************************************************************************
1045 * modPrepare [internal]
1047 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1049 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1051 if (midiSeq == NULL) {
1052 WARN("can't prepare !\n");
1053 return MMSYSERR_NOTENABLED;
1056 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1057 * asks to prepare MIDIHDR which dwFlags != 0.
1058 * So at least check for the inqueue flag
1060 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1061 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
1062 lpMidiHdr->dwBufferLength >= 0x10000ul) {
1063 WARN("%p %p %08x %d\n", lpMidiHdr, lpMidiHdr ? lpMidiHdr->lpData : NULL,
1064 lpMidiHdr ? lpMidiHdr->dwFlags : 0, dwSize);
1065 return MMSYSERR_INVALPARAM;
1068 lpMidiHdr->lpNext = 0;
1069 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1070 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1071 return MMSYSERR_NOERROR;
1074 /**************************************************************************
1075 * modUnprepare [internal]
1077 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1079 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1081 if (midiSeq == NULL) {
1082 WARN("can't unprepare !\n");
1083 return MMSYSERR_NOTENABLED;
1086 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1087 return MMSYSERR_INVALPARAM;
1088 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1089 return MIDIERR_STILLPLAYING;
1090 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1091 return MMSYSERR_NOERROR;
1094 /**************************************************************************
1095 * modReset [internal]
1097 static DWORD modReset(WORD wDevID)
1099 unsigned chn;
1101 TRACE("(%04X);\n", wDevID);
1103 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1104 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1106 /* stop all notes */
1107 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1108 * it's channel dependent...
1110 for (chn = 0; chn < 16; chn++) {
1111 /* turn off every note */
1112 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1113 /* remove sustain on all channels */
1114 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1116 /* FIXME: the LongData buffers must also be returned to the app */
1117 return MMSYSERR_NOERROR;
1121 /**************************************************************************
1122 * ALSA_AddMidiPort [internal]
1124 * Helper for ALSA_MidiInit
1126 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1128 char midiPortName[MAXPNAMELEN];
1130 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1131 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1132 snd_seq_client_info_get_name(cinfo),
1133 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1134 snd_seq_port_info_get_port(pinfo),
1135 snd_seq_port_info_get_name(pinfo),
1136 type);
1138 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1139 return;
1140 if (!type)
1141 return;
1143 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1145 /* Manufac ID. We do not have access to this with soundcard.h
1146 * Does not seem to be a problem, because in mmsystem.h only
1147 * Microsoft's ID is listed.
1149 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1150 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1151 /* Product Version. We simply say "1" */
1152 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1153 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1155 /* FIXME Do we have this information?
1156 * Assuming the soundcards can handle
1157 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1158 * not MIDICAPS_CACHE.
1160 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1162 /* Try to use both client and port names, if this is too long take the port name only.
1163 In the second case the port name should be explicit enough due to its big size.
1165 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1166 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1167 } else {
1168 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1169 midiPortName[MAXPNAMELEN-1] = 0;
1171 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1172 MidiOutDev[MODM_NumDevs].caps.szPname,
1173 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1175 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1176 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1178 /* FIXME Is it possible to know the maximum
1179 * number of simultaneous notes of a soundcard ?
1180 * I believe we don't have this information, but
1181 * it's probably equal or more than wVoices
1183 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1184 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1186 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1187 "\tALSA info: midi dev-type=%x, capa=0\n",
1188 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1189 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1190 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1191 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1192 type);
1194 MODM_NumDevs++;
1196 if (cap & SND_SEQ_PORT_CAP_READ) {
1197 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1198 snd_seq_client_info_get_name(cinfo),
1199 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1200 snd_seq_port_info_get_port(pinfo),
1201 snd_seq_port_info_get_name(pinfo),
1202 type);
1204 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1205 return;
1206 if (!type)
1207 return;
1209 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1211 /* Manufac ID. We do not have access to this with soundcard.h
1212 * Does not seem to be a problem, because in mmsystem.h only
1213 * Microsoft's ID is listed.
1215 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1216 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1217 /* Product Version. We simply say "1" */
1218 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1220 /* FIXME Do we have this information?
1221 * Assuming the soundcards can handle
1222 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1223 * not MIDICAPS_CACHE.
1225 MidiInDev[MIDM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1227 /* Try to use both client and port names, if this is too long take the port name only.
1228 In the second case the port name should be explicit enough due to its big size.
1230 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1231 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1232 } else {
1233 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1234 midiPortName[MAXPNAMELEN-1] = 0;
1236 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1237 MidiInDev[MIDM_NumDevs].caps.szPname,
1238 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1239 MidiInDev[MIDM_NumDevs].state = 0;
1241 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1242 "\tALSA info: midi dev-type=%x, capa=0\n",
1243 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1244 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1245 type);
1247 MIDM_NumDevs++;
1251 #endif /* HAVE_ALSA */
1254 /*======================================================================*
1255 * MIDI entry points *
1256 *======================================================================*/
1258 /**************************************************************************
1259 * ALSA_MidiInit [internal]
1261 * Initializes the MIDI devices information variables
1263 LONG ALSA_MidiInit(void)
1265 #ifdef HAVE_ALSA
1266 static BOOL bInitDone = FALSE;
1267 snd_seq_client_info_t *cinfo;
1268 snd_seq_port_info_t *pinfo;
1270 if (bInitDone)
1271 return TRUE;
1273 TRACE("Initializing the MIDI variables.\n");
1274 bInitDone = TRUE;
1276 /* try to open device */
1277 if (midiOpenSeq(0) == -1) {
1278 return TRUE;
1281 #if 0 /* Debug purpose */
1282 snd_lib_error_set_handler(error_handler);
1283 #endif
1284 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1285 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1287 /* First, search for all internal midi devices */
1288 snd_seq_client_info_set_client(cinfo, -1);
1289 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1290 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1291 snd_seq_port_info_set_port(pinfo, -1);
1292 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1293 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1294 unsigned int type = snd_seq_port_info_get_type(pinfo);
1295 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1296 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1300 /* Second, search for all external ports */
1301 snd_seq_client_info_set_client(cinfo, -1);
1302 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1303 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1304 snd_seq_port_info_set_port(pinfo, -1);
1305 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1306 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1307 unsigned int type = snd_seq_port_info_get_type(pinfo);
1308 if (type & SND_SEQ_PORT_TYPE_PORT)
1309 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1313 /* close file and exit */
1314 midiCloseSeq();
1315 HeapFree( GetProcessHeap(), 0, cinfo );
1316 HeapFree( GetProcessHeap(), 0, pinfo );
1318 TRACE("End\n");
1319 #endif
1320 return TRUE;
1323 /**************************************************************************
1324 * midMessage (WINEALSA.@)
1326 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1327 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1329 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1330 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1331 switch (wMsg) {
1332 #ifdef HAVE_ALSA
1333 case DRVM_INIT:
1334 case DRVM_EXIT:
1335 case DRVM_ENABLE:
1336 case DRVM_DISABLE:
1337 /* FIXME: Pretend this is supported */
1338 return 0;
1339 case MIDM_OPEN:
1340 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1341 case MIDM_CLOSE:
1342 return midClose(wDevID);
1343 case MIDM_ADDBUFFER:
1344 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1345 case MIDM_PREPARE:
1346 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1347 case MIDM_UNPREPARE:
1348 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1349 case MIDM_GETDEVCAPS:
1350 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1351 case MIDM_GETNUMDEVS:
1352 return MIDM_NumDevs;
1353 case MIDM_RESET:
1354 return midReset(wDevID);
1355 case MIDM_START:
1356 return midStart(wDevID);
1357 case MIDM_STOP:
1358 return midStop(wDevID);
1359 #endif
1360 default:
1361 TRACE("Unsupported message\n");
1363 return MMSYSERR_NOTSUPPORTED;
1366 /**************************************************************************
1367 * modMessage (WINEALSA.@)
1369 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1370 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1372 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1373 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1375 switch (wMsg) {
1376 #ifdef HAVE_ALSA
1377 case DRVM_INIT:
1378 case DRVM_EXIT:
1379 case DRVM_ENABLE:
1380 case DRVM_DISABLE:
1381 /* FIXME: Pretend this is supported */
1382 return 0;
1383 case MODM_OPEN:
1384 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1385 case MODM_CLOSE:
1386 return modClose(wDevID);
1387 case MODM_DATA:
1388 return modData(wDevID, dwParam1);
1389 case MODM_LONGDATA:
1390 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1391 case MODM_PREPARE:
1392 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1393 case MODM_UNPREPARE:
1394 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1395 case MODM_GETDEVCAPS:
1396 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1397 case MODM_GETNUMDEVS:
1398 return MODM_NumDevs;
1399 case MODM_GETVOLUME:
1400 return 0;
1401 case MODM_SETVOLUME:
1402 return 0;
1403 case MODM_RESET:
1404 return modReset(wDevID);
1405 #endif
1406 default:
1407 TRACE("Unsupported message\n");
1409 return MMSYSERR_NOTSUPPORTED;
1412 /*-----------------------------------------------------------------------*/