From 7ad29c8bb78a058e7e94c99b97695e2ae8e36e97 Mon Sep 17 00:00:00 2001 From: Emmanuel Maillard Date: Wed, 25 Apr 2007 00:55:33 +0200 Subject: [PATCH] winecoreaudio: Initial MIDI support on Mac OS X. --- configure | 2 +- configure.ac | 2 +- dlls/winecoreaudio.drv/Makefile.in | 4 +- dlls/winecoreaudio.drv/audiounit.c | 114 +++++++++++ dlls/winecoreaudio.drv/coreaudio.c | 4 + dlls/winecoreaudio.drv/coreaudio.h | 3 +- dlls/winecoreaudio.drv/coremidi.c | 55 +++++ dlls/winecoreaudio.drv/coremidi.h | 61 ++++++ dlls/winecoreaudio.drv/midi.c | 189 ++++++++++++++++++ dlls/winecoreaudio.drv/winecoreaudio.drv.spec | 1 + 10 files changed, 431 insertions(+), 4 deletions(-) create mode 100644 dlls/winecoreaudio.drv/coremidi.c create mode 100644 dlls/winecoreaudio.drv/coremidi.h create mode 100644 dlls/winecoreaudio.drv/midi.c diff --git a/configure b/configure index 3e550eafbe2..e07ad7496d0 100755 --- a/configure +++ b/configure @@ -13173,7 +13173,7 @@ fi fi if test "$ac_cv_header_CoreAudio_CoreAudio_h" = "yes" -a "$ac_cv_header_AudioUnit_AudioUnit_h" = "yes" then - COREAUDIO="-framework CoreAudio -framework AudioUnit -framework CoreServices" + COREAUDIO="-framework CoreAudio -framework AudioUnit -framework CoreServices -framework AudioToolbox -framework CoreMIDI" fi case $host_cpu in diff --git a/configure.ac b/configure.ac index 1ea3ad24d4e..757e9570b24 100644 --- a/configure.ac +++ b/configure.ac @@ -988,7 +988,7 @@ case $host_os in if test "$ac_cv_header_CoreAudio_CoreAudio_h" = "yes" -a "$ac_cv_header_AudioUnit_AudioUnit_h" = "yes" then dnl CoreServices needed by AudioUnit - AC_SUBST(COREAUDIO,"-framework CoreAudio -framework AudioUnit -framework CoreServices") + AC_SUBST(COREAUDIO,"-framework CoreAudio -framework AudioUnit -framework CoreServices -framework AudioToolbox -framework CoreMIDI") fi case $host_cpu in *powerpc*) diff --git a/dlls/winecoreaudio.drv/Makefile.in b/dlls/winecoreaudio.drv/Makefile.in index a54c476b160..c31933dcc96 100644 --- a/dlls/winecoreaudio.drv/Makefile.in +++ b/dlls/winecoreaudio.drv/Makefile.in @@ -9,7 +9,9 @@ EXTRALIBS = $(LIBUUID) @COREAUDIO@ C_SRCS = \ audio.c \ audiounit.c \ - coreaudio.c + coreaudio.c \ + coremidi.c \ + midi.c @MAKE_DLL_RULES@ diff --git a/dlls/winecoreaudio.drv/audiounit.c b/dlls/winecoreaudio.drv/audiounit.c index b6da9531295..08fd5ec91e5 100644 --- a/dlls/winecoreaudio.drv/audiounit.c +++ b/dlls/winecoreaudio.drv/audiounit.c @@ -25,6 +25,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(wave); #ifdef HAVE_AUDIOUNIT_AUDIOUNIT_H #include +#include + +WINE_DECLARE_DEBUG_CHANNEL(midi); extern OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, @@ -312,4 +315,115 @@ error: return 0; } +/* + * MIDI Synth Unit + */ +int SynthUnit_CreateDefaultSynthUnit(AUGraph *graph, AudioUnit *synth) +{ + OSStatus err; + ComponentDescription desc; + AUNode synthNode; + AUNode outNode; + + err = NewAUGraph(graph); + if (err != noErr) + { + ERR_(midi)("NewAUGraph return %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + /* create synth node */ + desc.componentType = kAudioUnitType_MusicDevice; + desc.componentSubType = kAudioUnitSubType_DLSSynth; + + err = AUGraphNewNode(*graph, &desc, 0, NULL, &synthNode); + if (err != noErr) + { + ERR_(midi)("AUGraphNewNode cannot create synthNode : %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + + /* create out node */ + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + + err = AUGraphNewNode(*graph, &desc, 0, NULL, &outNode); + if (err != noErr) + { + ERR_(midi)("AUGraphNewNode cannot create outNode %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + + err = AUGraphOpen(*graph); + if (err != noErr) + { + ERR_(midi)("AUGraphOpen return %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + + /* connecting the nodes */ + err = AUGraphConnectNodeInput(*graph, synthNode, 0, outNode, 0); + if (err != noErr) + { + ERR_(midi)("AUGraphConnectNodeInput cannot connect synthNode to outNode : %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + + /* Get the synth unit */ + err = AUGraphGetNodeInfo(*graph, synthNode, 0, 0, 0, synth); + if (err != noErr) + { + ERR_(midi)("AUGraphGetNodeInfo return %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + + return 1; +} + +int SynthUnit_Initialize(AudioUnit synth, AUGraph graph) +{ + OSStatus err = noErr; + + err = AUGraphInitialize(graph); + if (err != noErr) + { + ERR_(midi)("AUGraphInitialize(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + + err = AUGraphStart(graph); + if (err != noErr) + { + ERR_(midi)("AUGraphStart(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + + return 1; +} + +int SynthUnit_Close(AUGraph graph) +{ + OSStatus err = noErr; + + err = AUGraphStop(graph); + if (err != noErr) + { + ERR_(midi)("AUGraphStop(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + + err = DisposeAUGraph(graph); + if (err != noErr) + { + ERR_(midi)("DisposeAUGraph(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err); + return 0; + } + + return 1; +} + #endif diff --git a/dlls/winecoreaudio.drv/coreaudio.c b/dlls/winecoreaudio.drv/coreaudio.c index 6fd21d7f12f..7cff0acfba5 100644 --- a/dlls/winecoreaudio.drv/coreaudio.c +++ b/dlls/winecoreaudio.drv/coreaudio.c @@ -43,6 +43,8 @@ static LRESULT CoreAudio_drvLoad(void) { TRACE("()\n"); CoreAudio_WaveInit(); + CoreAudio_MIDIInit(); + return 1; } @@ -53,6 +55,8 @@ static LRESULT CoreAudio_drvFree(void) { TRACE("()\n"); CoreAudio_WaveRelease(); + CoreAudio_MIDIRelease(); + return 1; } diff --git a/dlls/winecoreaudio.drv/coreaudio.h b/dlls/winecoreaudio.drv/coreaudio.h index 312ba0e09df..92c78d0fc7e 100644 --- a/dlls/winecoreaudio.drv/coreaudio.h +++ b/dlls/winecoreaudio.drv/coreaudio.h @@ -23,6 +23,7 @@ extern LONG CoreAudio_WaveInit(void); extern void CoreAudio_WaveRelease(void); -/* extern BOOL CoreAudio_MidiInit(void); */ +extern LONG CoreAudio_MIDIInit(void); +extern void CoreAudio_MIDIRelease(void); #endif /* __WINE_COREAUDIO_H */ diff --git a/dlls/winecoreaudio.drv/coremidi.c b/dlls/winecoreaudio.drv/coremidi.c new file mode 100644 index 00000000000..59101ee982d --- /dev/null +++ b/dlls/winecoreaudio.drv/coremidi.c @@ -0,0 +1,55 @@ +/* + * Wine Midi driver for MacOSX + * + * Copyright 2006 Emmanuel Maillard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#include "config.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(midi); + +#ifdef HAVE_COREAUDIO_COREAUDIO_H +#include + +#include "coremidi.h" + +MIDIClientRef CoreMIDI_CreateClient(CFStringRef name) +{ + MIDIClientRef client = NULL; + + if (MIDIClientCreate(name, NULL /* FIXME use notify proc */, NULL, &client) != noErr) + return NULL; + + return client; +} + +void CoreMIDI_GetObjectName(MIDIObjectRef obj, char *name, int size) +{ + OSStatus err = noErr; + CFStringRef cfname; + + err = MIDIObjectGetStringProperty(obj, kMIDIPropertyName, &cfname); + if (err == noErr) + { + CFStringGetCString(cfname, name, size, kCFStringEncodingASCII); + CFRelease(cfname); + } +} + +#endif /* HAVE_COREAUDIO_COREAUDIO_H */ diff --git a/dlls/winecoreaudio.drv/coremidi.h b/dlls/winecoreaudio.drv/coremidi.h new file mode 100644 index 00000000000..38f1507bd8b --- /dev/null +++ b/dlls/winecoreaudio.drv/coremidi.h @@ -0,0 +1,61 @@ +/* + * Wine Midi driver for MacOSX + * + * Copyright 2006 Emmanuel Maillard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef WINE_COREMIDI_H +#define WINE_COREMIDI_H + +#include + +#ifdef WINE_DEFINITIONS +/* + * Due to CoreMIDI headers conflict redefine some types for Wine + */ +typedef void *MIDIClientRef; +typedef void *MIDIEndpointRef; +typedef void *MIDIPortRef; +typedef void *MIDIObjectRef; + +typedef struct MIDIPacketList MIDIPacketList; + +/* + * functions + */ +extern OSStatus MIDIClientDispose(MIDIClientRef client); +extern unsigned MIDIGetNumberOfDestinations(void); +extern MIDIEndpointRef MIDIGetDestination(unsigned i); +extern unsigned MIDIGetNumberOfSources(void); +extern MIDIEndpointRef MIDIGetSource(unsigned i); +extern OSStatus MIDIOutputPortCreate(MIDIClientRef client, CFStringRef portName, MIDIPortRef *outPort); + +extern OSStatus MIDIObjectGetProperties(MIDIObjectRef obj, CFPropertyListRef *outProperties, Boolean deep); + +/* + * Due to AudioUnit headers conflict redefine some types. + */ +typedef void *AudioUnit; +typedef void *AUGraph; + +#endif + +/* coremidi.c */ +extern MIDIClientRef CoreMIDI_CreateClient(CFStringRef name); +extern void CoreMIDI_GetObjectName(MIDIObjectRef obj, char *name, int size); + +#endif diff --git a/dlls/winecoreaudio.drv/midi.c b/dlls/winecoreaudio.drv/midi.c new file mode 100644 index 00000000000..314dbe3e44f --- /dev/null +++ b/dlls/winecoreaudio.drv/midi.c @@ -0,0 +1,189 @@ +/* + * Sample MIDI Wine Driver for MacOSX (based on OSS midi driver) + * + * Copyright 1994 Martin Ayotte + * Copyright 1998 Luiz Otavio L. Zorzella (init procedures) + * Copyright 1998/1999 Eric POUECH : + * 98/7 changes for making this MIDI driver work on OSS + * current support is limited to MIDI ports of OSS systems + * 98/9 rewriting MCI code for MIDI + * 98/11 splitted in midi.c and mcimidi.c + * Copyright 2006 Emmanuel Maillard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "mmddk.h" +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(midi); + +#if defined(HAVE_COREAUDIO_COREAUDIO_H) +#include + +#define WINE_DEFINITIONS +#include "coremidi.h" + +static MIDIClientRef wineMIDIClient = NULL; + +static DWORD MIDIOut_NumDevs = 0; + +typedef struct tagMIDIDestination { + /* graph and synth are only used for MIDI Synth */ + AUGraph graph; + AudioUnit synth; + + MIDIPortRef port; + MIDIOUTCAPSW caps; + MIDIOPENDESC midiDesc; + WORD wFlags; +} MIDIDestination; + +#define MAX_MIDI_SYNTHS 1 + +MIDIDestination *destinations; + +extern int SynthUnit_CreateDefaultSynthUnit(AUGraph *graph, AudioUnit *synth); +extern int SynthUnit_Initialize(AudioUnit synth, AUGraph graph); +extern int SynthUnit_Close(AUGraph graph); + + +LONG CoreAudio_MIDIInit(void) +{ + int i; + CHAR szPname[MAXPNAMELEN] = {0}; + + int numDest = MIDIGetNumberOfDestinations(); + CFStringRef name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("wineMIDIClient.%d"), getpid()); + + wineMIDIClient = CoreMIDI_CreateClient( name ); + if (wineMIDIClient == NULL) + { + CFRelease(name); + ERR("can't create wineMIDIClient\n"); + return 0; + } + CFRelease(name); + + MIDIOut_NumDevs = MAX_MIDI_SYNTHS; + MIDIOut_NumDevs += numDest; + + TRACE("MIDIOut_NumDevs %d\n", MIDIOut_NumDevs); + + destinations = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MIDIOut_NumDevs * sizeof(MIDIDestination)); + + /* initialise MIDI synths */ + for (i = 0; i < MAX_MIDI_SYNTHS; i++) + { + snprintf(szPname, sizeof(szPname), "CoreAudio MIDI Synth %d", i); + MultiByteToWideChar(CP_ACP, 0, szPname, -1, destinations[i].caps.szPname, sizeof(destinations[i].caps.szPname)/sizeof(WCHAR)); + + destinations[i].caps.wTechnology = MOD_SYNTH; + destinations[i].caps.wChannelMask = 0xFFFF; + + destinations[i].caps.wMid = 0x00FF; /* Manufac ID */ + destinations[i].caps.wPid = 0x0001; /* Product ID */ + destinations[i].caps.vDriverVersion = 0x0001; + destinations[i].caps.dwSupport = MIDICAPS_VOLUME; + destinations[i].caps.wVoices = 16; + destinations[i].caps.wNotes = 16; + } + /* initialise available destinations */ + for (i = MAX_MIDI_SYNTHS; i < numDest + MAX_MIDI_SYNTHS; i++) + { + MIDIEndpointRef endpoint = MIDIGetDestination(i - MAX_MIDI_SYNTHS); + + CoreMIDI_GetObjectName(endpoint, szPname, sizeof(szPname)); + MultiByteToWideChar(CP_ACP, 0, szPname, -1, destinations[i].caps.szPname, sizeof(destinations[i].caps.szPname)/sizeof(WCHAR)); + + name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("WineOutputPort.%d.%u"), i, getpid()); + MIDIOutputPortCreate(wineMIDIClient, name, &destinations[i].port); + CFRelease(name); + + destinations[i].caps.wTechnology = MOD_MIDIPORT; + destinations[i].caps.wChannelMask = 0xFFFF; + + destinations[i].caps.wMid = 0x00FF; /* Manufac ID */ + destinations[i].caps.wPid = 0x0001; + destinations[i].caps.vDriverVersion = 0x0001; + destinations[i].caps.dwSupport = 0; + destinations[i].caps.wVoices = 16; + destinations[i].caps.wNotes = 16; + } + return 1; +} + +LONG CoreAudio_MIDIRelease(void) +{ + TRACE("\n"); + if (wineMIDIClient) MIDIClientDispose(wineMIDIClient); /* MIDIClientDispose will close all ports */ + HeapFree(GetProcessHeap(), 0, destinations); + return 1; +} + +/************************************************************************** +* modMessage +*/ +DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2) +{ + TRACE("%d %08x %08x %08x %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); + + switch (wMsg) { + case DRVM_INIT: + case DRVM_EXIT: + case DRVM_ENABLE: + case DRVM_DISABLE: + return 0; + case MODM_OPEN: + case MODM_CLOSE: + case MODM_DATA: + case MODM_LONGDATA: + case MODM_PREPARE: + case MODM_UNPREPARE: + case MODM_GETDEVCAPS: + case MODM_GETNUMDEVS: + case MODM_GETVOLUME: + case MODM_SETVOLUME: + case MODM_RESET: + default: + TRACE("Unsupported message (08%x)\n", wMsg); + } + return MMSYSERR_NOTSUPPORTED; +} + + + +#else + +DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2) +{ + TRACE("%08x, %08x, %08x, %08x, %08x\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); + return MMSYSERR_NOTENABLED; +} + +#endif diff --git a/dlls/winecoreaudio.drv/winecoreaudio.drv.spec b/dlls/winecoreaudio.drv/winecoreaudio.drv.spec index 49fbfb2212e..dced551bebb 100644 --- a/dlls/winecoreaudio.drv/winecoreaudio.drv.spec +++ b/dlls/winecoreaudio.drv/winecoreaudio.drv.spec @@ -1,3 +1,4 @@ @ stdcall -private DriverProc(long long long long long) CoreAudio_DriverProc @ stdcall -private widMessage(long long long long long) CoreAudio_widMessage @ stdcall -private wodMessage(long long long long long) CoreAudio_wodMessage +@ stdcall -private modMessage(long long long long long) CoreAudio_modMessage