OpenTTD
cocoa_s.cpp
Go to the documentation of this file.
1 /* $Id: cocoa_s.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
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 "../debug.h"
22 #include "../driver.h"
23 #include "../mixer.h"
24 #include "../core/endian_type.hpp"
25 #include "cocoa_s.h"
26 
27 #define Rect OTTDRect
28 #define Point OTTDPoint
29 #include <AudioUnit/AudioUnit.h>
30 #undef Rect
31 #undef Point
32 
33 #include "../safeguards.h"
34 
35 static FSoundDriver_Cocoa iFSoundDriver_Cocoa;
36 
37 static AudioUnit _outputAudioUnit;
38 
39 /* The CoreAudio callback */
40 static OSStatus audioCallback(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData)
41 {
42  MxMixSamples(ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize / 4);
43 
44  return noErr;
45 }
46 
47 
48 const char *SoundDriver_Cocoa::Start(const char * const *parm)
49 {
50  Component comp;
51  ComponentDescription desc;
52  struct AURenderCallbackStruct callback;
53  AudioStreamBasicDescription requestedDesc;
54 
55  /* Setup a AudioStreamBasicDescription with the requested format */
56  requestedDesc.mFormatID = kAudioFormatLinearPCM;
57  requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
58  requestedDesc.mChannelsPerFrame = 2;
59  requestedDesc.mSampleRate = GetDriverParamInt(parm, "hz", 44100);
60 
61  requestedDesc.mBitsPerChannel = 16;
62  requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
63 
64 #if TTD_ENDIAN == TTD_BIG_ENDIAN
65  requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
66 #endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
67 
68  requestedDesc.mFramesPerPacket = 1;
69  requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
70  requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;
71 
72  MxInitialize((uint)requestedDesc.mSampleRate);
73 
74  /* Locate the default output audio unit */
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  comp = FindNextComponent (NULL, &desc);
82  if (comp == NULL) {
83  return "cocoa_s: Failed to start CoreAudio: FindNextComponent returned NULL";
84  }
85 
86  /* Open & initialize the default output audio unit */
87  if (OpenAComponent(comp, &_outputAudioUnit) != noErr) {
88  return "cocoa_s: Failed to start CoreAudio: OpenAComponent";
89  }
90 
91  if (AudioUnitInitialize(_outputAudioUnit) != noErr) {
92  return "cocoa_s: Failed to start CoreAudio: AudioUnitInitialize";
93  }
94 
95  /* Set the input format of the audio unit. */
96  if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &requestedDesc, sizeof(requestedDesc)) != noErr) {
97  return "cocoa_s: Failed to start CoreAudio: AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)";
98  }
99 
100  /* Set the audio callback */
101  callback.inputProc = audioCallback;
102  callback.inputProcRefCon = NULL;
103  if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)) != noErr) {
104  return "cocoa_s: Failed to start CoreAudio: AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback)";
105  }
106 
107  /* Finally, start processing of the audio unit */
108  if (AudioOutputUnitStart(_outputAudioUnit) != noErr) {
109  return "cocoa_s: Failed to start CoreAudio: AudioOutputUnitStart";
110  }
111 
112  /* We're running! */
113  return NULL;
114 }
115 
116 
118 {
119  struct AURenderCallbackStruct callback;
120 
121  /* stop processing the audio unit */
122  if (AudioOutputUnitStop(_outputAudioUnit) != noErr) {
123  DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: AudioOutputUnitStop failed");
124  return;
125  }
126 
127  /* Remove the input callback */
128  callback.inputProc = 0;
129  callback.inputProcRefCon = 0;
130  if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)) != noErr) {
131  DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback) failed");
132  return;
133  }
134 
135  if (CloseComponent(_outputAudioUnit) != noErr) {
136  DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: CloseComponent failed");
137  return;
138  }
139 }
140 
141 #endif /* WITH_COCOA */