OpenTTD
cocoa_s.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD 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, version 2.
4  * OpenTTD 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.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 /*****************************************************************************
11  * Cocoa sound driver *
12  * Known things left to do: *
13  * - Might need to do endian checking for it to work on both ppc and x86 *
14  *****************************************************************************/
15 
16 #ifdef WITH_COCOA
17 
18 #include "../stdafx.h"
19 #include "../os/macosx/macos.h"
20 #include "../debug.h"
21 #include "../driver.h"
22 #include "../mixer.h"
23 #include "../core/endian_type.hpp"
24 #include "cocoa_s.h"
25 
26 #define Rect OTTDRect
27 #define Point OTTDPoint
28 #include <AudioUnit/AudioUnit.h>
29 #undef Rect
30 #undef Point
31 
32 #include "../safeguards.h"
33 
34 static FSoundDriver_Cocoa iFSoundDriver_Cocoa;
35 
36 static AudioUnit _outputAudioUnit;
37 
38 /* The CoreAudio callback */
39 static OSStatus audioCallback(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData)
40 {
41  MxMixSamples(ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize / 4);
42 
43  return noErr;
44 }
45 
46 
47 const char *SoundDriver_Cocoa::Start(const char * const *parm)
48 {
49  struct AURenderCallbackStruct callback;
50  AudioStreamBasicDescription requestedDesc;
51 
52  /* Setup a AudioStreamBasicDescription with the requested format */
53  requestedDesc.mFormatID = kAudioFormatLinearPCM;
54  requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
55  requestedDesc.mChannelsPerFrame = 2;
56  requestedDesc.mSampleRate = GetDriverParamInt(parm, "hz", 44100);
57 
58  requestedDesc.mBitsPerChannel = 16;
59  requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
60 
61 #if TTD_ENDIAN == TTD_BIG_ENDIAN
62  requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
63 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
64 
65  requestedDesc.mFramesPerPacket = 1;
66  requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
67  requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;
68 
69  MxInitialize((uint)requestedDesc.mSampleRate);
70 
71 #if defined(__AUDIOCOMPONENT_H__) || defined(HAVE_OSX_107_SDK)
72  if (MacOSVersionIsAtLeast(10, 6, 0)) {
73  /* Locate the default output audio unit */
74  AudioComponentDescription desc;
75  desc.componentType = kAudioUnitType_Output;
76  desc.componentSubType = kAudioUnitSubType_HALOutput;
77  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
78  desc.componentFlags = 0;
79  desc.componentFlagsMask = 0;
80 
81  AudioComponent comp = AudioComponentFindNext (nullptr, &desc);
82  if (comp == nullptr) {
83  return "cocoa_s: Failed to start CoreAudio: AudioComponentFindNext returned nullptr";
84  }
85 
86  /* Open & initialize the default output audio unit */
87  if (AudioComponentInstanceNew(comp, &_outputAudioUnit) != noErr) {
88  return "cocoa_s: Failed to start CoreAudio: AudioComponentInstanceNew";
89  }
90  } else
91 #endif
92  {
93 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6)
94  /* Locate the default output audio unit */
95  ComponentDescription desc;
96  desc.componentType = kAudioUnitType_Output;
97  desc.componentSubType = kAudioUnitSubType_HALOutput;
98  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
99  desc.componentFlags = 0;
100  desc.componentFlagsMask = 0;
101 
102  Component comp = FindNextComponent (nullptr, &desc);
103  if (comp == nullptr) {
104  return "cocoa_s: Failed to start CoreAudio: FindNextComponent returned nullptr";
105  }
106 
107  /* Open & initialize the default output audio unit */
108  if (OpenAComponent(comp, &_outputAudioUnit) != noErr) {
109  return "cocoa_s: Failed to start CoreAudio: OpenAComponent";
110  }
111 #else
112  return "cocoa_s: Not supported on this OS X version";
113 #endif
114  }
115 
116  if (AudioUnitInitialize(_outputAudioUnit) != noErr) {
117  return "cocoa_s: Failed to start CoreAudio: AudioUnitInitialize";
118  }
119 
120  /* Set the input format of the audio unit. */
121  if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &requestedDesc, sizeof(requestedDesc)) != noErr) {
122  return "cocoa_s: Failed to start CoreAudio: AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)";
123  }
124 
125  /* Set the audio callback */
126  callback.inputProc = audioCallback;
127  callback.inputProcRefCon = nullptr;
128  if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)) != noErr) {
129  return "cocoa_s: Failed to start CoreAudio: AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback)";
130  }
131 
132  /* Finally, start processing of the audio unit */
133  if (AudioOutputUnitStart(_outputAudioUnit) != noErr) {
134  return "cocoa_s: Failed to start CoreAudio: AudioOutputUnitStart";
135  }
136 
137  /* We're running! */
138  return nullptr;
139 }
140 
141 
143 {
144  struct AURenderCallbackStruct callback;
145 
146  /* stop processing the audio unit */
147  if (AudioOutputUnitStop(_outputAudioUnit) != noErr) {
148  DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: AudioOutputUnitStop failed");
149  return;
150  }
151 
152  /* Remove the input callback */
153  callback.inputProc = 0;
154  callback.inputProcRefCon = 0;
155  if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)) != noErr) {
156  DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback) failed");
157  return;
158  }
159 
160 #if defined(__AUDIOCOMPONENT_H__) || defined(HAVE_OSX_107_SDK)
161  if (MacOSVersionIsAtLeast(10, 6, 0)) {
162  if (AudioComponentInstanceDispose(_outputAudioUnit) != noErr) {
163  DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: AudioComponentInstanceDispose failed");
164  return;
165  }
166  } else
167 #endif
168  {
169 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6)
170  if (CloseComponent(_outputAudioUnit) != noErr) {
171  DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: CloseComponent failed");
172  return;
173  }
174 #endif
175  }
176 }
177 
178 #endif /* WITH_COCOA */
static bool MacOSVersionIsAtLeast(long major, long minor, long bugfix)
Check if we are at least running on the specified version of Mac OS.
Definition: macos.h:25
Base for Cocoa sound handling.
void Stop() override
Stop this driver.
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:35
int GetDriverParamInt(const char *const *parm, const char *name, int def)
Get an integer parameter the list of parameters.
Definition: driver.cpp:73
const char * Start(const char *const *param) override
Start this driver.