From 7970562356790a274cbc96b32dc84801536e3bd0 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sat, 2 Jul 2005 12:52:30 +0000 Subject: Hackish native MIDI HW support for Mac OS X (incomplete) svn-id: r18481 --- backends/midi/coreaudio.cpp | 277 ++++++++++++++++++++++++++++++-------------- configure | 2 +- 2 files changed, 190 insertions(+), 89 deletions(-) diff --git a/backends/midi/coreaudio.cpp b/backends/midi/coreaudio.cpp index 8ed208575d..8648ca7a68 100644 --- a/backends/midi/coreaudio.cpp +++ b/backends/midi/coreaudio.cpp @@ -25,7 +25,9 @@ #include "common/util.h" #include "sound/mpu401.h" +#include //for file stuff #include +#include //for AUGraph // Activating the following switch disables reverb support in the CoreAudio @@ -35,13 +37,21 @@ //#define COREAUDIO_DISABLE_REVERB +// Enable the following switch to make ScummVM try to use native MIDI hardware +// on your computer for MIDI output. This is currently quite hackish, in +// particular you have no way to specify which device is used (it'll always +// use the first output device it can find), nor is there a switch to +// force it to use the soft synth instead of the MIDI HW. +//#define ENABLE_HACKISH_NATIVE_MIDI_SUPPORT + /* CoreAudio MIDI driver * Based on code by Benjamin W. Zale * Extended by Max Horn */ class MidiDriver_CORE : public MidiDriver_MPU401 { public: - MidiDriver_CORE() : au_MusicDevice(0), au_output(0) { } + MidiDriver_CORE(); + ~MidiDriver_CORE(); int open(); void close(); void send(uint32 b); @@ -50,118 +60,209 @@ public: private: AudioUnit au_MusicDevice; AudioUnit au_output; + + MIDIClientRef mClient; + MIDIPortRef mOutPort; + MIDIEndpointRef mDest; }; +MidiDriver_CORE::MidiDriver_CORE() + : au_MusicDevice(0), au_output(0), mClient(0), mOutPort(0), mDest(0) { + + OSStatus err; + err = MIDIClientCreate(CFSTR("ScummVM MIDI Driver for OS X"), NULL, NULL, &mClient); +} + +MidiDriver_CORE::~MidiDriver_CORE() { + if (mClient) + MIDIClientDispose(mClient); + mClient = 0; +} int MidiDriver_CORE::open() { if (au_output != NULL) return MERR_ALREADY_OPEN; - OSStatus err; - AudioUnitConnection auconnect; - ComponentDescription compdesc; - Component compid; - - // Open the Music Device. - // We use the AudioUnit v1 API, even though it is deprecated, because - // this way we stay compatible with older OS X versions. - // For v2, we'd use kAudioUnitType_MusicDevice and kAudioUnitSubType_DLSSynth - compdesc.componentType = kAudioUnitComponentType; - compdesc.componentSubType = kAudioUnitSubType_MusicDevice; - compdesc.componentManufacturer = kAudioUnitID_DLSSynth; - compdesc.componentFlags = 0; - compdesc.componentFlagsMask = 0; - compid = FindNextComponent(NULL, &compdesc); - au_MusicDevice = static_cast(OpenComponent(compid)); + OSStatus err = noErr; + + int dests = MIDIGetNumberOfDestinations(); + mOutPort = 0; +#if ENABLE_HACKISH_NATIVE_MIDI_SUPPORT + if (dests > 0 && mClient) { + mDest = MIDIGetDestination(0); + err = MIDIOutputPortCreate( mClient, + CFSTR("scummvm_output_port"), + &mOutPort); + } +#endif + + if (err != noErr || !mOutPort) { + AudioUnitConnection auconnect; + ComponentDescription compdesc; + Component compid; - if (au_MusicDevice == 0) - error("Failed opening CoreAudio music device"); - - // Load custom soundfont, if specified - // FIXME: This is kind of a temporary hack. Better (IMO) would be to - // query QuickTime for whatever custom soundfont was set in the - // QuickTime Preferences, and use that automatically. - if (ConfMan.hasKey("soundfont")) { - FSRef fsref; - FSSpec fsSpec; - const char *soundfont = ConfMan.get("soundfont").c_str(); + // Open the Music Device. + // We use the AudioUnit v1 API, even though it is deprecated, because + // this way we stay compatible with older OS X versions. + // For v2, we'd use kAudioUnitType_MusicDevice and kAudioUnitSubType_DLSSynth + compdesc.componentType = kAudioUnitComponentType; + compdesc.componentSubType = kAudioUnitSubType_MusicDevice; + compdesc.componentManufacturer = kAudioUnitID_DLSSynth; + compdesc.componentFlags = 0; + compdesc.componentFlagsMask = 0; + compid = FindNextComponent(NULL, &compdesc); + au_MusicDevice = static_cast(OpenComponent(compid)); + + if (au_MusicDevice == 0) + error("Failed opening CoreAudio music device"); - err = FSPathMakeRef ((const byte *)soundfont, &fsref, NULL); - - if (err == noErr) { - err = FSGetCatalogInfo (&fsref, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL); - } + // Load custom soundfont, if specified + // FIXME: This is kind of a temporary hack. Better (IMO) would be to + // query QuickTime for whatever custom soundfont was set in the + // QuickTime Preferences, and use that automatically. + if (ConfMan.hasKey("soundfont")) { + FSRef fsref; + FSSpec fsSpec; + const char *soundfont = ConfMan.get("soundfont").c_str(); + + err = FSPathMakeRef ((const byte *)soundfont, &fsref, NULL); - if (err == noErr) { - err = AudioUnitSetProperty ( - au_MusicDevice, - kMusicDeviceProperty_SoundBankFSSpec, kAudioUnitScope_Global, - 0, - &fsSpec, sizeof(fsSpec) - ); + if (err == noErr) { + err = FSGetCatalogInfo (&fsref, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL); + } + + if (err == noErr) { + err = AudioUnitSetProperty ( + au_MusicDevice, + kMusicDeviceProperty_SoundBankFSSpec, kAudioUnitScope_Global, + 0, + &fsSpec, sizeof(fsSpec) + ); + } + + if (err != noErr) + warning("Failed loading custom sound font '%s' (error %d)\n", soundfont, err); } - - if (err != noErr) - warning("Failed loading custom sound font '%s' (error %d)\n", soundfont, err); + + // open the output unit + au_output = (AudioUnit) OpenDefaultComponent(kAudioUnitComponentType, kAudioUnitSubType_Output); + if (au_output == 0) + error("Failed opening output audio unit"); + + // connect the units + auconnect.sourceAudioUnit = au_MusicDevice; + auconnect.sourceOutputNumber = 0; + auconnect.destInputNumber = 0; + err = + AudioUnitSetProperty(au_output, kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, 0, + (void *)&auconnect, sizeof(AudioUnitConnection)); + + #ifdef COREAUDIO_DISABLE_REVERB + UInt32 usesReverb = 0; + AudioUnitSetProperty (au_MusicDevice, kMusicDeviceProperty_UsesInternalReverb, + kAudioUnitScope_Global, 0, &usesReverb, sizeof (usesReverb)); + #endif + + // initialize the units + AudioUnitInitialize(au_MusicDevice); + AudioUnitInitialize(au_output); + + // start the output + AudioOutputUnitStart(au_output); + } - // open the output unit - au_output = (AudioUnit) OpenDefaultComponent(kAudioUnitComponentType, kAudioUnitSubType_Output); - if (au_output == 0) - error("Failed opening output audio unit"); - - // connect the units - auconnect.sourceAudioUnit = au_MusicDevice; - auconnect.sourceOutputNumber = 0; - auconnect.destInputNumber = 0; - err = - AudioUnitSetProperty(au_output, kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, 0, - (void *)&auconnect, sizeof(AudioUnitConnection)); - -#ifdef COREAUDIO_DISABLE_REVERB - UInt32 usesReverb = 0; - AudioUnitSetProperty (au_MusicDevice, kMusicDeviceProperty_UsesInternalReverb, - kAudioUnitScope_Global, 0, &usesReverb, sizeof (usesReverb)); -#endif - - // initialize the units - AudioUnitInitialize(au_MusicDevice); - AudioUnitInitialize(au_output); - - // start the output - AudioOutputUnitStart(au_output); - return 0; } void MidiDriver_CORE::close() { MidiDriver_MPU401::close(); - // Stop the output - AudioOutputUnitStop(au_output); - - // Cleanup - CloseComponent(au_output); - au_output = 0; - CloseComponent(au_MusicDevice); - au_MusicDevice = 0; + if (mOutPort && mDest) { + MIDIPortDispose(mOutPort); + mOutPort = 0; + mDest = 0; + } else { + // Stop the output + AudioOutputUnitStop(au_output); + + // Cleanup + CloseComponent(au_output); + au_output = 0; + CloseComponent(au_MusicDevice); + au_MusicDevice = 0; + } } void MidiDriver_CORE::send(uint32 b) { - assert(au_output != NULL); - assert(au_MusicDevice != NULL); - unsigned char first_byte, second_byte, status_byte; - status_byte = (b & 0x000000FF); - first_byte = (b & 0x0000FF00) >> 8; - second_byte = (b & 0x00FF0000) >> 16; - - MusicDeviceMIDIEvent(au_MusicDevice, status_byte, first_byte, second_byte, 0); + byte status_byte = (b & 0x000000FF); + byte first_byte = (b & 0x0000FF00) >> 8; + byte second_byte = (b & 0x00FF0000) >> 16; + + if (mOutPort && mDest) { + MIDIPacketList packetList; + MIDIPacket *packet = &packetList.packet[0]; + + packetList.numPackets = 1; + + packet->timeStamp = 0; + packet->length = 3; + packet->data[0] = status_byte; + packet->data[1] = first_byte; + packet->data[2] = second_byte; + + MIDISend(mOutPort, mDest, &packetList); + } else { + assert(au_output != NULL); + assert(au_MusicDevice != NULL); + MusicDeviceMIDIEvent(au_MusicDevice, status_byte, first_byte, second_byte, 0); + } } void MidiDriver_CORE::sysEx(byte *msg, uint16 length) { - assert(au_output != NULL); - assert(au_MusicDevice != NULL); - MusicDeviceSysEx(au_MusicDevice, msg, length); + + if (mOutPort && mDest) { + byte buf[384]; + MIDIPacketList *packetList = (MIDIPacketList *)buf; + MIDIPacket *packet = packetList->packet; + + assert(sizeof(buf) >= sizeof(UInt32) + sizeof(MIDITimeStamp) + sizeof(UInt16) + length + 2); + + packetList->numPackets = 1; + + packet->timeStamp = 0; + + // Add SysEx frame if missing + if (*msg != 0xF0) { + packet->length = length + 2; + packet->data[0] = 0xF0; + memcpy(packet->data + 1, msg, length); + packet->data[length + 1] = 0xF7; + } else { + packet->length = length; + memcpy(packet->data, msg, length); + } + + MIDISend(mOutPort, mDest, packetList); + } else { + assert(au_output != NULL); + assert(au_MusicDevice != NULL); + + // Add SysEx frame if missing + byte *buf = 0; + if (*msg != 0xF0) { + buf = (byte *)malloc(length + 2); + buf[0] = 0xF0; + memcpy(buf+1, msg, length); + buf[length+1] = 0xF7; + msg = buf; + length += 2; + } + + MusicDeviceSysEx(au_MusicDevice, msg, length); + + free(buf); + } } MidiDriver *MidiDriver_CORE_create() { diff --git a/configure b/configure index ae1ba3508c..e65bb34e60 100755 --- a/configure +++ b/configure @@ -688,7 +688,7 @@ else ;; darwin*) DEFINES="$DEFINES -DUNIX -DMACOSX" - LIBS="$LIBS -framework QuickTime -framework AudioUnit -framework Carbon" + LIBS="$LIBS -framework QuickTime -framework AudioUnit -framework Carbon -framework CoreMidi" # TODO: Add proper check for Altivec support in the compiler... DEFINES="$DEFINES -DHAS_ALTIVEC" CXXFLAGS="$CXXFLAGS -faltivec" -- cgit v1.2.3