wordpad: Add Ukrainian translations.
[wine/hacks.git] / dlls / winealsa.drv / midi.c
blobb62a8852b5f958c4f9a4a995e9ccee0d75ab1292
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 = 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 split 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->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; ptr->lpNext != 0;
598 ptr = ptr->lpNext);
599 ptr->lpNext = lpMidiHdr;
601 LeaveCriticalSection(&crit_sect);
603 return MMSYSERR_NOERROR;
606 /**************************************************************************
607 * midPrepare [internal]
609 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
611 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
613 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
614 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
615 lpMidiHdr->dwBufferLength >= 0x10000ul)
616 return MMSYSERR_INVALPARAM;
618 lpMidiHdr->lpNext = 0;
619 lpMidiHdr->dwFlags |= MHDR_PREPARED;
620 lpMidiHdr->dwBytesRecorded = 0;
622 return MMSYSERR_NOERROR;
625 /**************************************************************************
626 * midUnprepare [internal]
628 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
630 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
632 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
633 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
635 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
636 lpMidiHdr->lpData == 0 || lpMidiHdr->dwBufferLength >= 0x10000ul)
637 return MMSYSERR_INVALPARAM;
639 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
640 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
642 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
644 return MMSYSERR_NOERROR;
647 /**************************************************************************
648 * midReset [internal]
650 static DWORD midReset(WORD wDevID)
652 DWORD dwTime = GetTickCount();
654 TRACE("(%04X);\n", wDevID);
656 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
657 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
659 EnterCriticalSection(&crit_sect);
660 while (MidiInDev[wDevID].lpQueueHdr) {
661 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
662 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
663 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
664 if (MIDI_NotifyClient(wDevID, MIM_LONGDATA,
665 (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
666 WARN("Couldn't notify client\n");
668 MidiInDev[wDevID].lpQueueHdr = MidiInDev[wDevID].lpQueueHdr->lpNext;
670 LeaveCriticalSection(&crit_sect);
672 return MMSYSERR_NOERROR;
675 /**************************************************************************
676 * midStart [internal]
678 static DWORD midStart(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 = 1;
686 MidiInDev[wDevID].startTime = GetTickCount();
687 return MMSYSERR_NOERROR;
690 /**************************************************************************
691 * midStop [internal]
693 static DWORD midStop(WORD wDevID)
695 TRACE("(%04X);\n", wDevID);
697 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
698 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
700 MidiInDev[wDevID].state = 0;
701 return MMSYSERR_NOERROR;
704 /**************************************************************************
705 * modGetDevCaps [internal]
707 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
709 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
711 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
712 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
714 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
716 return MMSYSERR_NOERROR;
719 /**************************************************************************
720 * modOpen [internal]
722 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
724 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
725 if (lpDesc == NULL) {
726 WARN("Invalid Parameter !\n");
727 return MMSYSERR_INVALPARAM;
729 if (wDevID >= MODM_NumDevs) {
730 TRACE("MAX_MIDIOUTDRV reached !\n");
731 return MMSYSERR_BADDEVICEID;
733 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
734 WARN("device already open !\n");
735 return MMSYSERR_ALLOCATED;
737 if (!MidiOutDev[wDevID].bEnabled) {
738 WARN("device disabled !\n");
739 return MIDIERR_NODEVICE;
741 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
742 WARN("bad dwFlags\n");
743 return MMSYSERR_INVALFLAG;
745 if (!MidiOutDev[wDevID].bEnabled) {
746 TRACE("disabled wDevID\n");
747 return MMSYSERR_NOTENABLED;
750 MidiOutDev[wDevID].lpExtra = 0;
752 switch (MidiOutDev[wDevID].caps.wTechnology) {
753 case MOD_FMSYNTH:
754 case MOD_MIDIPORT:
755 case MOD_SYNTH:
756 if (midiOpenSeq(1) < 0) {
757 return MMSYSERR_ALLOCATED;
759 break;
760 default:
761 WARN("Technology not supported (yet) %d !\n",
762 MidiOutDev[wDevID].caps.wTechnology);
763 return MMSYSERR_NOTENABLED;
766 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
768 MidiOutDev[wDevID].lpQueueHdr = NULL;
769 MidiOutDev[wDevID].dwTotalPlayed = 0;
770 MidiOutDev[wDevID].bufsize = 0x3FFF;
771 MidiOutDev[wDevID].midiDesc = *lpDesc;
773 /* Connect our app port to the device port */
774 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
775 return MMSYSERR_NOTENABLED;
777 if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
778 WARN("can't notify client !\n");
779 return MMSYSERR_INVALPARAM;
781 TRACE("Successful !\n");
782 return MMSYSERR_NOERROR;
786 /**************************************************************************
787 * modClose [internal]
789 static DWORD modClose(WORD wDevID)
791 int ret = MMSYSERR_NOERROR;
793 TRACE("(%04X);\n", wDevID);
795 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
796 WARN("device not opened !\n");
797 return MMSYSERR_ERROR;
799 /* FIXME: should test that no pending buffer is still in the queue for
800 * playing */
802 if (midiSeq == NULL) {
803 WARN("can't close !\n");
804 return MMSYSERR_ERROR;
807 switch (MidiOutDev[wDevID].caps.wTechnology) {
808 case MOD_FMSYNTH:
809 case MOD_MIDIPORT:
810 case MOD_SYNTH:
811 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
812 midiCloseSeq();
813 break;
814 default:
815 WARN("Technology not supported (yet) %d !\n",
816 MidiOutDev[wDevID].caps.wTechnology);
817 return MMSYSERR_NOTENABLED;
820 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
821 MidiOutDev[wDevID].lpExtra = 0;
823 MidiOutDev[wDevID].bufsize = 0;
824 if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
825 WARN("can't notify client !\n");
826 ret = MMSYSERR_INVALPARAM;
828 MidiOutDev[wDevID].midiDesc.hMidi = 0;
829 return ret;
832 /**************************************************************************
833 * modData [internal]
835 static DWORD modData(WORD wDevID, DWORD dwParam)
837 BYTE evt = LOBYTE(LOWORD(dwParam));
838 BYTE d1 = HIBYTE(LOWORD(dwParam));
839 BYTE d2 = LOBYTE(HIWORD(dwParam));
841 TRACE("(%04X, %08X);\n", wDevID, dwParam);
843 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
844 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
846 if (midiSeq == NULL) {
847 WARN("can't play !\n");
848 return MIDIERR_NODEVICE;
850 switch (MidiOutDev[wDevID].caps.wTechnology) {
851 case MOD_SYNTH:
852 case MOD_MIDIPORT:
854 int handled = 1; /* Assume event is handled */
855 snd_seq_event_t event;
856 snd_seq_ev_clear(&event);
857 snd_seq_ev_set_direct(&event);
858 snd_seq_ev_set_source(&event, port_out);
859 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
861 switch (evt & 0xF0) {
862 case MIDI_CMD_NOTE_OFF:
863 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
864 break;
865 case MIDI_CMD_NOTE_ON:
866 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
867 break;
868 case MIDI_CMD_NOTE_PRESSURE:
869 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
870 break;
871 case MIDI_CMD_CONTROL:
872 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
873 break;
874 case MIDI_CMD_BENDER:
875 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
876 break;
877 case MIDI_CMD_PGM_CHANGE:
878 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
879 break;
880 case MIDI_CMD_CHANNEL_PRESSURE:
881 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
882 break;
883 case MIDI_CMD_COMMON_SYSEX:
884 switch (evt & 0x0F) {
885 case 0x00: /* System Exclusive, don't do it on modData,
886 * should require modLongData*/
887 case 0x01: /* Undefined */
888 case 0x04: /* Undefined. */
889 case 0x05: /* Undefined. */
890 case 0x07: /* End of Exclusive. */
891 case 0x09: /* Undefined. */
892 case 0x0D: /* Undefined. */
893 handled = 0;
894 break;
895 case 0x06: /* Tune Request */
896 case 0x08: /* Timing Clock. */
897 case 0x0A: /* Start. */
898 case 0x0B: /* Continue */
899 case 0x0C: /* Stop */
900 case 0x0E: /* Active Sensing. */
901 /* FIXME: Is this function suitable for these purposes
902 (and also Song Select and Song Position Pointer) */
903 snd_seq_ev_set_sysex(&event, 1, &evt);
904 break;
905 case 0x0F: /* Reset */
906 /* snd_seq_ev_set_sysex(&event, 1, &evt);
907 this other way may be better */
909 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
910 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
912 break;
913 case 0x03: /* Song Select. */
915 BYTE buf[2];
916 buf[0] = evt;
917 buf[1] = d1;
918 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
920 break;
921 case 0x02: /* Song Position Pointer. */
923 BYTE buf[3];
924 buf[0] = evt;
925 buf[1] = d1;
926 buf[2] = d2;
927 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
929 break;
931 break;
933 if (handled)
934 snd_seq_event_output_direct(midiSeq, &event);
936 break;
937 default:
938 WARN("Technology not supported (yet) %d !\n",
939 MidiOutDev[wDevID].caps.wTechnology);
940 return MMSYSERR_NOTENABLED;
943 return MMSYSERR_NOERROR;
946 /**************************************************************************
947 * modLongData [internal]
949 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
951 int len_add = 0;
952 LPBYTE lpData, lpNewData = NULL;
953 snd_seq_event_t event;
955 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
957 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
958 * but it seems to be used only for midi input.
959 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
962 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
963 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
965 if (midiSeq == NULL) {
966 WARN("can't play !\n");
967 return MIDIERR_NODEVICE;
970 lpData = (LPBYTE) lpMidiHdr->lpData;
972 if (lpData == NULL)
973 return MIDIERR_UNPREPARED;
974 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
975 return MIDIERR_UNPREPARED;
976 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
977 return MIDIERR_STILLPLAYING;
978 lpMidiHdr->dwFlags &= ~MHDR_DONE;
979 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
981 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
982 * data, or can it also contain raw MIDI data, to be split up and sent to
983 * modShortData() ?
984 * If the latest is true, then the following WARNing will fire up
986 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
987 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
988 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
991 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
992 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
993 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
994 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
996 switch (MidiOutDev[wDevID].caps.wTechnology) {
997 case MOD_FMSYNTH:
998 /* FIXME: I don't think there is much to do here */
999 break;
1000 case MOD_MIDIPORT:
1001 if (lpData[0] != 0xF0) {
1002 /* Send start of System Exclusive */
1003 len_add = 1;
1004 lpData[0] = 0xF0;
1005 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
1006 WARN("Adding missing 0xF0 marker at the beginning of "
1007 "system exclusive byte stream\n");
1009 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
1010 /* Send end of System Exclusive */
1011 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
1012 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
1013 len_add++;
1014 WARN("Adding missing 0xF7 marker at the end of "
1015 "system exclusive byte stream\n");
1017 snd_seq_ev_clear(&event);
1018 snd_seq_ev_set_direct(&event);
1019 snd_seq_ev_set_source(&event, port_out);
1020 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1021 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1022 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1023 snd_seq_event_output_direct(midiSeq, &event);
1024 if (lpNewData)
1025 HeapFree(GetProcessHeap(), 0, lpData);
1026 break;
1027 default:
1028 WARN("Technology not supported (yet) %d !\n",
1029 MidiOutDev[wDevID].caps.wTechnology);
1030 HeapFree(GetProcessHeap(), 0, lpNewData);
1031 return MMSYSERR_NOTENABLED;
1034 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1035 lpMidiHdr->dwFlags |= MHDR_DONE;
1036 if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
1037 WARN("can't notify client !\n");
1038 return MMSYSERR_INVALPARAM;
1040 return MMSYSERR_NOERROR;
1043 /**************************************************************************
1044 * modPrepare [internal]
1046 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1048 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1050 if (midiSeq == NULL) {
1051 WARN("can't prepare !\n");
1052 return MMSYSERR_NOTENABLED;
1055 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1056 * asks to prepare MIDIHDR which dwFlags != 0.
1057 * So at least check for the inqueue flag
1059 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
1060 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
1061 lpMidiHdr->dwBufferLength >= 0x10000ul) {
1062 WARN("%p %p %08x %d\n", lpMidiHdr, lpMidiHdr ? lpMidiHdr->lpData : NULL,
1063 lpMidiHdr ? lpMidiHdr->dwFlags : 0, dwSize);
1064 return MMSYSERR_INVALPARAM;
1067 lpMidiHdr->lpNext = 0;
1068 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1069 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1070 return MMSYSERR_NOERROR;
1073 /**************************************************************************
1074 * modUnprepare [internal]
1076 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1078 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1080 if (midiSeq == NULL) {
1081 WARN("can't unprepare !\n");
1082 return MMSYSERR_NOTENABLED;
1085 if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
1086 return MMSYSERR_INVALPARAM;
1087 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1088 return MIDIERR_STILLPLAYING;
1089 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1090 return MMSYSERR_NOERROR;
1093 /**************************************************************************
1094 * modReset [internal]
1096 static DWORD modReset(WORD wDevID)
1098 unsigned chn;
1100 TRACE("(%04X);\n", wDevID);
1102 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1103 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1105 /* stop all notes */
1106 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1107 * it's channel dependent...
1109 for (chn = 0; chn < 16; chn++) {
1110 /* turn off every note */
1111 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1112 /* remove sustain on all channels */
1113 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1115 /* FIXME: the LongData buffers must also be returned to the app */
1116 return MMSYSERR_NOERROR;
1120 /**************************************************************************
1121 * ALSA_AddMidiPort [internal]
1123 * Helper for ALSA_MidiInit
1125 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1127 char midiPortName[MAXPNAMELEN];
1129 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1130 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1131 snd_seq_client_info_get_name(cinfo),
1132 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1133 snd_seq_port_info_get_port(pinfo),
1134 snd_seq_port_info_get_name(pinfo),
1135 type);
1137 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1138 return;
1139 if (!type)
1140 return;
1142 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1144 /* Manufac ID. We do not have access to this with soundcard.h
1145 * Does not seem to be a problem, because in mmsystem.h only
1146 * Microsoft's ID is listed.
1148 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1149 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1150 /* Product Version. We simply say "1" */
1151 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1152 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1154 /* FIXME Do we have this information?
1155 * Assuming the soundcards can handle
1156 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1157 * not MIDICAPS_CACHE.
1159 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1161 /* Try to use both client and port names, if this is too long take the port name only.
1162 In the second case the port name should be explicit enough due to its big size.
1164 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1165 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1166 } else {
1167 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1168 midiPortName[MAXPNAMELEN-1] = 0;
1170 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1171 MidiOutDev[MODM_NumDevs].caps.szPname,
1172 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1174 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1175 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1177 /* FIXME Is it possible to know the maximum
1178 * number of simultaneous notes of a soundcard ?
1179 * I believe we don't have this information, but
1180 * it's probably equal or more than wVoices
1182 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1183 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1185 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1186 "\tALSA info: midi dev-type=%x, capa=0\n",
1187 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1188 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1189 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1190 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1191 type);
1193 MODM_NumDevs++;
1195 if (cap & SND_SEQ_PORT_CAP_READ) {
1196 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1197 snd_seq_client_info_get_name(cinfo),
1198 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1199 snd_seq_port_info_get_port(pinfo),
1200 snd_seq_port_info_get_name(pinfo),
1201 type);
1203 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1204 return;
1205 if (!type)
1206 return;
1208 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1210 /* Manufac ID. We do not have access to this with soundcard.h
1211 * Does not seem to be a problem, because in mmsystem.h only
1212 * Microsoft's ID is listed.
1214 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1215 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1216 /* Product Version. We simply say "1" */
1217 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1219 /* FIXME Do we have this information?
1220 * Assuming the soundcards can handle
1221 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1222 * not MIDICAPS_CACHE.
1224 MidiInDev[MIDM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1226 /* Try to use both client and port names, if this is too long take the port name only.
1227 In the second case the port name should be explicit enough due to its big size.
1229 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1230 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1231 } else {
1232 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN-1);
1233 midiPortName[MAXPNAMELEN-1] = 0;
1235 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1236 MidiInDev[MIDM_NumDevs].caps.szPname,
1237 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1238 MidiInDev[MIDM_NumDevs].state = 0;
1240 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1241 "\tALSA info: midi dev-type=%x, capa=0\n",
1242 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1243 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1244 type);
1246 MIDM_NumDevs++;
1250 #endif /* HAVE_ALSA */
1253 /*======================================================================*
1254 * MIDI entry points *
1255 *======================================================================*/
1257 /**************************************************************************
1258 * ALSA_MidiInit [internal]
1260 * Initializes the MIDI devices information variables
1262 LONG ALSA_MidiInit(void)
1264 #ifdef HAVE_ALSA
1265 static BOOL bInitDone = FALSE;
1266 snd_seq_client_info_t *cinfo;
1267 snd_seq_port_info_t *pinfo;
1269 if (bInitDone)
1270 return TRUE;
1272 TRACE("Initializing the MIDI variables.\n");
1273 bInitDone = TRUE;
1275 /* try to open device */
1276 if (midiOpenSeq(0) == -1) {
1277 return TRUE;
1280 #if 0 /* Debug purpose */
1281 snd_lib_error_set_handler(error_handler);
1282 #endif
1283 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1284 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1286 /* First, search for all internal midi devices */
1287 snd_seq_client_info_set_client(cinfo, -1);
1288 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1289 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1290 snd_seq_port_info_set_port(pinfo, -1);
1291 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1292 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1293 unsigned int type = snd_seq_port_info_get_type(pinfo);
1294 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1295 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1299 /* Second, search for all external ports */
1300 snd_seq_client_info_set_client(cinfo, -1);
1301 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1302 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1303 snd_seq_port_info_set_port(pinfo, -1);
1304 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1305 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1306 unsigned int type = snd_seq_port_info_get_type(pinfo);
1307 if (type & SND_SEQ_PORT_TYPE_PORT)
1308 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1312 /* close file and exit */
1313 midiCloseSeq();
1314 HeapFree( GetProcessHeap(), 0, cinfo );
1315 HeapFree( GetProcessHeap(), 0, pinfo );
1317 TRACE("End\n");
1318 #endif
1319 return TRUE;
1322 /**************************************************************************
1323 * midMessage (WINEALSA.@)
1325 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1326 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1328 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1329 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1330 switch (wMsg) {
1331 #ifdef HAVE_ALSA
1332 case DRVM_INIT:
1333 case DRVM_EXIT:
1334 case DRVM_ENABLE:
1335 case DRVM_DISABLE:
1336 /* FIXME: Pretend this is supported */
1337 return 0;
1338 case MIDM_OPEN:
1339 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1340 case MIDM_CLOSE:
1341 return midClose(wDevID);
1342 case MIDM_ADDBUFFER:
1343 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1344 case MIDM_PREPARE:
1345 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1346 case MIDM_UNPREPARE:
1347 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1348 case MIDM_GETDEVCAPS:
1349 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1350 case MIDM_GETNUMDEVS:
1351 return MIDM_NumDevs;
1352 case MIDM_RESET:
1353 return midReset(wDevID);
1354 case MIDM_START:
1355 return midStart(wDevID);
1356 case MIDM_STOP:
1357 return midStop(wDevID);
1358 #endif
1359 default:
1360 TRACE("Unsupported message\n");
1362 return MMSYSERR_NOTSUPPORTED;
1365 /**************************************************************************
1366 * modMessage (WINEALSA.@)
1368 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1369 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1371 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1372 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1374 switch (wMsg) {
1375 #ifdef HAVE_ALSA
1376 case DRVM_INIT:
1377 case DRVM_EXIT:
1378 case DRVM_ENABLE:
1379 case DRVM_DISABLE:
1380 /* FIXME: Pretend this is supported */
1381 return 0;
1382 case MODM_OPEN:
1383 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1384 case MODM_CLOSE:
1385 return modClose(wDevID);
1386 case MODM_DATA:
1387 return modData(wDevID, dwParam1);
1388 case MODM_LONGDATA:
1389 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1390 case MODM_PREPARE:
1391 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1392 case MODM_UNPREPARE:
1393 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1394 case MODM_GETDEVCAPS:
1395 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1396 case MODM_GETNUMDEVS:
1397 return MODM_NumDevs;
1398 case MODM_GETVOLUME:
1399 return 0;
1400 case MODM_SETVOLUME:
1401 return 0;
1402 case MODM_RESET:
1403 return modReset(wDevID);
1404 #endif
1405 default:
1406 TRACE("Unsupported message\n");
1408 return MMSYSERR_NOTSUPPORTED;
1411 /*-----------------------------------------------------------------------*/