msdaps: Add server side stubs for IAccessor.
[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 = 0;
195         destinations[i].caps.wNotes = 0;
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         WARN("%p %p %08x %lu/%d\n", lpMidiHdr, lpMidiHdr->lpData,
475                    lpMidiHdr->dwFlags, sizeof(MIDIHDR), dwSize);
476         return MMSYSERR_INVALPARAM;
477     }
478
479     lpMidiHdr->lpNext = 0;
480     lpMidiHdr->dwFlags |= MHDR_PREPARED;
481     lpMidiHdr->dwFlags &= ~MHDR_DONE;
482     return MMSYSERR_NOERROR;
483 }
484
485 /**************************************************************************
486  *                              MIDIOut_Unprepare                       [internal]
487  */
488 static DWORD MIDIOut_Unprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
489 {
490     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
491
492     if (wDevID >= MIDIOut_NumDevs) {
493         WARN("bad device ID : %d\n", wDevID);
494         return MMSYSERR_BADDEVICEID;
495     }
496     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0)
497         return MMSYSERR_INVALPARAM;
498     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
499         return MIDIERR_STILLPLAYING;
500
501     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
502
503     return MMSYSERR_NOERROR;
504 }
505
506 static DWORD MIDIOut_GetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
507 {
508     TRACE("wDevID=%d lpCaps=%p dwSize=%d\n", wDevID, lpCaps, dwSize);
509
510     if (lpCaps == NULL) {
511         WARN("Invalid Parameter\n");
512         return MMSYSERR_INVALPARAM;
513     }
514
515     if (wDevID >= MIDIOut_NumDevs) {
516         WARN("bad device ID : %d\n", wDevID);
517         return MMSYSERR_BADDEVICEID;
518     }
519     memcpy(lpCaps, &destinations[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
520     return MMSYSERR_NOERROR;
521 }
522
523 static DWORD MIDIOut_GetNumDevs(void)
524 {
525     TRACE("\n");
526     return MIDIOut_NumDevs;
527 }
528
529 static DWORD MIDIOut_GetVolume(WORD wDevID, DWORD *lpdwVolume)
530 {
531     TRACE("%d\n", wDevID);
532
533     if (wDevID >= MIDIOut_NumDevs) {
534         WARN("bad device ID : %d\n", wDevID);
535         return MMSYSERR_BADDEVICEID;
536     }
537     if (lpdwVolume == NULL) {
538         WARN("Invalid Parameter\n");
539         return MMSYSERR_INVALPARAM;
540     }
541
542     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
543     {
544         float left;
545         float right;
546         AudioUnit_GetVolume(destinations[wDevID].synth, &left, &right);
547
548         *lpdwVolume = (WORD) (left * 0xFFFF) + ((WORD) (right * 0xFFFF) << 16);
549
550         return MMSYSERR_NOERROR;
551     }
552
553     return MMSYSERR_NOTSUPPORTED;
554 }
555
556 static DWORD MIDIOut_SetVolume(WORD wDevID, DWORD dwVolume)
557 {
558     TRACE("%d\n", wDevID);
559
560     if (wDevID >= MIDIOut_NumDevs) {
561         WARN("bad device ID : %d\n", wDevID);
562         return MMSYSERR_BADDEVICEID;
563     }
564     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
565     {
566         float left;
567         float right;
568
569         left  = LOWORD(dwVolume) / 65535.0f;
570         right = HIWORD(dwVolume) / 65535.0f;
571         AudioUnit_SetVolume(destinations[wDevID].synth, left, right);
572
573         return MMSYSERR_NOERROR;
574     }
575
576     return MMSYSERR_NOTSUPPORTED;
577 }
578
579 static DWORD MIDIOut_Reset(WORD wDevID)
580 {
581     unsigned chn;
582
583     TRACE("%d\n", wDevID);
584
585     if (wDevID >= MIDIOut_NumDevs) {
586         WARN("bad device ID : %d\n", wDevID);
587         return MMSYSERR_BADDEVICEID;
588     }
589     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
590     {
591         for (chn = 0; chn < 16; chn++) {
592             /* turn off every note */
593             MusicDeviceMIDIEvent(destinations[wDevID].synth, 0xB0 | chn, 0x7B, 0, 0);
594             /* remove sustain on channel */
595             MusicDeviceMIDIEvent(destinations[wDevID].synth, 0xB0 | chn, 0x40, 0, 0);
596         }
597     }
598     else FIXME("MOD_MIDIPORT\n");
599
600     /* FIXME: the LongData buffers must also be returned to the app */
601     return MMSYSERR_NOERROR;
602 }
603
604 static DWORD MIDIIn_Open(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
605 {
606     TRACE("wDevID=%d lpDesc=%p dwFlags=%08x\n", wDevID, lpDesc, dwFlags);
607
608     if (lpDesc == NULL) {
609         WARN("Invalid Parameter\n");
610         return MMSYSERR_INVALPARAM;
611     }
612     if (wDevID >= MIDIIn_NumDevs) {
613         WARN("bad device ID : %d\n", wDevID);
614         return MMSYSERR_BADDEVICEID;
615     }
616     if (sources[wDevID].midiDesc.hMidi != 0) {
617         WARN("device already open !\n");
618         return MMSYSERR_ALLOCATED;
619     }
620     if ((dwFlags & MIDI_IO_STATUS) != 0) {
621         WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
622         dwFlags &= ~MIDI_IO_STATUS;
623     }
624     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
625         FIXME("Bad dwFlags\n");
626         return MMSYSERR_INVALFLAG;
627     }
628
629     sources[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
630     sources[wDevID].lpQueueHdr = NULL;
631     sources[wDevID].midiDesc = *lpDesc;
632     sources[wDevID].startTime = 0;
633     sources[wDevID].state = 0;
634
635     return MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
636 }
637
638 static DWORD MIDIIn_Close(WORD wDevID)
639 {
640     DWORD ret = MMSYSERR_NOERROR;
641
642     TRACE("wDevID=%d\n", wDevID);
643
644     if (wDevID >= MIDIIn_NumDevs) {
645         WARN("bad device ID : %d\n", wDevID);
646         return MMSYSERR_BADDEVICEID;
647     }
648
649     if (sources[wDevID].midiDesc.hMidi == 0) {
650         WARN("device not opened !\n");
651         return MMSYSERR_ERROR;
652     }
653     if (sources[wDevID].lpQueueHdr != 0) {
654         return MIDIERR_STILLPLAYING;
655     }
656
657     ret = MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
658     sources[wDevID].midiDesc.hMidi = 0;
659     return ret;
660 }
661
662 static DWORD MIDIIn_AddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
663 {
664     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
665
666     if (wDevID >= MIDIIn_NumDevs) {
667         WARN("bad device ID : %d\n", wDevID);
668         return MMSYSERR_BADDEVICEID;
669     }
670     if (lpMidiHdr == NULL) {
671         WARN("Invalid Parameter\n");
672         return MMSYSERR_INVALPARAM;
673     }
674     if (sizeof(MIDIHDR) > dwSize) {
675         WARN("Invalid Parameter\n");
676         return MMSYSERR_INVALPARAM;
677     }
678     if (lpMidiHdr->dwBufferLength == 0) {
679         WARN("Invalid Parameter\n");
680         return MMSYSERR_INVALPARAM;
681     }
682     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) {
683         WARN("Still playing\n");
684         return MIDIERR_STILLPLAYING;
685     }
686     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) {
687         WARN("Unprepared\n");
688         return MIDIERR_UNPREPARED;
689     }
690
691     EnterCriticalSection(&midiInLock);
692     lpMidiHdr->dwFlags &= ~WHDR_DONE;
693     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
694     lpMidiHdr->dwBytesRecorded = 0;
695     lpMidiHdr->lpNext = 0;
696     if (sources[wDevID].lpQueueHdr == 0) {
697         sources[wDevID].lpQueueHdr = lpMidiHdr;
698     } else {
699         LPMIDIHDR ptr;
700         for (ptr = sources[wDevID].lpQueueHdr;
701              ptr->lpNext != 0;
702              ptr = (LPMIDIHDR)ptr->lpNext);
703         ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
704     }
705     LeaveCriticalSection(&midiInLock);
706
707     return MMSYSERR_NOERROR;
708 }
709
710 static DWORD MIDIIn_Prepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
711 {
712     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
713
714     if (wDevID >= MIDIIn_NumDevs) {
715         WARN("bad device ID : %d\n", wDevID);
716         return MMSYSERR_BADDEVICEID;
717     }
718     /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
719      * asks to prepare MIDIHDR which dwFlags != 0.
720      * So at least check for the inqueue flag
721      */
722     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
723         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0) {
724         WARN("Invalid parameter %p %p %08x %d\n", lpMidiHdr, lpMidiHdr->lpData,
725                    lpMidiHdr->dwFlags, dwSize);
726         return MMSYSERR_INVALPARAM;
727     }
728
729     lpMidiHdr->lpNext = 0;
730     lpMidiHdr->dwFlags |= MHDR_PREPARED;
731     lpMidiHdr->dwFlags &= ~MHDR_DONE;
732     return MMSYSERR_NOERROR;
733 }
734
735 static DWORD MIDIIn_Unprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
736 {
737     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
738     if (wDevID >= MIDIIn_NumDevs) {
739         WARN("bad device ID : %d\n", wDevID);
740         return MMSYSERR_BADDEVICEID;
741     }
742     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0) {
743         WARN("Invalid Parameter\n");
744         return MMSYSERR_INVALPARAM;
745     }
746     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) {
747         WARN("Still playing\n");
748         return MIDIERR_STILLPLAYING;
749     }
750
751     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
752     return MMSYSERR_NOERROR;
753 }
754
755 static DWORD MIDIIn_GetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
756 {
757     TRACE("wDevID=%d lpCaps=%p dwSize=%d\n", wDevID, lpCaps, dwSize);
758
759     if (lpCaps == NULL) {
760         WARN("Invalid Parameter\n");
761         return MMSYSERR_INVALPARAM;
762     }
763
764     if (wDevID >= MIDIIn_NumDevs) {
765         WARN("bad device ID : %d\n", wDevID);
766         return MMSYSERR_BADDEVICEID;
767     }
768     memcpy(lpCaps, &sources[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
769     return MMSYSERR_NOERROR;
770 }
771
772 static DWORD MIDIIn_GetNumDevs(void)
773 {
774     TRACE("\n");
775     return MIDIIn_NumDevs;
776 }
777
778 static DWORD MIDIIn_Start(WORD wDevID)
779 {
780     TRACE("%d\n", wDevID);
781
782     if (wDevID >= MIDIIn_NumDevs) {
783         WARN("bad device ID : %d\n", wDevID);
784         return MMSYSERR_BADDEVICEID;
785     }
786     sources[wDevID].state = 1;
787     sources[wDevID].startTime = GetTickCount();
788     return MMSYSERR_NOERROR;
789 }
790
791 static DWORD MIDIIn_Stop(WORD wDevID)
792 {
793     TRACE("%d\n", wDevID);
794     if (wDevID >= MIDIIn_NumDevs) {
795         WARN("bad device ID : %d\n", wDevID);
796         return MMSYSERR_BADDEVICEID;
797     }
798     sources[wDevID].state = 0;
799     return MMSYSERR_NOERROR;
800 }
801
802 static DWORD MIDIIn_Reset(WORD wDevID)
803 {
804     DWORD dwTime = GetTickCount();
805
806     TRACE("%d\n", wDevID);
807     if (wDevID >= MIDIIn_NumDevs) {
808         WARN("bad device ID : %d\n", wDevID);
809         return MMSYSERR_BADDEVICEID;
810     }
811
812     EnterCriticalSection(&midiInLock);
813     while (sources[wDevID].lpQueueHdr) {
814         sources[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
815         sources[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
816         /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
817         if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)sources[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
818             WARN("Couldn't notify client\n");
819         }
820         sources[wDevID].lpQueueHdr = (LPMIDIHDR)sources[wDevID].lpQueueHdr->lpNext;
821     }
822     LeaveCriticalSection(&midiInLock);
823
824     return MMSYSERR_NOERROR;
825 }
826
827 /*
828  * MIDI In Mach message handling
829  */
830
831 /*
832  *  Call from CoreMIDI IO threaded callback,
833  *  we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
834  */
835 void MIDIIn_SendMessage(MIDIMessage msg)
836 {
837     CFDataRef data;
838
839     CFMessagePortRef messagePort;
840     messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, MIDIInThreadPortName);
841
842     data = CFDataCreate(kCFAllocatorDefault, (UInt8 *) &msg, sizeof(msg));
843     if (data)
844     {
845         CFMessagePortSendRequest(messagePort, 0, data, 0.0, 0.0, NULL, NULL);
846         CFRelease(data);
847         CFRelease(messagePort);
848     }
849 }
850
851 static CFDataRef MIDIIn_MessageHandler(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info)
852 {
853     MIDIMessage *msg = NULL;
854     int i = 0;
855     MIDISource *src = NULL;
856     DWORD sendData = 0;
857     int pos = 0;
858     DWORD currentTime;
859
860     switch (msgid)
861     {
862         case 0:
863             msg = (MIDIMessage *) CFDataGetBytePtr(data);
864             TRACE("devID=%d\n", msg->devID);
865              for (i = 0; i < msg->length; ++i) {
866                 TRACE("%02X ", msg->data[i]);
867             }
868             TRACE("\n");
869             src = &sources[msg->devID];
870             if (src->state < 1)
871             {
872                 TRACE("input not started, thrown away\n");
873                 goto done;
874             }
875             /* FIXME skipping SysEx */
876             if (msg->data[0] == 0xF0)
877             {
878                 FIXME("Starting System Exclusive\n");
879                 src->state |= 2;
880             }
881             if (src->state & 2)
882             {
883                 for (i = 0; i < msg->length; ++i)
884                 {
885                     if (msg->data[i] == 0xF7)
886                     {
887                         FIXME("Ending System Exclusive\n");
888                         src->state &= ~2;
889                     }
890                 }
891                 goto done;
892             }
893             EnterCriticalSection(&midiInLock);
894             currentTime = GetTickCount() - src->startTime;
895
896             while (pos < msg->length)
897             {
898                 sendData = 0;
899                 switch (msg->data[pos] & 0xF0)
900                 {
901                     case 0xF0:
902                         sendData = (msg->data[pos] <<  0);
903                         pos++;
904                         break;
905
906                     case 0xC0:
907                     case 0xD0:
908                         sendData = (msg->data[pos + 1] <<  8) | (msg->data[pos] <<  0);
909                         pos += 2;
910                         break;
911                     default:
912                         sendData = (msg->data[pos + 2] << 16) |
913                                     (msg->data[pos + 1] <<  8) |
914                                     (msg->data[pos] <<  0);
915                         pos += 3;
916                         break;
917                 }
918                 MIDI_NotifyClient(msg->devID, MIM_DATA, sendData, currentTime);
919             }
920             LeaveCriticalSection(&midiInLock);
921             break;
922         default:
923             CFRunLoopStop(CFRunLoopGetCurrent());
924             break;
925     }
926 done:
927     return NULL;
928 }
929
930 static DWORD WINAPI MIDIIn_MessageThread(LPVOID p)
931 {
932     CFMessagePortRef local;
933     CFRunLoopSourceRef source;
934     Boolean info;
935
936     local = CFMessagePortCreateLocal(kCFAllocatorDefault, MIDIInThreadPortName, &MIDIIn_MessageHandler, NULL, &info);
937
938     source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, local, (CFIndex)0);
939     CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
940
941     CFRunLoopRun();
942
943     CFRunLoopSourceInvalidate(source);
944     CFRelease(source);
945     CFRelease(local);
946     CFRelease(MIDIInThreadPortName);
947     MIDIInThreadPortName = NULL;
948
949     return 0;
950 }
951
952 /**************************************************************************
953 *                               modMessage
954 */
955 DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
956 {
957     TRACE("%d %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
958
959     switch (wMsg) {
960         case DRVM_INIT:
961         case DRVM_EXIT:
962         case DRVM_ENABLE:
963         case DRVM_DISABLE:
964             return 0;
965         case MODM_OPEN:
966             return MIDIOut_Open(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
967         case MODM_CLOSE:
968             return MIDIOut_Close(wDevID);
969         case MODM_DATA:
970             return MIDIOut_Data(wDevID, dwParam1);
971         case MODM_LONGDATA:
972             return MIDIOut_LongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
973         case MODM_PREPARE:
974             return MIDIOut_Prepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
975         case MODM_UNPREPARE:
976             return MIDIOut_Unprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
977         case MODM_GETDEVCAPS:
978             return MIDIOut_GetDevCaps(wDevID, (LPMIDIOUTCAPSW) dwParam1, dwParam2);
979         case MODM_GETNUMDEVS:
980             return MIDIOut_GetNumDevs();
981         case MODM_GETVOLUME:
982             return MIDIOut_GetVolume(wDevID, (DWORD*)dwParam1);
983         case MODM_SETVOLUME:
984             return MIDIOut_SetVolume(wDevID, dwParam1);
985         case MODM_RESET:
986             return MIDIOut_Reset(wDevID);
987         default:
988             TRACE("Unsupported message (08%x)\n", wMsg);
989     }
990     return MMSYSERR_NOTSUPPORTED;
991 }
992
993 /**************************************************************************
994 *                       midMessage
995 */
996 DWORD WINAPI CoreAudio_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
997 {
998     TRACE("%d %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
999     switch (wMsg) {
1000         case DRVM_INIT:
1001         case DRVM_EXIT:
1002         case DRVM_ENABLE:
1003         case DRVM_DISABLE:
1004             return 0;
1005         case MIDM_OPEN:
1006             return MIDIIn_Open(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1007         case MIDM_CLOSE:
1008             return MIDIIn_Close(wDevID);
1009         case MIDM_ADDBUFFER:
1010             return MIDIIn_AddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1011         case MIDM_PREPARE:
1012             return MIDIIn_Prepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1013         case MIDM_UNPREPARE:
1014             return MIDIIn_Unprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1015         case MIDM_GETDEVCAPS:
1016             return MIDIIn_GetDevCaps(wDevID, (LPMIDIINCAPSW) dwParam1, dwParam2);
1017         case MIDM_GETNUMDEVS:
1018             return MIDIIn_GetNumDevs();
1019         case MIDM_START:
1020             return MIDIIn_Start(wDevID);
1021         case MIDM_STOP:
1022             return MIDIIn_Stop(wDevID);
1023         case MIDM_RESET:
1024             return MIDIIn_Reset(wDevID);
1025         default:
1026             TRACE("Unsupported message\n");
1027     }
1028     return MMSYSERR_NOTSUPPORTED;
1029 }
1030 #else
1031
1032 DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
1033 {
1034     TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1035     return MMSYSERR_NOTENABLED;
1036 }
1037
1038 DWORD WINAPI CoreAudio_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1039                                   DWORD dwParam1, DWORD dwParam2)
1040 {
1041     TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1042     return MMSYSERR_NOTENABLED;
1043 }
1044 #endif