winecoreaudio: Move midi_out_get_devcaps to the unixlib.
[wine.git] / dlls / winecoreaudio.drv / coremidi.c
blobdd217ea95f396d807338893fc720bff879f2cd7b
1 /*
2 * MIDI driver for macOS (unixlib)
4 * Copyright 1994 Martin Ayotte
5 * Copyright 1998 Luiz Otavio L. Zorzella
6 * Copyright 1998, 1999 Eric POUECH
7 * Copyright 2005, 2006 Emmanuel Maillard
8 * Copyright 2021 Huw Davies
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #if 0
25 #pragma makedep unix
26 #endif
28 #include "config.h"
30 #define ULONG __carbon_ULONG
31 #define E_INVALIDARG __carbon_E_INVALIDARG
32 #define E_OUTOFMEMORY __carbon_E_OUTOFMEMORY
33 #define E_HANDLE __carbon_E_HANDLE
34 #define E_ACCESSDENIED __carbon_E_ACCESSDENIED
35 #define E_UNEXPECTED __carbon_E_UNEXPECTED
36 #define E_FAIL __carbon_E_FAIL
37 #define E_ABORT __carbon_E_ABORT
38 #define E_POINTER __carbon_E_POINTER
39 #define E_NOINTERFACE __carbon_E_NOINTERFACE
40 #define E_NOTIMPL __carbon_E_NOTIMPL
41 #define S_FALSE __carbon_S_FALSE
42 #define S_OK __carbon_S_OK
43 #define HRESULT_FACILITY __carbon_HRESULT_FACILITY
44 #define IS_ERROR __carbon_IS_ERROR
45 #define FAILED __carbon_FAILED
46 #define SUCCEEDED __carbon_SUCCEEDED
47 #define MAKE_HRESULT __carbon_MAKE_HRESULT
48 #define HRESULT __carbon_HRESULT
49 #define STDMETHODCALLTYPE __carbon_STDMETHODCALLT
50 #include <mach/mach_time.h>
51 #include <CoreMIDI/CoreMIDI.h>
52 #include <AudioUnit/AudioUnit.h>
53 #include <AudioToolbox/AudioToolbox.h>
54 #undef ULONG
55 #undef E_INVALIDARG
56 #undef E_OUTOFMEMORY
57 #undef E_HANDLE
58 #undef E_ACCESSDENIED
59 #undef E_UNEXPECTED
60 #undef E_FAIL
61 #undef E_ABORT
62 #undef E_POINTER
63 #undef E_NOINTERFACE
64 #undef E_NOTIMPL
65 #undef S_FALSE
66 #undef S_OK
67 #undef HRESULT_FACILITY
68 #undef IS_ERROR
69 #undef FAILED
70 #undef SUCCEEDED
71 #undef MAKE_HRESULT
72 #undef HRESULT
73 #undef STDMETHODCALLTYPE
75 #include "ntstatus.h"
76 #define WIN32_NO_STATUS
77 #include "windef.h"
78 #include "winbase.h"
79 #include "winnls.h"
80 #include "winreg.h"
81 #include "mmsystem.h"
82 #include "mmddk.h"
83 #include "mmdeviceapi.h"
84 #include "audioclient.h"
85 #include "wine/debug.h"
86 #include "wine/unicode.h"
87 #include "wine/unixlib.h"
89 #include "coreaudio.h"
90 #include "coremidi.h"
91 #include "unixlib.h"
93 WINE_DEFAULT_DEBUG_CHANNEL(midi);
95 static MIDIClientRef midi_client;
96 static MIDIPortRef midi_out_port, midi_in_port;
97 static UINT num_dests, num_srcs;
98 static struct midi_dest *dests;
99 static struct midi_src *srcs;
100 static CFStringRef midi_in_thread_port_name;
103 * CoreMIDI IO threaded callback,
104 * we can't call Wine debug channels, critical section or anything using NtCurrentTeb here.
106 static void midi_in_read_proc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon)
108 CFMessagePortRef msg_port = CFMessagePortCreateRemote(kCFAllocatorDefault, midi_in_thread_port_name);
109 MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
110 CFMutableDataRef data;
111 MIDIMessage msg;
112 unsigned int i;
114 for (i = 0; i < pktlist->numPackets; ++i)
116 msg.devID = *(UInt16 *)connRefCon;
117 msg.length = packet->length;
118 data = CFDataCreateMutable(kCFAllocatorDefault, sizeof(msg) + packet->length);
119 if (data)
121 CFDataAppendBytes(data, (UInt8 *)&msg, sizeof(msg));
122 CFDataAppendBytes(data, packet->data, packet->length);
123 CFMessagePortSendRequest(msg_port, 0, data, 0.0, 0.0, NULL, NULL);
124 CFRelease(data);
126 packet = MIDIPacketNext(packet);
128 CFRelease(msg_port);
131 NTSTATUS midi_init(void *args)
133 CFStringRef name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("wineMIDIClient.%d"), getpid());
134 struct midi_init_params *params = args;
135 OSStatus sc;
136 UINT i;
138 sc = MIDIClientCreate(name, NULL /* FIXME use notify proc */, NULL, &midi_client);
139 CFRelease(name);
140 if (sc)
142 ERR("can't create MIDI Client\n");
143 *params->err = DRV_FAILURE;
144 return STATUS_SUCCESS;
147 num_dests = MAX_MIDI_SYNTHS + MIDIGetNumberOfDestinations();
148 num_srcs = MIDIGetNumberOfSources();
150 TRACE("num_dests %d num_srcs %d\n", num_dests, num_srcs);
152 dests = calloc(num_dests, sizeof(*dests));
153 srcs = calloc(num_srcs, sizeof(*srcs));
155 if (num_srcs > 0)
157 midi_in_thread_port_name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("MIDIInThreadPortName.%u"), getpid());
158 name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("WineInputPort.%u"), getpid());
159 MIDIInputPortCreate(midi_client, name, midi_in_read_proc, NULL, &midi_in_port);
160 CFRelease(name);
163 if (num_dests > MAX_MIDI_SYNTHS)
165 name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("WineOutputPort.%u"), getpid());
166 MIDIOutputPortCreate(midi_client, name, &midi_out_port);
167 CFRelease(name);
170 /* initialize sources */
171 for (i = 0; i < num_srcs; i++)
173 srcs[i].wDevID = i;
174 srcs[i].source = MIDIGetSource(i);
176 sc = MIDIObjectGetStringProperty(srcs[i].source, kMIDIPropertyName, &name);
177 if (!sc)
179 int len = min(CFStringGetLength(name), ARRAY_SIZE(srcs[i].caps.szPname) - 1);
180 CFStringGetCharacters(name, CFRangeMake(0, len), srcs[i].caps.szPname);
181 srcs[i].caps.szPname[len] = '\0';
183 MIDIPortConnectSource(midi_in_port, srcs[i].source, &srcs[i].wDevID);
185 srcs[i].state = 0;
186 /* FIXME */
187 srcs[i].caps.wMid = 0x00FF; /* Manufac ID */
188 srcs[i].caps.wPid = 0x0001; /* Product ID */
189 srcs[i].caps.vDriverVersion = 0x0001;
190 srcs[i].caps.dwSupport = 0;
193 /* initialise MIDI synths */
194 for (i = 0; i < MAX_MIDI_SYNTHS; i++)
196 static const WCHAR synth_name[] = {'C','o','r','e','A','u','d','i','o',' ','M','I','D','I',' ','S','y','n','t','h',' '};
198 C_ASSERT(MAX_MIDI_SYNTHS < 10);
199 memcpy(dests[i].caps.szPname, synth_name, sizeof(synth_name));
200 dests[i].caps.szPname[ARRAY_SIZE(synth_name)] = '1' + i;
201 dests[i].caps.szPname[ARRAY_SIZE(synth_name) + 1] = '\0';
203 dests[i].caps.wTechnology = MOD_SYNTH;
204 dests[i].caps.wChannelMask = 0xFFFF;
206 dests[i].caps.wMid = 0x00FF; /* Manufac ID */
207 dests[i].caps.wPid = 0x0001; /* Product ID */
208 dests[i].caps.vDriverVersion = 0x0001;
209 dests[i].caps.dwSupport = MIDICAPS_VOLUME;
210 dests[i].caps.wVoices = 16;
211 dests[i].caps.wNotes = 16;
213 /* initialise available destinations */
214 for (i = MAX_MIDI_SYNTHS; i < num_dests; i++)
216 dests[i].dest = MIDIGetDestination(i - MAX_MIDI_SYNTHS);
218 sc = MIDIObjectGetStringProperty(dests[i].dest, kMIDIPropertyName, &name);
219 if (!sc)
221 int len = min(CFStringGetLength(name), ARRAY_SIZE(dests[i].caps.szPname) - 1);
222 CFStringGetCharacters(name, CFRangeMake(0, len), dests[i].caps.szPname);
223 dests[i].caps.szPname[len] = '\0';
226 dests[i].caps.wTechnology = MOD_MIDIPORT;
227 dests[i].caps.wChannelMask = 0xFFFF;
229 dests[i].caps.wMid = 0x00FF; /* Manufac ID */
230 dests[i].caps.wPid = 0x0001;
231 dests[i].caps.vDriverVersion = 0x0001;
232 dests[i].caps.dwSupport = 0;
233 dests[i].caps.wVoices = 0;
234 dests[i].caps.wNotes = 0;
237 params->num_dests = num_dests;
238 params->num_srcs = num_srcs;
239 params->dests = dests;
240 params->srcs = srcs;
241 params->midi_out_port = (void *)midi_out_port;
242 params->midi_in_port = (void *)midi_in_port;
244 *params->err = DRV_SUCCESS;
245 return STATUS_SUCCESS;
248 NTSTATUS midi_release(void *args)
250 CFMessagePortRef msg_port;
252 if (num_srcs)
254 /* Stop CFRunLoop in MIDIIn_MessageThread */
255 msg_port = CFMessagePortCreateRemote(kCFAllocatorDefault, midi_in_thread_port_name);
256 CFMessagePortSendRequest(msg_port, 1, NULL, 0.0, 0.0, NULL, NULL);
257 CFRelease(msg_port);
260 if (midi_client) MIDIClientDispose(midi_client); /* MIDIClientDispose will close all ports */
262 free(srcs);
263 free(dests);
265 return STATUS_SUCCESS;
269 * MIDI Synth Unit
271 static BOOL synth_unit_create_default(AUGraph *graph, AudioUnit *synth)
273 AudioComponentDescription desc;
274 AUNode synth_node;
275 AUNode out_node;
276 OSStatus sc;
278 sc = NewAUGraph(graph);
279 if (sc != noErr)
281 ERR("NewAUGraph return %s\n", wine_dbgstr_fourcc(sc));
282 return FALSE;
285 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
286 desc.componentFlags = 0;
287 desc.componentFlagsMask = 0;
289 /* create synth node */
290 desc.componentType = kAudioUnitType_MusicDevice;
291 desc.componentSubType = kAudioUnitSubType_DLSSynth;
293 sc = AUGraphAddNode(*graph, &desc, &synth_node);
294 if (sc != noErr)
296 ERR("AUGraphAddNode cannot create synthNode : %s\n", wine_dbgstr_fourcc(sc));
297 return FALSE;
300 /* create out node */
301 desc.componentType = kAudioUnitType_Output;
302 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
304 sc = AUGraphAddNode(*graph, &desc, &out_node);
305 if (sc != noErr)
307 ERR("AUGraphAddNode cannot create outNode %s\n", wine_dbgstr_fourcc(sc));
308 return FALSE;
311 sc = AUGraphOpen(*graph);
312 if (sc != noErr)
314 ERR("AUGraphOpen returns %s\n", wine_dbgstr_fourcc(sc));
315 return FALSE;
318 /* connecting the nodes */
319 sc = AUGraphConnectNodeInput(*graph, synth_node, 0, out_node, 0);
320 if (sc != noErr)
322 ERR("AUGraphConnectNodeInput cannot connect synthNode to outNode : %s\n",
323 wine_dbgstr_fourcc(sc));
324 return FALSE;
327 /* Get the synth unit */
328 sc = AUGraphNodeInfo(*graph, synth_node, 0, synth);
329 if (sc != noErr)
331 ERR("AUGraphNodeInfo return %s\n", wine_dbgstr_fourcc(sc));
332 return FALSE;
335 return TRUE;
338 static BOOL synth_unit_init(AudioUnit synth, AUGraph graph)
340 OSStatus sc;
342 sc = AUGraphInitialize(graph);
343 if (sc != noErr)
345 ERR("AUGraphInitialize(%p) returns %s\n", graph, wine_dbgstr_fourcc(sc));
346 return FALSE;
349 sc = AUGraphStart(graph);
350 if (sc != noErr)
352 ERR("AUGraphStart(%p) returns %s\n", graph, wine_dbgstr_fourcc(sc));
353 return FALSE;
356 return TRUE;
359 static BOOL synth_unit_close(AUGraph graph)
361 OSStatus sc;
363 sc = AUGraphStop(graph);
364 if (sc != noErr)
366 ERR("AUGraphStop(%p) returns %s\n", graph, wine_dbgstr_fourcc(sc));
367 return FALSE;
370 sc = DisposeAUGraph(graph);
371 if (sc != noErr)
373 ERR("DisposeAUGraph(%p) returns %s\n", graph, wine_dbgstr_fourcc(sc));
374 return FALSE;
377 return TRUE;
380 static void set_out_notify(struct notify_context *notify, struct midi_dest *dest, WORD dev_id, WORD msg,
381 DWORD_PTR param_1, DWORD_PTR param_2)
383 notify->send_notify = TRUE;
384 notify->dev_id = dev_id;
385 notify->msg = msg;
386 notify->param_1 = param_1;
387 notify->param_2 = param_2;
388 notify->callback = dest->midiDesc.dwCallback;
389 notify->flags = dest->wFlags;
390 notify->device = dest->midiDesc.hMidi;
391 notify->instance = dest->midiDesc.dwInstance;
394 static DWORD midi_out_open(WORD dev_id, MIDIOPENDESC *midi_desc, DWORD flags, struct notify_context *notify)
396 struct midi_dest *dest;
398 TRACE("dev_id = %d desc = %p flags = %08x\n", dev_id, midi_desc, flags);
400 if (!midi_desc) return MMSYSERR_INVALPARAM;
402 if (dev_id >= num_dests)
404 WARN("bad device ID : %d\n", dev_id);
405 return MMSYSERR_BADDEVICEID;
407 if (dests[dev_id].midiDesc.hMidi != 0)
409 WARN("device already open!\n");
410 return MMSYSERR_ALLOCATED;
412 if ((flags & ~CALLBACK_TYPEMASK) != 0)
414 WARN("bad flags\n");
415 return MMSYSERR_INVALFLAG;
418 dest = dests + dev_id;
419 if (dest->caps.wTechnology == MOD_SYNTH)
421 if (!synth_unit_create_default(&dest->graph, &dest->synth))
423 ERR("SynthUnit_CreateDefaultSynthUnit dest=%p failed\n", dest);
424 return MMSYSERR_ERROR;
426 if (!synth_unit_init(dest->synth, dest->graph))
428 ERR("SynthUnit_Initialise dest=%p failed\n", dest);
429 return MMSYSERR_ERROR;
432 dest->wFlags = HIWORD(flags & CALLBACK_TYPEMASK);
433 dest->midiDesc = *midi_desc;
435 set_out_notify(notify, dest, dev_id, MOM_OPEN, 0, 0);
437 return MMSYSERR_NOERROR;
440 static DWORD midi_out_close(WORD dev_id, struct notify_context *notify)
442 struct midi_dest *dest;
444 TRACE("dev_id = %d\n", dev_id);
446 if (dev_id >= num_dests)
448 WARN("bad device ID : %d\n", dev_id);
449 return MMSYSERR_BADDEVICEID;
452 dest = dests + dev_id;
454 if (dest->caps.wTechnology == MOD_SYNTH)
455 synth_unit_close(dest->graph);
456 dest->graph = 0;
457 dest->synth = 0;
459 set_out_notify(notify, dest, dev_id, MOM_CLOSE, 0, 0);
461 dest->midiDesc.hMidi = 0;
463 return MMSYSERR_NOERROR;
466 static void midi_send(MIDIPortRef port, MIDIEndpointRef dest, UInt8 *buffer, unsigned len)
468 Byte packet_buf[512];
469 MIDIPacketList *packet_list = (MIDIPacketList *)packet_buf;
470 MIDIPacket *packet = MIDIPacketListInit(packet_list);
472 packet = MIDIPacketListAdd(packet_list, sizeof(packet_buf), packet, mach_absolute_time(), len, buffer);
473 if (packet) MIDISend(port, dest, packet_list);
476 static DWORD midi_out_data(WORD dev_id, DWORD data)
478 struct midi_dest *dest;
479 UInt8 bytes[3];
480 OSStatus sc;
482 TRACE("dev_id = %d data = %08x\n", dev_id, data);
484 if (dev_id >= num_dests)
486 WARN("bad device ID : %d\n", dev_id);
487 return MMSYSERR_BADDEVICEID;
490 bytes[0] = data & 0xff;
491 bytes[1] = (data >> 8) & 0xff;
492 bytes[2] = (data >> 16) & 0xff;
494 dest = dests + dev_id;
495 if (dest->caps.wTechnology == MOD_SYNTH)
497 sc = MusicDeviceMIDIEvent(dest->synth, bytes[0], bytes[1], bytes[2], 0);
498 if (sc != noErr)
500 ERR("MusicDeviceMIDIEvent returns %s\n", wine_dbgstr_fourcc(sc));
501 return MMSYSERR_ERROR;
504 else
506 midi_send(midi_out_port, dest->dest, bytes, sizeof(bytes));
509 return MMSYSERR_NOERROR;
512 static DWORD midi_out_long_data(WORD dev_id, MIDIHDR *hdr, DWORD hdr_size, struct notify_context *notify)
514 struct midi_dest *dest;
515 OSStatus sc;
517 TRACE("dev_id = %d midi_hdr = %p hdr_size = %d\n", dev_id, hdr, hdr_size);
519 if (dev_id >= num_dests)
521 WARN("bad device ID : %d\n", dev_id);
522 return MMSYSERR_BADDEVICEID;
524 if (!hdr)
526 WARN("Invalid Parameter\n");
527 return MMSYSERR_INVALPARAM;
529 if (!hdr->lpData || !(hdr->dwFlags & MHDR_PREPARED))
530 return MIDIERR_UNPREPARED;
532 if (hdr->dwFlags & MHDR_INQUEUE)
533 return MIDIERR_STILLPLAYING;
535 hdr->dwFlags &= ~MHDR_DONE;
536 hdr->dwFlags |= MHDR_INQUEUE;
538 if ((UInt8)hdr->lpData[0] != 0xf0)
539 /* System Exclusive */
540 ERR("Add missing 0xf0 marker at the beginning of system exclusive byte stream\n");
542 if ((UInt8)hdr->lpData[hdr->dwBufferLength - 1] != 0xF7)
543 /* Send end of System Exclusive */
544 ERR("Add missing 0xf7 marker at the end of system exclusive byte stream\n");
546 dest = dests + dev_id;
548 if (dest->caps.wTechnology == MOD_SYNTH) /* FIXME */
550 sc = MusicDeviceSysEx(dest->synth, (const UInt8 *)hdr->lpData, hdr->dwBufferLength);
551 if (sc != noErr)
553 ERR("MusicDeviceSysEx returns %s\n", wine_dbgstr_fourcc(sc));
554 return MMSYSERR_ERROR;
557 else if (dest->caps.wTechnology == MOD_MIDIPORT)
558 midi_send(midi_out_port, dest->dest, (UInt8 *)hdr->lpData, hdr->dwBufferLength);
560 hdr->dwFlags &= ~MHDR_INQUEUE;
561 hdr->dwFlags |= MHDR_DONE;
563 set_out_notify(notify, dest, dev_id, MOM_DONE, (DWORD_PTR)hdr, 0);
565 return MMSYSERR_NOERROR;
568 static DWORD midi_out_prepare(WORD dev_id, MIDIHDR *hdr, DWORD hdr_size)
570 TRACE("dev_id = %d midi_hdr = %p hdr_size = %d\n", dev_id, hdr, hdr_size);
572 if (hdr_size < offsetof(MIDIHDR, dwOffset) || !hdr || !hdr->lpData)
573 return MMSYSERR_INVALPARAM;
574 if (hdr->dwFlags & MHDR_PREPARED)
575 return MMSYSERR_NOERROR;
577 hdr->lpNext = 0;
578 hdr->dwFlags |= MHDR_PREPARED;
579 hdr->dwFlags &= ~(MHDR_DONE | MHDR_INQUEUE);
580 return MMSYSERR_NOERROR;
583 static DWORD midi_out_unprepare(WORD dev_id, MIDIHDR *hdr, DWORD hdr_size)
585 TRACE("dev_id = %d midi_hdr = %p hdr_size = %d\n", dev_id, hdr, hdr_size);
587 if (hdr_size < offsetof(MIDIHDR, dwOffset) || !hdr || !hdr->lpData)
588 return MMSYSERR_INVALPARAM;
589 if (!(hdr->dwFlags & MHDR_PREPARED))
590 return MMSYSERR_NOERROR;
591 if (hdr->dwFlags & MHDR_INQUEUE)
592 return MIDIERR_STILLPLAYING;
594 hdr->dwFlags &= ~MHDR_PREPARED;
595 return MMSYSERR_NOERROR;
598 static DWORD midi_out_get_devcaps(WORD dev_id, MIDIOUTCAPSW *caps, DWORD size)
600 TRACE("dev_id = %d caps = %p size = %d\n", dev_id, caps, size);
602 if (!caps)
604 WARN("Invalid Parameter\n");
605 return MMSYSERR_INVALPARAM;
608 if (dev_id >= num_dests)
610 WARN("bad device ID : %d\n", dev_id);
611 return MMSYSERR_BADDEVICEID;
613 memcpy(caps, &dests[dev_id].caps, min(size, sizeof(*caps)));
614 return MMSYSERR_NOERROR;
617 NTSTATUS midi_out_message(void *args)
619 struct midi_out_message_params *params = args;
621 params->notify->send_notify = FALSE;
623 switch (params->msg)
625 case DRVM_INIT:
626 case DRVM_EXIT:
627 case DRVM_ENABLE:
628 case DRVM_DISABLE:
629 *params->err = MMSYSERR_NOERROR;
630 break;
631 case MODM_OPEN:
632 *params->err = midi_out_open(params->dev_id, (MIDIOPENDESC *)params->param_1, params->param_2, params->notify);
633 break;
634 case MODM_CLOSE:
635 *params->err = midi_out_close(params->dev_id, params->notify);
636 break;
637 case MODM_DATA:
638 *params->err = midi_out_data(params->dev_id, params->param_1);
639 break;
640 case MODM_LONGDATA:
641 *params->err = midi_out_long_data(params->dev_id, (MIDIHDR *)params->param_1, params->param_2, params->notify);
642 break;
643 case MODM_PREPARE:
644 *params->err = midi_out_prepare(params->dev_id, (MIDIHDR *)params->param_1, params->param_2);
645 break;
646 case MODM_UNPREPARE:
647 *params->err = midi_out_unprepare(params->dev_id, (MIDIHDR *)params->param_1, params->param_2);
648 break;
649 case MODM_GETDEVCAPS:
650 *params->err = midi_out_get_devcaps(params->dev_id, (MIDIOUTCAPSW *)params->param_1, params->param_2);
651 break;
652 default:
653 TRACE("Unsupported message\n");
654 *params->err = MMSYSERR_NOTSUPPORTED;
657 return STATUS_SUCCESS;