e14743d1 |
1 | /* |
2 | SDL - Simple DirectMedia Layer |
3 | Copyright (C) 1997-2009 Sam Lantinga |
4 | |
5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | This library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with this library; if not, write to the Free Software |
17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
18 | |
19 | Sam Lantinga |
20 | slouken@libsdl.org |
21 | */ |
22 | #include "SDL_config.h" |
23 | |
24 | #include <CoreAudio/CoreAudio.h> |
25 | #include <CoreServices/CoreServices.h> |
26 | #include <AudioUnit/AudioUnit.h> |
27 | #if MAC_OS_X_VERSION_MAX_ALLOWED <= 1050 |
28 | #include <AudioUnit/AUNTComponent.h> |
29 | #endif |
30 | |
31 | #include "SDL_audio.h" |
32 | #include "../SDL_audio_c.h" |
33 | #include "../SDL_sysaudio.h" |
34 | #include "SDL_coreaudio.h" |
35 | |
36 | |
37 | /* Audio driver functions */ |
38 | |
39 | static int Core_OpenAudio(_THIS, SDL_AudioSpec *spec); |
40 | static void Core_WaitAudio(_THIS); |
41 | static void Core_PlayAudio(_THIS); |
42 | static Uint8 *Core_GetAudioBuf(_THIS); |
43 | static void Core_CloseAudio(_THIS); |
44 | |
45 | /* Audio driver bootstrap functions */ |
46 | |
47 | static int Audio_Available(void) |
48 | { |
49 | return(1); |
50 | } |
51 | |
52 | static void Audio_DeleteDevice(SDL_AudioDevice *device) |
53 | { |
54 | SDL_free(device->hidden); |
55 | SDL_free(device); |
56 | } |
57 | |
58 | static SDL_AudioDevice *Audio_CreateDevice(int devindex) |
59 | { |
60 | SDL_AudioDevice *this; |
61 | |
62 | /* Initialize all variables that we clean on shutdown */ |
63 | this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); |
64 | if ( this ) { |
65 | SDL_memset(this, 0, (sizeof *this)); |
66 | this->hidden = (struct SDL_PrivateAudioData *) |
67 | SDL_malloc((sizeof *this->hidden)); |
68 | } |
69 | if ( (this == NULL) || (this->hidden == NULL) ) { |
70 | SDL_OutOfMemory(); |
71 | if ( this ) { |
72 | SDL_free(this); |
73 | } |
74 | return(0); |
75 | } |
76 | SDL_memset(this->hidden, 0, (sizeof *this->hidden)); |
77 | |
78 | /* Set the function pointers */ |
79 | this->OpenAudio = Core_OpenAudio; |
80 | this->WaitAudio = Core_WaitAudio; |
81 | this->PlayAudio = Core_PlayAudio; |
82 | this->GetAudioBuf = Core_GetAudioBuf; |
83 | this->CloseAudio = Core_CloseAudio; |
84 | |
85 | this->free = Audio_DeleteDevice; |
86 | |
87 | return this; |
88 | } |
89 | |
90 | AudioBootStrap COREAUDIO_bootstrap = { |
91 | "coreaudio", "Mac OS X CoreAudio", |
92 | Audio_Available, Audio_CreateDevice |
93 | }; |
94 | |
95 | /* The CoreAudio callback */ |
96 | static OSStatus audioCallback (void *inRefCon, |
97 | AudioUnitRenderActionFlags *ioActionFlags, |
98 | const AudioTimeStamp *inTimeStamp, |
99 | UInt32 inBusNumber, |
100 | UInt32 inNumberFrames, |
101 | AudioBufferList *ioData) |
102 | { |
103 | SDL_AudioDevice *this = (SDL_AudioDevice *)inRefCon; |
104 | UInt32 remaining, len; |
105 | AudioBuffer *abuf; |
106 | void *ptr; |
107 | UInt32 i; |
108 | |
109 | /* Only do anything if audio is enabled and not paused */ |
110 | if ( ! this->enabled || this->paused ) { |
111 | for (i = 0; i < ioData->mNumberBuffers; i++) { |
112 | abuf = &ioData->mBuffers[i]; |
113 | SDL_memset(abuf->mData, this->spec.silence, abuf->mDataByteSize); |
114 | } |
115 | return 0; |
116 | } |
117 | |
118 | /* No SDL conversion should be needed here, ever, since we accept |
119 | any input format in OpenAudio, and leave the conversion to CoreAudio. |
120 | */ |
121 | /* |
122 | assert(!this->convert.needed); |
123 | assert(this->spec.channels == ioData->mNumberChannels); |
124 | */ |
125 | |
126 | for (i = 0; i < ioData->mNumberBuffers; i++) { |
127 | abuf = &ioData->mBuffers[i]; |
128 | remaining = abuf->mDataByteSize; |
129 | ptr = abuf->mData; |
130 | while (remaining > 0) { |
131 | if (bufferOffset >= bufferSize) { |
132 | /* Generate the data */ |
133 | SDL_memset(buffer, this->spec.silence, bufferSize); |
134 | SDL_mutexP(this->mixer_lock); |
135 | (*this->spec.callback)(this->spec.userdata, |
136 | buffer, bufferSize); |
137 | SDL_mutexV(this->mixer_lock); |
138 | bufferOffset = 0; |
139 | } |
140 | |
141 | len = bufferSize - bufferOffset; |
142 | if (len > remaining) |
143 | len = remaining; |
144 | SDL_memcpy(ptr, (char *)buffer + bufferOffset, len); |
145 | ptr = (char *)ptr + len; |
146 | remaining -= len; |
147 | bufferOffset += len; |
148 | } |
149 | } |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | /* Dummy functions -- we don't use thread-based audio */ |
155 | void Core_WaitAudio(_THIS) |
156 | { |
157 | return; |
158 | } |
159 | |
160 | void Core_PlayAudio(_THIS) |
161 | { |
162 | return; |
163 | } |
164 | |
165 | Uint8 *Core_GetAudioBuf(_THIS) |
166 | { |
167 | return(NULL); |
168 | } |
169 | |
170 | void Core_CloseAudio(_THIS) |
171 | { |
172 | OSStatus result; |
173 | struct AURenderCallbackStruct callback; |
174 | |
175 | /* stop processing the audio unit */ |
176 | result = AudioOutputUnitStop (outputAudioUnit); |
177 | if (result != noErr) { |
178 | SDL_SetError("Core_CloseAudio: AudioOutputUnitStop"); |
179 | return; |
180 | } |
181 | |
182 | /* Remove the input callback */ |
183 | callback.inputProc = 0; |
184 | callback.inputProcRefCon = 0; |
185 | result = AudioUnitSetProperty (outputAudioUnit, |
186 | kAudioUnitProperty_SetRenderCallback, |
187 | kAudioUnitScope_Input, |
188 | 0, |
189 | &callback, |
190 | sizeof(callback)); |
191 | if (result != noErr) { |
192 | SDL_SetError("Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)"); |
193 | return; |
194 | } |
195 | |
196 | result = CloseComponent(outputAudioUnit); |
197 | if (result != noErr) { |
198 | SDL_SetError("Core_CloseAudio: CloseComponent"); |
199 | return; |
200 | } |
201 | |
202 | SDL_free(buffer); |
203 | } |
204 | |
205 | #define CHECK_RESULT(msg) \ |
206 | if (result != noErr) { \ |
207 | SDL_SetError("Failed to start CoreAudio: " msg); \ |
208 | return -1; \ |
209 | } |
210 | |
211 | |
212 | int Core_OpenAudio(_THIS, SDL_AudioSpec *spec) |
213 | { |
214 | OSStatus result = noErr; |
215 | Component comp; |
216 | ComponentDescription desc; |
217 | struct AURenderCallbackStruct callback; |
218 | AudioStreamBasicDescription requestedDesc; |
219 | |
220 | /* Setup a AudioStreamBasicDescription with the requested format */ |
221 | requestedDesc.mFormatID = kAudioFormatLinearPCM; |
222 | requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked; |
223 | requestedDesc.mChannelsPerFrame = spec->channels; |
224 | requestedDesc.mSampleRate = spec->freq; |
225 | |
226 | requestedDesc.mBitsPerChannel = spec->format & 0xFF; |
227 | if (spec->format & 0x8000) |
228 | requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; |
229 | if (spec->format & 0x1000) |
230 | requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; |
231 | |
232 | requestedDesc.mFramesPerPacket = 1; |
233 | requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8; |
234 | requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket; |
235 | |
236 | |
237 | /* Locate the default output audio unit */ |
238 | desc.componentType = kAudioUnitType_Output; |
239 | desc.componentSubType = kAudioUnitSubType_DefaultOutput; |
240 | desc.componentManufacturer = kAudioUnitManufacturer_Apple; |
241 | desc.componentFlags = 0; |
242 | desc.componentFlagsMask = 0; |
243 | |
244 | comp = FindNextComponent (NULL, &desc); |
245 | if (comp == NULL) { |
246 | SDL_SetError ("Failed to start CoreAudio: FindNextComponent returned NULL"); |
247 | return -1; |
248 | } |
249 | |
250 | /* Open & initialize the default output audio unit */ |
251 | result = OpenAComponent (comp, &outputAudioUnit); |
252 | CHECK_RESULT("OpenAComponent") |
253 | |
254 | result = AudioUnitInitialize (outputAudioUnit); |
255 | CHECK_RESULT("AudioUnitInitialize") |
256 | |
257 | /* Set the input format of the audio unit. */ |
258 | result = AudioUnitSetProperty (outputAudioUnit, |
259 | kAudioUnitProperty_StreamFormat, |
260 | kAudioUnitScope_Input, |
261 | 0, |
262 | &requestedDesc, |
263 | sizeof (requestedDesc)); |
264 | CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)") |
265 | |
266 | /* Set the audio callback */ |
267 | callback.inputProc = audioCallback; |
268 | callback.inputProcRefCon = this; |
269 | result = AudioUnitSetProperty (outputAudioUnit, |
270 | kAudioUnitProperty_SetRenderCallback, |
271 | kAudioUnitScope_Input, |
272 | 0, |
273 | &callback, |
274 | sizeof(callback)); |
275 | CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)") |
276 | |
277 | /* Calculate the final parameters for this audio specification */ |
278 | SDL_CalculateAudioSpec(spec); |
279 | |
280 | /* Allocate a sample buffer */ |
281 | bufferOffset = bufferSize = this->spec.size; |
282 | buffer = SDL_malloc(bufferSize); |
283 | |
284 | /* Finally, start processing of the audio unit */ |
285 | result = AudioOutputUnitStart (outputAudioUnit); |
286 | CHECK_RESULT("AudioOutputUnitStart") |
287 | |
288 | |
289 | /* We're running! */ |
290 | return(1); |
291 | } |