DOSBox-X
|
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;