ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

coreaudio.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 // Disable symbol overrides so that we can use system headers.
00024 #define FORBIDDEN_SYMBOL_ALLOW_ALL
00025 
00026 #include "common/scummsys.h"
00027 
00028 #ifdef MACOSX
00029 
00030 #include <AvailabilityMacros.h>
00031 
00032 // With the release of Mac OS X 10.5 in October 2007, Apple deprecated the
00033 // AUGraphNewNode & AUGraphGetNodeInfo APIs in favor of the new AUGraphAddNode &
00034 // AUGraphNodeInfo APIs. While it is easy to switch to those, it breaks
00035 // compatibility with all pre-10.5 systems.
00036 //
00037 // Since 10.5 was the last system to support PowerPC, we use the old, deprecated
00038 // APIs on PowerPC based systems by default. On all other systems (such as Mac
00039 // OS X running on Intel hardware, or iOS running on ARM), we use the new API by
00040 // default.
00041 //
00042 // This leaves Mac OS X 10.4 running on x86 processors as the only system
00043 // combination that this code will not support by default. It seems quite
00044 // reasonable to assume that anybody with an Intel system has since then moved
00045 // on to a newer Mac OS X release. But if for some reason you absolutely need to
00046 // build an x86 version of this code using the old, deprecated API, you can
00047 // simply do so by manually enable the USE_DEPRECATED_COREAUDIO_API switch (e.g.
00048 // by adding setting it suitably in CPPFLAGS).
00049 #if !defined(USE_DEPRECATED_COREAUDIO_API)
00050     #if TARGET_CPU_PPC || TARGET_CPU_PPC64 || !defined(MAC_OS_X_VERSION_10_6)
00051         #define USE_DEPRECATED_COREAUDIO_API 1
00052     #else
00053         #define USE_DEPRECATED_COREAUDIO_API 0
00054     #endif
00055 #endif
00056 
00057 #if USE_DEPRECATED_COREAUDIO_API
00058     // Try to silence warnings about use of deprecated APIs
00059     #undef DEPRECATED_ATTRIBUTE
00060     #define DEPRECATED_ATTRIBUTE
00061 #endif
00062 
00063 
00064 #include "common/config-manager.h"
00065 #include "common/error.h"
00066 #include "common/textconsole.h"
00067 #include "common/util.h"
00068 #include "audio/musicplugin.h"
00069 #include "audio/mpu401.h"
00070 
00071 #include <CoreServices/CoreServices.h>
00072 #include <AudioToolbox/AUGraph.h>
00073 
00074 
00075 // Activating the following switch disables reverb support in the CoreAudio
00076 // midi backend. Reverb will suck away a *lot* of CPU time, so on slower
00077 // systems, you may want to turn it off completely.
00078 // TODO: Maybe make this a config option?
00079 //#define COREAUDIO_DISABLE_REVERB
00080 
00081 
00082 // A macro to simplify error handling a bit.
00083 #define RequireNoErr(error)                                         \
00084 do {                                                                \
00085     err = error;                                                    \
00086     if (err != noErr)                                               \
00087         goto bail;                                                  \
00088 } while (false)
00089 
00090 
00091 /* CoreAudio MIDI driver
00092  * By Max Horn / Fingolfin
00093  * Based on code by Benjamin W. Zale
00094  */
00095 class MidiDriver_CORE : public MidiDriver_MPU401 {
00096 public:
00097     MidiDriver_CORE();
00098     ~MidiDriver_CORE();
00099     int open();
00100     bool isOpen() const { return _auGraph != 0; }
00101     void close();
00102     void send(uint32 b);
00103     void sysEx(const byte *msg, uint16 length);
00104 
00105 private:
00106     void loadSoundFont(const char *soundfont);
00107     AUGraph _auGraph;
00108     AudioUnit _synth;
00109 };
00110 
00111 MidiDriver_CORE::MidiDriver_CORE()
00112     : _auGraph(0) {
00113 }
00114 
00115 MidiDriver_CORE::~MidiDriver_CORE() {
00116     if (_auGraph) {
00117         AUGraphStop(_auGraph);
00118         DisposeAUGraph(_auGraph);
00119         _auGraph = 0;
00120     }
00121 }
00122 
00123 int MidiDriver_CORE::open() {
00124     OSStatus err = 0;
00125 
00126     if (isOpen())
00127         return MERR_ALREADY_OPEN;
00128 
00129     // Open the Music Device.
00130     RequireNoErr(NewAUGraph(&_auGraph));
00131 
00132     AUNode outputNode, synthNode;
00133 #if USE_DEPRECATED_COREAUDIO_API
00134     ComponentDescription desc;
00135 #else
00136     AudioComponentDescription desc;
00137 #endif
00138 
00139     // The default output device
00140     desc.componentType = kAudioUnitType_Output;
00141     desc.componentSubType = kAudioUnitSubType_DefaultOutput;
00142     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
00143     desc.componentFlags = 0;
00144     desc.componentFlagsMask = 0;
00145 #if USE_DEPRECATED_COREAUDIO_API
00146     RequireNoErr(AUGraphNewNode(_auGraph, &desc, 0, NULL, &outputNode));
00147 #else
00148     RequireNoErr(AUGraphAddNode(_auGraph, &desc, &outputNode));
00149 #endif
00150 
00151     // The built-in default (softsynth) music device
00152     desc.componentType = kAudioUnitType_MusicDevice;
00153     desc.componentSubType = kAudioUnitSubType_DLSSynth;
00154     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
00155 #if USE_DEPRECATED_COREAUDIO_API
00156     RequireNoErr(AUGraphNewNode(_auGraph, &desc, 0, NULL, &synthNode));
00157 #else
00158     RequireNoErr(AUGraphAddNode(_auGraph, &desc, &synthNode));
00159 #endif
00160 
00161     // Connect the softsynth to the default output
00162     RequireNoErr(AUGraphConnectNodeInput(_auGraph, synthNode, 0, outputNode, 0));
00163 
00164     // Open and initialize the whole graph
00165     RequireNoErr(AUGraphOpen(_auGraph));
00166     RequireNoErr(AUGraphInitialize(_auGraph));
00167 
00168     // Get the music device from the graph.
00169 #if USE_DEPRECATED_COREAUDIO_API
00170     RequireNoErr(AUGraphGetNodeInfo(_auGraph, synthNode, NULL, NULL, NULL, &_synth));
00171 #else
00172     RequireNoErr(AUGraphNodeInfo(_auGraph, synthNode, NULL, &_synth));
00173 #endif
00174 
00175     // Load custom soundfont, if specified
00176     if (ConfMan.hasKey("soundfont"))
00177         loadSoundFont(ConfMan.get("soundfont").c_str());
00178 
00179 #ifdef COREAUDIO_DISABLE_REVERB
00180     // Disable reverb mode, as that sucks up a lot of CPU power, which can
00181     // be painful on low end machines.
00182     // TODO: Make this customizable via a config key?
00183     UInt32 usesReverb = 0;
00184     AudioUnitSetProperty (_synth, kMusicDeviceProperty_UsesInternalReverb,
00185         kAudioUnitScope_Global, 0, &usesReverb, sizeof (usesReverb));
00186 #endif
00187 
00188 
00189     // Finally: Start the graph!
00190     RequireNoErr(AUGraphStart(_auGraph));
00191 
00192     return 0;
00193 
00194 bail:
00195     if (_auGraph) {
00196         AUGraphStop(_auGraph);
00197         DisposeAUGraph(_auGraph);
00198         _auGraph = 0;
00199     }
00200     return MERR_CANNOT_CONNECT;
00201 }
00202 
00203 void MidiDriver_CORE::loadSoundFont(const char *soundfont) {
00204     // TODO: We should really check whether the file contains an
00205     // actual soundfont...
00206 
00207     OSStatus err = 0;
00208 
00209 #if USE_DEPRECATED_COREAUDIO_API
00210     FSRef fsref;
00211     err = FSPathMakeRef((const byte *)soundfont, &fsref, NULL);
00212 
00213     SInt32 version;
00214     err = Gestalt(gestaltSystemVersion, &version);
00215 
00216     if (err == noErr) {
00217         if (version >= 0x1030) {
00218             // Use kMusicDeviceProperty_SoundBankFSRef in >= 10.3
00219 
00220             // HACK HACK HACK HACK SUPER HACK: Using the value of 1012 instead of
00221             // kMusicDeviceProperty_SoundBankFSRef so this compiles with the 10.2
00222             // SDK (which does not have that symbol).
00223             if (err == noErr) {
00224                 err = AudioUnitSetProperty(
00225                     _synth,
00226                     /*kMusicDeviceProperty_SoundBankFSRef*/ 1012, kAudioUnitScope_Global,
00227                     0,
00228                     &fsref, sizeof(fsref)
00229                 );
00230             }
00231         } else {
00232             // In 10.2, only kMusicDeviceProperty_SoundBankFSSpec is available
00233             FSSpec fsSpec;
00234 
00235             if (err == noErr)
00236                 err = FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, &fsSpec, NULL);
00237 
00238             if (err == noErr) {
00239                 err = AudioUnitSetProperty(
00240                     _synth,
00241                     kMusicDeviceProperty_SoundBankFSSpec, kAudioUnitScope_Global,
00242                     0,
00243                     &fsSpec, sizeof(fsSpec)
00244                 );
00245             }
00246         }
00247     }
00248 #else
00249     // kMusicDeviceProperty_SoundBankURL was added in 10.5 as a replacement
00250     // In addition, the File Manager API became deprecated starting in 10.8
00251     CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)soundfont, strlen(soundfont), false);
00252 
00253     if (url) {
00254         err = AudioUnitSetProperty(
00255             _synth,
00256             kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global,
00257             0,
00258             &url, sizeof(url)
00259         );
00260 
00261         CFRelease(url);
00262     } else {
00263         warning("Failed to allocate CFURLRef from '%s'", soundfont);
00264     }
00265 #endif // USE_DEPRECATED_COREAUDIO_API
00266 
00267     if (err != noErr)
00268         error("Failed loading custom sound font '%s' (error %ld)", soundfont, (long)err);
00269 }
00270 
00271 void MidiDriver_CORE::close() {
00272     MidiDriver_MPU401::close();
00273     if (_auGraph) {
00274         AUGraphStop(_auGraph);
00275         DisposeAUGraph(_auGraph);
00276         _auGraph = 0;
00277     }
00278 }
00279 
00280 void MidiDriver_CORE::send(uint32 b) {
00281     assert(isOpen());
00282 
00283     byte status_byte = (b & 0x000000FF);
00284     byte first_byte = (b & 0x0000FF00) >> 8;
00285     byte second_byte = (b & 0x00FF0000) >> 16;
00286 
00287     MusicDeviceMIDIEvent(_synth, status_byte, first_byte, second_byte, 0);
00288 }
00289 
00290 void MidiDriver_CORE::sysEx(const byte *msg, uint16 length) {
00291     unsigned char buf[266];
00292 
00293     assert(length + 2 <= ARRAYSIZE(buf));
00294     assert(isOpen());
00295 
00296     // Add SysEx frame
00297     buf[0] = 0xF0;
00298     memcpy(buf + 1, msg, length);
00299     buf[length + 1] = 0xF7;
00300 
00301     // Send it
00302     MusicDeviceSysEx(_synth, buf, length+2);
00303 }
00304 
00305 
00306 // Plugin interface
00307 
00308 class CoreAudioMusicPlugin : public MusicPluginObject {
00309 public:
00310     const char *getName() const {
00311         return "Apple DLS Software Synthesizer";
00312     }
00313 
00314     const char *getId() const {
00315         return "core";
00316     }
00317 
00318     MusicDevices getDevices() const;
00319     Common::Error createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle = 0) const;
00320 };
00321 
00322 MusicDevices CoreAudioMusicPlugin::getDevices() const {
00323     MusicDevices devices;
00324     devices.push_back(MusicDevice(this, "", MT_GM));
00325     return devices;
00326 }
00327 
00328 Common::Error CoreAudioMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const {
00329     *mididriver = new MidiDriver_CORE();
00330 
00331     return Common::kNoError;
00332 }
00333 
00334 //#if PLUGIN_ENABLED_DYNAMIC(COREAUDIO)
00335     //REGISTER_PLUGIN_DYNAMIC(COREAUDIO, PLUGIN_TYPE_MUSIC, CoreAudioMusicPlugin);
00336 //#else
00337     REGISTER_PLUGIN_STATIC(COREAUDIO, PLUGIN_TYPE_MUSIC, CoreAudioMusicPlugin);
00338 //#endif
00339 
00340 #endif // MACOSX


Generated on Sat Mar 23 2019 05:01:24 for ResidualVM by doxygen 1.7.1
curved edge   curved edge