Mercurial > sdl-ios-xcode
comparison src/audio/macosx/SDL_coreaudio.c @ 935:f8d5ddc7aef1
Audio improvements from Max Horn, including a new CoreAudio driver for MacOSX
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Sat, 21 Aug 2004 02:06:30 +0000 |
parents | |
children | 84f930aebaeb |
comparison
equal
deleted
inserted
replaced
934:af585d6efec8 | 935:f8d5ddc7aef1 |
---|---|
1 /* | |
2 SDL - Simple DirectMedia Layer | |
3 Copyright (C) 1997-2004 Sam Lantinga | |
4 | |
5 This library is free software; you can redistribute it and/or | |
6 modify it under the terms of the GNU Library General Public | |
7 License as published by the Free Software Foundation; either | |
8 version 2 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 Library General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU Library General Public | |
16 License along with this library; if not, write to the Free | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 Sam Lantinga | |
20 slouken@libsdl.org | |
21 */ | |
22 | |
23 #ifdef SAVE_RCSID | |
24 static char rcsid = | |
25 "@(#) $Id$"; | |
26 #endif | |
27 | |
28 #include <AudioUnit/AudioUnit.h> | |
29 | |
30 #include <assert.h> | |
31 #include <stdlib.h> | |
32 #include <stdio.h> | |
33 #include <string.h> | |
34 | |
35 #include "SDL_endian.h" | |
36 #include "SDL_audio.h" | |
37 #include "SDL_audio_c.h" | |
38 #include "SDL_audiomem.h" | |
39 #include "SDL_sysaudio.h" | |
40 #include "SDL_coreaudio.h" | |
41 | |
42 | |
43 /* Audio driver functions */ | |
44 | |
45 static int Core_OpenAudio(_THIS, SDL_AudioSpec *spec); | |
46 static void Core_WaitAudio(_THIS); | |
47 static void Core_PlayAudio(_THIS); | |
48 static Uint8 *Core_GetAudioBuf(_THIS); | |
49 static void Core_CloseAudio(_THIS); | |
50 | |
51 /* Audio driver bootstrap functions */ | |
52 | |
53 static int Audio_Available(void) | |
54 { | |
55 return(1); | |
56 } | |
57 | |
58 static void Audio_DeleteDevice(SDL_AudioDevice *device) | |
59 { | |
60 free(device->hidden); | |
61 free(device); | |
62 } | |
63 | |
64 static SDL_AudioDevice *Audio_CreateDevice(int devindex) | |
65 { | |
66 SDL_AudioDevice *this; | |
67 | |
68 /* Initialize all variables that we clean on shutdown */ | |
69 this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); | |
70 if ( this ) { | |
71 memset(this, 0, (sizeof *this)); | |
72 this->hidden = (struct SDL_PrivateAudioData *) | |
73 malloc((sizeof *this->hidden)); | |
74 } | |
75 if ( (this == NULL) || (this->hidden == NULL) ) { | |
76 SDL_OutOfMemory(); | |
77 if ( this ) { | |
78 free(this); | |
79 } | |
80 return(0); | |
81 } | |
82 memset(this->hidden, 0, (sizeof *this->hidden)); | |
83 | |
84 /* Set the function pointers */ | |
85 this->OpenAudio = Core_OpenAudio; | |
86 this->WaitAudio = Core_WaitAudio; | |
87 this->PlayAudio = Core_PlayAudio; | |
88 this->GetAudioBuf = Core_GetAudioBuf; | |
89 this->CloseAudio = Core_CloseAudio; | |
90 | |
91 this->free = Audio_DeleteDevice; | |
92 | |
93 return this; | |
94 } | |
95 | |
96 AudioBootStrap COREAUDIO_bootstrap = { | |
97 "coreaudio", "Mac OS X CoreAudio", | |
98 Audio_Available, Audio_CreateDevice | |
99 }; | |
100 | |
101 /* The CoreAudio callback */ | |
102 static OSStatus audioCallback (void *inRefCon, | |
103 AudioUnitRenderActionFlags inActionFlags, | |
104 const AudioTimeStamp *inTimeStamp, | |
105 UInt32 inBusNumber, | |
106 AudioBuffer *ioData) | |
107 { | |
108 SDL_AudioDevice *this = (SDL_AudioDevice *)inRefCon; | |
109 UInt32 remaining, len; | |
110 void *ptr; | |
111 | |
112 /* Only do anything if audio is enabled and not paused */ | |
113 if ( ! this->enabled || this->paused ) { | |
114 memset(ioData->mData, this->spec.silence, ioData->mDataByteSize); | |
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 assert(!this->convert.needed); | |
122 assert(this->spec.channels == ioData->mNumberChannels); | |
123 | |
124 remaining = ioData->mDataByteSize; | |
125 ptr = ioData->mData; | |
126 while (remaining > 0) { | |
127 if (bufferOffset >= bufferSize) { | |
128 /* Generate the data */ | |
129 memset(buffer, this->spec.silence, bufferSize); | |
130 SDL_mutexP(this->mixer_lock); | |
131 (*this->spec.callback)(this->spec.userdata, | |
132 buffer, bufferSize); | |
133 SDL_mutexV(this->mixer_lock); | |
134 bufferOffset = 0; | |
135 } | |
136 | |
137 len = bufferSize - bufferOffset; | |
138 if (len > remaining) | |
139 len = remaining; | |
140 memcpy(ptr, buffer + bufferOffset, len); | |
141 ptr += len; | |
142 remaining -= len; | |
143 bufferOffset += len; | |
144 } | |
145 | |
146 return 0; | |
147 } | |
148 | |
149 /* Dummy functions -- we don't use thread-based audio */ | |
150 void Core_WaitAudio(_THIS) | |
151 { | |
152 return; | |
153 } | |
154 | |
155 void Core_PlayAudio(_THIS) | |
156 { | |
157 return; | |
158 } | |
159 | |
160 Uint8 *Core_GetAudioBuf(_THIS) | |
161 { | |
162 return(NULL); | |
163 } | |
164 | |
165 void Core_CloseAudio(_THIS) | |
166 { | |
167 OSStatus result; | |
168 AudioUnitInputCallback callback; | |
169 | |
170 /* stop processing the audio unit */ | |
171 result = AudioOutputUnitStop (outputAudioUnit); | |
172 if (result != noErr) { | |
173 SDL_SetError("Core_CloseAudio: AudioOutputUnitStop"); | |
174 return; | |
175 } | |
176 | |
177 /* Remove the input callback */ | |
178 callback.inputProc = 0; | |
179 callback.inputProcRefCon = 0; | |
180 result = AudioUnitSetProperty (outputAudioUnit, | |
181 kAudioUnitProperty_SetInputCallback, | |
182 kAudioUnitScope_Input, | |
183 0, | |
184 &callback, | |
185 sizeof(callback)); | |
186 if (result != noErr) { | |
187 SDL_SetError("Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)"); | |
188 return; | |
189 } | |
190 | |
191 result = CloseComponent(outputAudioUnit); | |
192 if (result != noErr) { | |
193 SDL_SetError("Core_CloseAudio: CloseComponent"); | |
194 return; | |
195 } | |
196 | |
197 free(buffer); | |
198 } | |
199 | |
200 #define CHECK_RESULT(msg) \ | |
201 if (result != noErr) { \ | |
202 SDL_SetError("Failed to start CoreAudio: " msg); \ | |
203 return -1; \ | |
204 } | |
205 | |
206 | |
207 int Core_OpenAudio(_THIS, SDL_AudioSpec *spec) | |
208 { | |
209 OSStatus result = noErr; | |
210 Component comp; | |
211 ComponentDescription desc; | |
212 AudioUnitInputCallback callback; | |
213 AudioStreamBasicDescription requestedDesc; | |
214 | |
215 /* Setup a AudioStreamBasicDescription with the requested format */ | |
216 requestedDesc.mFormatID = kAudioFormatLinearPCM; | |
217 requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked; | |
218 requestedDesc.mChannelsPerFrame = spec->channels; | |
219 requestedDesc.mSampleRate = spec->freq; | |
220 | |
221 requestedDesc.mBitsPerChannel = spec->format & 0xFF; | |
222 if (spec->format & 0x8000) | |
223 requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; | |
224 if (spec->format & 0x1000) | |
225 requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; | |
226 | |
227 requestedDesc.mFramesPerPacket = 1; | |
228 requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8; | |
229 requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket; | |
230 | |
231 | |
232 /* Locate the default output audio unit */ | |
233 desc.componentType = kAudioUnitComponentType; | |
234 desc.componentSubType = kAudioUnitSubType_Output; | |
235 desc.componentManufacturer = kAudioUnitID_DefaultOutput; | |
236 desc.componentFlags = 0; | |
237 desc.componentFlagsMask = 0; | |
238 | |
239 comp = FindNextComponent (NULL, &desc); | |
240 if (comp == NULL) { | |
241 SDL_SetError ("Failed to start CoreAudio: FindNextComponent returned NULL"); | |
242 return -1; | |
243 } | |
244 | |
245 /* Open & initialize the default output audio unit */ | |
246 result = OpenAComponent (comp, &outputAudioUnit); | |
247 CHECK_RESULT("OpenAComponent") | |
248 | |
249 result = AudioUnitInitialize (outputAudioUnit); | |
250 CHECK_RESULT("AudioUnitInitialize") | |
251 | |
252 /* Set the input format of the audio unit. */ | |
253 result = AudioUnitSetProperty (outputAudioUnit, | |
254 kAudioUnitProperty_StreamFormat, | |
255 kAudioUnitScope_Input, | |
256 0, | |
257 &requestedDesc, | |
258 sizeof (requestedDesc)); | |
259 CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)") | |
260 | |
261 /* Set the audio callback */ | |
262 callback.inputProc = audioCallback; | |
263 callback.inputProcRefCon = this; | |
264 result = AudioUnitSetProperty (outputAudioUnit, | |
265 kAudioUnitProperty_SetInputCallback, | |
266 kAudioUnitScope_Input, | |
267 0, | |
268 &callback, | |
269 sizeof(callback)); | |
270 CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)") | |
271 | |
272 /* Calculate the final parameters for this audio specification */ | |
273 SDL_CalculateAudioSpec(spec); | |
274 | |
275 /* Allocate a sample buffer */ | |
276 bufferOffset = bufferSize = this->spec.size; | |
277 buffer = malloc(bufferSize); | |
278 assert(buffer); | |
279 | |
280 /* Finally, start processing of the audio unit */ | |
281 result = AudioOutputUnitStart (outputAudioUnit); | |
282 CHECK_RESULT("AudioOutputUnitStart") | |
283 | |
284 | |
285 /* We're running! */ | |
286 return(1); | |
287 } |