crypt32: Remove unneeded comments.
[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 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         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)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 = (LPMIDIHDR)ptr->lpNext);
698         ptr->lpNext = (struct midihdr_tag*)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         sources[wDevID].lpQueueHdr->dwFlags &= ~MHDR_INQUEUE;
810         sources[wDevID].lpQueueHdr->dwFlags |= MHDR_DONE;
811         /* FIXME: when called from 16 bit, lpQueueHdr needs to be a segmented ptr */
812         MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD)sources[wDevID].lpQueueHdr, dwTime);
813         sources[wDevID].lpQueueHdr = (LPMIDIHDR)sources[wDevID].lpQueueHdr->lpNext;
814     }
815     LeaveCriticalSection(&midiInLock);
816
817     return MMSYSERR_NOERROR;
818 }
819
820 /*
821  * MIDI In Mach message handling
822  */
823
824 /*
825  *  Call from CoreMIDI IO threaded callback,
826  *  we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
827  */
828 void MIDIIn_SendMessage(MIDIMessage msg)
829 {
830     CFDataRef data;
831
832     CFMessagePortRef messagePort;
833     messagePort = CFMessagePortCreateRemote(kCFAllocatorDefault, MIDIInThreadPortName);
834
835     data = CFDataCreate(kCFAllocatorDefault, (UInt8 *) &msg, sizeof(msg));
836     if (data)
837     {
838         CFMessagePortSendRequest(messagePort, 0, data, 0.0, 0.0, NULL, NULL);
839         CFRelease(data);
840         CFRelease(messagePort);
841     }
842 }
843
844 static CFDataRef MIDIIn_MessageHandler(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info)
845 {
846     MIDIMessage *msg = NULL;
847     int i = 0;
848     MIDISource *src = NULL;
849     DWORD sendData = 0;
850     int pos = 0;
851     DWORD currentTime;
852
853     switch (msgid)
854     {
855         case 0:
856             msg = (MIDIMessage *) CFDataGetBytePtr(data);
857             TRACE("devID=%d\n", msg->devID);
858              for (i = 0; i < msg->length; ++i) {
859                 TRACE("%02X ", msg->data[i]);
860             }
861             TRACE("\n");
862             src = &sources[msg->devID];
863             if (src->state < 1)
864             {
865                 TRACE("input not started, thrown away\n");
866                 goto done;
867             }
868             /* FIXME skipping SysEx */
869             if (msg->data[0] == 0xF0)
870             {
871                 FIXME("Starting System Exclusive\n");
872                 src->state |= 2;
873             }
874             if (src->state & 2)
875             {
876                 for (i = 0; i < msg->length; ++i)
877                 {
878                     if (msg->data[i] == 0xF7)
879                     {
880                         FIXME("Ending System Exclusive\n");
881                         src->state &= ~2;
882                     }
883                 }
884                 goto done;
885             }
886             EnterCriticalSection(&midiInLock);
887             currentTime = GetTickCount() - src->startTime;
888
889             while (pos < msg->length)
890             {
891                 sendData = 0;
892                 switch (msg->data[pos] & 0xF0)
893                 {
894                     case 0xF0:
895                         sendData = (msg->data[pos] <<  0);
896                         pos++;
897                         break;
898
899                     case 0xC0:
900                     case 0xD0:
901                         sendData = (msg->data[pos + 1] <<  8) | (msg->data[pos] <<  0);
902                         pos += 2;
903                         break;
904                     default:
905                         sendData = (msg->data[pos + 2] << 16) |
906                                     (msg->data[pos + 1] <<  8) |
907                                     (msg->data[pos] <<  0);
908                         pos += 3;
909                         break;
910                 }
911                 MIDI_NotifyClient(msg->devID, MIM_DATA, sendData, currentTime);
912             }
913             LeaveCriticalSection(&midiInLock);
914             break;
915         default:
916             CFRunLoopStop(CFRunLoopGetCurrent());
917             break;
918     }
919 done:
920     return NULL;
921 }
922
923 static DWORD WINAPI MIDIIn_MessageThread(LPVOID p)
924 {
925     CFMessagePortRef local;
926     CFRunLoopSourceRef source;
927     Boolean info;
928
929     local = CFMessagePortCreateLocal(kCFAllocatorDefault, MIDIInThreadPortName, &MIDIIn_MessageHandler, NULL, &info);
930
931     source = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, local, 0);
932     CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
933
934     CFRunLoopRun();
935
936     CFRunLoopSourceInvalidate(source);
937     CFRelease(source);
938     CFRelease(local);
939     CFRelease(MIDIInThreadPortName);
940     MIDIInThreadPortName = NULL;
941
942     return 0;
943 }
944
945 /**************************************************************************
946 *                               modMessage
947 */
948 DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
949 {
950     TRACE("%d %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
951
952     switch (wMsg) {
953         case DRVM_INIT:
954         case DRVM_EXIT:
955         case DRVM_ENABLE:
956         case DRVM_DISABLE:
957             return 0;
958         case MODM_OPEN:
959             return MIDIOut_Open(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
960         case MODM_CLOSE:
961             return MIDIOut_Close(wDevID);
962         case MODM_DATA:
963             return MIDIOut_Data(wDevID, dwParam1);
964         case MODM_LONGDATA:
965             return MIDIOut_LongData(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
966         case MODM_PREPARE:
967             return MIDIOut_Prepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
968         case MODM_UNPREPARE:
969             return MIDIOut_Unprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
970         case MODM_GETDEVCAPS:
971             return MIDIOut_GetDevCaps(wDevID, (LPMIDIOUTCAPSW) dwParam1, dwParam2);
972         case MODM_GETNUMDEVS:
973             return MIDIOut_GetNumDevs();
974         case MODM_GETVOLUME:
975             return MIDIOut_GetVolume(wDevID, (DWORD*)dwParam1);
976         case MODM_SETVOLUME:
977             return MIDIOut_SetVolume(wDevID, dwParam1);
978         case MODM_RESET:
979             return MIDIOut_Reset(wDevID);
980         default:
981             TRACE("Unsupported message (08%x)\n", wMsg);
982     }
983     return MMSYSERR_NOTSUPPORTED;
984 }
985
986 /**************************************************************************
987 *                       midMessage
988 */
989 DWORD WINAPI CoreAudio_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
990 {
991     TRACE("%d %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
992     switch (wMsg) {
993         case DRVM_INIT:
994         case DRVM_EXIT:
995         case DRVM_ENABLE:
996         case DRVM_DISABLE:
997             return 0;
998         case MIDM_OPEN:
999             return MIDIIn_Open(wDevID, (LPMIDIOPENDESC)dwParam1, dwParam2);
1000         case MIDM_CLOSE:
1001             return MIDIIn_Close(wDevID);
1002         case MIDM_ADDBUFFER:
1003             return MIDIIn_AddBuffer(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1004         case MIDM_PREPARE:
1005             return MIDIIn_Prepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1006         case MIDM_UNPREPARE:
1007             return MIDIIn_Unprepare(wDevID, (LPMIDIHDR)dwParam1, dwParam2);
1008         case MIDM_GETDEVCAPS:
1009             return MIDIIn_GetDevCaps(wDevID, (LPMIDIINCAPSW) dwParam1, dwParam2);
1010         case MIDM_GETNUMDEVS:
1011             return MIDIIn_GetNumDevs();
1012         case MIDM_START:
1013             return MIDIIn_Start(wDevID);
1014         case MIDM_STOP:
1015             return MIDIIn_Stop(wDevID);
1016         case MIDM_RESET:
1017             return MIDIIn_Reset(wDevID);
1018         default:
1019             TRACE("Unsupported message\n");
1020     }
1021     return MMSYSERR_NOTSUPPORTED;
1022 }
1023 #else
1024
1025 DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
1026 {
1027     TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1028     return MMSYSERR_NOTENABLED;
1029 }
1030
1031 DWORD WINAPI CoreAudio_midMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1032                                   DWORD dwParam1, DWORD dwParam2)
1033 {
1034     TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1035     return MMSYSERR_NOTENABLED;
1036 }
1037 #endif