2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2006 Sam Lantinga
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.
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.
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
22 #include "SDL_config.h"
24 /* This should work on PowerPC and Intel Mac OS X, and Carbonized Mac OS 9. */
26 #if defined(__APPLE__) && defined(__MACH__)
27 # define SDL_MACOS_NAME "Mac OS X"
28 # include <Carbon/Carbon.h>
29 #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
30 # define SDL_MACOS_NAME "Mac OS 9"
33 # define SDL_MACOS_NAME "Mac OS 9"
34 # include <Sound.h> /* SoundManager interface */
36 # include <DriverServices.h>
39 #if !defined(NewSndCallBackUPP) && (UNIVERSAL_INTERFACES_VERSION < 0x0335)
40 #if !defined(NewSndCallBackProc) /* avoid circular redefinition... */
41 #define NewSndCallBackUPP NewSndCallBackProc
43 #if !defined(NewSndCallBackUPP)
44 #define NewSndCallBackUPP NewSndCallBackProc
48 #include "SDL_audio.h"
49 #include "../SDL_audio_c.h"
50 #include "../SDL_sysaudio.h"
51 #include "SDL_romaudio.h"
53 #pragma options align=power
55 static volatile SInt32 audio_is_locked = 0;
56 static volatile SInt32 need_to_mix = 0;
58 static UInt8 *buffer[2];
59 static volatile UInt32 running = 0;
60 static CmpSoundHeader header;
61 static volatile Uint32 fill_me = 0;
65 mix_buffer(SDL_AudioDevice * audio, UInt8 * buffer)
69 SDL_mutexP(audio->mixer_lock);
71 if (audio->convert.needed) {
72 audio->spec.callback(audio->spec.userdata,
73 (Uint8 *) audio->convert.buf,
75 SDL_ConvertAudio(&audio->convert);
76 if (audio->convert.len_cvt != audio->spec.size) {
77 /* Uh oh... probably crashes here */ ;
79 SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
81 audio->spec.callback(audio->spec.userdata, buffer,
85 SDL_mutexV(audio->mixer_lock);
89 DecrementAtomic((SInt32 *) & need_to_mix);
94 SNDMGR_LockDevice(_THIS)
96 IncrementAtomic((SInt32 *) & audio_is_locked);
100 SNDMGR_UnlockDevice(_THIS)
104 oldval = DecrementAtomic((SInt32 *) & audio_is_locked);
105 if (oldval != 1) /* != 1 means audio is still locked. */
108 /* Did we miss the chance to mix in an interrupt? Do it now. */
109 if (BitAndAtomic(0xFFFFFFFF, (UInt32 *) & need_to_mix)) {
111 * Note that this could be a problem if you missed an interrupt
112 * while the audio was locked, and get preempted by a second
113 * interrupt here, but that means you locked for way too long anyhow.
115 mix_buffer(this, buffer[fill_me]);
121 callBackProc(SndChannel * chan, SndCommand * cmd_passed)
125 SDL_AudioDevice *audio = (SDL_AudioDevice *) chan->userInfo;
127 IncrementAtomic((SInt32 *) & need_to_mix);
129 fill_me = cmd_passed->param2; /* buffer that has just finished playing, so fill it */
130 play_me = !fill_me; /* filled buffer to play _now_ */
132 if (!audio->enabled) {
136 /* queue previously mixed buffer for playback. */
137 header.samplePtr = (Ptr) buffer[play_me];
140 cmd.param2 = (long) &header;
141 SndDoCommand(chan, &cmd, 0);
143 SDL_memset(buffer[fill_me], 0, audio->spec.size);
146 * if audio device isn't locked, mix the next buffer to be queued in
147 * the memory block that just finished playing.
149 if (!BitAndAtomic(0xFFFFFFFF, (UInt32 *) & audio_is_locked)) {
150 mix_buffer(audio, buffer[fill_me]);
153 /* set this callback to run again when current buffer drains. */
155 cmd.cmd = callBackCmd;
157 cmd.param2 = play_me;
159 SndDoCommand(chan, &cmd, 0);
164 SNDMGR_CloseDevice(_THIS)
168 if (this->hidden != NULL) {
169 if (this->hidden->channel) {
170 SndDisposeChannel(this->hidden->channel, true);
171 this->hidden->channel = NULL;
176 buffer[0] = buffer[1] = NULL;
178 SDL_free(this->hidden);
184 SNDMGR_OpenDevice(_THIS, const char *devname, int iscapture)
186 SDL_AudioSpec *spec = &this->spec;
187 SndChannelPtr channel = NULL;
188 SndCallBackUPP callback;
193 /* Initialize all variables that we clean on shutdown */
194 this->hidden = (struct SDL_PrivateAudioData *)
195 SDL_malloc((sizeof *this->hidden));
196 if (this->hidden == NULL) {
200 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
202 /* !!! FIXME: iterate through format matrix... */
203 /* Very few conversions are required, but... */
204 switch (spec->format) {
206 spec->format = AUDIO_U8;
209 spec->format = AUDIO_S16LSB;
212 spec->format = AUDIO_S16MSB;
215 spec->format = AUDIO_F32MSB;
218 SDL_CalculateAudioSpec(&this->spec);
220 /* initialize bufferCmd header */
221 SDL_memset(&header, 0, sizeof(header));
222 callback = (SndCallBackUPP) NewSndCallBackUPP(callBackProc);
223 sample_bits = spec->size / spec->samples / spec->channels * 8;
227 "Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n",
228 spec->format, spec->channels, sample_bits, spec->freq);
229 #endif /* DEBUG_AUDIO */
231 header.numChannels = spec->channels;
232 header.sampleSize = sample_bits;
233 header.sampleRate = spec->freq << 16;
234 header.numFrames = spec->samples;
235 header.encode = cmpSH;
237 /* Note that we install the 16bitLittleEndian Converter if needed. */
238 if (spec->format == AUDIO_S16LSB) {
239 header.compressionID = fixedCompression;
240 header.format = k16BitLittleEndianFormat;
241 } else if (spec->format == AUDIO_S32MSB) {
242 header.compressionID = fixedCompression;
243 header.format = k32BitFormat;
244 } else if (spec->format == AUDIO_S32LSB) {
245 header.compressionID = fixedCompression;
246 header.format = k32BitLittleEndianFormat;
247 } else if (spec->format == AUDIO_F32MSB) {
248 header.compressionID = fixedCompression;
249 header.format = kFloat32Format;
252 /* allocate 2 buffers */
253 for (i = 0; i < 2; i++) {
254 buffer[i] = (UInt8 *) SDL_malloc(sizeof(UInt8) * spec->size);
255 if (buffer[i] == NULL) {
256 SNDMGR_CloseDevice(this);
260 SDL_memset(buffer[i], 0, spec->size);
263 /* Create the sound manager channel */
264 channel = (SndChannelPtr) SDL_malloc(sizeof(*channel));
265 if (channel == NULL) {
266 SNDMGR_CloseDevice(this);
270 this->hidden->channel = channel;
271 if (spec->channels >= 2) {
272 initOptions = initStereo;
274 initOptions = initMono;
276 channel->userInfo = (long) this;
277 channel->qLength = 128;
278 if (SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr) {
279 SNDMGR_CloseDevice(this);
280 SDL_SetError("Unable to create audio channel");
287 cmd.cmd = callBackCmd;
290 SndDoCommand(channel, &cmd, 0);
297 SNDMGR_Init(SDL_AudioDriverImpl * impl)
299 /* Set the function pointers */
300 impl->OpenDevice = SNDMGR_OpenDevice;
301 impl->CloseDevice = SNDMGR_CloseDevice;
302 impl->ProvidesOwnCallbackThread = 1;
303 impl->OnlyHasDefaultOutputDevice = 1;
305 /* Mac OS X uses threaded audio, so normal thread code is okay */
307 impl->LockDevice = SNDMGR_LockDevice;
308 impl->UnlockDevice = SNDMGR_UnlockDevice;
309 impl->SkipMixerLock = 1;
315 AudioBootStrap SNDMGR_bootstrap = {
316 "sndmgr", SDL_MACOS_NAME " SoundManager", SNDMGR_Init, 0
319 /* vi: set ts=4 sw=4 expandtab: */