makefiles: Output rules for building generated .rc files.
[wine.git] / dlls / winealsa.drv / midi.c
blob58b10bf666bd9ae6ba9b4d87bd058bbe5fc67d57
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
4 * Sample MIDI Wine Driver for ALSA (basically Linux)
6 * Copyright 1994 Martin Ayotte
7 * Copyright 1998 Luiz Otavio L. Zorzella (init procedures)
8 * Copyright 1998/1999 Eric POUECH :
9 * 98/7 changes for making this MIDI driver work on OSS
10 * current support is limited to MIDI ports of OSS systems
11 * 98/9 rewriting MCI code for MIDI
12 * 98/11 split in midi.c and mcimidi.c
13 * Copyright 2003 Christian Costa :
14 * ALSA port
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 * TODO: Finish midi record
34 #include "config.h"
36 #include <string.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
42 #include <fcntl.h>
43 #include <errno.h>
45 #include "windef.h"
46 #include "winbase.h"
47 #include "wingdi.h"
48 #include "winuser.h"
49 #include "winnls.h"
50 #include "mmddk.h"
51 #include "mmreg.h"
52 #include "dsound.h"
53 #include "wine/debug.h"
55 #include <alsa/asoundlib.h>
57 WINE_DEFAULT_DEBUG_CHANNEL(midi);
59 #ifndef SND_SEQ_PORT_TYPE_PORT
60 #define SND_SEQ_PORT_TYPE_PORT (1<<19) /* Appears in version 1.0.12rc1 */
61 #endif
63 typedef struct {
64 int state; /* -1 disabled, 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
65 DWORD bufsize;
66 MIDIOPENDESC midiDesc;
67 WORD wFlags;
68 LPMIDIHDR lpQueueHdr;
69 DWORD dwTotalPlayed;
70 unsigned char incoming[3];
71 unsigned char incPrev;
72 char incLen;
73 DWORD startTime;
74 MIDIINCAPSW caps;
75 snd_seq_addr_t addr;
76 } WINE_MIDIIN;
78 typedef struct {
79 BOOL bEnabled;
80 DWORD bufsize;
81 MIDIOPENDESC midiDesc;
82 WORD wFlags;
83 LPMIDIHDR lpQueueHdr;
84 DWORD dwTotalPlayed;
85 void* lpExtra; /* according to port type (MIDI, FM...), extra data when needed */
86 MIDIOUTCAPSW caps;
87 snd_seq_addr_t addr;
88 } WINE_MIDIOUT;
90 static WINE_MIDIIN MidiInDev [MAX_MIDIINDRV ];
91 static WINE_MIDIOUT MidiOutDev[MAX_MIDIOUTDRV];
93 /* this is the total number of MIDI out devices found (synth and port) */
94 static int MODM_NumDevs = 0;
95 /* this is the total number of MIDI out devices found */
96 static int MIDM_NumDevs = 0;
98 static CRITICAL_SECTION midiSeqLock;
99 static CRITICAL_SECTION_DEBUG midiSeqLockDebug =
101 0, 0, &midiSeqLock,
102 { &midiSeqLockDebug.ProcessLocksList, &midiSeqLockDebug.ProcessLocksList },
103 0, 0, { (DWORD_PTR)(__FILE__ ": midiSeqLock") }
105 static CRITICAL_SECTION midiSeqLock = { &midiSeqLockDebug, -1, 0, 0, 0, 0 };
106 static snd_seq_t* midiSeq = NULL;
107 static int numOpenMidiSeq = 0;
108 static int numStartedMidiIn = 0;
110 static int port_in;
111 static int port_out;
113 static CRITICAL_SECTION crit_sect; /* protects all MidiIn buffer queues */
114 static CRITICAL_SECTION_DEBUG critsect_debug =
116 0, 0, &crit_sect,
117 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
118 0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
120 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
122 static int end_thread;
123 static HANDLE hThread;
125 /*======================================================================*
126 * Low level MIDI implementation *
127 *======================================================================*/
129 #if 0 /* Debug Purpose */
130 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
132 va_list arg;
133 if (err == ENOENT)
134 return;
135 va_start(arg, fmt);
136 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
137 vfprintf(stderr, fmt, arg);
138 if (err)
139 fprintf(stderr, ": %s", snd_strerror(err));
140 putc('\n', stderr);
141 va_end(arg);
143 #endif
145 /**************************************************************************
146 * MIDI_unixToWindowsDeviceType [internal]
148 * return the Windows equivalent to a Unix Device Type
151 static int MIDI_AlsaToWindowsDeviceType(unsigned int type)
153 /* MOD_MIDIPORT output port
154 * MOD_SYNTH generic internal synth
155 * MOD_SQSYNTH square wave internal synth
156 * MOD_FMSYNTH FM internal synth
157 * MOD_MAPPER MIDI mapper
158 * MOD_WAVETABLE hardware wavetable internal synth
159 * MOD_SWSYNTH software internal synth
162 /* FIXME Is this really the correct equivalence from ALSA to
163 Windows Sound type? */
165 if (type & SND_SEQ_PORT_TYPE_SYNTH)
166 return MOD_FMSYNTH;
168 if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
169 return MOD_SYNTH;
171 if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION))
172 return MOD_MIDIPORT;
174 ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type);
175 return MOD_FMSYNTH;
178 /**************************************************************************
179 * MIDI_NotifyClient [internal]
181 static void MIDI_NotifyClient(UINT wDevID, WORD wMsg,
182 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
184 DWORD_PTR dwCallBack;
185 UINT uFlags;
186 HANDLE hDev;
187 DWORD_PTR dwInstance;
189 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
190 wDevID, wMsg, dwParam1, dwParam2);
192 switch (wMsg) {
193 case MOM_OPEN:
194 case MOM_CLOSE:
195 case MOM_DONE:
196 case MOM_POSITIONCB:
197 if (wDevID > MODM_NumDevs) return;
199 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
200 uFlags = MidiOutDev[wDevID].wFlags;
201 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
202 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
203 break;
205 case MIM_OPEN:
206 case MIM_CLOSE:
207 case MIM_DATA:
208 case MIM_LONGDATA:
209 case MIM_ERROR:
210 case MIM_LONGERROR:
211 case MIM_MOREDATA:
212 if (wDevID > MIDM_NumDevs) return;
214 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
215 uFlags = MidiInDev[wDevID].wFlags;
216 hDev = MidiInDev[wDevID].midiDesc.hMidi;
217 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
218 break;
219 default:
220 ERR("Unsupported MSW-MIDI message %u\n", wMsg);
221 return;
224 DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
227 static BOOL midi_warn = TRUE;
228 /**************************************************************************
229 * midiOpenSeq [internal]
231 static int midiOpenSeq(BOOL create_client)
233 EnterCriticalSection(&midiSeqLock);
234 if (numOpenMidiSeq == 0) {
235 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
237 if (midi_warn)
239 WARN("Error opening ALSA sequencer.\n");
241 midi_warn = FALSE;
242 LeaveCriticalSection(&midiSeqLock);
243 return -1;
246 if (create_client) {
247 /* Setting the client name is the only init to do */
248 snd_seq_set_client_name(midiSeq, "WINE midi driver");
250 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
251 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
252 SND_SEQ_PORT_TYPE_APPLICATION);
253 if (port_out < 0)
254 TRACE("Unable to create output port\n");
255 else
256 TRACE("Outport port %d created successfully\n", port_out);
257 #else
258 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
259 SND_SEQ_PORT_TYPE_APPLICATION);
260 if (port_out < 0)
261 TRACE("Unable to create output port\n");
262 else
263 TRACE("Outport port %d created successfully\n", port_out);
265 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
266 SND_SEQ_PORT_TYPE_APPLICATION);
267 if (port_in < 0)
268 TRACE("Unable to create input port\n");
269 else
270 TRACE("Input port %d created successfully\n", port_in);
271 #endif
274 numOpenMidiSeq++;
275 LeaveCriticalSection(&midiSeqLock);
276 return 0;
279 /**************************************************************************
280 * midiCloseSeq [internal]
282 static int midiCloseSeq(void)
284 EnterCriticalSection(&midiSeqLock);
285 if (--numOpenMidiSeq == 0) {
286 snd_seq_delete_simple_port(midiSeq, port_out);
287 snd_seq_delete_simple_port(midiSeq, port_in);
288 snd_seq_close(midiSeq);
289 midiSeq = NULL;
291 LeaveCriticalSection(&midiSeqLock);
292 return 0;
295 static void handle_midi_event(snd_seq_event_t *ev)
297 WORD wDevID;
299 /* Find the target device */
300 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
301 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
302 break;
303 if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
304 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
305 else {
306 DWORD dwTime, toSend = 0;
307 int value = 0;
308 /* FIXME: Should use ev->time instead for better accuracy */
309 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
310 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
311 switch(ev->type)
313 case SND_SEQ_EVENT_NOTEOFF:
314 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
315 break;
316 case SND_SEQ_EVENT_NOTEON:
317 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
318 break;
319 case SND_SEQ_EVENT_KEYPRESS:
320 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
321 break;
322 case SND_SEQ_EVENT_CONTROLLER:
323 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
324 break;
325 case SND_SEQ_EVENT_PITCHBEND:
326 value = ev->data.control.value + 0x2000;
327 toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
328 break;
329 case SND_SEQ_EVENT_PGMCHANGE:
330 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
331 break;
332 case SND_SEQ_EVENT_CHANPRESS:
333 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
334 break;
335 case SND_SEQ_EVENT_CLOCK:
336 toSend = 0xF8;
337 break;
338 case SND_SEQ_EVENT_START:
339 toSend = 0xFA;
340 break;
341 case SND_SEQ_EVENT_CONTINUE:
342 toSend = 0xFB;
343 break;
344 case SND_SEQ_EVENT_STOP:
345 toSend = 0xFC;
346 break;
347 case SND_SEQ_EVENT_SONGPOS:
348 toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_POS;
349 break;
350 case SND_SEQ_EVENT_SONGSEL:
351 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_SELECT;
352 break;
353 case SND_SEQ_EVENT_RESET:
354 toSend = 0xFF;
355 break;
356 case SND_SEQ_EVENT_QFRAME:
357 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_MTC_QUARTER;
358 break;
359 case SND_SEQ_EVENT_SYSEX:
361 int pos = 0;
362 int len = ev->data.ext.len;
363 LPBYTE ptr = ev->data.ext.ptr;
364 LPMIDIHDR lpMidiHdr;
366 EnterCriticalSection(&crit_sect);
367 while (len) {
368 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
369 int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
370 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
371 lpMidiHdr->dwBytesRecorded += copylen;
372 len -= copylen;
373 pos += copylen;
374 /* We check if we reach the end of buffer or the end of sysex before notifying
375 * to handle the case where ALSA split the sysex into several events */
376 if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
377 (*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
378 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
379 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
380 lpMidiHdr->dwFlags |= MHDR_DONE;
381 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
383 } else {
384 FIXME("Sysex data received but no buffer to store it!\n");
385 break;
388 LeaveCriticalSection(&crit_sect);
390 break;
391 case SND_SEQ_EVENT_SENSING:
392 /* Noting to do */
393 break;
394 default:
395 FIXME("Unhandled event received, type = %x\n", ev->type);
396 break;
398 if (toSend != 0) {
399 TRACE("Received event %08x from %d:%d\n", toSend, ev->source.client, ev->source.port);
400 MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
405 static DWORD WINAPI midRecThread(LPVOID arg)
407 int npfd;
408 struct pollfd *pfd;
409 int ret;
411 TRACE("Thread startup\n");
413 while(!end_thread) {
414 TRACE("Thread loop\n");
415 EnterCriticalSection(&midiSeqLock);
416 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
417 pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
418 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
419 LeaveCriticalSection(&midiSeqLock);
421 /* Check if an event is present */
422 if (poll(pfd, npfd, 250) <= 0) {
423 HeapFree(GetProcessHeap(), 0, pfd);
424 continue;
427 /* Note: This definitely does not work.
428 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
429 snd_seq_event_t* ev;
430 snd_seq_event_input(midiSeq, &ev);
431 ....................
432 snd_seq_free_event(ev);
435 do {
436 snd_seq_event_t *ev;
438 EnterCriticalSection(&midiSeqLock);
439 snd_seq_event_input(midiSeq, &ev);
440 LeaveCriticalSection(&midiSeqLock);
442 if (ev) {
443 handle_midi_event(ev);
444 snd_seq_free_event(ev);
447 EnterCriticalSection(&midiSeqLock);
448 ret = snd_seq_event_input_pending(midiSeq, 0);
449 LeaveCriticalSection(&midiSeqLock);
450 } while(ret > 0);
452 HeapFree(GetProcessHeap(), 0, pfd);
454 return 0;
457 /**************************************************************************
458 * midGetDevCaps [internal]
460 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
462 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
464 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
465 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
467 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
469 return MMSYSERR_NOERROR;
473 /**************************************************************************
474 * midOpen [internal]
476 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
478 int ret = 0;
480 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
482 if (lpDesc == NULL) {
483 WARN("Invalid Parameter !\n");
484 return MMSYSERR_INVALPARAM;
487 /* FIXME :
488 * how to check that content of lpDesc is correct ?
490 if (wDevID >= MIDM_NumDevs) {
491 WARN("wDevID too large (%u) !\n", wDevID);
492 return MMSYSERR_BADDEVICEID;
494 if (MidiInDev[wDevID].state == -1) {
495 WARN("device disabled\n");
496 return MIDIERR_NODEVICE;
498 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
499 WARN("device already open !\n");
500 return MMSYSERR_ALLOCATED;
502 if ((dwFlags & MIDI_IO_STATUS) != 0) {
503 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
504 dwFlags &= ~MIDI_IO_STATUS;
506 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
507 FIXME("Bad dwFlags\n");
508 return MMSYSERR_INVALFLAG;
511 if (midiOpenSeq(TRUE) < 0) {
512 return MMSYSERR_ERROR;
515 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
517 MidiInDev[wDevID].lpQueueHdr = NULL;
518 MidiInDev[wDevID].dwTotalPlayed = 0;
519 MidiInDev[wDevID].bufsize = 0x3FFF;
520 MidiInDev[wDevID].midiDesc = *lpDesc;
521 MidiInDev[wDevID].state = 0;
522 MidiInDev[wDevID].incLen = 0;
523 MidiInDev[wDevID].startTime = 0;
525 /* Connect our app port to the device port */
526 EnterCriticalSection(&midiSeqLock);
527 ret = snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client,
528 MidiInDev[wDevID].addr.port);
529 LeaveCriticalSection(&midiSeqLock);
530 if (ret < 0)
531 return MMSYSERR_NOTENABLED;
533 TRACE("Input port :%d connected %d:%d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
535 if (numStartedMidiIn++ == 0) {
536 end_thread = 0;
537 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
538 if (!hThread) {
539 numStartedMidiIn = 0;
540 WARN("Couldn't create thread for midi-in\n");
541 midiCloseSeq();
542 return MMSYSERR_ERROR;
544 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
545 TRACE("Created thread for midi-in\n");
548 MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
549 return MMSYSERR_NOERROR;
552 /**************************************************************************
553 * midClose [internal]
555 static DWORD midClose(WORD wDevID)
557 int ret = MMSYSERR_NOERROR;
559 TRACE("(%04X);\n", wDevID);
561 if (wDevID >= MIDM_NumDevs) {
562 WARN("wDevID too big (%u) !\n", wDevID);
563 return MMSYSERR_BADDEVICEID;
565 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
566 WARN("device not opened !\n");
567 return MMSYSERR_ERROR;
569 if (MidiInDev[wDevID].lpQueueHdr != 0) {
570 return MIDIERR_STILLPLAYING;
573 if (midiSeq == NULL) {
574 WARN("ooops !\n");
575 return MMSYSERR_ERROR;
577 if (--numStartedMidiIn == 0) {
578 TRACE("Stopping thread for midi-in\n");
579 end_thread = 1;
580 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
581 WARN("Thread end not signaled, force termination\n");
582 TerminateThread(hThread, 0);
584 TRACE("Stopped thread for midi-in\n");
587 EnterCriticalSection(&midiSeqLock);
588 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
589 LeaveCriticalSection(&midiSeqLock);
590 midiCloseSeq();
592 MidiInDev[wDevID].bufsize = 0;
593 MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
594 MidiInDev[wDevID].midiDesc.hMidi = 0;
596 return ret;
600 /**************************************************************************
601 * midAddBuffer [internal]
603 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
605 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
607 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
608 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
610 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
611 if (dwSize < offsetof(MIDIHDR,dwOffset)) return MMSYSERR_INVALPARAM;
612 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
613 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
614 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
616 EnterCriticalSection(&crit_sect);
617 lpMidiHdr->dwFlags &= ~WHDR_DONE;
618 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
619 lpMidiHdr->dwBytesRecorded = 0;
620 lpMidiHdr->lpNext = 0;
621 if (MidiInDev[wDevID].lpQueueHdr == 0) {
622 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
623 } else {
624 LPMIDIHDR ptr;
626 for (ptr = MidiInDev[wDevID].lpQueueHdr; ptr->lpNext != 0;
627 ptr = ptr->lpNext);
628 ptr->lpNext = lpMidiHdr;
630 LeaveCriticalSection(&crit_sect);
632 return MMSYSERR_NOERROR;
635 /**************************************************************************
636 * midPrepare [internal]
638 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
640 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
642 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
643 return MMSYSERR_INVALPARAM;
644 if (lpMidiHdr->dwFlags & MHDR_PREPARED)
645 return MMSYSERR_NOERROR;
647 lpMidiHdr->lpNext = 0;
648 lpMidiHdr->dwFlags |= MHDR_PREPARED;
649 lpMidiHdr->dwFlags &= ~(MHDR_DONE|MHDR_INQUEUE); /* flags cleared since w2k */
651 return MMSYSERR_NOERROR;
654 /**************************************************************************
655 * midUnprepare [internal]
657 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
659 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
661 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
662 return MMSYSERR_INVALPARAM;
663 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
664 return MMSYSERR_NOERROR;
665 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
666 return MIDIERR_STILLPLAYING;
668 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
670 return MMSYSERR_NOERROR;
673 /**************************************************************************
674 * midReset [internal]
676 static DWORD midReset(WORD wDevID)
678 DWORD dwTime = GetTickCount();
680 TRACE("(%04X);\n", wDevID);
682 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
683 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
685 EnterCriticalSection(&crit_sect);
686 while (MidiInDev[wDevID].lpQueueHdr) {
687 LPMIDIHDR lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
688 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
689 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
690 lpMidiHdr->dwFlags |= MHDR_DONE;
691 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
693 LeaveCriticalSection(&crit_sect);
695 return MMSYSERR_NOERROR;
698 /**************************************************************************
699 * midStart [internal]
701 static DWORD midStart(WORD wDevID)
703 TRACE("(%04X);\n", wDevID);
705 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
706 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
708 MidiInDev[wDevID].state = 1;
709 MidiInDev[wDevID].startTime = GetTickCount();
710 return MMSYSERR_NOERROR;
713 /**************************************************************************
714 * midStop [internal]
716 static DWORD midStop(WORD wDevID)
718 TRACE("(%04X);\n", wDevID);
720 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
721 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
723 MidiInDev[wDevID].state = 0;
724 return MMSYSERR_NOERROR;
727 /**************************************************************************
728 * modGetDevCaps [internal]
730 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
732 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
734 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
735 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
737 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
739 return MMSYSERR_NOERROR;
742 /**************************************************************************
743 * modOpen [internal]
745 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
747 int ret;
749 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
750 if (lpDesc == NULL) {
751 WARN("Invalid Parameter !\n");
752 return MMSYSERR_INVALPARAM;
754 if (wDevID >= MODM_NumDevs) {
755 TRACE("MAX_MIDIOUTDRV reached !\n");
756 return MMSYSERR_BADDEVICEID;
758 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
759 WARN("device already open !\n");
760 return MMSYSERR_ALLOCATED;
762 if (!MidiOutDev[wDevID].bEnabled) {
763 WARN("device disabled !\n");
764 return MIDIERR_NODEVICE;
766 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
767 WARN("bad dwFlags\n");
768 return MMSYSERR_INVALFLAG;
771 MidiOutDev[wDevID].lpExtra = 0;
773 switch (MidiOutDev[wDevID].caps.wTechnology) {
774 case MOD_FMSYNTH:
775 case MOD_MIDIPORT:
776 case MOD_SYNTH:
777 if (midiOpenSeq(TRUE) < 0) {
778 return MMSYSERR_ALLOCATED;
780 break;
781 default:
782 WARN("Technology not supported (yet) %d !\n",
783 MidiOutDev[wDevID].caps.wTechnology);
784 return MMSYSERR_NOTENABLED;
787 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
789 MidiOutDev[wDevID].lpQueueHdr = NULL;
790 MidiOutDev[wDevID].dwTotalPlayed = 0;
791 MidiOutDev[wDevID].bufsize = 0x3FFF;
792 MidiOutDev[wDevID].midiDesc = *lpDesc;
794 /* Connect our app port to the device port */
795 EnterCriticalSection(&midiSeqLock);
796 ret = snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client,
797 MidiOutDev[wDevID].addr.port);
798 LeaveCriticalSection(&midiSeqLock);
799 if (ret < 0)
800 return MMSYSERR_NOTENABLED;
802 TRACE("Output port :%d connected %d:%d\n",port_out,MidiOutDev[wDevID].addr.client,MidiOutDev[wDevID].addr.port);
804 MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
805 return MMSYSERR_NOERROR;
809 /**************************************************************************
810 * modClose [internal]
812 static DWORD modClose(WORD wDevID)
814 int ret = MMSYSERR_NOERROR;
816 TRACE("(%04X);\n", wDevID);
818 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
819 WARN("device not opened !\n");
820 return MMSYSERR_ERROR;
822 /* FIXME: should test that no pending buffer is still in the queue for
823 * playing */
825 if (midiSeq == NULL) {
826 WARN("can't close !\n");
827 return MMSYSERR_ERROR;
830 switch (MidiOutDev[wDevID].caps.wTechnology) {
831 case MOD_FMSYNTH:
832 case MOD_MIDIPORT:
833 case MOD_SYNTH:
834 EnterCriticalSection(&midiSeqLock);
835 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
836 LeaveCriticalSection(&midiSeqLock);
837 midiCloseSeq();
838 break;
839 default:
840 WARN("Technology not supported (yet) %d !\n",
841 MidiOutDev[wDevID].caps.wTechnology);
842 return MMSYSERR_NOTENABLED;
845 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
846 MidiOutDev[wDevID].lpExtra = 0;
848 MidiOutDev[wDevID].bufsize = 0;
849 MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
850 MidiOutDev[wDevID].midiDesc.hMidi = 0;
851 return ret;
854 /**************************************************************************
855 * modData [internal]
857 static DWORD modData(WORD wDevID, DWORD dwParam)
859 BYTE evt = LOBYTE(LOWORD(dwParam));
860 BYTE d1 = HIBYTE(LOWORD(dwParam));
861 BYTE d2 = LOBYTE(HIWORD(dwParam));
863 TRACE("(%04X, %08X);\n", wDevID, dwParam);
865 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
866 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
868 if (midiSeq == NULL) {
869 WARN("can't play !\n");
870 return MIDIERR_NODEVICE;
872 switch (MidiOutDev[wDevID].caps.wTechnology) {
873 case MOD_SYNTH:
874 case MOD_MIDIPORT:
876 int handled = 1; /* Assume event is handled */
877 snd_seq_event_t event;
878 snd_seq_ev_clear(&event);
879 snd_seq_ev_set_direct(&event);
880 snd_seq_ev_set_source(&event, port_out);
881 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
883 switch (evt & 0xF0) {
884 case MIDI_CMD_NOTE_OFF:
885 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
886 break;
887 case MIDI_CMD_NOTE_ON:
888 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
889 break;
890 case MIDI_CMD_NOTE_PRESSURE:
891 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
892 break;
893 case MIDI_CMD_CONTROL:
894 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
895 break;
896 case MIDI_CMD_BENDER:
897 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
898 break;
899 case MIDI_CMD_PGM_CHANGE:
900 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
901 break;
902 case MIDI_CMD_CHANNEL_PRESSURE:
903 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
904 break;
905 case MIDI_CMD_COMMON_SYSEX:
906 switch (evt & 0x0F) {
907 case 0x00: /* System Exclusive, don't do it on modData,
908 * should require modLongData*/
909 case 0x04: /* Undefined. */
910 case 0x05: /* Undefined. */
911 case 0x07: /* End of Exclusive. */
912 case 0x09: /* Undefined. */
913 case 0x0D: /* Undefined. */
914 handled = 0;
915 break;
916 case 0x06: /* Tune Request */
917 case 0x08: /* Timing Clock. */
918 case 0x0A: /* Start. */
919 case 0x0B: /* Continue */
920 case 0x0C: /* Stop */
921 case 0x0E: /* Active Sensing. */
923 snd_midi_event_t *midi_event;
925 snd_midi_event_new(1, &midi_event);
926 snd_midi_event_init(midi_event);
927 snd_midi_event_encode_byte(midi_event, evt, &event);
928 snd_midi_event_free(midi_event);
929 break;
931 case 0x0F: /* Reset */
932 /* snd_seq_ev_set_sysex(&event, 1, &evt);
933 this other way may be better */
935 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
936 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
938 break;
939 case 0x01: /* MTC Quarter frame */
940 case 0x03: /* Song Select. */
942 BYTE buf[2];
943 buf[0] = evt;
944 buf[1] = d1;
945 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
947 break;
948 case 0x02: /* Song Position Pointer. */
950 BYTE buf[3];
951 buf[0] = evt;
952 buf[1] = d1;
953 buf[2] = d2;
954 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
956 break;
958 break;
960 if (handled) {
961 EnterCriticalSection(&midiSeqLock);
962 snd_seq_event_output_direct(midiSeq, &event);
963 LeaveCriticalSection(&midiSeqLock);
966 break;
967 default:
968 WARN("Technology not supported (yet) %d !\n",
969 MidiOutDev[wDevID].caps.wTechnology);
970 return MMSYSERR_NOTENABLED;
973 return MMSYSERR_NOERROR;
976 /**************************************************************************
977 * modLongData [internal]
979 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
981 int len_add = 0;
982 BYTE *lpData, *lpNewData = NULL;
983 snd_seq_event_t event;
985 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
987 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
988 * but it seems to be used only for midi input.
989 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
992 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
993 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
995 if (midiSeq == NULL) {
996 WARN("can't play !\n");
997 return MIDIERR_NODEVICE;
1000 lpData = (BYTE*)lpMidiHdr->lpData;
1002 if (lpData == NULL)
1003 return MIDIERR_UNPREPARED;
1004 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1005 return MIDIERR_UNPREPARED;
1006 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1007 return MIDIERR_STILLPLAYING;
1008 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1009 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1011 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
1012 * data, or can it also contain raw MIDI data, to be split up and sent to
1013 * modShortData() ?
1014 * If the latest is true, then the following WARNing will fire up
1016 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
1017 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
1018 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
1021 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
1022 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
1023 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
1024 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
1026 switch (MidiOutDev[wDevID].caps.wTechnology) {
1027 case MOD_FMSYNTH:
1028 /* FIXME: I don't think there is much to do here */
1029 HeapFree(GetProcessHeap(), 0, lpNewData);
1030 break;
1031 case MOD_MIDIPORT:
1032 if (lpData[0] != 0xF0) {
1033 /* Send start of System Exclusive */
1034 len_add = 1;
1035 lpNewData[0] = 0xF0;
1036 memcpy(lpNewData + 1, lpData, lpMidiHdr->dwBufferLength);
1037 WARN("Adding missing 0xF0 marker at the beginning of system exclusive byte stream\n");
1039 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
1040 /* Send end of System Exclusive */
1041 if (!len_add)
1042 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
1043 lpNewData[lpMidiHdr->dwBufferLength + len_add] = 0xF7;
1044 len_add++;
1045 WARN("Adding missing 0xF7 marker at the end of system exclusive byte stream\n");
1047 snd_seq_ev_clear(&event);
1048 snd_seq_ev_set_direct(&event);
1049 snd_seq_ev_set_source(&event, port_out);
1050 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1051 TRACE("destination %d:%d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1052 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1053 EnterCriticalSection(&midiSeqLock);
1054 snd_seq_event_output_direct(midiSeq, &event);
1055 LeaveCriticalSection(&midiSeqLock);
1056 HeapFree(GetProcessHeap(), 0, lpNewData);
1057 break;
1058 default:
1059 WARN("Technology not supported (yet) %d !\n",
1060 MidiOutDev[wDevID].caps.wTechnology);
1061 HeapFree(GetProcessHeap(), 0, lpNewData);
1062 return MMSYSERR_NOTENABLED;
1065 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1066 lpMidiHdr->dwFlags |= MHDR_DONE;
1067 MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
1068 return MMSYSERR_NOERROR;
1071 /**************************************************************************
1072 * modPrepare [internal]
1074 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1076 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
1078 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
1079 return MMSYSERR_INVALPARAM;
1080 if (lpMidiHdr->dwFlags & MHDR_PREPARED)
1081 return MMSYSERR_NOERROR;
1083 lpMidiHdr->lpNext = 0;
1084 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1085 lpMidiHdr->dwFlags &= ~(MHDR_DONE|MHDR_INQUEUE); /* flags cleared since w2k */
1086 return MMSYSERR_NOERROR;
1089 /**************************************************************************
1090 * modUnprepare [internal]
1092 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1094 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
1096 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
1097 return MMSYSERR_INVALPARAM;
1098 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1099 return MMSYSERR_NOERROR;
1100 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1101 return MIDIERR_STILLPLAYING;
1102 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1103 return MMSYSERR_NOERROR;
1106 /**************************************************************************
1107 * modGetVolume [internal]
1109 static DWORD modGetVolume(WORD wDevID, DWORD* lpdwVolume)
1111 if (!lpdwVolume) return MMSYSERR_INVALPARAM;
1112 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1113 *lpdwVolume = 0xFFFFFFFF;
1114 return (MidiOutDev[wDevID].caps.dwSupport & MIDICAPS_VOLUME) ? 0 : MMSYSERR_NOTSUPPORTED;
1117 /**************************************************************************
1118 * modReset [internal]
1120 static DWORD modReset(WORD wDevID)
1122 unsigned chn;
1124 TRACE("(%04X);\n", wDevID);
1126 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1127 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1129 /* stop all notes */
1130 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1131 * it's channel dependent...
1133 for (chn = 0; chn < 16; chn++) {
1134 /* turn off every note */
1135 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1136 /* remove sustain on all channels */
1137 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1139 /* FIXME: the LongData buffers must also be returned to the app */
1140 return MMSYSERR_NOERROR;
1144 /**************************************************************************
1145 * ALSA_AddMidiPort [internal]
1147 * Helper for ALSA_MidiInit
1149 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1151 char midiPortName[MAXPNAMELEN];
1153 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1154 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1155 snd_seq_client_info_get_name(cinfo),
1156 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1157 snd_seq_port_info_get_port(pinfo),
1158 snd_seq_port_info_get_name(pinfo),
1159 type);
1161 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1162 return;
1163 if (!type)
1164 return;
1166 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1168 /* Manufac ID. We do not have access to this with soundcard.h
1169 * Does not seem to be a problem, because in mmsystem.h only
1170 * Microsoft's ID is listed.
1172 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1173 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1174 /* Product Version. We simply say "1" */
1175 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1176 /* The following are mandatory for MOD_MIDIPORT */
1177 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1178 MidiOutDev[MODM_NumDevs].caps.wVoices = 0;
1179 MidiOutDev[MODM_NumDevs].caps.wNotes = 0;
1180 MidiOutDev[MODM_NumDevs].caps.dwSupport = 0;
1182 /* Try to use both client and port names, if this is too long take the port name only.
1183 In the second case the port name should be explicit enough due to its big size.
1185 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1186 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1187 } else {
1188 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1190 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1, MidiOutDev[MODM_NumDevs].caps.szPname,
1191 ARRAY_SIZE(MidiOutDev[MODM_NumDevs].caps.szPname));
1193 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1195 if (MOD_MIDIPORT != MidiOutDev[MODM_NumDevs].caps.wTechnology) {
1196 /* FIXME Do we have this information?
1197 * Assuming the soundcards can handle
1198 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1199 * not MIDICAPS_CACHE.
1201 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1202 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1204 /* FIXME Is it possible to know the maximum
1205 * number of simultaneous notes of a soundcard ?
1206 * I believe we don't have this information, but
1207 * it's probably equal or more than wVoices
1209 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1211 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1213 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1214 "\tALSA info: midi dev-type=%x, capa=0\n",
1215 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1216 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1217 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1218 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1219 type);
1221 MODM_NumDevs++;
1223 if (cap & SND_SEQ_PORT_CAP_READ) {
1224 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1225 snd_seq_client_info_get_name(cinfo),
1226 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1227 snd_seq_port_info_get_port(pinfo),
1228 snd_seq_port_info_get_name(pinfo),
1229 type);
1231 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1232 return;
1233 if (!type)
1234 return;
1236 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1238 /* Manufac ID. We do not have access to this with soundcard.h
1239 * Does not seem to be a problem, because in mmsystem.h only
1240 * Microsoft's ID is listed.
1242 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1243 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1244 /* Product Version. We simply say "1" */
1245 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1246 MidiInDev[MIDM_NumDevs].caps.dwSupport = 0; /* mandatory with MIDIINCAPS */
1248 /* Try to use both client and port names, if this is too long take the port name only.
1249 In the second case the port name should be explicit enough due to its big size.
1251 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1252 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1253 } else {
1254 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1256 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1, MidiInDev[MIDM_NumDevs].caps.szPname,
1257 ARRAY_SIZE(MidiInDev[MIDM_NumDevs].caps.szPname));
1258 MidiInDev[MIDM_NumDevs].state = 0;
1260 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1261 "\tALSA info: midi dev-type=%x, capa=0\n",
1262 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1263 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1264 type);
1266 MIDM_NumDevs++;
1271 /*======================================================================*
1272 * MIDI entry points *
1273 *======================================================================*/
1275 /**************************************************************************
1276 * ALSA_MidiInit [internal]
1278 * Initializes the MIDI devices information variables
1280 static BOOL ALSA_MidiInit(void)
1282 static BOOL bInitDone = FALSE;
1283 snd_seq_client_info_t *cinfo;
1284 snd_seq_port_info_t *pinfo;
1286 if (bInitDone)
1287 return TRUE;
1289 TRACE("Initializing the MIDI variables.\n");
1290 bInitDone = TRUE;
1292 /* try to open device */
1293 if (midiOpenSeq(FALSE) == -1) {
1294 return TRUE;
1297 #if 0 /* Debug purpose */
1298 snd_lib_error_set_handler(error_handler);
1299 #endif
1300 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1301 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1303 /* First, search for all internal midi devices */
1304 snd_seq_client_info_set_client(cinfo, -1);
1305 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1306 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1307 snd_seq_port_info_set_port(pinfo, -1);
1308 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1309 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1310 unsigned int type = snd_seq_port_info_get_type(pinfo);
1311 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1312 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1316 /* Second, search for all external ports */
1317 snd_seq_client_info_set_client(cinfo, -1);
1318 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1319 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1320 snd_seq_port_info_set_port(pinfo, -1);
1321 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1322 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1323 unsigned int type = snd_seq_port_info_get_type(pinfo);
1324 if (type & SND_SEQ_PORT_TYPE_PORT)
1325 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1329 /* close file and exit */
1330 midiCloseSeq();
1331 HeapFree( GetProcessHeap(), 0, cinfo );
1332 HeapFree( GetProcessHeap(), 0, pinfo );
1334 TRACE("End\n");
1335 return TRUE;
1338 /**************************************************************************
1339 * midMessage (WINEALSA.@)
1341 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1342 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1344 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1345 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1346 switch (wMsg) {
1347 case DRVM_INIT:
1348 ALSA_MidiInit();
1349 return 0;
1350 case DRVM_EXIT:
1351 case DRVM_ENABLE:
1352 case DRVM_DISABLE:
1353 /* FIXME: Pretend this is supported */
1354 return 0;
1355 case MIDM_OPEN:
1356 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1357 case MIDM_CLOSE:
1358 return midClose(wDevID);
1359 case MIDM_ADDBUFFER:
1360 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1361 case MIDM_PREPARE:
1362 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1363 case MIDM_UNPREPARE:
1364 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1365 case MIDM_GETDEVCAPS:
1366 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1367 case MIDM_GETNUMDEVS:
1368 return MIDM_NumDevs;
1369 case MIDM_RESET:
1370 return midReset(wDevID);
1371 case MIDM_START:
1372 return midStart(wDevID);
1373 case MIDM_STOP:
1374 return midStop(wDevID);
1375 default:
1376 TRACE("Unsupported message\n");
1378 return MMSYSERR_NOTSUPPORTED;
1381 /**************************************************************************
1382 * modMessage (WINEALSA.@)
1384 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1385 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1387 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1388 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1390 switch (wMsg) {
1391 case DRVM_INIT:
1392 ALSA_MidiInit();
1393 return 0;
1394 case DRVM_EXIT:
1395 case DRVM_ENABLE:
1396 case DRVM_DISABLE:
1397 /* FIXME: Pretend this is supported */
1398 return 0;
1399 case MODM_OPEN:
1400 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1401 case MODM_CLOSE:
1402 return modClose(wDevID);
1403 case MODM_DATA:
1404 return modData(wDevID, dwParam1);
1405 case MODM_LONGDATA:
1406 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1407 case MODM_PREPARE:
1408 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1409 case MODM_UNPREPARE:
1410 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1411 case MODM_GETDEVCAPS:
1412 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1413 case MODM_GETNUMDEVS:
1414 return MODM_NumDevs;
1415 case MODM_GETVOLUME:
1416 return modGetVolume(wDevID, (DWORD*)dwParam1);
1417 case MODM_SETVOLUME:
1418 return 0;
1419 case MODM_RESET:
1420 return modReset(wDevID);
1421 default:
1422 TRACE("Unsupported message\n");
1424 return MMSYSERR_NOTSUPPORTED;
1427 /**************************************************************************
1428 * DriverProc (WINEALSA.@)
1430 LRESULT CALLBACK ALSA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1431 LPARAM dwParam1, LPARAM dwParam2)
1433 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
1434 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
1436 switch(wMsg) {
1437 case DRV_LOAD:
1438 case DRV_FREE:
1439 case DRV_OPEN:
1440 case DRV_CLOSE:
1441 case DRV_ENABLE:
1442 case DRV_DISABLE:
1443 case DRV_QUERYCONFIGURE:
1444 case DRV_CONFIGURE:
1445 return 1;
1446 case DRV_INSTALL:
1447 case DRV_REMOVE:
1448 return DRV_SUCCESS;
1449 default:
1450 return 0;