makefiles: Generate explicit build rules for C source files from makedep.
[wine.git] / dlls / winealsa.drv / midi.c
blob748fa79980fe00eed1cb647da7d1d500057e8747
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 snd_seq_t* midiSeq = NULL;
99 static int numOpenMidiSeq = 0;
100 static int numStartedMidiIn = 0;
102 static int port_in;
103 static int port_out;
105 static CRITICAL_SECTION crit_sect; /* protects all MidiIn buffer queues */
106 static CRITICAL_SECTION_DEBUG critsect_debug =
108 0, 0, &crit_sect,
109 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
110 0, 0, { (DWORD_PTR)(__FILE__ ": crit_sect") }
112 static CRITICAL_SECTION crit_sect = { &critsect_debug, -1, 0, 0, 0, 0 };
114 static int end_thread;
115 static HANDLE hThread;
117 /*======================================================================*
118 * Low level MIDI implementation *
119 *======================================================================*/
121 #if 0 /* Debug Purpose */
122 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
124 va_list arg;
125 if (err == ENOENT)
126 return;
127 va_start(arg, fmt);
128 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
129 vfprintf(stderr, fmt, arg);
130 if (err)
131 fprintf(stderr, ": %s", snd_strerror(err));
132 putc('\n', stderr);
133 va_end(arg);
135 #endif
137 /**************************************************************************
138 * MIDI_unixToWindowsDeviceType [internal]
140 * return the Windows equivalent to a Unix Device Type
143 static int MIDI_AlsaToWindowsDeviceType(unsigned int type)
145 /* MOD_MIDIPORT output port
146 * MOD_SYNTH generic internal synth
147 * MOD_SQSYNTH square wave internal synth
148 * MOD_FMSYNTH FM internal synth
149 * MOD_MAPPER MIDI mapper
150 * MOD_WAVETABLE hardware wavetable internal synth
151 * MOD_SWSYNTH software internal synth
154 /* FIXME Is this really the correct equivalence from ALSA to
155 Windows Sound type? */
157 if (type & SND_SEQ_PORT_TYPE_SYNTH)
158 return MOD_FMSYNTH;
160 if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
161 return MOD_SYNTH;
163 if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION))
164 return MOD_MIDIPORT;
166 ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type);
167 return MOD_FMSYNTH;
170 /**************************************************************************
171 * MIDI_NotifyClient [internal]
173 static void MIDI_NotifyClient(UINT wDevID, WORD wMsg,
174 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
176 DWORD_PTR dwCallBack;
177 UINT uFlags;
178 HANDLE hDev;
179 DWORD_PTR dwInstance;
181 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
182 wDevID, wMsg, dwParam1, dwParam2);
184 switch (wMsg) {
185 case MOM_OPEN:
186 case MOM_CLOSE:
187 case MOM_DONE:
188 case MOM_POSITIONCB:
189 if (wDevID > MODM_NumDevs) return;
191 dwCallBack = MidiOutDev[wDevID].midiDesc.dwCallback;
192 uFlags = MidiOutDev[wDevID].wFlags;
193 hDev = MidiOutDev[wDevID].midiDesc.hMidi;
194 dwInstance = MidiOutDev[wDevID].midiDesc.dwInstance;
195 break;
197 case MIM_OPEN:
198 case MIM_CLOSE:
199 case MIM_DATA:
200 case MIM_LONGDATA:
201 case MIM_ERROR:
202 case MIM_LONGERROR:
203 case MIM_MOREDATA:
204 if (wDevID > MIDM_NumDevs) return;
206 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
207 uFlags = MidiInDev[wDevID].wFlags;
208 hDev = MidiInDev[wDevID].midiDesc.hMidi;
209 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
210 break;
211 default:
212 ERR("Unsupported MSW-MIDI message %u\n", wMsg);
213 return;
216 DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
219 static int midi_warn = 1;
220 /**************************************************************************
221 * midiOpenSeq [internal]
223 static int midiOpenSeq(int create_client)
225 if (numOpenMidiSeq == 0) {
226 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
228 if (midi_warn)
230 WARN("Error opening ALSA sequencer.\n");
232 midi_warn = 0;
233 return -1;
236 if (create_client) {
237 /* Setting the client name is the only init to do */
238 snd_seq_set_client_name(midiSeq, "WINE midi driver");
240 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
241 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
242 SND_SEQ_PORT_TYPE_APPLICATION);
243 if (port_out < 0)
244 TRACE("Unable to create output port\n");
245 else
246 TRACE("Outport port %d created successfully\n", port_out);
247 #else
248 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
249 SND_SEQ_PORT_TYPE_APPLICATION);
250 if (port_out < 0)
251 TRACE("Unable to create output port\n");
252 else
253 TRACE("Outport port %d created successfully\n", port_out);
255 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
256 SND_SEQ_PORT_TYPE_APPLICATION);
257 if (port_in < 0)
258 TRACE("Unable to create input port\n");
259 else
260 TRACE("Input port %d created successfully\n", port_in);
261 #endif
264 numOpenMidiSeq++;
265 return 0;
268 /**************************************************************************
269 * midiCloseSeq [internal]
271 static int midiCloseSeq(void)
273 if (--numOpenMidiSeq == 0) {
274 snd_seq_delete_simple_port(midiSeq, port_out);
275 snd_seq_delete_simple_port(midiSeq, port_in);
276 snd_seq_close(midiSeq);
277 midiSeq = NULL;
279 return 0;
282 static DWORD WINAPI midRecThread(LPVOID arg)
284 int npfd;
285 struct pollfd *pfd;
287 TRACE("Thread startup\n");
289 while(!end_thread) {
290 TRACE("Thread loop\n");
291 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
292 pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
293 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
295 /* Check if an event is present */
296 if (poll(pfd, npfd, 250) <= 0) {
297 HeapFree(GetProcessHeap(), 0, pfd);
298 continue;
301 /* Note: This definitely does not work.
302 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
303 snd_seq_event_t* ev;
304 snd_seq_event_input(midiSeq, &ev);
305 ....................
306 snd_seq_free_event(ev);
309 do {
310 WORD wDevID;
311 snd_seq_event_t* ev;
312 snd_seq_event_input(midiSeq, &ev);
313 /* Find the target device */
314 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
315 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
316 break;
317 if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
318 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
319 else {
320 DWORD dwTime, toSend = 0;
321 int value = 0;
322 /* FIXME: Should use ev->time instead for better accuracy */
323 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
324 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
325 switch(ev->type)
327 case SND_SEQ_EVENT_NOTEOFF:
328 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
329 break;
330 case SND_SEQ_EVENT_NOTEON:
331 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
332 break;
333 case SND_SEQ_EVENT_KEYPRESS:
334 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
335 break;
336 case SND_SEQ_EVENT_CONTROLLER:
337 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
338 break;
339 case SND_SEQ_EVENT_PITCHBEND:
340 value = ev->data.control.value + 0x2000;
341 toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
342 break;
343 case SND_SEQ_EVENT_PGMCHANGE:
344 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
345 break;
346 case SND_SEQ_EVENT_CHANPRESS:
347 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
348 break;
349 case SND_SEQ_EVENT_CLOCK:
350 toSend = 0xF8;
351 break;
352 case SND_SEQ_EVENT_START:
353 toSend = 0xFA;
354 break;
355 case SND_SEQ_EVENT_CONTINUE:
356 toSend = 0xFB;
357 break;
358 case SND_SEQ_EVENT_STOP:
359 toSend = 0xFC;
360 break;
361 case SND_SEQ_EVENT_SONGPOS:
362 toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_POS;
363 break;
364 case SND_SEQ_EVENT_SONGSEL:
365 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_SELECT;
366 break;
367 case SND_SEQ_EVENT_RESET:
368 toSend = 0xFF;
369 break;
370 case SND_SEQ_EVENT_QFRAME:
371 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_MTC_QUARTER;
372 break;
373 case SND_SEQ_EVENT_SYSEX:
375 int pos = 0;
376 int len = ev->data.ext.len;
377 LPBYTE ptr = ev->data.ext.ptr;
378 LPMIDIHDR lpMidiHdr;
380 EnterCriticalSection(&crit_sect);
381 while (len) {
382 if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
383 int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
384 memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
385 lpMidiHdr->dwBytesRecorded += copylen;
386 len -= copylen;
387 pos += copylen;
388 /* We check if we reach the end of buffer or the end of sysex before notifying
389 * to handle the case where ALSA split the sysex into several events */
390 if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
391 (*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
392 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
393 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
394 lpMidiHdr->dwFlags |= MHDR_DONE;
395 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
397 } else {
398 FIXME("Sysex data received but no buffer to store it!\n");
399 break;
402 LeaveCriticalSection(&crit_sect);
404 break;
405 case SND_SEQ_EVENT_SENSING:
406 /* Noting to do */
407 break;
408 default:
409 FIXME("Unhandled event received, type = %x\n", ev->type);
410 break;
412 if (toSend != 0) {
413 TRACE("Received event %08x from %d:%d\n", toSend, ev->source.client, ev->source.port);
414 MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
417 snd_seq_free_event(ev);
418 } while(snd_seq_event_input_pending(midiSeq, 0) > 0);
420 HeapFree(GetProcessHeap(), 0, pfd);
422 return 0;
425 /**************************************************************************
426 * midGetDevCaps [internal]
428 static DWORD midGetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
430 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
432 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
433 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
435 memcpy(lpCaps, &MidiInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
437 return MMSYSERR_NOERROR;
441 /**************************************************************************
442 * midOpen [internal]
444 static DWORD midOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
446 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
448 if (lpDesc == NULL) {
449 WARN("Invalid Parameter !\n");
450 return MMSYSERR_INVALPARAM;
453 /* FIXME :
454 * how to check that content of lpDesc is correct ?
456 if (wDevID >= MIDM_NumDevs) {
457 WARN("wDevID too large (%u) !\n", wDevID);
458 return MMSYSERR_BADDEVICEID;
460 if (MidiInDev[wDevID].state == -1) {
461 WARN("device disabled\n");
462 return MIDIERR_NODEVICE;
464 if (MidiInDev[wDevID].midiDesc.hMidi != 0) {
465 WARN("device already open !\n");
466 return MMSYSERR_ALLOCATED;
468 if ((dwFlags & MIDI_IO_STATUS) != 0) {
469 WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
470 dwFlags &= ~MIDI_IO_STATUS;
472 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
473 FIXME("Bad dwFlags\n");
474 return MMSYSERR_INVALFLAG;
477 if (midiOpenSeq(1) < 0) {
478 return MMSYSERR_ERROR;
481 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
483 MidiInDev[wDevID].lpQueueHdr = NULL;
484 MidiInDev[wDevID].dwTotalPlayed = 0;
485 MidiInDev[wDevID].bufsize = 0x3FFF;
486 MidiInDev[wDevID].midiDesc = *lpDesc;
487 MidiInDev[wDevID].state = 0;
488 MidiInDev[wDevID].incLen = 0;
489 MidiInDev[wDevID].startTime = 0;
491 /* Connect our app port to the device port */
492 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
493 return MMSYSERR_NOTENABLED;
495 TRACE("Input port :%d connected %d:%d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
497 if (numStartedMidiIn++ == 0) {
498 end_thread = 0;
499 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
500 if (!hThread) {
501 numStartedMidiIn = 0;
502 WARN("Couldn't create thread for midi-in\n");
503 midiCloseSeq();
504 return MMSYSERR_ERROR;
506 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
507 TRACE("Created thread for midi-in\n");
510 MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
511 return MMSYSERR_NOERROR;
514 /**************************************************************************
515 * midClose [internal]
517 static DWORD midClose(WORD wDevID)
519 int ret = MMSYSERR_NOERROR;
521 TRACE("(%04X);\n", wDevID);
523 if (wDevID >= MIDM_NumDevs) {
524 WARN("wDevID too big (%u) !\n", wDevID);
525 return MMSYSERR_BADDEVICEID;
527 if (MidiInDev[wDevID].midiDesc.hMidi == 0) {
528 WARN("device not opened !\n");
529 return MMSYSERR_ERROR;
531 if (MidiInDev[wDevID].lpQueueHdr != 0) {
532 return MIDIERR_STILLPLAYING;
535 if (midiSeq == NULL) {
536 WARN("ooops !\n");
537 return MMSYSERR_ERROR;
539 if (--numStartedMidiIn == 0) {
540 TRACE("Stopping thread for midi-in\n");
541 end_thread = 1;
542 if (WaitForSingleObject(hThread, 5000) != WAIT_OBJECT_0) {
543 WARN("Thread end not signaled, force termination\n");
544 TerminateThread(hThread, 0);
546 TRACE("Stopped thread for midi-in\n");
549 snd_seq_disconnect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port);
550 midiCloseSeq();
552 MidiInDev[wDevID].bufsize = 0;
553 MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
554 MidiInDev[wDevID].midiDesc.hMidi = 0;
556 return ret;
560 /**************************************************************************
561 * midAddBuffer [internal]
563 static DWORD midAddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
565 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
567 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
568 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
570 if (lpMidiHdr == NULL) return MMSYSERR_INVALPARAM;
571 if (dwSize < offsetof(MIDIHDR,dwOffset)) return MMSYSERR_INVALPARAM;
572 if (lpMidiHdr->dwBufferLength == 0) return MMSYSERR_INVALPARAM;
573 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
574 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
576 EnterCriticalSection(&crit_sect);
577 lpMidiHdr->dwFlags &= ~WHDR_DONE;
578 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
579 lpMidiHdr->dwBytesRecorded = 0;
580 lpMidiHdr->lpNext = 0;
581 if (MidiInDev[wDevID].lpQueueHdr == 0) {
582 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr;
583 } else {
584 LPMIDIHDR ptr;
586 for (ptr = MidiInDev[wDevID].lpQueueHdr; ptr->lpNext != 0;
587 ptr = ptr->lpNext);
588 ptr->lpNext = lpMidiHdr;
590 LeaveCriticalSection(&crit_sect);
592 return MMSYSERR_NOERROR;
595 /**************************************************************************
596 * midPrepare [internal]
598 static DWORD midPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
600 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
602 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
603 return MMSYSERR_INVALPARAM;
604 if (lpMidiHdr->dwFlags & MHDR_PREPARED)
605 return MMSYSERR_NOERROR;
607 lpMidiHdr->lpNext = 0;
608 lpMidiHdr->dwFlags |= MHDR_PREPARED;
609 lpMidiHdr->dwFlags &= ~(MHDR_DONE|MHDR_INQUEUE); /* flags cleared since w2k */
611 return MMSYSERR_NOERROR;
614 /**************************************************************************
615 * midUnprepare [internal]
617 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
619 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
621 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
622 return MMSYSERR_INVALPARAM;
623 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
624 return MMSYSERR_NOERROR;
625 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
626 return MIDIERR_STILLPLAYING;
628 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
630 return MMSYSERR_NOERROR;
633 /**************************************************************************
634 * midReset [internal]
636 static DWORD midReset(WORD wDevID)
638 DWORD dwTime = GetTickCount();
640 TRACE("(%04X);\n", wDevID);
642 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
643 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
645 EnterCriticalSection(&crit_sect);
646 while (MidiInDev[wDevID].lpQueueHdr) {
647 LPMIDIHDR lpMidiHdr = MidiInDev[wDevID].lpQueueHdr;
648 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
649 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
650 lpMidiHdr->dwFlags |= MHDR_DONE;
651 MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
653 LeaveCriticalSection(&crit_sect);
655 return MMSYSERR_NOERROR;
658 /**************************************************************************
659 * midStart [internal]
661 static DWORD midStart(WORD wDevID)
663 TRACE("(%04X);\n", wDevID);
665 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
666 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
668 MidiInDev[wDevID].state = 1;
669 MidiInDev[wDevID].startTime = GetTickCount();
670 return MMSYSERR_NOERROR;
673 /**************************************************************************
674 * midStop [internal]
676 static DWORD midStop(WORD wDevID)
678 TRACE("(%04X);\n", wDevID);
680 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
681 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
683 MidiInDev[wDevID].state = 0;
684 return MMSYSERR_NOERROR;
687 /**************************************************************************
688 * modGetDevCaps [internal]
690 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
692 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
694 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
695 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
697 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
699 return MMSYSERR_NOERROR;
702 /**************************************************************************
703 * modOpen [internal]
705 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
707 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
708 if (lpDesc == NULL) {
709 WARN("Invalid Parameter !\n");
710 return MMSYSERR_INVALPARAM;
712 if (wDevID >= MODM_NumDevs) {
713 TRACE("MAX_MIDIOUTDRV reached !\n");
714 return MMSYSERR_BADDEVICEID;
716 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
717 WARN("device already open !\n");
718 return MMSYSERR_ALLOCATED;
720 if (!MidiOutDev[wDevID].bEnabled) {
721 WARN("device disabled !\n");
722 return MIDIERR_NODEVICE;
724 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
725 WARN("bad dwFlags\n");
726 return MMSYSERR_INVALFLAG;
729 MidiOutDev[wDevID].lpExtra = 0;
731 switch (MidiOutDev[wDevID].caps.wTechnology) {
732 case MOD_FMSYNTH:
733 case MOD_MIDIPORT:
734 case MOD_SYNTH:
735 if (midiOpenSeq(1) < 0) {
736 return MMSYSERR_ALLOCATED;
738 break;
739 default:
740 WARN("Technology not supported (yet) %d !\n",
741 MidiOutDev[wDevID].caps.wTechnology);
742 return MMSYSERR_NOTENABLED;
745 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
747 MidiOutDev[wDevID].lpQueueHdr = NULL;
748 MidiOutDev[wDevID].dwTotalPlayed = 0;
749 MidiOutDev[wDevID].bufsize = 0x3FFF;
750 MidiOutDev[wDevID].midiDesc = *lpDesc;
752 /* Connect our app port to the device port */
753 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
754 return MMSYSERR_NOTENABLED;
756 TRACE("Output port :%d connected %d:%d\n",port_out,MidiOutDev[wDevID].addr.client,MidiOutDev[wDevID].addr.port);
758 MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
759 return MMSYSERR_NOERROR;
763 /**************************************************************************
764 * modClose [internal]
766 static DWORD modClose(WORD wDevID)
768 int ret = MMSYSERR_NOERROR;
770 TRACE("(%04X);\n", wDevID);
772 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
773 WARN("device not opened !\n");
774 return MMSYSERR_ERROR;
776 /* FIXME: should test that no pending buffer is still in the queue for
777 * playing */
779 if (midiSeq == NULL) {
780 WARN("can't close !\n");
781 return MMSYSERR_ERROR;
784 switch (MidiOutDev[wDevID].caps.wTechnology) {
785 case MOD_FMSYNTH:
786 case MOD_MIDIPORT:
787 case MOD_SYNTH:
788 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
789 midiCloseSeq();
790 break;
791 default:
792 WARN("Technology not supported (yet) %d !\n",
793 MidiOutDev[wDevID].caps.wTechnology);
794 return MMSYSERR_NOTENABLED;
797 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
798 MidiOutDev[wDevID].lpExtra = 0;
800 MidiOutDev[wDevID].bufsize = 0;
801 MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
802 MidiOutDev[wDevID].midiDesc.hMidi = 0;
803 return ret;
806 /**************************************************************************
807 * modData [internal]
809 static DWORD modData(WORD wDevID, DWORD dwParam)
811 BYTE evt = LOBYTE(LOWORD(dwParam));
812 BYTE d1 = HIBYTE(LOWORD(dwParam));
813 BYTE d2 = LOBYTE(HIWORD(dwParam));
815 TRACE("(%04X, %08X);\n", wDevID, dwParam);
817 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
818 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
820 if (midiSeq == NULL) {
821 WARN("can't play !\n");
822 return MIDIERR_NODEVICE;
824 switch (MidiOutDev[wDevID].caps.wTechnology) {
825 case MOD_SYNTH:
826 case MOD_MIDIPORT:
828 int handled = 1; /* Assume event is handled */
829 snd_seq_event_t event;
830 snd_seq_ev_clear(&event);
831 snd_seq_ev_set_direct(&event);
832 snd_seq_ev_set_source(&event, port_out);
833 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
835 switch (evt & 0xF0) {
836 case MIDI_CMD_NOTE_OFF:
837 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
838 break;
839 case MIDI_CMD_NOTE_ON:
840 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
841 break;
842 case MIDI_CMD_NOTE_PRESSURE:
843 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
844 break;
845 case MIDI_CMD_CONTROL:
846 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
847 break;
848 case MIDI_CMD_BENDER:
849 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
850 break;
851 case MIDI_CMD_PGM_CHANGE:
852 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
853 break;
854 case MIDI_CMD_CHANNEL_PRESSURE:
855 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
856 break;
857 case MIDI_CMD_COMMON_SYSEX:
858 switch (evt & 0x0F) {
859 case 0x00: /* System Exclusive, don't do it on modData,
860 * should require modLongData*/
861 case 0x04: /* Undefined. */
862 case 0x05: /* Undefined. */
863 case 0x07: /* End of Exclusive. */
864 case 0x09: /* Undefined. */
865 case 0x0D: /* Undefined. */
866 handled = 0;
867 break;
868 case 0x06: /* Tune Request */
869 case 0x08: /* Timing Clock. */
870 case 0x0A: /* Start. */
871 case 0x0B: /* Continue */
872 case 0x0C: /* Stop */
873 case 0x0E: /* Active Sensing. */
874 /* FIXME: Is this function suitable for these purposes
875 (and also Song Select and Song Position Pointer) */
876 snd_seq_ev_set_sysex(&event, 1, &evt);
877 break;
878 case 0x0F: /* Reset */
879 /* snd_seq_ev_set_sysex(&event, 1, &evt);
880 this other way may be better */
882 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
883 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
885 break;
886 case 0x01: /* MTC Quarter frame */
887 case 0x03: /* Song Select. */
889 BYTE buf[2];
890 buf[0] = evt;
891 buf[1] = d1;
892 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
894 break;
895 case 0x02: /* Song Position Pointer. */
897 BYTE buf[3];
898 buf[0] = evt;
899 buf[1] = d1;
900 buf[2] = d2;
901 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
903 break;
905 break;
907 if (handled)
908 snd_seq_event_output_direct(midiSeq, &event);
910 break;
911 default:
912 WARN("Technology not supported (yet) %d !\n",
913 MidiOutDev[wDevID].caps.wTechnology);
914 return MMSYSERR_NOTENABLED;
917 return MMSYSERR_NOERROR;
920 /**************************************************************************
921 * modLongData [internal]
923 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
925 int len_add = 0;
926 BYTE *lpData, *lpNewData = NULL;
927 snd_seq_event_t event;
929 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
931 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
932 * but it seems to be used only for midi input.
933 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
936 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
937 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
939 if (midiSeq == NULL) {
940 WARN("can't play !\n");
941 return MIDIERR_NODEVICE;
944 lpData = (BYTE*)lpMidiHdr->lpData;
946 if (lpData == NULL)
947 return MIDIERR_UNPREPARED;
948 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
949 return MIDIERR_UNPREPARED;
950 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
951 return MIDIERR_STILLPLAYING;
952 lpMidiHdr->dwFlags &= ~MHDR_DONE;
953 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
955 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
956 * data, or can it also contain raw MIDI data, to be split up and sent to
957 * modShortData() ?
958 * If the latest is true, then the following WARNing will fire up
960 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
961 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
962 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
965 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
966 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
967 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
968 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
970 switch (MidiOutDev[wDevID].caps.wTechnology) {
971 case MOD_FMSYNTH:
972 /* FIXME: I don't think there is much to do here */
973 HeapFree(GetProcessHeap(), 0, lpNewData);
974 break;
975 case MOD_MIDIPORT:
976 if (lpData[0] != 0xF0) {
977 /* Send start of System Exclusive */
978 len_add = 1;
979 lpNewData[0] = 0xF0;
980 memcpy(lpNewData + 1, lpData, lpMidiHdr->dwBufferLength);
981 WARN("Adding missing 0xF0 marker at the beginning of system exclusive byte stream\n");
983 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
984 /* Send end of System Exclusive */
985 if (!len_add)
986 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
987 lpNewData[lpMidiHdr->dwBufferLength + len_add] = 0xF7;
988 len_add++;
989 WARN("Adding missing 0xF7 marker at the end of system exclusive byte stream\n");
991 snd_seq_ev_clear(&event);
992 snd_seq_ev_set_direct(&event);
993 snd_seq_ev_set_source(&event, port_out);
994 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
995 TRACE("destination %d:%d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
996 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
997 snd_seq_event_output_direct(midiSeq, &event);
998 HeapFree(GetProcessHeap(), 0, lpNewData);
999 break;
1000 default:
1001 WARN("Technology not supported (yet) %d !\n",
1002 MidiOutDev[wDevID].caps.wTechnology);
1003 HeapFree(GetProcessHeap(), 0, lpNewData);
1004 return MMSYSERR_NOTENABLED;
1007 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1008 lpMidiHdr->dwFlags |= MHDR_DONE;
1009 MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
1010 return MMSYSERR_NOERROR;
1013 /**************************************************************************
1014 * modPrepare [internal]
1016 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1018 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
1020 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
1021 return MMSYSERR_INVALPARAM;
1022 if (lpMidiHdr->dwFlags & MHDR_PREPARED)
1023 return MMSYSERR_NOERROR;
1025 lpMidiHdr->lpNext = 0;
1026 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1027 lpMidiHdr->dwFlags &= ~(MHDR_DONE|MHDR_INQUEUE); /* flags cleared since w2k */
1028 return MMSYSERR_NOERROR;
1031 /**************************************************************************
1032 * modUnprepare [internal]
1034 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1036 TRACE("(%04X, %p, %d);\n", wDevID, lpMidiHdr, dwSize);
1038 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 || lpMidiHdr->lpData == 0)
1039 return MMSYSERR_INVALPARAM;
1040 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1041 return MMSYSERR_NOERROR;
1042 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1043 return MIDIERR_STILLPLAYING;
1044 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1045 return MMSYSERR_NOERROR;
1048 /**************************************************************************
1049 * modGetVolume [internal]
1051 static DWORD modGetVolume(WORD wDevID, DWORD* lpdwVolume)
1053 if (!lpdwVolume) return MMSYSERR_INVALPARAM;
1054 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1055 *lpdwVolume = 0xFFFFFFFF;
1056 return (MidiOutDev[wDevID].caps.dwSupport & MIDICAPS_VOLUME) ? 0 : MMSYSERR_NOTSUPPORTED;
1059 /**************************************************************************
1060 * modReset [internal]
1062 static DWORD modReset(WORD wDevID)
1064 unsigned chn;
1066 TRACE("(%04X);\n", wDevID);
1068 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1069 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1071 /* stop all notes */
1072 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1073 * it's channel dependent...
1075 for (chn = 0; chn < 16; chn++) {
1076 /* turn off every note */
1077 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1078 /* remove sustain on all channels */
1079 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1081 /* FIXME: the LongData buffers must also be returned to the app */
1082 return MMSYSERR_NOERROR;
1086 /**************************************************************************
1087 * ALSA_AddMidiPort [internal]
1089 * Helper for ALSA_MidiInit
1091 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1093 char midiPortName[MAXPNAMELEN];
1095 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1096 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1097 snd_seq_client_info_get_name(cinfo),
1098 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1099 snd_seq_port_info_get_port(pinfo),
1100 snd_seq_port_info_get_name(pinfo),
1101 type);
1103 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1104 return;
1105 if (!type)
1106 return;
1108 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1110 /* Manufac ID. We do not have access to this with soundcard.h
1111 * Does not seem to be a problem, because in mmsystem.h only
1112 * Microsoft's ID is listed.
1114 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1115 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1116 /* Product Version. We simply say "1" */
1117 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1118 /* The following are mandatory for MOD_MIDIPORT */
1119 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1120 MidiOutDev[MODM_NumDevs].caps.wVoices = 0;
1121 MidiOutDev[MODM_NumDevs].caps.wNotes = 0;
1122 MidiOutDev[MODM_NumDevs].caps.dwSupport = 0;
1124 /* Try to use both client and port names, if this is too long take the port name only.
1125 In the second case the port name should be explicit enough due to its big size.
1127 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1128 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1129 } else {
1130 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1132 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1133 MidiOutDev[MODM_NumDevs].caps.szPname,
1134 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1136 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1138 if (MOD_MIDIPORT != MidiOutDev[MODM_NumDevs].caps.wTechnology) {
1139 /* FIXME Do we have this information?
1140 * Assuming the soundcards can handle
1141 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1142 * not MIDICAPS_CACHE.
1144 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1145 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1147 /* FIXME Is it possible to know the maximum
1148 * number of simultaneous notes of a soundcard ?
1149 * I believe we don't have this information, but
1150 * it's probably equal or more than wVoices
1152 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1154 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1156 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1157 "\tALSA info: midi dev-type=%x, capa=0\n",
1158 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1159 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1160 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1161 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1162 type);
1164 MODM_NumDevs++;
1166 if (cap & SND_SEQ_PORT_CAP_READ) {
1167 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1168 snd_seq_client_info_get_name(cinfo),
1169 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1170 snd_seq_port_info_get_port(pinfo),
1171 snd_seq_port_info_get_name(pinfo),
1172 type);
1174 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1175 return;
1176 if (!type)
1177 return;
1179 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1181 /* Manufac ID. We do not have access to this with soundcard.h
1182 * Does not seem to be a problem, because in mmsystem.h only
1183 * Microsoft's ID is listed.
1185 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1186 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1187 /* Product Version. We simply say "1" */
1188 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1189 MidiInDev[MIDM_NumDevs].caps.dwSupport = 0; /* mandatory with MIDIINCAPS */
1191 /* Try to use both client and port names, if this is too long take the port name only.
1192 In the second case the port name should be explicit enough due to its big size.
1194 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1195 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1196 } else {
1197 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1199 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1200 MidiInDev[MIDM_NumDevs].caps.szPname,
1201 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1202 MidiInDev[MIDM_NumDevs].state = 0;
1204 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1205 "\tALSA info: midi dev-type=%x, capa=0\n",
1206 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1207 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1208 type);
1210 MIDM_NumDevs++;
1215 /*======================================================================*
1216 * MIDI entry points *
1217 *======================================================================*/
1219 /**************************************************************************
1220 * ALSA_MidiInit [internal]
1222 * Initializes the MIDI devices information variables
1224 static LONG ALSA_MidiInit(void)
1226 static BOOL bInitDone = FALSE;
1227 snd_seq_client_info_t *cinfo;
1228 snd_seq_port_info_t *pinfo;
1230 if (bInitDone)
1231 return TRUE;
1233 TRACE("Initializing the MIDI variables.\n");
1234 bInitDone = TRUE;
1236 /* try to open device */
1237 if (midiOpenSeq(0) == -1) {
1238 return TRUE;
1241 #if 0 /* Debug purpose */
1242 snd_lib_error_set_handler(error_handler);
1243 #endif
1244 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1245 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1247 /* First, search for all internal midi devices */
1248 snd_seq_client_info_set_client(cinfo, -1);
1249 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1250 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1251 snd_seq_port_info_set_port(pinfo, -1);
1252 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1253 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1254 unsigned int type = snd_seq_port_info_get_type(pinfo);
1255 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1256 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1260 /* Second, search for all external ports */
1261 snd_seq_client_info_set_client(cinfo, -1);
1262 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1263 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1264 snd_seq_port_info_set_port(pinfo, -1);
1265 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1266 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1267 unsigned int type = snd_seq_port_info_get_type(pinfo);
1268 if (type & SND_SEQ_PORT_TYPE_PORT)
1269 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1273 /* close file and exit */
1274 midiCloseSeq();
1275 HeapFree( GetProcessHeap(), 0, cinfo );
1276 HeapFree( GetProcessHeap(), 0, pinfo );
1278 TRACE("End\n");
1279 return TRUE;
1282 /**************************************************************************
1283 * midMessage (WINEALSA.@)
1285 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1286 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1288 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1289 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1290 switch (wMsg) {
1291 case DRVM_INIT:
1292 ALSA_MidiInit();
1293 return 0;
1294 case DRVM_EXIT:
1295 case DRVM_ENABLE:
1296 case DRVM_DISABLE:
1297 /* FIXME: Pretend this is supported */
1298 return 0;
1299 case MIDM_OPEN:
1300 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1301 case MIDM_CLOSE:
1302 return midClose(wDevID);
1303 case MIDM_ADDBUFFER:
1304 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1305 case MIDM_PREPARE:
1306 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1307 case MIDM_UNPREPARE:
1308 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1309 case MIDM_GETDEVCAPS:
1310 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1311 case MIDM_GETNUMDEVS:
1312 return MIDM_NumDevs;
1313 case MIDM_RESET:
1314 return midReset(wDevID);
1315 case MIDM_START:
1316 return midStart(wDevID);
1317 case MIDM_STOP:
1318 return midStop(wDevID);
1319 default:
1320 TRACE("Unsupported message\n");
1322 return MMSYSERR_NOTSUPPORTED;
1325 /**************************************************************************
1326 * modMessage (WINEALSA.@)
1328 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1329 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1331 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1332 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1334 switch (wMsg) {
1335 case DRVM_INIT:
1336 ALSA_MidiInit();
1337 return 0;
1338 case DRVM_EXIT:
1339 case DRVM_ENABLE:
1340 case DRVM_DISABLE:
1341 /* FIXME: Pretend this is supported */
1342 return 0;
1343 case MODM_OPEN:
1344 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1345 case MODM_CLOSE:
1346 return modClose(wDevID);
1347 case MODM_DATA:
1348 return modData(wDevID, dwParam1);
1349 case MODM_LONGDATA:
1350 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1351 case MODM_PREPARE:
1352 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1353 case MODM_UNPREPARE:
1354 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1355 case MODM_GETDEVCAPS:
1356 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1357 case MODM_GETNUMDEVS:
1358 return MODM_NumDevs;
1359 case MODM_GETVOLUME:
1360 return modGetVolume(wDevID, (DWORD*)dwParam1);
1361 case MODM_SETVOLUME:
1362 return 0;
1363 case MODM_RESET:
1364 return modReset(wDevID);
1365 default:
1366 TRACE("Unsupported message\n");
1368 return MMSYSERR_NOTSUPPORTED;
1371 /**************************************************************************
1372 * DriverProc (WINEALSA.@)
1374 LRESULT CALLBACK ALSA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1375 LPARAM dwParam1, LPARAM dwParam2)
1377 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
1378 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
1380 switch(wMsg) {
1381 case DRV_LOAD:
1382 case DRV_FREE:
1383 case DRV_OPEN:
1384 case DRV_CLOSE:
1385 case DRV_ENABLE:
1386 case DRV_DISABLE:
1387 case DRV_QUERYCONFIGURE:
1388 case DRV_CONFIGURE:
1389 return 1;
1390 case DRV_INSTALL:
1391 case DRV_REMOVE:
1392 return DRV_SUCCESS;
1393 default:
1394 return 0;