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