|
1 /* $Id$ */ |
|
2 |
|
3 /***************************************************************************** |
|
4 * Cocoa sound driver * |
|
5 * Known things left to do: * |
|
6 * - Might need to do endian checking for it to work on both ppc and x86 * |
|
7 *****************************************************************************/ |
|
8 |
|
9 #ifdef WITH_COCOA |
|
10 |
|
11 #include <AudioUnit/AudioUnit.h> |
|
12 |
|
13 /* Name conflict */ |
|
14 #define Rect OTTDRect |
|
15 #define Point OTTDPoint |
|
16 #define WindowClass OTTDWindowClass |
|
17 /* Defined in stdbool.h */ |
|
18 #ifndef __cplusplus |
|
19 # ifndef __BEOS__ |
|
20 # undef bool |
|
21 # undef false |
|
22 # undef true |
|
23 # endif |
|
24 #endif |
|
25 |
|
26 #include "../stdafx.h" |
|
27 #include "../openttd.h" |
|
28 #include "../debug.h" |
|
29 #include "../driver.h" |
|
30 #include "../mixer.h" |
|
31 #include "../sdl.h" |
|
32 |
|
33 #include "cocoa_s.h" |
|
34 |
|
35 #undef WindowClass |
|
36 #undef Point |
|
37 #undef Rect |
|
38 |
|
39 |
|
40 static AudioUnit _outputAudioUnit; |
|
41 |
|
42 /* The CoreAudio callback */ |
|
43 static OSStatus audioCallback(void *inRefCon, AudioUnitRenderActionFlags inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, AudioBuffer *ioData) |
|
44 { |
|
45 MxMixSamples(ioData->mData, ioData->mDataByteSize / 4); |
|
46 |
|
47 return noErr; |
|
48 } |
|
49 |
|
50 |
|
51 static const char *CocoaSoundStart(const char * const *parm) |
|
52 { |
|
53 Component comp; |
|
54 ComponentDescription desc; |
|
55 struct AudioUnitInputCallback callback; |
|
56 AudioStreamBasicDescription requestedDesc; |
|
57 |
|
58 /* Setup a AudioStreamBasicDescription with the requested format */ |
|
59 requestedDesc.mFormatID = kAudioFormatLinearPCM; |
|
60 requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked; |
|
61 requestedDesc.mChannelsPerFrame = 2; |
|
62 requestedDesc.mSampleRate = GetDriverParamInt(parm, "hz", 11025); |
|
63 |
|
64 requestedDesc.mBitsPerChannel = 16; |
|
65 requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; |
|
66 |
|
67 #ifdef TTD_BIG_ENDIAN |
|
68 requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; |
|
69 #endif |
|
70 |
|
71 requestedDesc.mFramesPerPacket = 1; |
|
72 requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8; |
|
73 requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket; |
|
74 |
|
75 |
|
76 /* Locate the default output audio unit */ |
|
77 desc.componentType = kAudioUnitComponentType; |
|
78 desc.componentSubType = kAudioUnitSubType_Output; |
|
79 desc.componentManufacturer = kAudioUnitID_DefaultOutput; |
|
80 desc.componentFlags = 0; |
|
81 desc.componentFlagsMask = 0; |
|
82 |
|
83 comp = FindNextComponent (NULL, &desc); |
|
84 if (comp == NULL) { |
|
85 return "cocoa_s: Failed to start CoreAudio: FindNextComponent returned NULL"; |
|
86 } |
|
87 |
|
88 /* Open & initialize the default output audio unit */ |
|
89 if (OpenAComponent(comp, &_outputAudioUnit) != noErr) { |
|
90 return "cocoa_s: Failed to start CoreAudio: OpenAComponent"; |
|
91 } |
|
92 |
|
93 if (AudioUnitInitialize(_outputAudioUnit) != noErr) { |
|
94 return "cocoa_s: Failed to start CoreAudio: AudioUnitInitialize"; |
|
95 } |
|
96 |
|
97 /* Set the input format of the audio unit. */ |
|
98 if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &requestedDesc, sizeof(requestedDesc)) != noErr) { |
|
99 return "cocoa_s: Failed to start CoreAudio: AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)"; |
|
100 } |
|
101 |
|
102 /* Set the audio callback */ |
|
103 callback.inputProc = audioCallback; |
|
104 callback.inputProcRefCon = NULL; |
|
105 if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_SetInputCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)) != noErr) { |
|
106 return "cocoa_s: Failed to start CoreAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)"; |
|
107 } |
|
108 |
|
109 /* Finally, start processing of the audio unit */ |
|
110 if (AudioOutputUnitStart(_outputAudioUnit) != noErr) { |
|
111 return "cocoa_s: Failed to start CoreAudio: AudioOutputUnitStart"; |
|
112 } |
|
113 |
|
114 /* We're running! */ |
|
115 return NULL; |
|
116 } |
|
117 |
|
118 |
|
119 static void CocoaSoundStop(void) |
|
120 { |
|
121 struct AudioUnitInputCallback callback; |
|
122 |
|
123 /* stop processing the audio unit */ |
|
124 if (AudioOutputUnitStop(_outputAudioUnit) != noErr) { |
|
125 DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: AudioOutputUnitStop failed"); |
|
126 return; |
|
127 } |
|
128 |
|
129 /* Remove the input callback */ |
|
130 callback.inputProc = 0; |
|
131 callback.inputProcRefCon = 0; |
|
132 if (AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_SetInputCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)) != noErr) { |
|
133 DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback) failed"); |
|
134 return; |
|
135 } |
|
136 |
|
137 if (CloseComponent(_outputAudioUnit) != noErr) { |
|
138 DEBUG(driver, 0, "cocoa_s: Core_CloseAudio: CloseComponent failed"); |
|
139 return; |
|
140 } |
|
141 } |
|
142 |
|
143 |
|
144 const HalSoundDriver _cocoa_sound_driver = { |
|
145 CocoaSoundStart, |
|
146 CocoaSoundStop, |
|
147 }; |
|
148 |
|
149 #endif /* WITH_COCOA */ |