DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/gui/midi_coreaudio.h
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <AudioToolbox/AUGraph.h>
00021 #include <CoreServices/CoreServices.h>
00022 
00023 // A macro to simplify error handling a bit.
00024 #define RequireNoErr(error)                                         \
00025 do {                                                                \
00026         err = error;                                                    \
00027         if (err != noErr)                                               \
00028                 goto bail;                                                  \
00029 } while (false)
00030 
00031 // With the release of Mac OS X 10.5 in October 2007, Apple deprecated the
00032 // AUGraphNewNode & AUGraphGetNodeInfo APIs in favor of the new AUGraphAddNode &
00033 // AUGraphNodeInfo APIs. While it is easy to switch to those, it breaks
00034 // compatibility with all pre-10.5 systems.
00035 //
00036 // Since 10.5 was the last system to support PowerPC, we use the old, deprecated
00037 // APIs on PowerPC based systems by default. On all other systems (such as Mac
00038 // OS X running on Intel hardware, or iOS running on ARM), we use the new API by
00039 // default.
00040 //
00041 // This leaves Mac OS X 10.4 running on x86 processors as the only system
00042 // combination that this code will not support by default. It seems quite
00043 // reasonable to assume that anybody with an Intel system has since then moved
00044 // on to a newer Mac OS X release. But if for some reason you absolutely need to
00045 // build an x86 version of this code using the old, deprecated API, you can
00046 // simply do so by manually enable the USE_DEPRECATED_COREAUDIO_API switch (e.g.
00047 // by adding setting it suitably in CPPFLAGS).
00048 #if !defined(USE_DEPRECATED_COREAUDIO_API)
00049 #   if TARGET_CPU_PPC || TARGET_CPU_PPC64
00050 #      define USE_DEPRECATED_COREAUDIO_API 1
00051 #   else
00052 #      define USE_DEPRECATED_COREAUDIO_API 0
00053 #   endif
00054 #endif
00055 
00056 class MidiHandler_coreaudio : public MidiHandler {
00057 private:
00058         AUGraph m_auGraph;
00059         AudioUnit m_synth;
00060         const char *soundfont;
00061 public:
00062         MidiHandler_coreaudio() : m_auGraph(0), m_synth(0) {}
00063         const char * GetName(void) { return "coreaudio"; }
00064         bool Open(const char * conf) {
00065                 OSStatus err = 0;
00066 
00067                 if (m_auGraph)
00068                         return false;
00069 
00070                 // Open the Music Device.
00071                 RequireNoErr(NewAUGraph(&m_auGraph));
00072 
00073                 AUNode outputNode, synthNode;
00074                 // OS X 10.5 SDK doesn't know AudioComponentDescription desc;
00075 #if USE_DEPRECATED_COREAUDIO_API || (MAC_OS_X_VERSION_MAX_ALLOWED <= 1050)
00076                 ComponentDescription desc;
00077 #else
00078                 AudioComponentDescription desc;
00079 #endif
00080 
00081                 // The default output device
00082                 desc.componentType = kAudioUnitType_Output;
00083                 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
00084                 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
00085                 desc.componentFlags = 0;
00086                 desc.componentFlagsMask = 0;
00087 #if USE_DEPRECATED_COREAUDIO_API
00088                 RequireNoErr(AUGraphNewNode(m_auGraph, &desc, 0, NULL, &outputNode));
00089 #else
00090                 RequireNoErr(AUGraphAddNode(m_auGraph, &desc, &outputNode));
00091 #endif
00092 
00093                 // The built-in default (softsynth) music device
00094                 desc.componentType = kAudioUnitType_MusicDevice;
00095                 desc.componentSubType = kAudioUnitSubType_DLSSynth;
00096                 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
00097 #if USE_DEPRECATED_COREAUDIO_API
00098                 RequireNoErr(AUGraphNewNode(m_auGraph, &desc, 0, NULL, &synthNode));
00099 #else
00100                 RequireNoErr(AUGraphAddNode(m_auGraph, &desc, &synthNode));
00101 #endif
00102 
00103                 // Connect the softsynth to the default output
00104                 RequireNoErr(AUGraphConnectNodeInput(m_auGraph, synthNode, 0, outputNode, 0));
00105 
00106                 // Open and initialize the whole graph
00107                 RequireNoErr(AUGraphOpen(m_auGraph));
00108                 RequireNoErr(AUGraphInitialize(m_auGraph));
00109 
00110                 // Get the music device from the graph.
00111 #if USE_DEPRECATED_COREAUDIO_API
00112                 RequireNoErr(AUGraphGetNodeInfo(m_auGraph, synthNode, NULL, NULL, NULL, &m_synth));
00113 #else
00114                 RequireNoErr(AUGraphNodeInfo(m_auGraph, synthNode, NULL, &m_synth));
00115 #endif
00116 
00117                 // Optionally load a soundfont 
00118                 if (conf && conf[0]) {
00119                         soundfont = conf;
00120                         OSErr err = 0;
00121 #if USE_DEPRECATED_COREAUDIO_API
00122                         FSRef soundfontRef;
00123                         err = FSPathMakeRef((const UInt8*)soundfont, &soundfontRef, NULL);
00124                         if (!err) {
00125                                 err = AudioUnitSetProperty(
00126                                                            m_synth,
00127                                                            kMusicDeviceProperty_SoundBankFSRef,
00128                                                            kAudioUnitScope_Global,
00129                                                            0,
00130                                                            &soundfontRef,
00131                                                            sizeof(soundfontRef)
00132                                                           );
00133                                         }
00134 #else
00135                                         // kMusicDeviceProperty_SoundBankFSRef is present on 10.6+, but
00136                                         // kMusicDeviceProperty_SoundBankURL was added in 10.5 as a future prooof replacement
00137                                         CFURLRef url = CFURLCreateFromFileSystemRepresentation(
00138                                                                                                kCFAllocatorDefault,
00139                                                                                                (const UInt8*)soundfont,
00140                                                                                                strlen(soundfont), false
00141                                                                                               );
00142                                         if (url) {
00143                                                 err = AudioUnitSetProperty(
00144                                                                            m_synth, kMusicDeviceProperty_SoundBankURL,
00145                                                                            kAudioUnitScope_Global, 0, &url, sizeof(url)
00146                                                                           );
00147                                                 CFRelease(url);
00148                                         } else {
00149                                                 LOG_MSG("Failed to allocate CFURLRef from  %s",soundfont);
00150                                         }
00151 #endif
00152                                         if (!err) {
00153                                                 LOG_MSG("MIDI:coreaudio: loaded soundfont: %s",soundfont);
00154                                         } else {
00155                                                 LOG_MSG("Error loading CoreAudio SoundFont %s",soundfont);
00156                                                 // after trying and failing to load a soundfont it's better
00157                                                 // to fail initializing the CoreAudio driver or it might crash
00158                                                 return false;
00159                                         }
00160                                 }               
00161 
00162                 // Finally: Start the graph!
00163                 RequireNoErr(AUGraphStart(m_auGraph));
00164 
00165                 return true;
00166 
00167         bail:
00168                 if (m_auGraph) {
00169                         AUGraphStop(m_auGraph);
00170                         DisposeAUGraph(m_auGraph);
00171                         m_auGraph = 0;
00172                 }
00173                 return false;
00174         }
00175 
00176         void Close(void) {
00177                 if (m_auGraph) {
00178                         AUGraphStop(m_auGraph);
00179                         DisposeAUGraph(m_auGraph);
00180                         m_auGraph = 0;
00181                 }
00182         }
00183 
00184         void PlayMsg(Bit8u * msg) {
00185                 MusicDeviceMIDIEvent(m_synth, msg[0], msg[1], msg[2], 0);
00186         }       
00187 
00188         void PlaySysex(Bit8u * sysex, Bitu len) {
00189                 MusicDeviceSysEx(m_synth, sysex, len);
00190         }
00191 };
00192 
00193 #undef RequireNoErr
00194 
00195 MidiHandler_coreaudio Midi_coreaudio;