msi/tests: Create only one log file and delete it afterwards.
[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
44 WINE_DEFAULT_DEBUG_CHANNEL(midi);
45
46 #if defined(HAVE_COREAUDIO_COREAUDIO_H)
47 #include <CoreAudio/CoreAudio.h>
48
49 #define WINE_DEFINITIONS
50 #include "coremidi.h"
51
52 static MIDIClientRef wineMIDIClient = NULL;
53
54 static DWORD MIDIOut_NumDevs = 0;
55 static DWORD MIDIIn_NumDevs = 0;
56
57 typedef struct tagMIDIDestination {
58     /* graph and synth are only used for MIDI Synth */
59     AUGraph graph;
60     AudioUnit synth;
61
62     MIDIEndpointRef dest;
63
64     MIDIOUTCAPSW caps;
65     MIDIOPENDESC midiDesc;
66     WORD wFlags;
67 } MIDIDestination;
68
69 typedef struct tagMIDISource {
70     MIDIEndpointRef source;
71
72     WORD wDevID;
73     int state; /* 0 is no recording started, 1 in recording, bit 2 set if in sys exclusive recording */
74     MIDIINCAPSW caps;
75     MIDIOPENDESC midiDesc;
76     LPMIDIHDR lpQueueHdr;
77     WORD wFlags;
78     DWORD startTime;
79 } MIDISource;
80
81 static CRITICAL_SECTION midiInLock; /* Critical section for MIDI In */
82 static CFStringRef MIDIInThreadPortName = NULL;
83
84 static DWORD WINAPI MIDIIn_MessageThread(LPVOID p);
85
86 static MIDIPortRef MIDIInPort = NULL;
87 static MIDIPortRef MIDIOutPort = NULL;
88
89 #define MAX_MIDI_SYNTHS 1
90
91 MIDIDestination *destinations;
92 MIDISource *sources;
93
94 extern int SynthUnit_CreateDefaultSynthUnit(AUGraph *graph, AudioUnit *synth);
95 extern int SynthUnit_Initialize(AudioUnit synth, AUGraph graph);
96 extern int SynthUnit_Close(AUGraph graph);
97
98
99 LONG CoreAudio_MIDIInit(void)
100 {
101     int i;
102     CHAR szPname[MAXPNAMELEN] = {0};
103
104     int numDest = MIDIGetNumberOfDestinations();
105     CFStringRef name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("wineMIDIClient.%d"), getpid());
106
107     wineMIDIClient = CoreMIDI_CreateClient( name );
108     if (wineMIDIClient == NULL)
109     {
110         CFRelease(name);
111         ERR("can't create wineMIDIClient\n");
112         return DRV_FAILURE;
113     }
114     CFRelease(name);
115
116     MIDIOut_NumDevs = MAX_MIDI_SYNTHS;
117     MIDIOut_NumDevs += numDest;
118
119     MIDIIn_NumDevs = MIDIGetNumberOfSources();
120
121     TRACE("MIDIOut_NumDevs %d MIDIIn_NumDevs %d\n", MIDIOut_NumDevs, MIDIIn_NumDevs);
122
123     destinations = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MIDIOut_NumDevs * sizeof(MIDIDestination));
124     sources = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MIDIIn_NumDevs * sizeof(MIDISource));
125
126     if (MIDIIn_NumDevs > 0)
127     {
128         InitializeCriticalSection(&midiInLock);
129         MIDIInThreadPortName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("MIDIInThreadPortName.%u"), getpid());
130         CreateThread(NULL, 0, MIDIIn_MessageThread, NULL, 0, NULL);
131
132         name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("WineInputPort.%u"), getpid());
133         MIDIInputPortCreate(wineMIDIClient, name, MIDIIn_ReadProc, NULL, &MIDIInPort);
134         CFRelease(name);
135     }
136     if (numDest > 0)
137     {
138         name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("WineOutputPort.%u"), getpid());
139         MIDIOutputPortCreate(wineMIDIClient, name, &MIDIOutPort);
140         CFRelease(name);
141     }
142
143     /* initialize sources */
144     for (i = 0; i < MIDIIn_NumDevs; i++)
145     {
146         sources[i].wDevID = i;
147         sources[i].source = MIDIGetSource(i);
148
149         CoreMIDI_GetObjectName(sources[i].source, szPname, sizeof(szPname));
150         MultiByteToWideChar(CP_ACP, 0, szPname, -1, sources[i].caps.szPname, sizeof(sources[i].caps.szPname)/sizeof(WCHAR));
151
152         MIDIPortConnectSource(MIDIInPort, sources[i].source, &sources[i].wDevID);
153
154         sources[i].state = 0;
155         /* FIXME */
156         sources[i].caps.wMid = 0x00FF;  /* Manufac ID */
157         sources[i].caps.wPid = 0x0001;  /* Product ID */
158         sources[i].caps.vDriverVersion = 0x0001;
159         sources[i].caps.dwSupport = 0;
160     }
161
162     /* initialise MIDI synths */
163     for (i = 0; i < MAX_MIDI_SYNTHS; i++)
164     {
165         snprintf(szPname, sizeof(szPname), "CoreAudio MIDI Synth %d", i);
166         MultiByteToWideChar(CP_ACP, 0, szPname, -1, destinations[i].caps.szPname, sizeof(destinations[i].caps.szPname)/sizeof(WCHAR));
167
168         destinations[i].caps.wTechnology = MOD_SYNTH;
169         destinations[i].caps.wChannelMask = 0xFFFF;
170
171         destinations[i].caps.wMid = 0x00FF;     /* Manufac ID */
172         destinations[i].caps.wPid = 0x0001;     /* Product ID */
173         destinations[i].caps.vDriverVersion = 0x0001;
174         destinations[i].caps.dwSupport = MIDICAPS_VOLUME;
175         destinations[i].caps.wVoices = 16;
176         destinations[i].caps.wNotes = 16;
177     }
178     /* initialise available destinations */
179     for (i = MAX_MIDI_SYNTHS; i < numDest + MAX_MIDI_SYNTHS; i++)
180     {
181         destinations[i].dest = MIDIGetDestination(i - MAX_MIDI_SYNTHS);
182
183         CoreMIDI_GetObjectName(destinations[i].dest, szPname, sizeof(szPname));
184         MultiByteToWideChar(CP_ACP, 0, szPname, -1, destinations[i].caps.szPname, sizeof(destinations[i].caps.szPname)/sizeof(WCHAR));
185
186         destinations[i].caps.wTechnology = MOD_MIDIPORT;
187         destinations[i].caps.wChannelMask = 0xFFFF;
188
189         destinations[i].caps.wMid = 0x00FF;     /* Manufac ID */
190         destinations[i].caps.wPid = 0x0001;
191         destinations[i].caps.vDriverVersion = 0x0001;
192         destinations[i].caps.dwSupport = 0;
193         destinations[i].caps.wVoices = 16;
194         destinations[i].caps.wNotes = 16;
195     }
196     return DRV_SUCCESS;
197 }
198
199 LONG CoreAudio_MIDIRelease(void)
200 {
201     TRACE("\n");
202     if (MIDIIn_NumDevs > 0)
203     {
204         CFMessagePortRef messagePort;
205         /* Stop CFRunLoop in MIDIIn_MessageThread */
206         messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, MIDIInThreadPortName);
207         CFMessagePortSendRequest(messagePort, 1, NULL, 0.0, 0.0, NULL, NULL);
208         CFRelease(messagePort);
209
210         DeleteCriticalSection(&midiInLock);
211     }
212
213     if (wineMIDIClient) MIDIClientDispose(wineMIDIClient); /* MIDIClientDispose will close all ports */
214
215     HeapFree(GetProcessHeap(), 0, sources);
216     HeapFree(GetProcessHeap(), 0, destinations);
217     return DRV_SUCCESS;
218 }
219
220
221 /**************************************************************************
222  *                      MIDI_NotifyClient                       [internal]
223  */
224 static DWORD MIDI_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
225 {
226     DWORD               dwCallBack;
227     UINT                uFlags;
228     HANDLE              hDev;
229     DWORD               dwInstance;
230
231     TRACE("wDevID=%d wMsg=%d dwParm1=%04X dwParam2=%04X\n", wDevID, wMsg, dwParam1, dwParam2);
232
233     switch (wMsg) {
234     case MOM_OPEN:
235     case MOM_CLOSE:
236     case MOM_DONE:
237     case MOM_POSITIONCB:
238         dwCallBack = destinations[wDevID].midiDesc.dwCallback;
239         uFlags = destinations[wDevID].wFlags;
240         hDev = destinations[wDevID].midiDesc.hMidi;
241         dwInstance = destinations[wDevID].midiDesc.dwInstance;
242         break;
243
244     case MIM_OPEN:
245     case MIM_CLOSE:
246     case MIM_DATA:
247     case MIM_LONGDATA:
248     case MIM_ERROR:
249     case MIM_LONGERROR:
250     case MIM_MOREDATA:
251         dwCallBack = sources[wDevID].midiDesc.dwCallback;
252         uFlags = sources[wDevID].wFlags;
253         hDev = sources[wDevID].midiDesc.hMidi;
254         dwInstance = sources[wDevID].midiDesc.dwInstance;
255         break;
256     default:
257         WARN("Unsupported MSW-MIDI message %u\n", wMsg);
258         return MMSYSERR_ERROR;
259     }
260
261     return DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2) ?
262         MMSYSERR_NOERROR : MMSYSERR_ERROR;
263 }
264
265 static DWORD MIDIOut_Open(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
266 {
267     MIDIDestination *dest;
268
269     TRACE("wDevID=%d lpDesc=%p dwFlags=%08x\n", wDevID, lpDesc, dwFlags);
270
271     if (lpDesc == NULL) {
272         WARN("Invalid Parameter\n");
273         return MMSYSERR_INVALPARAM;
274     }
275
276     if (wDevID >= MIDIOut_NumDevs) {
277         WARN("bad device ID : %d\n", wDevID);
278         return MMSYSERR_BADDEVICEID;
279     }
280
281     if (destinations[wDevID].midiDesc.hMidi != 0) {
282         WARN("device already open !\n");
283         return MMSYSERR_ALLOCATED;
284     }
285
286     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
287         WARN("bad dwFlags\n");
288         return MMSYSERR_INVALFLAG;
289     }
290     dest = &destinations[wDevID];
291
292     if (dest->caps.wTechnology == MOD_SYNTH)
293     {
294         if (!SynthUnit_CreateDefaultSynthUnit(&dest->graph, &dest->synth))
295         {
296             ERR("SynthUnit_CreateDefaultSynthUnit dest=%p failed\n", dest);
297             return MMSYSERR_ERROR;
298         }
299
300         if (!SynthUnit_Initialize(dest->synth, dest->graph))
301         {
302             ERR("SynthUnit_Initialise dest=%p failed\n", dest);
303             return MMSYSERR_ERROR;
304         }
305     }
306     dest->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
307     dest->midiDesc = *lpDesc;
308
309     return MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
310 }
311
312 static DWORD MIDIOut_Close(WORD wDevID)
313 {
314     DWORD ret = MMSYSERR_NOERROR;
315
316     TRACE("wDevID=%d\n", wDevID);
317
318     if (wDevID >= MIDIOut_NumDevs) {
319         WARN("bad device ID : %d\n", wDevID);
320         return MMSYSERR_BADDEVICEID;
321     }
322
323     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
324         SynthUnit_Close(destinations[wDevID].graph);
325
326     destinations[wDevID].graph = 0;
327     destinations[wDevID].synth = 0;
328
329     if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
330         WARN("can't notify client !\n");
331         ret = MMSYSERR_INVALPARAM;
332     }
333     destinations[wDevID].midiDesc.hMidi = 0;
334
335     return ret;
336 }
337
338 static DWORD MIDIOut_Data(WORD wDevID, DWORD dwParam)
339 {
340     WORD evt = LOBYTE(LOWORD(dwParam));
341     UInt8 chn = (evt & 0x0F);
342
343     TRACE("wDevID=%d dwParam=%08X\n", wDevID, dwParam);
344
345     if (wDevID >= MIDIOut_NumDevs) {
346         WARN("bad device ID : %d\n", wDevID);
347         return MMSYSERR_BADDEVICEID;
348     }
349
350     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
351     {
352         WORD d1  = HIBYTE(LOWORD(dwParam));
353         WORD d2  = LOBYTE(HIWORD(dwParam));
354         OSStatus err = noErr;
355
356         err = MusicDeviceMIDIEvent(destinations[wDevID].synth, (evt & 0xF0) | chn, d1, d2, 0);
357         if (err != noErr)
358         {
359             ERR("MusicDeviceMIDIEvent(%p, %04x, %04x, %04x, %d) return %c%c%c%c\n", destinations[wDevID].synth, (evt & 0xF0) | chn, d1, d2, 0, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
360             return MMSYSERR_ERROR;
361         }
362     }
363     else
364     {
365         UInt8 buffer[3];
366         buffer[0] = (evt & 0xF0) | chn;
367         buffer[1] = HIBYTE(LOWORD(dwParam));
368         buffer[2] = LOBYTE(HIWORD(dwParam));
369
370         MIDIOut_Send(MIDIOutPort, destinations[wDevID].dest, buffer, 3);
371     }
372
373     return MMSYSERR_NOERROR;
374 }
375
376 static DWORD MIDIOut_LongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
377 {
378     LPBYTE lpData;
379     OSStatus err = noErr;
380
381     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
382
383     /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
384      * but it seems to be used only for midi input.
385      * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
386      */
387
388     if (wDevID >= MIDIOut_NumDevs) {
389         WARN("bad device ID : %d\n", wDevID);
390         return MMSYSERR_BADDEVICEID;
391     }
392
393     if (lpMidiHdr == NULL) {
394         WARN("Invalid Parameter\n");
395         return MMSYSERR_INVALPARAM;
396     }
397
398     lpData = (LPBYTE) lpMidiHdr->lpData;
399
400     if (lpData == NULL)
401         return MIDIERR_UNPREPARED;
402     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
403         return MIDIERR_UNPREPARED;
404     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
405         return MIDIERR_STILLPLAYING;
406     lpMidiHdr->dwFlags &= ~MHDR_DONE;
407     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
408
409     /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
410      * data, or can it also contain raw MIDI data, to be split up and sent to
411      * modShortData() ?
412      * If the latest is true, then the following WARNing will fire up
413      */
414     if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
415         WARN("Alledged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
416     }
417
418     TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
419     TRACE("                 %02X %02X %02X ... %02X %02X %02X\n",
420           lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
421           lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
422
423
424     if (lpData[0] != 0xF0) {
425         /* System Exclusive */
426         ERR("Add missing 0xF0 marker at the beginning of system exclusive byte stream\n");
427     }
428     if (lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
429         /* Send end of System Exclusive */
430         ERR("Add missing 0xF7 marker at the end of system exclusive byte stream\n");
431     }
432     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH) /* FIXME */
433     {
434         err = MusicDeviceSysEx(destinations[wDevID].synth, (const UInt8 *) lpData, lpMidiHdr->dwBufferLength);
435         if (err != noErr)
436         {
437             ERR("MusicDeviceSysEx(%p, %p, %d) return %c%c%c%c\n", destinations[wDevID].synth, lpData, lpMidiHdr->dwBufferLength, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
438             return MMSYSERR_ERROR;
439         }
440     }
441     else
442     {
443         FIXME("MOD_MIDIPORT\n");
444     }
445
446     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
447     lpMidiHdr->dwFlags |= MHDR_DONE;
448     if (MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD)lpMidiHdr, 0L) != MMSYSERR_NOERROR) {
449         WARN("can't notify client !\n");
450         return MMSYSERR_INVALPARAM;
451     }
452     return MMSYSERR_NOERROR;
453 }
454
455 /**************************************************************************
456  *                      MIDIOut_Prepare                         [internal]
457  */
458 static DWORD MIDIOut_Prepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
459 {
460     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
461
462     if (wDevID >= MIDIOut_NumDevs) {
463         WARN("bad device ID : %d\n", wDevID);
464         return MMSYSERR_BADDEVICEID;
465     }
466
467     /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
468      * asks to prepare MIDIHDR which dwFlags != 0.
469      * So at least check for the inqueue flag
470      */
471     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
472         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
473         lpMidiHdr->dwBufferLength >= 0x10000ul) {
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 * 0xFFFFl) + (((WORD) right * 0xFFFFl) << 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     if (sources[wDevID].lpQueueHdr == 0) {
693         sources[wDevID].lpQueueHdr = lpMidiHdr;
694     } else {
695         LPMIDIHDR ptr;
696         for (ptr = sources[wDevID].lpQueueHdr;
697              ptr->lpNext != 0;
698              ptr = (LPMIDIHDR)ptr->lpNext);
699         ptr->lpNext = (struct midihdr_tag*)lpMidiHdr;
700     }
701     LeaveCriticalSection(&midiInLock);
702
703     return MMSYSERR_NOERROR;
704 }
705
706 static DWORD MIDIIn_Prepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
707 {
708     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
709
710     if (wDevID >= MIDIIn_NumDevs) {
711         WARN("bad device ID : %d\n", wDevID);
712         return MMSYSERR_BADDEVICEID;
713     }
714     /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
715      * asks to prepare MIDIHDR which dwFlags != 0.
716      * So at least check for the inqueue flag
717      */
718     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0 ||
719         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0 ||
720         lpMidiHdr->dwBufferLength >= 0x10000ul) {
721         WARN("Invalid parameter %p %p %08x %d\n", lpMidiHdr, lpMidiHdr->lpData,
722                    lpMidiHdr->dwFlags, dwSize);
723         return MMSYSERR_INVALPARAM;
724     }
725
726     lpMidiHdr->lpNext = 0;
727     lpMidiHdr->dwFlags |= MHDR_PREPARED;
728     lpMidiHdr->dwFlags &= ~MHDR_DONE;
729     return MMSYSERR_NOERROR;
730 }
731
732 static DWORD MIDIIn_Unprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
733 {
734     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
735     if (wDevID >= MIDIIn_NumDevs) {
736         WARN("bad device ID : %d\n", wDevID);
737         return MMSYSERR_BADDEVICEID;
738     }
739     if (dwSize < sizeof(MIDIHDR) || lpMidiHdr == 0) {
740         WARN("Invalid Parameter\n");
741         return MMSYSERR_INVALPARAM;
742     }
743     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) {
744         WARN("Still playing\n");
745         return MIDIERR_STILLPLAYING;
746     }
747
748     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
749     return MMSYSERR_NOERROR;
750 }
751
752 static DWORD MIDIIn_GetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
753 {
754     TRACE("wDevID=%d lpCaps=%p dwSize=%d\n", wDevID, lpCaps, dwSize);
755
756     if (lpCaps == NULL) {
757         WARN("Invalid Parameter\n");
758         return MMSYSERR_INVALPARAM;
759     }
760
761     if (wDevID >= MIDIIn_NumDevs) {
762         WARN("bad device ID : %d\n", wDevID);
763         return MMSYSERR_BADDEVICEID;
764     }
765     memcpy(lpCaps, &sources[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
766     return MMSYSERR_NOERROR;
767 }
768
769 static DWORD MIDIIn_GetNumDevs(void)
770 {
771     TRACE("\n");
772     return MIDIIn_NumDevs;
773 }
774
775 static DWORD MIDIIn_Start(WORD wDevID)
776 {
777     TRACE("%d\n", wDevID);
778
779     if (wDevID >= MIDIIn_NumDevs) {
780         WARN("bad device ID : %d\n", wDevID);
781         return MMSYSERR_BADDEVICEID;
782     }
783     sources[wDevID].state = 1;
784     sources[wDevID].startTime = GetTickCount();
785     return MMSYSERR_NOERROR;
786 }
787
788 static DWORD MIDIIn_Stop(WORD wDevID)
789 {
790     TRACE("%d\n", wDevID);
791     if (wDevID >= MIDIIn_NumDevs) {
792         WARN("bad device ID : %d\n", wDevID);
793         return MMSYSERR_BADDEVICEID;
794     }
795     sources[wDevID].state = 0;
796     return MMSYSERR_NOERROR;
797 }
798
799 static DWORD MIDIIn_Reset(WORD wDevID)
800 {
801     DWORD dwTime = GetTickCount();
802
803     TRACE("%d\n", wDevID);
804     if (wDevID >= MIDIIn_NumDevs) {
805         WARN("bad device ID : %d\n", wDevID);
806         return MMSYSERR_BADDEVICEID;
807     }
808
809     EnterCriticalSection(&midiInLock);
810     while (sources[wDevID].lpQueueHdr) {
811         sources[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
812         sources[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
813         /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
814         if (MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)sources[wDevID].lpQueueHdr, dwTime) != MMSYSERR_NOERROR) {
815             WARN("Couldn't notify client\n");
816         }
817         sources[wDevID].lpQueueHdr = (LPMIDIHDR)sources[wDevID].lpQueueHdr->lpNext;
818     }
819     LeaveCriticalSection(&midiInLock);
820
821     return MMSYSERR_NOERROR;
822 }
823
824 /*
825  * MIDI In Mach message handling
826  */
827
828 /*
829  *  Call from CoreMIDI IO threaded callback,
830  *  we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
831  */
832 void MIDIIn_SendMessage(MIDIMessage msg)
833 {
834     CFDataRef data;
835
836     CFMessagePortRef messagePort;
837     messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, MIDIInThreadPortName);
838
839     data = CFDataCreate(kCFAllocatorDefault, (UInt8 *) &msg, sizeof(msg));
840     if (data)
841     {
842         CFMessagePortSendRequest(messagePort, 0, data, 0.0, 0.0, NULL, NULL);
843         CFRelease(data);
844         CFRelease(messagePort);
845     }
846 }
847
848 static CFDataRef MIDIIn_MessageHandler(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info)
849 {
850     MIDIMessage *msg = NULL;
851     int i = 0;
852     MIDISource *src = NULL;
853     DWORD sendData = 0;
854     int pos = 0;
855     DWORD currentTime;
856
857     switch (msgid)
858     {
859         case 0:
860             msg = (MIDIMessage *) CFDataGetBytePtr(data);
861             TRACE("devID=%d\n", msg->devID);
862              for (i = 0; i < msg->length; ++i) {
863                 TRACE("%02X ", msg->data[i]);
864             }
865             TRACE("\n");
866             src = &sources[msg->devID];
867             if (src->state < 1)
868             {
869                 TRACE("input not started, thrown away\n");
870                 goto done;
871             }
872             /* FIXME skipping SysEx */
873             if (msg->data[0] == 0xF0)
874             {
875                 FIXME("Starting System Exclusive\n");
876                 src->state |= 2;
877             }
878             if (src->state & 2)
879             {
880                 for (i = 0; i < msg->length; ++i)
881                 {
882                     if (msg->data[i] == 0xF7)
883                     {
884                         FIXME("Ending System Exclusive\n");
885                         src->state &= ~2;
886                     }
887                 }
888                 goto done;
889             }
890             EnterCriticalSection(&midiInLock);
891             currentTime = GetTickCount() - src->startTime;
892
893             while (pos < msg->length)
894             {
895                 sendData = 0;
896                 switch (msg->data[pos] & 0xF0)
897                 {
898                     case 0xF0:
899                         sendData = (msg->data[pos] <<  0);
900                         pos++;
901                         break;
902
903                     case 0xC0:
904                     case 0xD0:
905                         sendData = (msg->data[pos + 1] <<  8) | (msg->data[pos] <<  0);
906                         pos += 2;
907                         break;
908                     default:
909                         sendData = (msg->data[pos + 2] << 16) |
910                                     (msg->data[pos + 1] <<  8) |
911                                     (msg->data[pos] <<  0);
912                         pos += 3;
913                         break;
914                 }
915                 MIDI_NotifyClient(msg->devID, MIM_DATA, sendData, currentTime);
916             }
917             LeaveCriticalSection(&midiInLock);
918             break;
919         default:
920             CFRunLoopStop(CFRunLoopGetCurrent());
921             break;
922     }
923 done:
924     return NULL;
925 }
926
927 static DWORD WINAPI MIDIIn_MessageThread(LPVOID p)
928 {
929     CFMessagePortRef local;
930     CFRunLoopSourceRef source;
931     Boolean info;
932
933     local = CFMessagePortCreateLocal(kCFAllocatorDefault, MIDIInThreadPortName, &MIDIIn_MessageHandler, NULL, &info);
934
935     source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, local, (CFIndex)0);
936     CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
937
938     CFRunLoopRun();
939
940     CFRunLoopSourceInvalidate(source);
941     CFRelease(source);
942     CFRelease(local);
943     CFRelease(MIDIInThreadPortName);
944     MIDIInThreadPortName = NULL;
945
946     return 0;
947 }
948
949 /**************************************************************************
950 *                               modMessage
951 */
952 DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
953 {
954     TRACE("%d %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
955
956     switch (wMsg) {
957         case DRVM_INIT:
958         case DRVM_EXIT:
959         case DRVM_ENABLE:
960         case DRVM_DISABLE:
961             return 0;
962         case MODM_OPEN:
963             return MIDIOut_Open(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
964         case MODM_CLOSE:
965             return MIDIOut_Close(wDevID);
966         case MODM_DATA:
967             return MIDIOut_Data(wDevID, dwParam1);
968         case MODM_LONGDATA:
969             return MIDIOut_LongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
970         case MODM_PREPARE:
971             return MIDIOut_Prepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
972         case MODM_UNPREPARE:
973             return MIDIOut_Unprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
974         case MODM_GETDEVCAPS:
975             return MIDIOut_GetDevCaps(wDevID, (LPMIDIOUTCAPSW) dwParam1, dwParam2);
976         case MODM_GETNUMDEVS:
977             return MIDIOut_GetNumDevs();
978         case MODM_GETVOLUME:
979             return MIDIOut_GetVolume(wDevID, (DWORD*)dwParam1);
980         case MODM_SETVOLUME:
981             return MIDIOut_SetVolume(wDevID, dwParam1);
982         case MODM_RESET:
983             return MIDIOut_Reset(wDevID);
984         default:
985             TRACE("Unsupported message (08%x)\n", wMsg);
986     }
987     return MMSYSERR_NOTSUPPORTED;
988 }
989
990 /**************************************************************************
991 *                       midMessage
992 */
993 DWORD WINAPI CoreAudio_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
994 {
995     TRACE("%d %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
996     switch (wMsg) {
997         case DRVM_INIT:
998         case DRVM_EXIT:
999         case DRVM_ENABLE:
1000         case DRVM_DISABLE:
1001             return 0;
1002         case MIDM_OPEN:
1003             return MIDIIn_Open(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1004         case MIDM_CLOSE:
1005             return MIDIIn_Close(wDevID);
1006         case MIDM_ADDBUFFER:
1007             return MIDIIn_AddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1008         case MIDM_PREPARE:
1009             return MIDIIn_Prepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1010         case MIDM_UNPREPARE:
1011             return MIDIIn_Unprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1012         case MIDM_GETDEVCAPS:
1013             return MIDIIn_GetDevCaps(wDevID, (LPMIDIINCAPSW) dwParam1, dwParam2);
1014         case MIDM_GETNUMDEVS:
1015             return MIDIIn_GetNumDevs();
1016         case MIDM_START:
1017             return MIDIIn_Start(wDevID);
1018         case MIDM_STOP:
1019             return MIDIIn_Stop(wDevID);
1020         case MIDM_RESET:
1021             return MIDIIn_Reset(wDevID);
1022         default:
1023             TRACE("Unsupported message\n");
1024     }
1025     return MMSYSERR_NOTSUPPORTED;
1026 }
1027 #else
1028
1029 DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
1030 {
1031     TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1032     return MMSYSERR_NOTENABLED;
1033 }
1034
1035 DWORD WINAPI CoreAudio_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1036                                   DWORD dwParam1, DWORD dwParam2)
1037 {
1038     TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1039     return MMSYSERR_NOTENABLED;
1040 }
1041 #endif