explorer: add a navbar to explorer
[wine/gsoc_explorer.git] / dlls / winealsa.drv / midi.c
blob8bd048ee925a715b92722067ba2458cb8a41ebc0
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 "dsdriver.h"
54 #include "alsa.h"
55 #include "wine/debug.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 static int midiOpenSeq(int);
122 static int midiCloseSeq(void);
124 #if 0 /* Debug Purpose */
125 static void error_handler(const char* file, int line, const char* function, int err, const char* fmt, ...)
127 va_list arg;
128 if (err == ENOENT)
129 return;
130 va_start(arg, fmt);
131 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
132 vfprintf(stderr, fmt, arg);
133 if (err)
134 fprintf(stderr, ": %s", snd_strerror(err));
135 putc('\n', stderr);
136 va_end(arg);
138 #endif
140 /**************************************************************************
141 * MIDI_unixToWindowsDeviceType [internal]
143 * return the Windows equivalent to a Unix Device Type
146 static int MIDI_AlsaToWindowsDeviceType(unsigned int type)
148 /* MOD_MIDIPORT output port
149 * MOD_SYNTH generic internal synth
150 * MOD_SQSYNTH square wave internal synth
151 * MOD_FMSYNTH FM internal synth
152 * MOD_MAPPER MIDI mapper
153 * MOD_WAVETABLE hardware watetable internal synth
154 * MOD_SWSYNTH software internal synth
157 /* FIXME Is this really the correct equivalence from ALSA to
158 Windows Sound type */
160 if (type & SND_SEQ_PORT_TYPE_SYNTH)
161 return MOD_FMSYNTH;
163 if (type & (SND_SEQ_PORT_TYPE_DIRECT_SAMPLE|SND_SEQ_PORT_TYPE_SAMPLE))
164 return MOD_SYNTH;
166 if (type & (SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION))
167 return MOD_MIDIPORT;
169 ERR("Cannot determine the type (alsa type is %x) of this midi device. Assuming FM Synth\n", type);
170 return MOD_FMSYNTH;
173 /**************************************************************************
174 * MIDI_NotifyClient [internal]
176 static void MIDI_NotifyClient(UINT wDevID, WORD wMsg,
177 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
179 DWORD dwCallBack;
180 UINT uFlags;
181 HANDLE hDev;
182 DWORD dwInstance;
184 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",
185 wDevID, wMsg, dwParam1, dwParam2);
187 switch (wMsg) {
188 case MOM_OPEN:
189 case MOM_CLOSE:
190 case MOM_DONE:
191 case MOM_POSITIONCB:
192 if (wDevID > MODM_NumDevs) return;
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) return;
209 dwCallBack = MidiInDev[wDevID].midiDesc.dwCallback;
210 uFlags = MidiInDev[wDevID].wFlags;
211 hDev = MidiInDev[wDevID].midiDesc.hMidi;
212 dwInstance = MidiInDev[wDevID].midiDesc.dwInstance;
213 break;
214 default:
215 ERR("Unsupported MSW-MIDI message %u\n", wMsg);
216 return;
219 DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
222 static int midi_warn = 1;
223 /**************************************************************************
224 * midiOpenSeq [internal]
226 static int midiOpenSeq(int create_client)
228 if (numOpenMidiSeq == 0) {
229 if (snd_seq_open(&midiSeq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0)
231 if (midi_warn)
233 WARN("Error opening ALSA sequencer.\n");
235 midi_warn = 0;
236 return -1;
239 if (create_client) {
240 /* Setting the client name is the only init to do */
241 snd_seq_set_client_name(midiSeq, "WINE midi driver");
243 #if 0 /* FIXME: Is it possible to use a port for READ & WRITE ops */
244 port_in = port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input/Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_WRITE,
245 SND_SEQ_PORT_TYPE_APPLICATION);
246 if (port_out < 0)
247 TRACE("Unable to create output port\n");
248 else
249 TRACE("Outport port created successfully (%d)\n", port_out);
250 #else
251 port_out = snd_seq_create_simple_port(midiSeq, "WINE ALSA Output", SND_SEQ_PORT_CAP_READ,
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 created successfully (%d)\n", port_out);
258 port_in = snd_seq_create_simple_port(midiSeq, "WINE ALSA Input", SND_SEQ_PORT_CAP_WRITE,
259 SND_SEQ_PORT_TYPE_APPLICATION);
260 if (port_in < 0)
261 TRACE("Unable to create input port\n");
262 else
263 TRACE("Input port created successfully (%d)\n", port_in);
264 #endif
267 numOpenMidiSeq++;
268 return 0;
271 /**************************************************************************
272 * midiCloseSeq [internal]
274 static int midiCloseSeq(void)
276 if (--numOpenMidiSeq == 0) {
277 snd_seq_delete_simple_port(midiSeq, port_out);
278 snd_seq_delete_simple_port(midiSeq, port_in);
279 snd_seq_close(midiSeq);
280 midiSeq = NULL;
282 return 0;
285 static DWORD WINAPI midRecThread(LPVOID arg)
287 int npfd;
288 struct pollfd *pfd;
290 TRACE("Thread startup\n");
292 while(!end_thread) {
293 TRACE("Thread loop\n");
294 npfd = snd_seq_poll_descriptors_count(midiSeq, POLLIN);
295 pfd = HeapAlloc(GetProcessHeap(), 0, npfd * sizeof(struct pollfd));
296 snd_seq_poll_descriptors(midiSeq, pfd, npfd, POLLIN);
298 /* Check if an event is present */
299 if (poll(pfd, npfd, 250) < 0) {
300 HeapFree(GetProcessHeap(), 0, pfd);
301 continue;
304 /* Note: This definitely does not work.
305 * while(snd_seq_event_input_pending(midiSeq, 0) > 0) {
306 snd_seq_event_t* ev;
307 snd_seq_event_input(midiSeq, &ev);
308 ....................
309 snd_seq_free_event(ev);
312 do {
313 WORD wDevID;
314 snd_seq_event_t* ev;
315 snd_seq_event_input(midiSeq, &ev);
316 /* Find the target device */
317 for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
318 if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
319 break;
320 if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
321 FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
322 else {
323 DWORD dwTime, toSend = 0;
324 int value = 0;
325 /* FIXME: Should use ev->time instead for better accuracy */
326 dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
327 TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
328 switch(ev->type)
330 case SND_SEQ_EVENT_NOTEOFF:
331 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
332 break;
333 case SND_SEQ_EVENT_NOTEON:
334 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
335 break;
336 case SND_SEQ_EVENT_KEYPRESS:
337 toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
338 break;
339 case SND_SEQ_EVENT_CONTROLLER:
340 toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
341 break;
342 case SND_SEQ_EVENT_PITCHBEND:
343 value = ev->data.control.value + 0x2000;
344 toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
345 break;
346 case SND_SEQ_EVENT_PGMCHANGE:
347 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
348 break;
349 case SND_SEQ_EVENT_CHANPRESS:
350 toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
351 break;
352 case SND_SEQ_EVENT_CLOCK:
353 toSend = 0xF8;
354 break;
355 case SND_SEQ_EVENT_START:
356 toSend = 0xFA;
357 break;
358 case SND_SEQ_EVENT_CONTINUE:
359 toSend = 0xFB;
360 break;
361 case SND_SEQ_EVENT_STOP:
362 toSend = 0xFC;
363 break;
364 case SND_SEQ_EVENT_SONGPOS:
365 toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | 0xF2;
366 break;
367 case SND_SEQ_EVENT_SONGSEL:
368 toSend = ((ev->data.control.value & 0x7f) << 8) | 0xF3;
369 break;
370 case SND_SEQ_EVENT_RESET:
371 toSend = 0xFF;
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 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
393 lpMidiHdr->dwFlags |= MHDR_DONE;
394 MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
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("Sending 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 /* Connect our app port to the device port */
482 if (snd_seq_connect_from(midiSeq, port_in, MidiInDev[wDevID].addr.client, MidiInDev[wDevID].addr.port) < 0)
483 return MMSYSERR_NOTENABLED;
485 TRACE("input port connected %d %d %d\n",port_in,MidiInDev[wDevID].addr.client,MidiInDev[wDevID].addr.port);
487 if (numStartedMidiIn++ == 0) {
488 end_thread = 0;
489 hThread = CreateThread(NULL, 0, midRecThread, NULL, 0, NULL);
490 if (!hThread) {
491 numStartedMidiIn = 0;
492 WARN("Couldn't create thread for midi-in\n");
493 midiCloseSeq();
494 return MMSYSERR_ERROR;
496 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
497 TRACE("Created thread for midi-in\n");
500 MidiInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
502 MidiInDev[wDevID].lpQueueHdr = NULL;
503 MidiInDev[wDevID].dwTotalPlayed = 0;
504 MidiInDev[wDevID].bufsize = 0x3FFF;
505 MidiInDev[wDevID].midiDesc = *lpDesc;
506 MidiInDev[wDevID].state = 0;
507 MidiInDev[wDevID].incLen = 0;
508 MidiInDev[wDevID].startTime = 0;
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, %08X);\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, %08X);\n", wDevID, lpMidiHdr, dwSize);
602 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
603 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0)
604 return MMSYSERR_INVALPARAM;
606 lpMidiHdr->lpNext = 0;
607 lpMidiHdr->dwFlags |= MHDR_PREPARED;
608 lpMidiHdr->dwBytesRecorded = 0;
610 return MMSYSERR_NOERROR;
613 /**************************************************************************
614 * midUnprepare [internal]
616 static DWORD midUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
618 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
620 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
621 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
623 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
624 lpMidiHdr->lpData == 0)
625 return MMSYSERR_INVALPARAM;
627 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED;
628 if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING;
630 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
632 return MMSYSERR_NOERROR;
635 /**************************************************************************
636 * midReset [internal]
638 static DWORD midReset(WORD wDevID)
640 DWORD dwTime = GetTickCount();
642 TRACE("(%04X);\n", wDevID);
644 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
645 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
647 EnterCriticalSection(&crit_sect);
648 while (MidiInDev[wDevID].lpQueueHdr) {
649 MidiInDev[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
650 MidiInDev[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
651 /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
652 MIDI_NotifyClient(wDevID, MIM_LONGDATA,
653 (DWORD_PTR)MidiInDev[wDevID].lpQueueHdr, dwTime);
654 MidiInDev[wDevID].lpQueueHdr = MidiInDev[wDevID].lpQueueHdr->lpNext;
656 LeaveCriticalSection(&crit_sect);
658 return MMSYSERR_NOERROR;
661 /**************************************************************************
662 * midStart [internal]
664 static DWORD midStart(WORD wDevID)
666 TRACE("(%04X);\n", wDevID);
668 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
669 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
671 MidiInDev[wDevID].state = 1;
672 MidiInDev[wDevID].startTime = GetTickCount();
673 return MMSYSERR_NOERROR;
676 /**************************************************************************
677 * midStop [internal]
679 static DWORD midStop(WORD wDevID)
681 TRACE("(%04X);\n", wDevID);
683 if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID;
684 if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE;
686 MidiInDev[wDevID].state = 0;
687 return MMSYSERR_NOERROR;
690 /**************************************************************************
691 * modGetDevCaps [internal]
693 static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
695 TRACE("(%04X, %p, %08X);\n", wDevID, lpCaps, dwSize);
697 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
698 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
700 memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
702 return MMSYSERR_NOERROR;
705 /**************************************************************************
706 * modOpen [internal]
708 static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
710 TRACE("(%04X, %p, %08X);\n", wDevID, lpDesc, dwFlags);
711 if (lpDesc == NULL) {
712 WARN("Invalid Parameter !\n");
713 return MMSYSERR_INVALPARAM;
715 if (wDevID >= MODM_NumDevs) {
716 TRACE("MAX_MIDIOUTDRV reached !\n");
717 return MMSYSERR_BADDEVICEID;
719 if (MidiOutDev[wDevID].midiDesc.hMidi != 0) {
720 WARN("device already open !\n");
721 return MMSYSERR_ALLOCATED;
723 if (!MidiOutDev[wDevID].bEnabled) {
724 WARN("device disabled !\n");
725 return MIDIERR_NODEVICE;
727 if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
728 WARN("bad dwFlags\n");
729 return MMSYSERR_INVALFLAG;
731 if (!MidiOutDev[wDevID].bEnabled) {
732 TRACE("disabled wDevID\n");
733 return MMSYSERR_NOTENABLED;
736 MidiOutDev[wDevID].lpExtra = 0;
738 switch (MidiOutDev[wDevID].caps.wTechnology) {
739 case MOD_FMSYNTH:
740 case MOD_MIDIPORT:
741 case MOD_SYNTH:
742 if (midiOpenSeq(1) < 0) {
743 return MMSYSERR_ALLOCATED;
745 break;
746 default:
747 WARN("Technology not supported (yet) %d !\n",
748 MidiOutDev[wDevID].caps.wTechnology);
749 return MMSYSERR_NOTENABLED;
752 MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
754 MidiOutDev[wDevID].lpQueueHdr = NULL;
755 MidiOutDev[wDevID].dwTotalPlayed = 0;
756 MidiOutDev[wDevID].bufsize = 0x3FFF;
757 MidiOutDev[wDevID].midiDesc = *lpDesc;
759 /* Connect our app port to the device port */
760 if (snd_seq_connect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port) < 0)
761 return MMSYSERR_NOTENABLED;
763 MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
764 TRACE("Successful !\n");
765 return MMSYSERR_NOERROR;
769 /**************************************************************************
770 * modClose [internal]
772 static DWORD modClose(WORD wDevID)
774 int ret = MMSYSERR_NOERROR;
776 TRACE("(%04X);\n", wDevID);
778 if (MidiOutDev[wDevID].midiDesc.hMidi == 0) {
779 WARN("device not opened !\n");
780 return MMSYSERR_ERROR;
782 /* FIXME: should test that no pending buffer is still in the queue for
783 * playing */
785 if (midiSeq == NULL) {
786 WARN("can't close !\n");
787 return MMSYSERR_ERROR;
790 switch (MidiOutDev[wDevID].caps.wTechnology) {
791 case MOD_FMSYNTH:
792 case MOD_MIDIPORT:
793 case MOD_SYNTH:
794 snd_seq_disconnect_to(midiSeq, port_out, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
795 midiCloseSeq();
796 break;
797 default:
798 WARN("Technology not supported (yet) %d !\n",
799 MidiOutDev[wDevID].caps.wTechnology);
800 return MMSYSERR_NOTENABLED;
803 HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra);
804 MidiOutDev[wDevID].lpExtra = 0;
806 MidiOutDev[wDevID].bufsize = 0;
807 MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
808 MidiOutDev[wDevID].midiDesc.hMidi = 0;
809 return ret;
812 /**************************************************************************
813 * modData [internal]
815 static DWORD modData(WORD wDevID, DWORD dwParam)
817 BYTE evt = LOBYTE(LOWORD(dwParam));
818 BYTE d1 = HIBYTE(LOWORD(dwParam));
819 BYTE d2 = LOBYTE(HIWORD(dwParam));
821 TRACE("(%04X, %08X);\n", wDevID, dwParam);
823 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
824 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
826 if (midiSeq == NULL) {
827 WARN("can't play !\n");
828 return MIDIERR_NODEVICE;
830 switch (MidiOutDev[wDevID].caps.wTechnology) {
831 case MOD_SYNTH:
832 case MOD_MIDIPORT:
834 int handled = 1; /* Assume event is handled */
835 snd_seq_event_t event;
836 snd_seq_ev_clear(&event);
837 snd_seq_ev_set_direct(&event);
838 snd_seq_ev_set_source(&event, port_out);
839 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
841 switch (evt & 0xF0) {
842 case MIDI_CMD_NOTE_OFF:
843 snd_seq_ev_set_noteoff(&event, evt&0x0F, d1, d2);
844 break;
845 case MIDI_CMD_NOTE_ON:
846 snd_seq_ev_set_noteon(&event, evt&0x0F, d1, d2);
847 break;
848 case MIDI_CMD_NOTE_PRESSURE:
849 snd_seq_ev_set_keypress(&event, evt&0x0F, d1, d2);
850 break;
851 case MIDI_CMD_CONTROL:
852 snd_seq_ev_set_controller(&event, evt&0x0F, d1, d2);
853 break;
854 case MIDI_CMD_BENDER:
855 snd_seq_ev_set_pitchbend(&event, evt&0x0F, ((WORD)d2 << 7 | (WORD)d1) - 0x2000);
856 break;
857 case MIDI_CMD_PGM_CHANGE:
858 snd_seq_ev_set_pgmchange(&event, evt&0x0F, d1);
859 break;
860 case MIDI_CMD_CHANNEL_PRESSURE:
861 snd_seq_ev_set_chanpress(&event, evt&0x0F, d1);
862 break;
863 case MIDI_CMD_COMMON_SYSEX:
864 switch (evt & 0x0F) {
865 case 0x00: /* System Exclusive, don't do it on modData,
866 * should require modLongData*/
867 case 0x01: /* Undefined */
868 case 0x04: /* Undefined. */
869 case 0x05: /* Undefined. */
870 case 0x07: /* End of Exclusive. */
871 case 0x09: /* Undefined. */
872 case 0x0D: /* Undefined. */
873 handled = 0;
874 break;
875 case 0x06: /* Tune Request */
876 case 0x08: /* Timing Clock. */
877 case 0x0A: /* Start. */
878 case 0x0B: /* Continue */
879 case 0x0C: /* Stop */
880 case 0x0E: /* Active Sensing. */
881 /* FIXME: Is this function suitable for these purposes
882 (and also Song Select and Song Position Pointer) */
883 snd_seq_ev_set_sysex(&event, 1, &evt);
884 break;
885 case 0x0F: /* Reset */
886 /* snd_seq_ev_set_sysex(&event, 1, &evt);
887 this other way may be better */
889 BYTE reset_sysex_seq[] = {MIDI_CMD_COMMON_SYSEX, 0x7e, 0x7f, 0x09, 0x01, 0xf7};
890 snd_seq_ev_set_sysex(&event, sizeof(reset_sysex_seq), reset_sysex_seq);
892 break;
893 case 0x03: /* Song Select. */
895 BYTE buf[2];
896 buf[0] = evt;
897 buf[1] = d1;
898 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
900 break;
901 case 0x02: /* Song Position Pointer. */
903 BYTE buf[3];
904 buf[0] = evt;
905 buf[1] = d1;
906 buf[2] = d2;
907 snd_seq_ev_set_sysex(&event, sizeof(buf), buf);
909 break;
911 break;
913 if (handled)
914 snd_seq_event_output_direct(midiSeq, &event);
916 break;
917 default:
918 WARN("Technology not supported (yet) %d !\n",
919 MidiOutDev[wDevID].caps.wTechnology);
920 return MMSYSERR_NOTENABLED;
923 return MMSYSERR_NOERROR;
926 /**************************************************************************
927 * modLongData [internal]
929 static DWORD modLongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
931 int len_add = 0;
932 LPBYTE lpData, lpNewData = NULL;
933 snd_seq_event_t event;
935 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
937 /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
938 * but it seems to be used only for midi input.
939 * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
942 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
943 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
945 if (midiSeq == NULL) {
946 WARN("can't play !\n");
947 return MIDIERR_NODEVICE;
950 lpData = (LPBYTE) lpMidiHdr->lpData;
952 if (lpData == NULL)
953 return MIDIERR_UNPREPARED;
954 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
955 return MIDIERR_UNPREPARED;
956 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
957 return MIDIERR_STILLPLAYING;
958 lpMidiHdr->dwFlags &= ~MHDR_DONE;
959 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
961 /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
962 * data, or can it also contain raw MIDI data, to be split up and sent to
963 * modShortData() ?
964 * If the latest is true, then the following WARNing will fire up
966 if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
967 WARN("Alleged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
968 lpNewData = HeapAlloc(GetProcessHeap(), 0, lpMidiHdr->dwBufferLength + 2);
971 TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
972 TRACE(" %02X %02X %02X ... %02X %02X %02X\n",
973 lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
974 lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
976 switch (MidiOutDev[wDevID].caps.wTechnology) {
977 case MOD_FMSYNTH:
978 /* FIXME: I don't think there is much to do here */
979 break;
980 case MOD_MIDIPORT:
981 if (lpData[0] != 0xF0) {
982 /* Send start of System Exclusive */
983 len_add = 1;
984 lpData[0] = 0xF0;
985 memcpy(lpNewData, lpData, lpMidiHdr->dwBufferLength);
986 WARN("Adding missing 0xF0 marker at the beginning of "
987 "system exclusive byte stream\n");
989 if (lpData[lpMidiHdr->dwBufferLength-1] != 0xF7) {
990 /* Send end of System Exclusive */
991 memcpy(lpData + len_add, lpData, lpMidiHdr->dwBufferLength);
992 lpNewData[lpMidiHdr->dwBufferLength + len_add - 1] = 0xF0;
993 len_add++;
994 WARN("Adding missing 0xF7 marker at the end of "
995 "system exclusive byte stream\n");
997 snd_seq_ev_clear(&event);
998 snd_seq_ev_set_direct(&event);
999 snd_seq_ev_set_source(&event, port_out);
1000 snd_seq_ev_set_dest(&event, MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1001 TRACE("client = %d port = %d\n", MidiOutDev[wDevID].addr.client, MidiOutDev[wDevID].addr.port);
1002 snd_seq_ev_set_sysex(&event, lpMidiHdr->dwBufferLength + len_add, lpNewData ? lpNewData : lpData);
1003 snd_seq_event_output_direct(midiSeq, &event);
1004 if (lpNewData)
1005 HeapFree(GetProcessHeap(), 0, lpData);
1006 break;
1007 default:
1008 WARN("Technology not supported (yet) %d !\n",
1009 MidiOutDev[wDevID].caps.wTechnology);
1010 HeapFree(GetProcessHeap(), 0, lpNewData);
1011 return MMSYSERR_NOTENABLED;
1014 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1015 lpMidiHdr->dwFlags |= MHDR_DONE;
1016 MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
1017 return MMSYSERR_NOERROR;
1020 /**************************************************************************
1021 * modPrepare [internal]
1023 static DWORD modPrepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1025 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1027 if (midiSeq == NULL) {
1028 WARN("can't prepare !\n");
1029 return MMSYSERR_NOTENABLED;
1032 /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
1033 * asks to prepare MIDIHDR which dwFlags != 0.
1034 * So at least check for the inqueue flag
1036 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
1037 lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0) {
1038 WARN("%p %p %08x %d\n", lpMidiHdr, lpMidiHdr ? lpMidiHdr->lpData : NULL,
1039 lpMidiHdr ? lpMidiHdr->dwFlags : 0, dwSize);
1040 return MMSYSERR_INVALPARAM;
1043 lpMidiHdr->lpNext = 0;
1044 lpMidiHdr->dwFlags |= MHDR_PREPARED;
1045 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1046 return MMSYSERR_NOERROR;
1049 /**************************************************************************
1050 * modUnprepare [internal]
1052 static DWORD modUnprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
1054 TRACE("(%04X, %p, %08X);\n", wDevID, lpMidiHdr, dwSize);
1056 if (midiSeq == NULL) {
1057 WARN("can't unprepare !\n");
1058 return MMSYSERR_NOTENABLED;
1061 if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0)
1062 return MMSYSERR_INVALPARAM;
1063 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1064 return MIDIERR_STILLPLAYING;
1065 lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
1066 return MMSYSERR_NOERROR;
1069 /**************************************************************************
1070 * modGetVolume [internal]
1072 static DWORD modGetVolume(WORD wDevID, DWORD* lpdwVolume)
1074 if (!lpdwVolume) return MMSYSERR_INVALPARAM;
1075 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1076 *lpdwVolume = 0xFFFFFFFF;
1077 return (MidiOutDev[wDevID].caps.dwSupport & MIDICAPS_VOLUME) ? 0 : MMSYSERR_NOTSUPPORTED;
1080 /**************************************************************************
1081 * modReset [internal]
1083 static DWORD modReset(WORD wDevID)
1085 unsigned chn;
1087 TRACE("(%04X);\n", wDevID);
1089 if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID;
1090 if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE;
1092 /* stop all notes */
1093 /* FIXME: check if 0x78B0 is channel dependent or not. I coded it so that
1094 * it's channel dependent...
1096 for (chn = 0; chn < 16; chn++) {
1097 /* turn off every note */
1098 modData(wDevID, 0x7800 | MIDI_CMD_CONTROL | chn);
1099 /* remove sustain on all channels */
1100 modData(wDevID, (MIDI_CTL_SUSTAIN << 8) | MIDI_CMD_CONTROL | chn);
1102 /* FIXME: the LongData buffers must also be returned to the app */
1103 return MMSYSERR_NOERROR;
1107 /**************************************************************************
1108 * ALSA_AddMidiPort [internal]
1110 * Helper for ALSA_MidiInit
1112 static void ALSA_AddMidiPort(snd_seq_client_info_t* cinfo, snd_seq_port_info_t* pinfo, unsigned int cap, unsigned int type)
1114 char midiPortName[MAXPNAMELEN];
1116 if (cap & SND_SEQ_PORT_CAP_WRITE) {
1117 TRACE("OUT (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1118 snd_seq_client_info_get_name(cinfo),
1119 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1120 snd_seq_port_info_get_port(pinfo),
1121 snd_seq_port_info_get_name(pinfo),
1122 type);
1124 if (MODM_NumDevs >= MAX_MIDIOUTDRV)
1125 return;
1126 if (!type)
1127 return;
1129 MidiOutDev[MODM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1131 /* Manufac ID. We do not have access to this with soundcard.h
1132 * Does not seem to be a problem, because in mmsystem.h only
1133 * Microsoft's ID is listed.
1135 MidiOutDev[MODM_NumDevs].caps.wMid = 0x00FF;
1136 MidiOutDev[MODM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1137 /* Product Version. We simply say "1" */
1138 MidiOutDev[MODM_NumDevs].caps.vDriverVersion = 0x001;
1139 /* The following are mandatory for MOD_MIDIPORT */
1140 MidiOutDev[MODM_NumDevs].caps.wChannelMask = 0xFFFF;
1141 MidiOutDev[MODM_NumDevs].caps.wVoices = 0;
1142 MidiOutDev[MODM_NumDevs].caps.wNotes = 0;
1143 MidiOutDev[MODM_NumDevs].caps.dwSupport = 0;
1145 /* Try to use both client and port names, if this is too long take the port name only.
1146 In the second case the port name should be explicit enough due to its big size.
1148 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1149 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1150 } else {
1151 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1153 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1154 MidiOutDev[MODM_NumDevs].caps.szPname,
1155 sizeof(MidiOutDev[MODM_NumDevs].caps.szPname) / sizeof(WCHAR));
1157 MidiOutDev[MODM_NumDevs].caps.wTechnology = MIDI_AlsaToWindowsDeviceType(type);
1159 if (MOD_MIDIPORT != MidiOutDev[MODM_NumDevs].caps.wTechnology) {
1160 /* FIXME Do we have this information?
1161 * Assuming the soundcards can handle
1162 * MIDICAPS_VOLUME and MIDICAPS_LRVOLUME but
1163 * not MIDICAPS_CACHE.
1165 MidiOutDev[MODM_NumDevs].caps.dwSupport = MIDICAPS_VOLUME|MIDICAPS_LRVOLUME;
1166 MidiOutDev[MODM_NumDevs].caps.wVoices = 16;
1168 /* FIXME Is it possible to know the maximum
1169 * number of simultaneous notes of a soundcard ?
1170 * I believe we don't have this information, but
1171 * it's probably equal or more than wVoices
1173 MidiOutDev[MODM_NumDevs].caps.wNotes = 16;
1175 MidiOutDev[MODM_NumDevs].bEnabled = TRUE;
1177 TRACE("MidiOut[%d]\tname='%s' techn=%d voices=%d notes=%d chnMsk=%04x support=%d\n"
1178 "\tALSA info: midi dev-type=%x, capa=0\n",
1179 MODM_NumDevs, wine_dbgstr_w(MidiOutDev[MODM_NumDevs].caps.szPname),
1180 MidiOutDev[MODM_NumDevs].caps.wTechnology,
1181 MidiOutDev[MODM_NumDevs].caps.wVoices, MidiOutDev[MODM_NumDevs].caps.wNotes,
1182 MidiOutDev[MODM_NumDevs].caps.wChannelMask, MidiOutDev[MODM_NumDevs].caps.dwSupport,
1183 type);
1185 MODM_NumDevs++;
1187 if (cap & SND_SEQ_PORT_CAP_READ) {
1188 TRACE("IN (%d:%s:%s:%d:%s:%x)\n",snd_seq_client_info_get_client(cinfo),
1189 snd_seq_client_info_get_name(cinfo),
1190 snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ? "user" : "kernel",
1191 snd_seq_port_info_get_port(pinfo),
1192 snd_seq_port_info_get_name(pinfo),
1193 type);
1195 if (MIDM_NumDevs >= MAX_MIDIINDRV)
1196 return;
1197 if (!type)
1198 return;
1200 MidiInDev[MIDM_NumDevs].addr = *snd_seq_port_info_get_addr(pinfo);
1202 /* Manufac ID. We do not have access to this with soundcard.h
1203 * Does not seem to be a problem, because in mmsystem.h only
1204 * Microsoft's ID is listed.
1206 MidiInDev[MIDM_NumDevs].caps.wMid = 0x00FF;
1207 MidiInDev[MIDM_NumDevs].caps.wPid = 0x0001; /* FIXME Product ID */
1208 /* Product Version. We simply say "1" */
1209 MidiInDev[MIDM_NumDevs].caps.vDriverVersion = 0x001;
1210 MidiInDev[MIDM_NumDevs].caps.dwSupport = 0; /* mandatory with MIDIINCAPS */
1212 /* Try to use both client and port names, if this is too long take the port name only.
1213 In the second case the port name should be explicit enough due to its big size.
1215 if ( (strlen(snd_seq_client_info_get_name(cinfo)) + strlen(snd_seq_port_info_get_name(pinfo)) + 3) < MAXPNAMELEN ) {
1216 sprintf(midiPortName, "%s - %s", snd_seq_client_info_get_name(cinfo), snd_seq_port_info_get_name(pinfo));
1217 } else {
1218 lstrcpynA(midiPortName, snd_seq_port_info_get_name(pinfo), MAXPNAMELEN);
1220 MultiByteToWideChar(CP_UNIXCP, 0, midiPortName, -1,
1221 MidiInDev[MIDM_NumDevs].caps.szPname,
1222 sizeof(MidiInDev[MIDM_NumDevs].caps.szPname) / sizeof(WCHAR));
1223 MidiInDev[MIDM_NumDevs].state = 0;
1225 TRACE("MidiIn [%d]\tname='%s' support=%d\n"
1226 "\tALSA info: midi dev-type=%x, capa=0\n",
1227 MIDM_NumDevs, wine_dbgstr_w(MidiInDev[MIDM_NumDevs].caps.szPname),
1228 MidiInDev[MIDM_NumDevs].caps.dwSupport,
1229 type);
1231 MIDM_NumDevs++;
1236 /*======================================================================*
1237 * MIDI entry points *
1238 *======================================================================*/
1240 /**************************************************************************
1241 * ALSA_MidiInit [internal]
1243 * Initializes the MIDI devices information variables
1245 static LONG ALSA_MidiInit(void)
1247 static BOOL bInitDone = FALSE;
1248 snd_seq_client_info_t *cinfo;
1249 snd_seq_port_info_t *pinfo;
1251 if (bInitDone)
1252 return TRUE;
1254 TRACE("Initializing the MIDI variables.\n");
1255 bInitDone = TRUE;
1257 /* try to open device */
1258 if (midiOpenSeq(0) == -1) {
1259 return TRUE;
1262 #if 0 /* Debug purpose */
1263 snd_lib_error_set_handler(error_handler);
1264 #endif
1265 cinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_client_info_sizeof() );
1266 pinfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_seq_port_info_sizeof() );
1268 /* First, search for all internal midi devices */
1269 snd_seq_client_info_set_client(cinfo, -1);
1270 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1271 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1272 snd_seq_port_info_set_port(pinfo, -1);
1273 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1274 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1275 unsigned int type = snd_seq_port_info_get_type(pinfo);
1276 if (!(type & SND_SEQ_PORT_TYPE_PORT))
1277 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1281 /* Second, search for all external ports */
1282 snd_seq_client_info_set_client(cinfo, -1);
1283 while(snd_seq_query_next_client(midiSeq, cinfo) >= 0) {
1284 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1285 snd_seq_port_info_set_port(pinfo, -1);
1286 while (snd_seq_query_next_port(midiSeq, pinfo) >= 0) {
1287 unsigned int cap = snd_seq_port_info_get_capability(pinfo);
1288 unsigned int type = snd_seq_port_info_get_type(pinfo);
1289 if (type & SND_SEQ_PORT_TYPE_PORT)
1290 ALSA_AddMidiPort(cinfo, pinfo, cap, type);
1294 /* close file and exit */
1295 midiCloseSeq();
1296 HeapFree( GetProcessHeap(), 0, cinfo );
1297 HeapFree( GetProcessHeap(), 0, pinfo );
1299 TRACE("End\n");
1300 return TRUE;
1303 /**************************************************************************
1304 * midMessage (WINEALSA.@)
1306 DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1307 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1309 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1310 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1311 switch (wMsg) {
1312 case DRVM_INIT:
1313 ALSA_MidiInit();
1314 case DRVM_EXIT:
1315 case DRVM_ENABLE:
1316 case DRVM_DISABLE:
1317 /* FIXME: Pretend this is supported */
1318 return 0;
1319 case MIDM_OPEN:
1320 return midOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1321 case MIDM_CLOSE:
1322 return midClose(wDevID);
1323 case MIDM_ADDBUFFER:
1324 return midAddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1325 case MIDM_PREPARE:
1326 return midPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1327 case MIDM_UNPREPARE:
1328 return midUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1329 case MIDM_GETDEVCAPS:
1330 return midGetDevCaps(wDevID, (LPMIDIINCAPSW)dwParam1,dwParam2);
1331 case MIDM_GETNUMDEVS:
1332 return MIDM_NumDevs;
1333 case MIDM_RESET:
1334 return midReset(wDevID);
1335 case MIDM_START:
1336 return midStart(wDevID);
1337 case MIDM_STOP:
1338 return midStop(wDevID);
1339 default:
1340 TRACE("Unsupported message\n");
1342 return MMSYSERR_NOTSUPPORTED;
1345 /**************************************************************************
1346 * modMessage (WINEALSA.@)
1348 DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
1349 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1351 TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
1352 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1354 switch (wMsg) {
1355 case DRVM_INIT:
1356 ALSA_MidiInit();
1357 case DRVM_EXIT:
1358 case DRVM_ENABLE:
1359 case DRVM_DISABLE:
1360 /* FIXME: Pretend this is supported */
1361 return 0;
1362 case MODM_OPEN:
1363 return modOpen(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1364 case MODM_CLOSE:
1365 return modClose(wDevID);
1366 case MODM_DATA:
1367 return modData(wDevID, dwParam1);
1368 case MODM_LONGDATA:
1369 return modLongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1370 case MODM_PREPARE:
1371 return modPrepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1372 case MODM_UNPREPARE:
1373 return modUnprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1374 case MODM_GETDEVCAPS:
1375 return modGetDevCaps(wDevID, (LPMIDIOUTCAPSW)dwParam1, dwParam2);
1376 case MODM_GETNUMDEVS:
1377 return MODM_NumDevs;
1378 case MODM_GETVOLUME:
1379 return modGetVolume(wDevID, (DWORD*)dwParam1);
1380 case MODM_SETVOLUME:
1381 return 0;
1382 case MODM_RESET:
1383 return modReset(wDevID);
1384 default:
1385 TRACE("Unsupported message\n");
1387 return MMSYSERR_NOTSUPPORTED;
1390 /*-----------------------------------------------------------------------*/