From fcf1af2ffc739d3efa430edc5280f83d3ab270fb Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 26 Dec 2005 11:18:25 +0000 Subject: New CoreMIDI midi backend for OS X svn-id: r19832 --- backends/midi/coreaudio.cpp | 279 ++++++++++++++++---------------------------- backends/midi/coremidi.cpp | 162 +++++++++++++++++++++++++ backends/module.mk | 1 + sound/mididrv.cpp | 37 +++--- sound/mididrv.h | 61 ++++++---- 5 files changed, 325 insertions(+), 215 deletions(-) create mode 100644 backends/midi/coremidi.cpp diff --git a/backends/midi/coreaudio.cpp b/backends/midi/coreaudio.cpp index c4f9f791c5..3c7aa64674 100644 --- a/backends/midi/coreaudio.cpp +++ b/backends/midi/coreaudio.cpp @@ -26,7 +26,7 @@ #include "sound/mpu401.h" #include -#include 7 + // Activating the following switch disables reverb support in the CoreAudio @@ -35,13 +35,12 @@ // TODO: Maybe make this a config option? //#define COREAUDIO_DISABLE_REVERB +/* +See here to see how to react to a change of the default output unit: +http://cvs.opendarwin.org/cgi-bin/cvsweb.cgi/proj/KDE-Darwin/arts/flow/audioiocoreaudio.cc?rev=HEAD&content-type=text/x-cvsweb-markup + +*/ -// 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 just always -// uses 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 1 /* CoreAudio MIDI driver * Based on code by Benjamin W. Zale @@ -50,7 +49,6 @@ class MidiDriver_CORE : public MidiDriver_MPU401 { public: MidiDriver_CORE(); - ~MidiDriver_CORE(); int open(); void close(); void send(uint32 b); @@ -59,117 +57,90 @@ 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; + : au_MusicDevice(0), au_output(0) { } int MidiDriver_CORE::open() { - if (au_output || mDest) + if (au_output) return MERR_ALREADY_OPEN; - OSStatus err = noErr; - - mOutPort = 0; -#ifdef ENABLE_HACKISH_NATIVE_MIDI_SUPPORT - int dests = MIDIGetNumberOfDestinations(); - 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; - - // 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"); - - // 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 = 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); + AudioUnitConnection auconnect; + ComponentDescription compdesc; + Component compid; + OSErr err; + + // 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"); + + // 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 = FSGetCatalogInfo (&fsref, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL); } - // open the output unit - au_output = (AudioUnit) OpenDefaultComponent(kAudioUnitComponentType, kAudioUnitSubType_Output); - if (au_output == 0) - error("Failed opening output audio unit"); + if (err == noErr) { + err = AudioUnitSetProperty ( + au_MusicDevice, + kMusicDeviceProperty_SoundBankFSSpec, kAudioUnitScope_Global, + 0, + &fsSpec, sizeof(fsSpec) + ); + } - // 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)); + if (err != noErr) + warning("Failed loading custom sound font '%s' (error %d)\n", soundfont, err); + } - #ifdef COREAUDIO_DISABLE_REVERB - UInt32 usesReverb = 0; - AudioUnitSetProperty (au_MusicDevice, kMusicDeviceProperty_UsesInternalReverb, - kAudioUnitScope_Global, 0, &usesReverb, sizeof (usesReverb)); - #endif + // 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); + // initialize the units + AudioUnitInitialize(au_MusicDevice); + AudioUnitInitialize(au_output); - // start the output - AudioOutputUnitStart(au_output); + // start the output + AudioOutputUnitStart(au_output); - } return 0; } @@ -177,91 +148,45 @@ int MidiDriver_CORE::open() { void MidiDriver_CORE::close() { MidiDriver_MPU401::close(); - 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; - } + // 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); + 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); - } + 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); + + // 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; + } - 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); + MusicDeviceSysEx(au_MusicDevice, msg, length); - free(buf); - } + free(buf); } MidiDriver *MidiDriver_CORE_create() { diff --git a/backends/midi/coremidi.cpp b/backends/midi/coremidi.cpp new file mode 100644 index 0000000000..f60b84cc5d --- /dev/null +++ b/backends/midi/coremidi.cpp @@ -0,0 +1,162 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2005 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $Header$ + */ + +#ifdef MACOSX + +#include "common/stdafx.h" +#include "common/config-manager.h" +#include "common/util.h" +#include "sound/mpu401.h" + +#include + + + +/* +For information on how to unify the CoreMidi and MusicDevice code: + +http://lists.apple.com/archives/Coreaudio-api/2005/Jun/msg00194.html +http://lists.apple.com/archives/coreaudio-api/2003/Mar/msg00248.html +http://lists.apple.com/archives/coreaudio-api/2003/Jul/msg00137.html + +*/ + + +/* CoreMIDI MIDI driver + * By Max Horn + */ +class MidiDriver_CoreMIDI : public MidiDriver_MPU401 { +public: + MidiDriver_CoreMIDI(); + ~MidiDriver_CoreMIDI(); + int open(); + void close(); + void send(uint32 b); + void sysEx(byte *msg, uint16 length); + +private: + MIDIClientRef mClient; + MIDIPortRef mOutPort; + MIDIEndpointRef mDest; +}; + +MidiDriver_CoreMIDI::MidiDriver_CoreMIDI() + : mClient(0), mOutPort(0), mDest(0) { + + OSStatus err; + err = MIDIClientCreate(CFSTR("ScummVM MIDI Driver for OS X"), NULL, NULL, &mClient); +} + +MidiDriver_CoreMIDI::~MidiDriver_CoreMIDI() { + if (mClient) + MIDIClientDispose(mClient); + mClient = 0; +} + +int MidiDriver_CoreMIDI::open() { + if (mDest) + return MERR_ALREADY_OPEN; + + OSStatus err = noErr; + + mOutPort = 0; + + int dests = MIDIGetNumberOfDestinations(); + if (dests > 0 && mClient) { + mDest = MIDIGetDestination(0); + err = MIDIOutputPortCreate( mClient, + CFSTR("scummvm_output_port"), + &mOutPort); + } else { + return MERR_DEVICE_NOT_AVAILABLE; + } + + if (err != noErr) + return MERR_CANNOT_CONNECT; + + return 0; +} + +void MidiDriver_CoreMIDI::close() { + MidiDriver_MPU401::close(); + + if (mOutPort && mDest) { + MIDIPortDispose(mOutPort); + mOutPort = 0; + mDest = 0; + } +} + +void MidiDriver_CoreMIDI::send(uint32 b) { + assert(mOutPort != NULL); + assert(mDest != NULL); + + byte status_byte = (b & 0x000000FF); + byte first_byte = (b & 0x0000FF00) >> 8; + byte second_byte = (b & 0x00FF0000) >> 16; + + 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); + +} + +void MidiDriver_CoreMIDI::sysEx(byte *msg, uint16 length) { + assert(mOutPort != NULL); + assert(mDest != NULL); + + 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); +} + +MidiDriver *MidiDriver_CoreMIDI_create() { + return new MidiDriver_CoreMIDI(); +} + +#endif // MACOSX diff --git a/backends/module.mk b/backends/module.mk index 737f126c6a..b861404bc1 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -8,6 +8,7 @@ MODULE_OBJS := \ backends/fs/amigaos4/amigaos4-fs.o \ backends/midi/alsa.o \ backends/midi/coreaudio.o \ + backends/midi/coremidi.o \ backends/midi/morphos.o \ backends/midi/null.o \ backends/midi/quicktime.o \ diff --git a/sound/mididrv.cpp b/sound/mididrv.cpp index 0931f334f2..97913864cd 100644 --- a/sound/mididrv.cpp +++ b/sound/mididrv.cpp @@ -30,46 +30,48 @@ /** Internal list of all available 'midi' drivers. */ static const struct MidiDriverDescription midiDrivers[] = { - {"auto", "Default", MD_AUTO}, - {"null", "No music", MD_NULL}, + {"auto", "Default", MD_AUTO, MDT_NONE}, + {"null", "No music", MD_NULL, MDT_NONE}, #if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__) - {"windows", "Windows MIDI", MD_WINDOWS}, + {"windows", "Windows MIDI", MD_WINDOWS, MDT_NATIVE}, #endif #if defined(UNIX) && !defined(__BEOS__) && !defined(MACOSX) - {"seq", "SEQ", MD_SEQ}, + {"seq", "SEQ", MD_SEQ, MDT_NONE}, #endif #if defined(MACOSX) - {"qt", "QuickTime", MD_QTMUSIC}, - {"core", "CoreAudio", MD_COREAUDIO}, + {"qt", "QuickTime", MD_QTMUSIC, MDT_NATIVE}, + {"core", "CoreAudio", MD_COREAUDIO, MDT_NATIVE}, + {"coreaudio", "CoreAudio", MD_COREAUDIO, MDT_NATIVE}, + {"coremidi", "CoreMIDI", MD_COREMIDI, MDT_NATIVE}, #endif #if defined(__MORPHOS__) - {"etude", "Etude", MD_ETUDE}, + {"etude", "Etude", MD_ETUDE, MDT_NONE}, #endif #if defined(UNIX) && defined(USE_ALSA) - {"alsa", "ALSA", MD_ALSA}, + {"alsa", "ALSA", MD_ALSA, MDT_NONE}, #endif - {"adlib", "Adlib", MD_ADLIB}, - {"towns", "FM Towns", MD_TOWNS}, - {"pcspk", "PC Speaker", MD_PCSPK}, - {"pcjr", "IBM PCjr", MD_PCJR}, + {"adlib", "Adlib", MD_ADLIB, MDT_ADLIB}, + {"towns", "FM Towns", MD_TOWNS, MDT_TOWNS}, + {"pcspk", "PC Speaker", MD_PCSPK, MDT_PCSPK}, + {"pcjr", "IBM PCjr", MD_PCJR, MDT_PCSPK}, #ifdef USE_FLUIDSYNTH - {"fluidsynth", "FluidSynth", MD_FLUIDSYNTH}, + {"fluidsynth", "FluidSynth", MD_FLUIDSYNTH, MDT_NONE}, #endif #ifdef USE_MT32EMU - {"mt32", "MT-32", MD_MT32}, + {"mt32", "MT-32", MD_MT32, MDT_NONE}, #endif #if defined(PALMOS_MODE) - {"ypa1", "Yamaha Pa1", MD_YPA1}, - {"zodiac", "Tapwave Zodiac", MD_ZODIAC}, + {"ypa1", "Yamaha Pa1", MD_YPA1, MDT_NATIVE}, + {"zodiac", "Tapwave Zodiac", MD_ZODIAC, MDT_NATIVE}, #endif - {0, 0, 0} + {0, 0, MD_NULL, MDT_NONE} }; const byte MidiDriver::_mt32ToGm[128] = { @@ -210,6 +212,7 @@ MidiDriver *MidiDriver::createMidi(int midiDriver) { #endif #if defined(MACOSX) case MD_COREAUDIO: return MidiDriver_CORE_create(); + case MD_COREMIDI: return MidiDriver_CoreMIDI_create(); #endif #if defined(UNIX) && defined(USE_ALSA) case MD_ALSA: return MidiDriver_ALSA_create(); diff --git a/sound/mididrv.h b/sound/mididrv.h index b9b361f0ca..62f00c31f9 100644 --- a/sound/mididrv.h +++ b/sound/mididrv.h @@ -33,28 +33,45 @@ namespace Audio { namespace Common { class String; } /** MIDI Driver Types */ -enum { - MD_AUTO = 0, - MD_NULL = 1, - MD_WINDOWS = 2, - MD_TIMIDITY = 3, - MD_SEQ = 4, - MD_QTMUSIC = 5, - MD_ETUDE = 6, - MD_COREAUDIO = 7, - MD_MIDIEMU = 8, - MD_ALSA = 9, - MD_ADLIB = 10, - MD_PCSPK = 11, - MD_PCJR = 12, - MD_TOWNS = 13, - MD_YPA1 = 14, // PalmOS - MD_ZODIAC = 15, // PalmOS - MD_MT32 = 16, - MD_FLUIDSYNTH = 17 +enum MidiDriverType { + MD_AUTO, + MD_NULL, + + // Windows + MD_WINDOWS, + + // Linux + MD_ALSA, + MD_SEQ, + + // Mac OS X + MD_QTMUSIC, + MD_COREAUDIO, + MD_COREMIDI, + + // PalmOS + MD_YPA1, + MD_ZODIAC, + + // MorphOS + MD_ETUDE, + + // "Fake" MIDI devices + MD_ADLIB, + MD_PCSPK, + MD_PCJR, + MD_TOWNS, + + // MIDI softsynths + MD_MT32, + MD_FLUIDSYNTH }; -enum MidiDriverType { +/** + * A set of bitmasks which can be used to specify what kind of midi + * driver is prefered. + */ +enum MidiDriverFlags { MDT_NONE = 0, MDT_PCSPK = 1, // MD_PCSPK and MD_PCJR MDT_ADLIB = 2, // MD_ADLIB @@ -71,7 +88,8 @@ enum MidiDriverType { struct MidiDriverDescription { const char *name; const char *description; - int id; + MidiDriverType id; + MidiDriverFlags flags; }; /** Abstract MIDI Driver Class */ @@ -199,6 +217,7 @@ extern MidiDriver *MidiDriver_WIN_create(); extern MidiDriver *MidiDriver_SEQ_create(); extern MidiDriver *MidiDriver_QT_create(); extern MidiDriver *MidiDriver_CORE_create(); +extern MidiDriver *MidiDriver_CoreMIDI_create(); extern MidiDriver *MidiDriver_ETUDE_create(); extern MidiDriver *MidiDriver_ALSA_create(); extern MidiDriver *MidiDriver_YM2612_create(Audio::Mixer *mixer); -- cgit v1.2.3