gdiplus: Remove the gdi32 line gradient drawing code.
[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 void MIDI_NotifyClient(UINT wDevID, WORD wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
226 {
227     DWORD               dwCallBack;
228     UINT                uFlags;
229     HANDLE              hDev;
230     DWORD               dwInstance;
231
232     TRACE("wDevID=%d wMsg=%d dwParm1=%04lX dwParam2=%04lX\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         ERR("Unsupported MSW-MIDI message %u\n", wMsg);
259         return;
260     }
261
262     DriverCallback(dwCallBack, uFlags, hDev, wMsg, dwInstance, dwParam1, dwParam2);
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     MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L);
310     return MMSYSERR_NOERROR;
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     MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L);
331     destinations[wDevID].midiDesc.hMidi = 0;
332
333     return ret;
334 }
335
336 static DWORD MIDIOut_Data(WORD wDevID, DWORD dwParam)
337 {
338     WORD evt = LOBYTE(LOWORD(dwParam));
339     UInt8 chn = (evt & 0x0F);
340
341     TRACE("wDevID=%d dwParam=%08X\n", wDevID, dwParam);
342
343     if (wDevID >= MIDIOut_NumDevs) {
344         WARN("bad device ID : %d\n", wDevID);
345         return MMSYSERR_BADDEVICEID;
346     }
347
348     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
349     {
350         WORD d1  = HIBYTE(LOWORD(dwParam));
351         WORD d2  = LOBYTE(HIWORD(dwParam));
352         OSStatus err = noErr;
353
354         err = MusicDeviceMIDIEvent(destinations[wDevID].synth, (evt & 0xF0) | chn, d1, d2, 0);
355         if (err != noErr)
356         {
357             ERR("MusicDeviceMIDIEvent(%p, %04x, %04x, %04x, %d) return %s\n", destinations[wDevID].synth, (evt & 0xF0) | chn, d1, d2, 0, wine_dbgstr_fourcc(err));
358             return MMSYSERR_ERROR;
359         }
360     }
361     else
362     {
363         UInt8 buffer[3];
364         buffer[0] = (evt & 0xF0) | chn;
365         buffer[1] = HIBYTE(LOWORD(dwParam));
366         buffer[2] = LOBYTE(HIWORD(dwParam));
367
368         MIDIOut_Send(MIDIOutPort, destinations[wDevID].dest, buffer, 3);
369     }
370
371     return MMSYSERR_NOERROR;
372 }
373
374 static DWORD MIDIOut_LongData(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
375 {
376     LPBYTE lpData;
377     OSStatus err = noErr;
378
379     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
380
381     /* Note: MS doc does not say much about the dwBytesRecorded member of the MIDIHDR structure
382      * but it seems to be used only for midi input.
383      * Taking a look at the WAVEHDR structure (which is quite similar) confirms this assumption.
384      */
385
386     if (wDevID >= MIDIOut_NumDevs) {
387         WARN("bad device ID : %d\n", wDevID);
388         return MMSYSERR_BADDEVICEID;
389     }
390
391     if (lpMidiHdr == NULL) {
392         WARN("Invalid Parameter\n");
393         return MMSYSERR_INVALPARAM;
394     }
395
396     lpData = (LPBYTE) lpMidiHdr->lpData;
397
398     if (lpData == NULL)
399         return MIDIERR_UNPREPARED;
400     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
401         return MIDIERR_UNPREPARED;
402     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
403         return MIDIERR_STILLPLAYING;
404     lpMidiHdr->dwFlags &= ~MHDR_DONE;
405     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
406
407     /* FIXME: MS doc is not 100% clear. Will lpData only contain system exclusive
408      * data, or can it also contain raw MIDI data, to be split up and sent to
409      * modShortData() ?
410      * If the latest is true, then the following WARNing will fire up
411      */
412     if (lpData[0] != 0xF0 || lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
413         WARN("Alledged system exclusive buffer is not correct\n\tPlease report with MIDI file\n");
414     }
415
416     TRACE("dwBufferLength=%u !\n", lpMidiHdr->dwBufferLength);
417     TRACE("                 %02X %02X %02X ... %02X %02X %02X\n",
418           lpData[0], lpData[1], lpData[2], lpData[lpMidiHdr->dwBufferLength-3],
419           lpData[lpMidiHdr->dwBufferLength-2], lpData[lpMidiHdr->dwBufferLength-1]);
420
421
422     if (lpData[0] != 0xF0) {
423         /* System Exclusive */
424         ERR("Add missing 0xF0 marker at the beginning of system exclusive byte stream\n");
425     }
426     if (lpData[lpMidiHdr->dwBufferLength - 1] != 0xF7) {
427         /* Send end of System Exclusive */
428         ERR("Add missing 0xF7 marker at the end of system exclusive byte stream\n");
429     }
430     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH) /* FIXME */
431     {
432         err = MusicDeviceSysEx(destinations[wDevID].synth, (const UInt8 *) lpData, lpMidiHdr->dwBufferLength);
433         if (err != noErr)
434         {
435             ERR("MusicDeviceSysEx(%p, %p, %d) return %s\n", destinations[wDevID].synth, lpData, lpMidiHdr->dwBufferLength, wine_dbgstr_fourcc(err));
436             return MMSYSERR_ERROR;
437         }
438     }
439     else
440     {
441         FIXME("MOD_MIDIPORT\n");
442     }
443
444     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
445     lpMidiHdr->dwFlags |= MHDR_DONE;
446     MIDI_NotifyClient(wDevID, MOM_DONE, (DWORD_PTR)lpMidiHdr, 0L);
447     return MMSYSERR_NOERROR;
448 }
449
450 /**************************************************************************
451  *                      MIDIOut_Prepare                         [internal]
452  */
453 static DWORD MIDIOut_Prepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
454 {
455     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
456
457     if (wDevID >= MIDIOut_NumDevs) {
458         WARN("bad device ID : %d\n", wDevID);
459         return MMSYSERR_BADDEVICEID;
460     }
461
462     /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
463      * asks to prepare MIDIHDR which dwFlags != 0.
464      * So at least check for the inqueue flag
465      */
466     if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
467         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0) {
468         WARN("%p %p %08x %lu/%d\n", lpMidiHdr, lpMidiHdr->lpData,
469                    lpMidiHdr->dwFlags, offsetof(MIDIHDR,dwOffset), dwSize);
470         return MMSYSERR_INVALPARAM;
471     }
472
473     lpMidiHdr->lpNext = 0;
474     lpMidiHdr->dwFlags |= MHDR_PREPARED;
475     lpMidiHdr->dwFlags &= ~MHDR_DONE;
476     return MMSYSERR_NOERROR;
477 }
478
479 /**************************************************************************
480  *                              MIDIOut_Unprepare                       [internal]
481  */
482 static DWORD MIDIOut_Unprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
483 {
484     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
485
486     if (wDevID >= MIDIOut_NumDevs) {
487         WARN("bad device ID : %d\n", wDevID);
488         return MMSYSERR_BADDEVICEID;
489     }
490     if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0)
491         return MMSYSERR_INVALPARAM;
492     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
493         return MIDIERR_STILLPLAYING;
494
495     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
496
497     return MMSYSERR_NOERROR;
498 }
499
500 static DWORD MIDIOut_GetDevCaps(WORD wDevID, LPMIDIOUTCAPSW lpCaps, DWORD dwSize)
501 {
502     TRACE("wDevID=%d lpCaps=%p dwSize=%d\n", wDevID, lpCaps, dwSize);
503
504     if (lpCaps == NULL) {
505         WARN("Invalid Parameter\n");
506         return MMSYSERR_INVALPARAM;
507     }
508
509     if (wDevID >= MIDIOut_NumDevs) {
510         WARN("bad device ID : %d\n", wDevID);
511         return MMSYSERR_BADDEVICEID;
512     }
513     memcpy(lpCaps, &destinations[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
514     return MMSYSERR_NOERROR;
515 }
516
517 static DWORD MIDIOut_GetNumDevs(void)
518 {
519     TRACE("\n");
520     return MIDIOut_NumDevs;
521 }
522
523 static DWORD MIDIOut_GetVolume(WORD wDevID, DWORD *lpdwVolume)
524 {
525     TRACE("%d\n", wDevID);
526
527     if (wDevID >= MIDIOut_NumDevs) {
528         WARN("bad device ID : %d\n", wDevID);
529         return MMSYSERR_BADDEVICEID;
530     }
531     if (lpdwVolume == NULL) {
532         WARN("Invalid Parameter\n");
533         return MMSYSERR_INVALPARAM;
534     }
535
536     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
537     {
538         float left;
539         float right;
540         AudioUnit_GetVolume(destinations[wDevID].synth, &left, &right);
541
542         *lpdwVolume = (WORD) (left * 0xFFFF) + ((WORD) (right * 0xFFFF) << 16);
543
544         return MMSYSERR_NOERROR;
545     }
546
547     return MMSYSERR_NOTSUPPORTED;
548 }
549
550 static DWORD MIDIOut_SetVolume(WORD wDevID, DWORD dwVolume)
551 {
552     TRACE("%d\n", wDevID);
553
554     if (wDevID >= MIDIOut_NumDevs) {
555         WARN("bad device ID : %d\n", wDevID);
556         return MMSYSERR_BADDEVICEID;
557     }
558     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
559     {
560         float left;
561         float right;
562
563         left  = LOWORD(dwVolume) / 65535.0f;
564         right = HIWORD(dwVolume) / 65535.0f;
565         AudioUnit_SetVolume(destinations[wDevID].synth, left, right);
566
567         return MMSYSERR_NOERROR;
568     }
569
570     return MMSYSERR_NOTSUPPORTED;
571 }
572
573 static DWORD MIDIOut_Reset(WORD wDevID)
574 {
575     unsigned chn;
576
577     TRACE("%d\n", wDevID);
578
579     if (wDevID >= MIDIOut_NumDevs) {
580         WARN("bad device ID : %d\n", wDevID);
581         return MMSYSERR_BADDEVICEID;
582     }
583     if (destinations[wDevID].caps.wTechnology == MOD_SYNTH)
584     {
585         for (chn = 0; chn < 16; chn++) {
586             /* turn off every note */
587             MusicDeviceMIDIEvent(destinations[wDevID].synth, 0xB0 | chn, 0x7B, 0, 0);
588             /* remove sustain on channel */
589             MusicDeviceMIDIEvent(destinations[wDevID].synth, 0xB0 | chn, 0x40, 0, 0);
590         }
591     }
592     else FIXME("MOD_MIDIPORT\n");
593
594     /* FIXME: the LongData buffers must also be returned to the app */
595     return MMSYSERR_NOERROR;
596 }
597
598 static DWORD MIDIIn_Open(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
599 {
600     TRACE("wDevID=%d lpDesc=%p dwFlags=%08x\n", wDevID, lpDesc, dwFlags);
601
602     if (lpDesc == NULL) {
603         WARN("Invalid Parameter\n");
604         return MMSYSERR_INVALPARAM;
605     }
606     if (wDevID >= MIDIIn_NumDevs) {
607         WARN("bad device ID : %d\n", wDevID);
608         return MMSYSERR_BADDEVICEID;
609     }
610     if (sources[wDevID].midiDesc.hMidi != 0) {
611         WARN("device already open !\n");
612         return MMSYSERR_ALLOCATED;
613     }
614     if ((dwFlags & MIDI_IO_STATUS) != 0) {
615         WARN("No support for MIDI_IO_STATUS in dwFlags yet, ignoring it\n");
616         dwFlags &= ~MIDI_IO_STATUS;
617     }
618     if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) {
619         FIXME("Bad dwFlags\n");
620         return MMSYSERR_INVALFLAG;
621     }
622
623     sources[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
624     sources[wDevID].lpQueueHdr = NULL;
625     sources[wDevID].midiDesc = *lpDesc;
626     sources[wDevID].startTime = 0;
627     sources[wDevID].state = 0;
628
629     MIDI_NotifyClient(wDevID, MIM_OPEN, 0L, 0L);
630     return MMSYSERR_NOERROR;
631 }
632
633 static DWORD MIDIIn_Close(WORD wDevID)
634 {
635     DWORD ret = MMSYSERR_NOERROR;
636
637     TRACE("wDevID=%d\n", wDevID);
638
639     if (wDevID >= MIDIIn_NumDevs) {
640         WARN("bad device ID : %d\n", wDevID);
641         return MMSYSERR_BADDEVICEID;
642     }
643
644     if (sources[wDevID].midiDesc.hMidi == 0) {
645         WARN("device not opened !\n");
646         return MMSYSERR_ERROR;
647     }
648     if (sources[wDevID].lpQueueHdr != 0) {
649         return MIDIERR_STILLPLAYING;
650     }
651
652     MIDI_NotifyClient(wDevID, MIM_CLOSE, 0L, 0L);
653     sources[wDevID].midiDesc.hMidi = 0;
654     return ret;
655 }
656
657 static DWORD MIDIIn_AddBuffer(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
658 {
659     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
660
661     if (wDevID >= MIDIIn_NumDevs) {
662         WARN("bad device ID : %d\n", wDevID);
663         return MMSYSERR_BADDEVICEID;
664     }
665     if (lpMidiHdr == NULL) {
666         WARN("Invalid Parameter\n");
667         return MMSYSERR_INVALPARAM;
668     }
669     if (dwSize < offsetof(MIDIHDR,dwOffset)) {
670         WARN("Invalid Parameter\n");
671         return MMSYSERR_INVALPARAM;
672     }
673     if (lpMidiHdr->dwBufferLength == 0) {
674         WARN("Invalid Parameter\n");
675         return MMSYSERR_INVALPARAM;
676     }
677     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) {
678         WARN("Still playing\n");
679         return MIDIERR_STILLPLAYING;
680     }
681     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) {
682         WARN("Unprepared\n");
683         return MIDIERR_UNPREPARED;
684     }
685
686     EnterCriticalSection(&midiInLock);
687     lpMidiHdr->dwFlags &= ~WHDR_DONE;
688     lpMidiHdr->dwFlags |= MHDR_INQUEUE;
689     lpMidiHdr->dwBytesRecorded = 0;
690     lpMidiHdr->lpNext = 0;
691     if (sources[wDevID].lpQueueHdr == 0) {
692         sources[wDevID].lpQueueHdr = lpMidiHdr;
693     } else {
694         LPMIDIHDR ptr;
695         for (ptr = sources[wDevID].lpQueueHdr;
696              ptr->lpNext != 0;
697              ptr = ptr->lpNext);
698         ptr->lpNext = lpMidiHdr;
699     }
700     LeaveCriticalSection(&midiInLock);
701
702     return MMSYSERR_NOERROR;
703 }
704
705 static DWORD MIDIIn_Prepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
706 {
707     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
708
709     if (wDevID >= MIDIIn_NumDevs) {
710         WARN("bad device ID : %d\n", wDevID);
711         return MMSYSERR_BADDEVICEID;
712     }
713     /* MS doc says that dwFlags must be set to zero, but (kinda funny) MS mciseq drivers
714      * asks to prepare MIDIHDR which dwFlags != 0.
715      * So at least check for the inqueue flag
716      */
717     if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0 ||
718         lpMidiHdr->lpData == 0 || (lpMidiHdr->dwFlags & MHDR_INQUEUE) != 0) {
719         WARN("Invalid parameter %p %p %08x %d\n", lpMidiHdr, lpMidiHdr->lpData,
720                    lpMidiHdr->dwFlags, dwSize);
721         return MMSYSERR_INVALPARAM;
722     }
723
724     lpMidiHdr->lpNext = 0;
725     lpMidiHdr->dwFlags |= MHDR_PREPARED;
726     lpMidiHdr->dwFlags &= ~MHDR_DONE;
727     return MMSYSERR_NOERROR;
728 }
729
730 static DWORD MIDIIn_Unprepare(WORD wDevID, LPMIDIHDR lpMidiHdr, DWORD dwSize)
731 {
732     TRACE("wDevID=%d lpMidiHdr=%p dwSize=%d\n", wDevID, lpMidiHdr, dwSize);
733     if (wDevID >= MIDIIn_NumDevs) {
734         WARN("bad device ID : %d\n", wDevID);
735         return MMSYSERR_BADDEVICEID;
736     }
737     if (dwSize < offsetof(MIDIHDR,dwOffset) || lpMidiHdr == 0) {
738         WARN("Invalid Parameter\n");
739         return MMSYSERR_INVALPARAM;
740     }
741     if (lpMidiHdr->dwFlags & MHDR_INQUEUE) {
742         WARN("Still playing\n");
743         return MIDIERR_STILLPLAYING;
744     }
745
746     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
747     return MMSYSERR_NOERROR;
748 }
749
750 static DWORD MIDIIn_GetDevCaps(WORD wDevID, LPMIDIINCAPSW lpCaps, DWORD dwSize)
751 {
752     TRACE("wDevID=%d lpCaps=%p dwSize=%d\n", wDevID, lpCaps, dwSize);
753
754     if (lpCaps == NULL) {
755         WARN("Invalid Parameter\n");
756         return MMSYSERR_INVALPARAM;
757     }
758
759     if (wDevID >= MIDIIn_NumDevs) {
760         WARN("bad device ID : %d\n", wDevID);
761         return MMSYSERR_BADDEVICEID;
762     }
763     memcpy(lpCaps, &sources[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
764     return MMSYSERR_NOERROR;
765 }
766
767 static DWORD MIDIIn_GetNumDevs(void)
768 {
769     TRACE("\n");
770     return MIDIIn_NumDevs;
771 }
772
773 static DWORD MIDIIn_Start(WORD wDevID)
774 {
775     TRACE("%d\n", wDevID);
776
777     if (wDevID >= MIDIIn_NumDevs) {
778         WARN("bad device ID : %d\n", wDevID);
779         return MMSYSERR_BADDEVICEID;
780     }
781     sources[wDevID].state = 1;
782     sources[wDevID].startTime = GetTickCount();
783     return MMSYSERR_NOERROR;
784 }
785
786 static DWORD MIDIIn_Stop(WORD wDevID)
787 {
788     TRACE("%d\n", wDevID);
789     if (wDevID >= MIDIIn_NumDevs) {
790         WARN("bad device ID : %d\n", wDevID);
791         return MMSYSERR_BADDEVICEID;
792     }
793     sources[wDevID].state = 0;
794     return MMSYSERR_NOERROR;
795 }
796
797 static DWORD MIDIIn_Reset(WORD wDevID)
798 {
799     DWORD dwTime = GetTickCount();
800
801     TRACE("%d\n", wDevID);
802     if (wDevID >= MIDIIn_NumDevs) {
803         WARN("bad device ID : %d\n", wDevID);
804         return MMSYSERR_BADDEVICEID;
805     }
806
807     EnterCriticalSection(&midiInLock);
808     while (sources[wDevID].lpQueueHdr) {
809         LPMIDIHDR lpMidiHdr = sources[wDevID].lpQueueHdr;
810         sources[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
811         lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
812         lpMidiHdr->dwFlags |= MHDR_DONE;
813         /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
814         MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
815     }
816     LeaveCriticalSection(&midiInLock);
817
818     return MMSYSERR_NOERROR;
819 }
820
821 /*
822  * MIDI In Mach message handling
823  */
824
825 /*
826  *  Call from CoreMIDI IO threaded callback,
827  *  we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
828  */
829 void MIDIIn_SendMessage(MIDIMessage msg)
830 {
831     CFDataRef data;
832
833     CFMessagePortRef messagePort;
834     messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, MIDIInThreadPortName);
835
836     data = CFDataCreate(kCFAllocatorDefault, (UInt8 *) &msg, sizeof(msg));
837     if (data)
838     {
839         CFMessagePortSendRequest(messagePort, 0, data, 0.0, 0.0, NULL, NULL);
840         CFRelease(data);
841         CFRelease(messagePort);
842     }
843 }
844
845 static CFDataRef MIDIIn_MessageHandler(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info)
846 {
847     MIDIMessage *msg = NULL;
848     int i = 0;
849     MIDISource *src = NULL;
850     DWORD sendData = 0;
851     int pos = 0;
852     DWORD currentTime;
853
854     switch (msgid)
855     {
856         case 0:
857             msg = (MIDIMessage *) CFDataGetBytePtr(data);
858             TRACE("devID=%d\n", msg->devID);
859              for (i = 0; i < msg->length; ++i) {
860                 TRACE("%02X ", msg->data[i]);
861             }
862             TRACE("\n");
863             src = &sources[msg->devID];
864             if (src->state < 1)
865             {
866                 TRACE("input not started, thrown away\n");
867                 goto done;
868             }
869             /* FIXME skipping SysEx */
870             if (msg->data[0] == 0xF0)
871             {
872                 FIXME("Starting System Exclusive\n");
873                 src->state |= 2;
874             }
875             if (src->state & 2)
876             {
877                 for (i = 0; i < msg->length; ++i)
878                 {
879                     if (msg->data[i] == 0xF7)
880                     {
881                         FIXME("Ending System Exclusive\n");
882                         src->state &= ~2;
883                     }
884                 }
885                 goto done;
886             }
887             EnterCriticalSection(&midiInLock);
888             currentTime = GetTickCount() - src->startTime;
889
890             while (pos < msg->length)
891             {
892                 sendData = 0;
893                 switch (msg->data[pos] & 0xF0)
894                 {
895                     case 0xF0:
896                         sendData = (msg->data[pos] <<  0);
897                         pos++;
898                         break;
899
900                     case 0xC0:
901                     case 0xD0:
902                         sendData = (msg->data[pos + 1] <<  8) | (msg->data[pos] <<  0);
903                         pos += 2;
904                         break;
905                     default:
906                         sendData = (msg->data[pos + 2] << 16) |
907                                     (msg->data[pos + 1] <<  8) |
908                                     (msg->data[pos] <<  0);
909                         pos += 3;
910                         break;
911                 }
912                 MIDI_NotifyClient(msg->devID, MIM_DATA, sendData, currentTime);
913             }
914             LeaveCriticalSection(&midiInLock);
915             break;
916         default:
917             CFRunLoopStop(CFRunLoopGetCurrent());
918             break;
919     }
920 done:
921     return NULL;
922 }
923
924 static DWORD WINAPI MIDIIn_MessageThread(LPVOID p)
925 {
926     CFMessagePortRef local;
927     CFRunLoopSourceRef source;
928     Boolean info;
929
930     local = CFMessagePortCreateLocal(kCFAllocatorDefault, MIDIInThreadPortName, &MIDIIn_MessageHandler, NULL, &info);
931
932     source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, local, 0);
933     CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
934
935     CFRunLoopRun();
936
937     CFRunLoopSourceInvalidate(source);
938     CFRelease(source);
939     CFRelease(local);
940     CFRelease(MIDIInThreadPortName);
941     MIDIInThreadPortName = NULL;
942
943     return 0;
944 }
945
946 /**************************************************************************
947 *                               modMessage
948 */
949 DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
950 {
951     TRACE("%d %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
952
953     switch (wMsg) {
954         case DRVM_INIT:
955         case DRVM_EXIT:
956         case DRVM_ENABLE:
957         case DRVM_DISABLE:
958             return 0;
959         case MODM_OPEN:
960             return MIDIOut_Open(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
961         case MODM_CLOSE:
962             return MIDIOut_Close(wDevID);
963         case MODM_DATA:
964             return MIDIOut_Data(wDevID, dwParam1);
965         case MODM_LONGDATA:
966             return MIDIOut_LongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
967         case MODM_PREPARE:
968             return MIDIOut_Prepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
969         case MODM_UNPREPARE:
970             return MIDIOut_Unprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
971         case MODM_GETDEVCAPS:
972             return MIDIOut_GetDevCaps(wDevID, (LPMIDIOUTCAPSW) dwParam1, dwParam2);
973         case MODM_GETNUMDEVS:
974             return MIDIOut_GetNumDevs();
975         case MODM_GETVOLUME:
976             return MIDIOut_GetVolume(wDevID, (DWORD*)dwParam1);
977         case MODM_SETVOLUME:
978             return MIDIOut_SetVolume(wDevID, dwParam1);
979         case MODM_RESET:
980             return MIDIOut_Reset(wDevID);
981         default:
982             TRACE("Unsupported message (08%x)\n", wMsg);
983     }
984     return MMSYSERR_NOTSUPPORTED;
985 }
986
987 /**************************************************************************
988 *                       midMessage
989 */
990 DWORD WINAPI CoreAudio_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
991 {
992     TRACE("%d %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
993     switch (wMsg) {
994         case DRVM_INIT:
995         case DRVM_EXIT:
996         case DRVM_ENABLE:
997         case DRVM_DISABLE:
998             return 0;
999         case MIDM_OPEN:
1000             return MIDIIn_Open(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1001         case MIDM_CLOSE:
1002             return MIDIIn_Close(wDevID);
1003         case MIDM_ADDBUFFER:
1004             return MIDIIn_AddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1005         case MIDM_PREPARE:
1006             return MIDIIn_Prepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1007         case MIDM_UNPREPARE:
1008             return MIDIIn_Unprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1009         case MIDM_GETDEVCAPS:
1010             return MIDIIn_GetDevCaps(wDevID, (LPMIDIINCAPSW) dwParam1, dwParam2);
1011         case MIDM_GETNUMDEVS:
1012             return MIDIIn_GetNumDevs();
1013         case MIDM_START:
1014             return MIDIIn_Start(wDevID);
1015         case MIDM_STOP:
1016             return MIDIIn_Stop(wDevID);
1017         case MIDM_RESET:
1018             return MIDIIn_Reset(wDevID);
1019         default:
1020             TRACE("Unsupported message\n");
1021     }
1022     return MMSYSERR_NOTSUPPORTED;
1023 }
1024 #else
1025
1026 DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
1027 {
1028     TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1029     return MMSYSERR_NOTENABLED;
1030 }
1031
1032 DWORD WINAPI CoreAudio_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1033                                   DWORD dwParam1, DWORD dwParam2)
1034 {
1035     TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1036     return MMSYSERR_NOTENABLED;
1037 }
1038 #endif