winecoreaudio: Fix an error check (bad copy-paste).
[wine] / dlls / winecoreaudio.drv / midi.c
1 /*
2  * Sample MIDI Wine Driver for Mac OS X (based on OSS midi driver)
3  *
4  * Copyright 1994       Martin Ayotte
5  * Copyright 1998       Luiz Otavio L. Zorzella (init procedures)
6  * Copyright 1998/1999  Eric POUECH :
7  *              98/7    changes for making this MIDI driver work on OSS
8  *                      current support is limited to MIDI ports of OSS systems
9  *              98/9    rewriting MCI code for MIDI
10  *              98/11   split in midi.c and mcimidi.c
11  * Copyright 2006       Emmanuel Maillard
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public
15  * License as published by the Free Software Foundation; either
16  * version 2.1 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public
24  * License along with this library; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26  */
27
28 #include "config.h"
29 #include "wine/port.h"
30
31 #include <string.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34
35 #include "windef.h"
36 #include "winbase.h"
37 #include "wingdi.h"
38 #include "winuser.h"
39 #include "winnls.h"
40 #include "mmddk.h"
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
43 #include "coreaudio.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(midi);
46
47 #if defined(HAVE_COREAUDIO_COREAUDIO_H)
48 #include <CoreAudio/CoreAudio.h>
49
50 #define WINE_DEFINITIONS
51 #include "coremidi.h"
52
53 static MIDIClientRef wineMIDIClient = NULL;
54
55 static DWORD MIDIOut_NumDevs = 0;
56 static DWORD MIDIIn_NumDevs = 0;
57
58 typedef struct tagMIDIDestination {
59     /* graph and synth are only used for MIDI Synth */
60     AUGraph graph;
61     AudioUnit synth;
62
63     MIDIEndpointRef dest;
64
65     MIDIOUTCAPSW caps;
66     MIDIOPENDESC midiDesc;
67     WORD wFlags;
68 } MIDIDestination;
69
70 typedef struct tagMIDISource {
71     MIDIEndpointRef source;
72
73     WORD wDevID;
74     int state; /* 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
75     MIDIINCAPSW caps;
76     MIDIOPENDESC midiDesc;
77     LPMIDIHDR lpQueueHdr;
78     WORD wFlags;
79     DWORD startTime;
80 } MIDISource;
81
82 static CRITICAL_SECTION midiInLock; /* Critical section for MIDI In */
83 static CFStringRef MIDIInThreadPortName = NULL;
84
85 static DWORD WINAPI MIDIIn_MessageThread(LPVOID p);
86
87 static MIDIPortRef MIDIInPort = NULL;
88 static MIDIPortRef MIDIOutPort = NULL;
89
90 #define MAX_MIDI_SYNTHS 1
91
92 MIDIDestination *destinations;
93 MIDISource *sources;
94
95 extern int SynthUnit_CreateDefaultSynthUnit(AUGraph *graph, AudioUnit *synth);
96 extern int SynthUnit_Initialize(AudioUnit synth, AUGraph graph);
97 extern int SynthUnit_Close(AUGraph graph);
98
99
100 LONG CoreAudio_MIDIInit(void)
101 {
102     int i;
103     CHAR szPname[MAXPNAMELEN] = {0};
104
105     int numDest = MIDIGetNumberOfDestinations();
106     CFStringRef name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("wineMIDIClient.%d"), getpid());
107
108     wineMIDIClient = CoreMIDI_CreateClient( name );
109     if (wineMIDIClient == NULL)
110     {
111         CFRelease(name);
112         ERR("can't create wineMIDIClient\n");
113         return DRV_FAILURE;
114     }
115     CFRelease(name);
116
117     MIDIOut_NumDevs = MAX_MIDI_SYNTHS;
118     MIDIOut_NumDevs += numDest;
119
120     MIDIIn_NumDevs = MIDIGetNumberOfSources();
121
122     TRACE("MIDIOut_NumDevs %d MIDIIn_NumDevs %d\n", MIDIOut_NumDevs, MIDIIn_NumDevs);
123
124     destinations = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MIDIOut_NumDevs * sizeof(MIDIDestination));
125     sources = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MIDIIn_NumDevs * sizeof(MIDISource));
126
127     if (MIDIIn_NumDevs > 0)
128     {
129         InitializeCriticalSection(&midiInLock);
130         MIDIInThreadPortName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("MIDIInThreadPortName.%u"), getpid());
131         CreateThread(NULL, 0, MIDIIn_MessageThread, NULL, 0, NULL);
132
133         name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("WineInputPort.%u"), getpid());
134         MIDIInputPortCreate(wineMIDIClient, name, MIDIIn_ReadProc, NULL, &MIDIInPort);
135         CFRelease(name);
136     }
137     if (numDest > 0)
138     {
139         name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("WineOutputPort.%u"), getpid());
140         MIDIOutputPortCreate(wineMIDIClient, name, &MIDIOutPort);
141         CFRelease(name);
142     }
143
144     /* initialize sources */
145     for (i = 0; i < MIDIIn_NumDevs; i++)
146     {
147         sources[i].wDevID = i;
148         sources[i].source = MIDIGetSource(i);
149
150         CoreMIDI_GetObjectName(sources[i].source, szPname, sizeof(szPname));
151         MultiByteToWideChar(CP_ACP, 0, szPname, -1, sources[i].caps.szPname, sizeof(sources[i].caps.szPname)/sizeof(WCHAR));
152
153         MIDIPortConnectSource(MIDIInPort, sources[i].source, &sources[i].wDevID);
154
155         sources[i].state = 0;
156         /* FIXME */
157         sources[i].caps.wMid = 0x00FF;  /* Manufac ID */
158         sources[i].caps.wPid = 0x0001;  /* Product ID */
159         sources[i].caps.vDriverVersion = 0x0001;
160         sources[i].caps.dwSupport = 0;
161     }
162
163     /* initialise MIDI synths */
164     for (i = 0; i < MAX_MIDI_SYNTHS; i++)
165     {
166         snprintf(szPname, sizeof(szPname), "CoreAudio MIDI Synth %d", i);
167         MultiByteToWideChar(CP_ACP, 0, szPname, -1, destinations[i].caps.szPname, sizeof(destinations[i].caps.szPname)/sizeof(WCHAR));
168
169         destinations[i].caps.wTechnology = MOD_SYNTH;
170         destinations[i].caps.wChannelMask = 0xFFFF;
171
172         destinations[i].caps.wMid = 0x00FF;     /* Manufac ID */
173         destinations[i].caps.wPid = 0x0001;     /* Product ID */
174         destinations[i].caps.vDriverVersion = 0x0001;
175         destinations[i].caps.dwSupport = MIDICAPS_VOLUME;
176         destinations[i].caps.wVoices = 16;
177         destinations[i].caps.wNotes = 16;
178     }
179     /* initialise available destinations */
180     for (i = MAX_MIDI_SYNTHS; i < numDest + MAX_MIDI_SYNTHS; i++)
181     {
182         destinations[i].dest = MIDIGetDestination(i - MAX_MIDI_SYNTHS);
183
184         CoreMIDI_GetObjectName(destinations[i].dest, szPname, sizeof(szPname));
185         MultiByteToWideChar(CP_ACP, 0, szPname, -1, destinations[i].caps.szPname, sizeof(destinations[i].caps.szPname)/sizeof(WCHAR));
186
187         destinations[i].caps.wTechnology = MOD_MIDIPORT;
188         destinations[i].caps.wChannelMask = 0xFFFF;
189
190         destinations[i].caps.wMid = 0x00FF;     /* Manufac ID */
191         destinations[i].caps.wPid = 0x0001;
192         destinations[i].caps.vDriverVersion = 0x0001;
193         destinations[i].caps.dwSupport = 0;
194         destinations[i].caps.wVoices = 16;
195         destinations[i].caps.wNotes = 16;
196     }
197     return DRV_SUCCESS;
198 }
199
200 LONG CoreAudio_MIDIRelease(void)
201 {
202     TRACE("\n");
203     if (MIDIIn_NumDevs > 0)
204     {
205         CFMessagePortRef messagePort;
206         /* Stop CFRunLoop in MIDIIn_MessageThread */
207         messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, MIDIInThreadPortName);
208         CFMessagePortSendRequest(messagePort, 1, NULL, 0.0, 0.0, NULL, NULL);
209         CFRelease(messagePort);
210
211         DeleteCriticalSection(&midiInLock);
212     }
213
214     if (wineMIDIClient) MIDIClientDispose(wineMIDIClient); /* MIDIClientDispose will close all ports */
215
216     HeapFree(GetProcessHeap(), 0, sources);
217     HeapFree(GetProcessHeap(), 0, destinations);
218     return DRV_SUCCESS;
219 }
220
221
222 /**************************************************************************
223  *                      MIDI_NotifyClient                       [internal]
224  */
225 static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
226 {
227     DWORD               dwCallBack;
228     UINT                uFlags;
229     HANDLE              hDev;
230     DWORD               dwInstance;
231
232     TRACE("wDevID=%d wMsg=%d dwParm1=%04X dwParam2=%04X\n", wDevID, wMsg, dwParam1, dwParam2);
233
234     switch (wMsg) {
235     case MOM_OPEN:
236     case MOM_CLOSE:
237     case MOM_DONE:
238     case MOM_POSITIONCB:
239         dwCallBack = destinations[wDevID].midiDesc.dwCallback;
240         uFlags = destinations[wDevID].wFlags;
241         hDev = destinations[wDevID].midiDesc.hMidi;
242         dwInstance = destinations[wDevID].midiDesc.dwInstance;
243         break;
244
245     case MIM_OPEN:
246     case MIM_CLOSE:
247     case MIM_DATA:
248     case MIM_LONGDATA:
249     case MIM_ERROR:
250     case MIM_LONGERROR:
251     case MIM_MOREDATA:
252         dwCallBack = sources[wDevID].midiDesc.dwCallback;
253         uFlags = sources[wDevID].wFlags;
254         hDev = sources[wDevID].midiDesc.hMidi;
255         dwInstance = sources[wDevID].midiDesc.dwInstance;
256         break;
257     default:
258         WARN("Unsupported MSW-MIDI message %u\n", wMsg);
259         return MMSYSERR_ERROR;
260     }
261
262     return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
263         MMSYSERR_NOERROR : MMSYSERR_ERROR;
264 }
265
266 static DWORD MIDIOut_Open(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
267 {
268     MIDIDestination *dest;
269
270     TRACE("wDevID=%d lpDesc=%p dwFlags=%08x\n", wDevID, lpDesc, dwFlags);
271
272     if (lpDesc == NULL) {
273         WARN("Invalid Parameter\n");
274         return MMSYSERR_INVALPARAM;
275     }
276
277     if (wDevID >= MIDIOut_NumDevs) {
278         WARN("bad device ID : %d\n", wDevID);
279         return MMSYSERR_BADDEVICEID;
280     }
281
282     if (destinations[wDevID].midiDesc.hMidi != 0) {
283         WARN("device already open !\n");
284         return MMSYSERR_ALLOCATED;
285     }
286
287     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
288         WARN("bad dwFlags\n");
289         return MMSYSERR_INVALFLAG;
290     }
291     dest = &destinations[wDevID];
292
293     if (dest->caps.wTechnology == MOD_SYNTH)
294     {
295         if (!SynthUnit_CreateDefaultSynthUnit(&dest->graph, &dest->synth))
296         {
297             ERR("SynthUnit_CreateDefaultSynthUnit dest=%p failed\n", dest);
298             return MMSYSERR_ERROR;
299         }
300
301         if (!SynthUnit_Initialize(dest->synth, dest->graph))
302         {
303             ERR("SynthUnit_Initialise dest=%p failed\n", dest);
304             return MMSYSERR_ERROR;
305         }
306     }
307     dest->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
308     dest->midiDesc = *lpDesc;
309
310     return MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
311 }
312
313 static DWORD MIDIOut_Close(WORD wDevID)
314 {
315     DWORD ret = MMSYSERR_NOERROR;
316
317     TRACE("wDevID=%d\n", wDevID);
318
319     if (wDevID >= MIDIOut_NumDevs) {
320         WARN("bad device ID : %d\n", wDevID);
321         return MMSYSERR_BADDEVICEID;
322     }
323
324     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
325         SynthUnit_Close(destinations[wDevID].graph);
326
327     destinations[wDevID].graph = 0;
328     destinations[wDevID].synth = 0;
329
330     if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
331         WARN("can't notify client !\n");
332         ret = MMSYSERR_INVALPARAM;
333     }
334     destinations[wDevID].midiDesc.hMidi = 0;
335
336     return ret;
337 }
338
339 static DWORD MIDIOut_Data(WORD wDevID, DWORD dwParam)
340 {
341     WORD evt = LOBYTE(LOWORD(dwParam));
342     UInt8 chn = (evt & 0x0F);
343
344     TRACE("wDevID=%d dwParam=%08X\n", wDevID, dwParam);
345
346     if (wDevID >= MIDIOut_NumDevs) {
347         WARN("bad device ID : %d\n", wDevID);
348         return MMSYSERR_BADDEVICEID;
349     }
350
351     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
352     {
353         WORD d1  = HIBYTE(LOWORD(dwParam));
354         WORD d2  = LOBYTE(HIWORD(dwParam));
355         OSStatus err = noErr;
356
357         err = MusicDeviceMIDIEvent(destinations[wDevID].synth, (evt & 0xF0) | chn, d1, d2, 0);
358         if (err != noErr)
359         {
360             ERR("MusicDeviceMIDIEvent(%p, %04x, %04x, %04x, %d) return %s\n", destinations[wDevID].synth, (evt & 0xF0) | chn, d1, d2, 0, wine_dbgstr_fourcc(err));
361             return MMSYSERR_ERROR;
362         }
363     }
364     else
365     {
366         UInt8 buffer[3];
367         buffer[0] = (evt & 0xF0) | chn;
368         buffer[1] = HIBYTE(LOWORD(dwParam));
369         buffer[2] = LOBYTE(HIWORD(dwParam));
370
371         MIDIOut_Send(MIDIOutPort, destinations[wDevID].dest, buffer, 3);
372     }
373
374     return MMSYSERR_NOERROR;
375 }
376
377 static DWORD MIDIOut_LongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
378 {
379     LPBYTE lpData;
380     OSStatus err = noErr;
381
382     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
383
384     /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
385      * but it seems to be used only for midi input.
386      * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
387      */
388
389     if (wDevID >= MIDIOut_NumDevs) {
390         WARN("bad device ID : %d\n", wDevID);
391         return MMSYSERR_BADDEVICEID;
392     }
393
394     if (lpMidiHdr == NULL) {
395         WARN("Invalid Parameter\n");
396         return MMSYSERR_INVALPARAM;
397     }
398
399     lpData = (LPBYTE) lpMidiHdr->lpData;
400
401     if (lpData == NULL)
402         return MIDIERR_UNPREPARED;
403     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
404         return MIDIERR_UNPREPARED;
405     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
406         return MIDIERR_STILLPLAYING;
407     lpMidiHdr->dwFlags &= ~MHDR_DONE;
408     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
409
410     /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
411      * data, or can it also contain raw MIDI data, to be split up and sent to
412      * modShortData() ?
413      * If the latest is true, then the following WARNing will fire up
414      */
415     if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
416         WARN("Alledged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
417     }
418
419     TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
420     TRACE("                 %02X %02X %02X ... %02X %02X %02X\n",
421           lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
422           lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
423
424
425     if (lpData[0] != 0xF0) {
426         /* System Exclusive */
427         ERR("Add missing 0xF0 marker at the beginning of system exclusive byte stream\n");
428     }
429     if (lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
430         /* Send end of System Exclusive */
431         ERR("Add missing 0xF7 marker at the end of system exclusive byte stream\n");
432     }
433     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH) /* FIXME */
434     {
435         err = MusicDeviceSysEx(destinations[wDevID].synth, (const UInt8 *) lpData, lpMidiHdr->dwBufferLength);
436         if (err != noErr)
437         {
438             ERR("MusicDeviceSysEx(%p, %p, %d) return %s\n", destinations[wDevID].synth, lpData, lpMidiHdr->dwBufferLength, wine_dbgstr_fourcc(err));
439             return MMSYSERR_ERROR;
440         }
441     }
442     else
443     {
444         FIXME("MOD_MIDIPORT\n");
445     }
446
447     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
448     lpMidiHdr->dwFlags |= MHDR_DONE;
449     if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
450         WARN("can't notify client !\n");
451         return MMSYSERR_INVALPARAM;
452     }
453     return MMSYSERR_NOERROR;
454 }
455
456 /**************************************************************************
457  *                      MIDIOut_Prepare                         [internal]
458  */
459 static DWORD MIDIOut_Prepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
460 {
461     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
462
463     if (wDevID >= MIDIOut_NumDevs) {
464         WARN("bad device ID : %d\n", wDevID);
465         return MMSYSERR_BADDEVICEID;
466     }
467
468     /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
469      * asks to prepare MIDIHDR which dwFlags != 0.
470      * So at least check for the inqueue flag
471      */
472     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
473         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
474         lpMidiHdr->dwBufferLength >= 0x10000ul) {
475         WARN("%p %p %08x %lu/%d\n", lpMidiHdr, lpMidiHdr->lpData,
476                    lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
477         return MMSYSERR_INVALPARAM;
478     }
479
480     lpMidiHdr->lpNext = 0;
481     lpMidiHdr->dwFlags |= MHDR_PREPARED;
482     lpMidiHdr->dwFlags &= ~MHDR_DONE;
483     return MMSYSERR_NOERROR;
484 }
485
486 /**************************************************************************
487  *                              MIDIOut_Unprepare                       [internal]
488  */
489 static DWORD MIDIOut_Unprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
490 {
491     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
492
493     if (wDevID >= MIDIOut_NumDevs) {
494         WARN("bad device ID : %d\n", wDevID);
495         return MMSYSERR_BADDEVICEID;
496     }
497     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
498         return MMSYSERR_INVALPARAM;
499     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
500         return MIDIERR_STILLPLAYING;
501
502     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
503
504     return MMSYSERR_NOERROR;
505 }
506
507 static DWORD MIDIOut_GetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
508 {
509     TRACE("wDevID=%d lpCaps=%p dwSize=%d\n", wDevID, lpCaps, dwSize);
510
511     if (lpCaps == NULL) {
512         WARN("Invalid Parameter\n");
513         return MMSYSERR_INVALPARAM;
514     }
515
516     if (wDevID >= MIDIOut_NumDevs) {
517         WARN("bad device ID : %d\n", wDevID);
518         return MMSYSERR_BADDEVICEID;
519     }
520     memcpy(lpCaps, &destinations[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
521     return MMSYSERR_NOERROR;
522 }
523
524 static DWORD MIDIOut_GetNumDevs(void)
525 {
526     TRACE("\n");
527     return MIDIOut_NumDevs;
528 }
529
530 static DWORD MIDIOut_GetVolume(WORD wDevID, DWORD *lpdwVolume)
531 {
532     TRACE("%d\n", wDevID);
533
534     if (wDevID >= MIDIOut_NumDevs) {
535         WARN("bad device ID : %d\n", wDevID);
536         return MMSYSERR_BADDEVICEID;
537     }
538     if (lpdwVolume == NULL) {
539         WARN("Invalid Parameter\n");
540         return MMSYSERR_INVALPARAM;
541     }
542
543     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
544     {
545         float left;
546         float right;
547         AudioUnit_GetVolume(destinations[wDevID].synth, &left, &right);
548
549         *lpdwVolume = ((WORD) left * 0xFFFFl) + (((WORD) right * 0xFFFFl) << 16);
550
551         return MMSYSERR_NOERROR;
552     }
553
554     return MMSYSERR_NOTSUPPORTED;
555 }
556
557 static DWORD MIDIOut_SetVolume(WORD wDevID, DWORD dwVolume)
558 {
559     TRACE("%d\n", wDevID);
560
561     if (wDevID >= MIDIOut_NumDevs) {
562         WARN("bad device ID : %d\n", wDevID);
563         return MMSYSERR_BADDEVICEID;
564     }
565     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
566     {
567         float left;
568         float right;
569
570         left  = LOWORD(dwVolume) / 65535.0f;
571         right = HIWORD(dwVolume) / 65535.0f;
572         AudioUnit_SetVolume(destinations[wDevID].synth, left, right);
573
574         return MMSYSERR_NOERROR;
575     }
576
577     return MMSYSERR_NOTSUPPORTED;
578 }
579
580 static DWORD MIDIOut_Reset(WORD wDevID)
581 {
582     unsigned chn;
583
584     TRACE("%d\n", wDevID);
585
586     if (wDevID >= MIDIOut_NumDevs) {
587         WARN("bad device ID : %d\n", wDevID);
588         return MMSYSERR_BADDEVICEID;
589     }
590     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
591     {
592         for (chn = 0; chn < 16; chn++) {
593             /* turn off every note */
594             MusicDeviceMIDIEvent(destinations[wDevID].synth, 0xB0 | chn, 0x7B, 0, 0);
595             /* remove sustain on channel */
596             MusicDeviceMIDIEvent(destinations[wDevID].synth, 0xB0 | chn, 0x40, 0, 0);
597         }
598     }
599     else FIXME("MOD_MIDIPORT\n");
600
601     /* FIXME: the LongData buffers must also be returned to the app */
602     return MMSYSERR_NOERROR;
603 }
604
605 static DWORD MIDIIn_Open(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
606 {
607     TRACE("wDevID=%d lpDesc=%p dwFlags=%08x\n", wDevID, lpDesc, dwFlags);
608
609     if (lpDesc == NULL) {
610         WARN("Invalid Parameter\n");
611         return MMSYSERR_INVALPARAM;
612     }
613     if (wDevID >= MIDIIn_NumDevs) {
614         WARN("bad device ID : %d\n", wDevID);
615         return MMSYSERR_BADDEVICEID;
616     }
617     if (sources[wDevID].midiDesc.hMidi != 0) {
618         WARN("device already open !\n");
619         return MMSYSERR_ALLOCATED;
620     }
621     if ((dwFlags & MIDI_IO_STATUS) != 0) {
622         WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
623         dwFlags &= ~MIDI_IO_STATUS;
624     }
625     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
626         FIXME("Bad dwFlags\n");
627         return MMSYSERR_INVALFLAG;
628     }
629
630     sources[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
631     sources[wDevID].lpQueueHdr = NULL;
632     sources[wDevID].midiDesc = *lpDesc;
633     sources[wDevID].startTime = 0;
634     sources[wDevID].state = 0;
635
636     return MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
637 }
638
639 static DWORD MIDIIn_Close(WORD wDevID)
640 {
641     DWORD ret = MMSYSERR_NOERROR;
642
643     TRACE("wDevID=%d\n", wDevID);
644
645     if (wDevID >= MIDIIn_NumDevs) {
646         WARN("bad device ID : %d\n", wDevID);
647         return MMSYSERR_BADDEVICEID;
648     }
649
650     if (sources[wDevID].midiDesc.hMidi == 0) {
651         WARN("device not opened !\n");
652         return MMSYSERR_ERROR;
653     }
654     if (sources[wDevID].lpQueueHdr != 0) {
655         return MIDIERR_STILLPLAYING;
656     }
657
658     ret = MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
659     sources[wDevID].midiDesc.hMidi = 0;
660     return ret;
661 }
662
663 static DWORD MIDIIn_AddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
664 {
665     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
666
667     if (wDevID >= MIDIIn_NumDevs) {
668         WARN("bad device ID : %d\n", wDevID);
669         return MMSYSERR_BADDEVICEID;
670     }
671     if (lpMidiHdr == NULL) {
672         WARN("Invalid Parameter\n");
673         return MMSYSERR_INVALPARAM;
674     }
675     if (sizeof(MIDIHDR) > dwSize) {
676         WARN("Invalid Parameter\n");
677         return MMSYSERR_INVALPARAM;
678     }
679     if (lpMidiHdr->dwBufferLength == 0) {
680         WARN("Invalid Parameter\n");
681         return MMSYSERR_INVALPARAM;
682     }
683     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) {
684         WARN("Still playing\n");
685         return MIDIERR_STILLPLAYING;
686     }
687     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) {
688         WARN("Unprepared\n");
689         return MIDIERR_UNPREPARED;
690     }
691
692     EnterCriticalSection(&midiInLock);
693     lpMidiHdr->dwFlags &= ~WHDR_DONE;
694     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
695     lpMidiHdr->dwBytesRecorded = 0;
696     lpMidiHdr->lpNext = 0;
697     if (sources[wDevID].lpQueueHdr == 0) {
698         sources[wDevID].lpQueueHdr = lpMidiHdr;
699     } else {
700         LPMIDIHDR ptr;
701         for (ptr = sources[wDevID].lpQueueHdr;
702              ptr->lpNext != 0;
703              ptr = (LPMIDIHDR)ptr->lpNext);
704         ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
705     }
706     LeaveCriticalSection(&midiInLock);
707
708     return MMSYSERR_NOERROR;
709 }
710
711 static DWORD MIDIIn_Prepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
712 {
713     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
714
715     if (wDevID >= MIDIIn_NumDevs) {
716         WARN("bad device ID : %d\n", wDevID);
717         return MMSYSERR_BADDEVICEID;
718     }
719     /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
720      * asks to prepare MIDIHDR which dwFlags != 0.
721      * So at least check for the inqueue flag
722      */
723     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
724         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
725         lpMidiHdr->dwBufferLength >= 0x10000ul) {
726         WARN("Invalid parameter %p %p %08x %d\n", lpMidiHdr, lpMidiHdr->lpData,
727                    lpMidiHdr->dwFlags, dwSize);
728         return MMSYSERR_INVALPARAM;
729     }
730
731     lpMidiHdr->lpNext = 0;
732     lpMidiHdr->dwFlags |= MHDR_PREPARED;
733     lpMidiHdr->dwFlags &= ~MHDR_DONE;
734     return MMSYSERR_NOERROR;
735 }
736
737 static DWORD MIDIIn_Unprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
738 {
739     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
740     if (wDevID >= MIDIIn_NumDevs) {
741         WARN("bad device ID : %d\n", wDevID);
742         return MMSYSERR_BADDEVICEID;
743     }
744     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0) {
745         WARN("Invalid Parameter\n");
746         return MMSYSERR_INVALPARAM;
747     }
748     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) {
749         WARN("Still playing\n");
750         return MIDIERR_STILLPLAYING;
751     }
752
753     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
754     return MMSYSERR_NOERROR;
755 }
756
757 static DWORD MIDIIn_GetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
758 {
759     TRACE("wDevID=%d lpCaps=%p dwSize=%d\n", wDevID, lpCaps, dwSize);
760
761     if (lpCaps == NULL) {
762         WARN("Invalid Parameter\n");
763         return MMSYSERR_INVALPARAM;
764     }
765
766     if (wDevID >= MIDIIn_NumDevs) {
767         WARN("bad device ID : %d\n", wDevID);
768         return MMSYSERR_BADDEVICEID;
769     }
770     memcpy(lpCaps, &sources[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
771     return MMSYSERR_NOERROR;
772 }
773
774 static DWORD MIDIIn_GetNumDevs(void)
775 {
776     TRACE("\n");
777     return MIDIIn_NumDevs;
778 }
779
780 static DWORD MIDIIn_Start(WORD wDevID)
781 {
782     TRACE("%d\n", wDevID);
783
784     if (wDevID >= MIDIIn_NumDevs) {
785         WARN("bad device ID : %d\n", wDevID);
786         return MMSYSERR_BADDEVICEID;
787     }
788     sources[wDevID].state = 1;
789     sources[wDevID].startTime = GetTickCount();
790     return MMSYSERR_NOERROR;
791 }
792
793 static DWORD MIDIIn_Stop(WORD wDevID)
794 {
795     TRACE("%d\n", wDevID);
796     if (wDevID >= MIDIIn_NumDevs) {
797         WARN("bad device ID : %d\n", wDevID);
798         return MMSYSERR_BADDEVICEID;
799     }
800     sources[wDevID].state = 0;
801     return MMSYSERR_NOERROR;
802 }
803
804 static DWORD MIDIIn_Reset(WORD wDevID)
805 {
806     DWORD dwTime = GetTickCount();
807
808     TRACE("%d\n", wDevID);
809     if (wDevID >= MIDIIn_NumDevs) {
810         WARN("bad device ID : %d\n", wDevID);
811         return MMSYSERR_BADDEVICEID;
812     }
813
814     EnterCriticalSection(&midiInLock);
815     while (sources[wDevID].lpQueueHdr) {
816         sources[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
817         sources[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
818         /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
819         if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)sources[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
820             WARN("Couldn't notify client\n");
821         }
822         sources[wDevID].lpQueueHdr = (LPMIDIHDR)sources[wDevID].lpQueueHdr->lpNext;
823     }
824     LeaveCriticalSection(&midiInLock);
825
826     return MMSYSERR_NOERROR;
827 }
828
829 /*
830  * MIDI In Mach message handling
831  */
832
833 /*
834  *  Call from CoreMIDI IO threaded callback,
835  *  we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
836  */
837 void MIDIIn_SendMessage(MIDIMessage msg)
838 {
839     CFDataRef data;
840
841     CFMessagePortRef messagePort;
842     messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, MIDIInThreadPortName);
843
844     data = CFDataCreate(kCFAllocatorDefault, (UInt8 *) &msg, sizeof(msg));
845     if (data)
846     {
847         CFMessagePortSendRequest(messagePort, 0, data, 0.0, 0.0, NULL, NULL);
848         CFRelease(data);
849         CFRelease(messagePort);
850     }
851 }
852
853 static CFDataRef MIDIIn_MessageHandler(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info)
854 {
855     MIDIMessage *msg = NULL;
856     int i = 0;
857     MIDISource *src = NULL;
858     DWORD sendData = 0;
859     int pos = 0;
860     DWORD currentTime;
861
862     switch (msgid)
863     {
864         case 0:
865             msg = (MIDIMessage *) CFDataGetBytePtr(data);
866             TRACE("devID=%d\n", msg->devID);
867              for (i = 0; i < msg->length; ++i) {
868                 TRACE("%02X ", msg->data[i]);
869             }
870             TRACE("\n");
871             src = &sources[msg->devID];
872             if (src->state < 1)
873             {
874                 TRACE("input not started, thrown away\n");
875                 goto done;
876             }
877             /* FIXME skipping SysEx */
878             if (msg->data[0] == 0xF0)
879             {
880                 FIXME("Starting System Exclusive\n");
881                 src->state |= 2;
882             }
883             if (src->state & 2)
884             {
885                 for (i = 0; i < msg->length; ++i)
886                 {
887                     if (msg->data[i] == 0xF7)
888                     {
889                         FIXME("Ending System Exclusive\n");
890                         src->state &= ~2;
891                     }
892                 }
893                 goto done;
894             }
895             EnterCriticalSection(&midiInLock);
896             currentTime = GetTickCount() - src->startTime;
897
898             while (pos < msg->length)
899             {
900                 sendData = 0;
901                 switch (msg->data[pos] & 0xF0)
902                 {
903                     case 0xF0:
904                         sendData = (msg->data[pos] <<  0);
905                         pos++;
906                         break;
907
908                     case 0xC0:
909                     case 0xD0:
910                         sendData = (msg->data[pos + 1] <<  8) | (msg->data[pos] <<  0);
911                         pos += 2;
912                         break;
913                     default:
914                         sendData = (msg->data[pos + 2] << 16) |
915                                     (msg->data[pos + 1] <<  8) |
916                                     (msg->data[pos] <<  0);
917                         pos += 3;
918                         break;
919                 }
920                 MIDI_NotifyClient(msg->devID, MIM_DATA, sendData, currentTime);
921             }
922             LeaveCriticalSection(&midiInLock);
923             break;
924         default:
925             CFRunLoopStop(CFRunLoopGetCurrent());
926             break;
927     }
928 done:
929     return NULL;
930 }
931
932 static DWORD WINAPI MIDIIn_MessageThread(LPVOID p)
933 {
934     CFMessagePortRef local;
935     CFRunLoopSourceRef source;
936     Boolean info;
937
938     local = CFMessagePortCreateLocal(kCFAllocatorDefault, MIDIInThreadPortName, &MIDIIn_MessageHandler, NULL, &info);
939
940     source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, local, (CFIndex)0);
941     CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
942
943     CFRunLoopRun();
944
945     CFRunLoopSourceInvalidate(source);
946     CFRelease(source);
947     CFRelease(local);
948     CFRelease(MIDIInThreadPortName);
949     MIDIInThreadPortName = NULL;
950
951     return 0;
952 }
953
954 /**************************************************************************
955 *                               modMessage
956 */
957 DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
958 {
959     TRACE("%d %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
960
961     switch (wMsg) {
962         case DRVM_INIT:
963         case DRVM_EXIT:
964         case DRVM_ENABLE:
965         case DRVM_DISABLE:
966             return 0;
967         case MODM_OPEN:
968             return MIDIOut_Open(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
969         case MODM_CLOSE:
970             return MIDIOut_Close(wDevID);
971         case MODM_DATA:
972             return MIDIOut_Data(wDevID, dwParam1);
973         case MODM_LONGDATA:
974             return MIDIOut_LongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
975         case MODM_PREPARE:
976             return MIDIOut_Prepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
977         case MODM_UNPREPARE:
978             return MIDIOut_Unprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
979         case MODM_GETDEVCAPS:
980             return MIDIOut_GetDevCaps(wDevID, (LPMIDIOUTCAPSW) dwParam1, dwParam2);
981         case MODM_GETNUMDEVS:
982             return MIDIOut_GetNumDevs();
983         case MODM_GETVOLUME:
984             return MIDIOut_GetVolume(wDevID, (DWORD*)dwParam1);
985         case MODM_SETVOLUME:
986             return MIDIOut_SetVolume(wDevID, dwParam1);
987         case MODM_RESET:
988             return MIDIOut_Reset(wDevID);
989         default:
990             TRACE("Unsupported message (08%x)\n", wMsg);
991     }
992     return MMSYSERR_NOTSUPPORTED;
993 }
994
995 /**************************************************************************
996 *                       midMessage
997 */
998 DWORD WINAPI CoreAudio_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
999 {
1000     TRACE("%d %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1001     switch (wMsg) {
1002         case DRVM_INIT:
1003         case DRVM_EXIT:
1004         case DRVM_ENABLE:
1005         case DRVM_DISABLE:
1006             return 0;
1007         case MIDM_OPEN:
1008             return MIDIIn_Open(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1009         case MIDM_CLOSE:
1010             return MIDIIn_Close(wDevID);
1011         case MIDM_ADDBUFFER:
1012             return MIDIIn_AddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1013         case MIDM_PREPARE:
1014             return MIDIIn_Prepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1015         case MIDM_UNPREPARE:
1016             return MIDIIn_Unprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1017         case MIDM_GETDEVCAPS:
1018             return MIDIIn_GetDevCaps(wDevID, (LPMIDIINCAPSW) dwParam1, dwParam2);
1019         case MIDM_GETNUMDEVS:
1020             return MIDIIn_GetNumDevs();
1021         case MIDM_START:
1022             return MIDIIn_Start(wDevID);
1023         case MIDM_STOP:
1024             return MIDIIn_Stop(wDevID);
1025         case MIDM_RESET:
1026             return MIDIIn_Reset(wDevID);
1027         default:
1028             TRACE("Unsupported message\n");
1029     }
1030     return MMSYSERR_NOTSUPPORTED;
1031 }
1032 #else
1033
1034 DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
1035 {
1036     TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1037     return MMSYSERR_NOTENABLED;
1038 }
1039
1040 DWORD WINAPI CoreAudio_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1041                                   DWORD dwParam1, DWORD dwParam2)
1042 {
1043     TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1044     return MMSYSERR_NOTENABLED;
1045 }
1046 #endif