Mercurial > sdl-ios-xcode
view src/audio/macrom/SDL_romaudio.c @ 1977:754847f19490
David Hedbor is no longer maintaining the Qtopia code.
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Thu, 10 Aug 2006 14:35:42 +0000 |
parents | c121d94672cb |
children | c27292a690b7 |
line wrap: on
line source
/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2006 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ #include "SDL_config.h" #if defined(__APPLE__) && defined(__MACH__) # include <Carbon/Carbon.h> #elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335) # include <Carbon.h> #else # include <Sound.h> /* SoundManager interface */ # include <Gestalt.h> # include <DriverServices.h> #endif #if !defined(NewSndCallBackUPP) && (UNIVERSAL_INTERFACES_VERSION < 0x0335) #if !defined(NewSndCallBackProc) /* avoid circular redefinition... */ #define NewSndCallBackUPP NewSndCallBackProc #endif #if !defined(NewSndCallBackUPP) #define NewSndCallBackUPP NewSndCallBackProc #endif #endif #include "SDL_audio.h" #include "../SDL_audio_c.h" #include "../SDL_sysaudio.h" #include "SDL_romaudio.h" /* Audio driver functions */ static void Mac_CloseAudio(_THIS); static int Mac_OpenAudio(_THIS, SDL_AudioSpec * spec); static void Mac_LockAudio(_THIS); static void Mac_UnlockAudio(_THIS); /* Audio driver bootstrap functions */ static int Audio_Available(void) { return (1); } static void Audio_DeleteDevice(SDL_AudioDevice * device) { SDL_free(device->hidden); SDL_free(device); } static SDL_AudioDevice * Audio_CreateDevice(int devindex) { SDL_AudioDevice *this; /* Initialize all variables that we clean on shutdown */ this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice)); if (this) { SDL_memset(this, 0, (sizeof *this)); this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc((sizeof *this->hidden)); } if ((this == NULL) || (this->hidden == NULL)) { SDL_OutOfMemory(); if (this) { SDL_free(this); } return (0); } SDL_memset(this->hidden, 0, (sizeof *this->hidden)); /* Set the function pointers */ this->OpenAudio = Mac_OpenAudio; this->CloseAudio = Mac_CloseAudio; this->LockAudio = Mac_LockAudio; this->UnlockAudio = Mac_UnlockAudio; this->free = Audio_DeleteDevice; #ifdef __MACOSX__ /* Mac OS X uses threaded audio, so normal thread code is okay */ this->LockAudio = NULL; this->UnlockAudio = NULL; #endif return this; } AudioBootStrap SNDMGR_bootstrap = { "sndmgr", "MacOS SoundManager 3.0", Audio_Available, Audio_CreateDevice }; #if defined(TARGET_API_MAC_CARBON) || defined(USE_RYANS_SOUNDCODE) /* This works correctly on Mac OS X */ #pragma options align=power static volatile SInt32 audio_is_locked = 0; static volatile SInt32 need_to_mix = 0; static UInt8 *buffer[2]; static volatile UInt32 running = 0; static CmpSoundHeader header; static volatile Uint32 fill_me = 0; static void mix_buffer(SDL_AudioDevice * audio, UInt8 * buffer) { if (!audio->paused) { #ifdef __MACOSX__ SDL_mutexP(audio->mixer_lock); #endif if (audio->convert.needed) { audio->spec.callback(audio->spec.userdata, (Uint8 *) audio->convert.buf, audio->convert.len); SDL_ConvertAudio(&audio->convert); if (audio->convert.len_cvt != audio->spec.size) { /* Uh oh... probably crashes here */ ; } SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt); } else { audio->spec.callback(audio->spec.userdata, buffer, audio->spec.size); } #ifdef __MACOSX__ SDL_mutexV(audio->mixer_lock); #endif } DecrementAtomic((SInt32 *) & need_to_mix); } static void Mac_LockAudio(_THIS) { IncrementAtomic((SInt32 *) & audio_is_locked); } static void Mac_UnlockAudio(_THIS) { SInt32 oldval; oldval = DecrementAtomic((SInt32 *) & audio_is_locked); if (oldval != 1) /* != 1 means audio is still locked. */ return; /* Did we miss the chance to mix in an interrupt? Do it now. */ if (BitAndAtomic(0xFFFFFFFF, (UInt32 *) & need_to_mix)) { /* * Note that this could be a problem if you missed an interrupt * while the audio was locked, and get preempted by a second * interrupt here, but that means you locked for way too long anyhow. */ mix_buffer(this, buffer[fill_me]); } } static void callBackProc(SndChannel * chan, SndCommand * cmd_passed) { UInt32 play_me; SndCommand cmd; SDL_AudioDevice *audio = (SDL_AudioDevice *) chan->userInfo; IncrementAtomic((SInt32 *) & need_to_mix); fill_me = cmd_passed->param2; /* buffer that has just finished playing, so fill it */ play_me = !fill_me; /* filled buffer to play _now_ */ if (!audio->enabled) { return; } /* queue previously mixed buffer for playback. */ header.samplePtr = (Ptr) buffer[play_me]; cmd.cmd = bufferCmd; cmd.param1 = 0; cmd.param2 = (long) &header; SndDoCommand(chan, &cmd, 0); memset(buffer[fill_me], 0, audio->spec.size); /* * if audio device isn't locked, mix the next buffer to be queued in * the memory block that just finished playing. */ if (!BitAndAtomic(0xFFFFFFFF, (UInt32 *) & audio_is_locked)) { mix_buffer(audio, buffer[fill_me]); } /* set this callback to run again when current buffer drains. */ if (running) { cmd.cmd = callBackCmd; cmd.param1 = 0; cmd.param2 = play_me; SndDoCommand(chan, &cmd, 0); } } static int Mac_OpenAudio(_THIS, SDL_AudioSpec * spec) { SndCallBackUPP callback; int sample_bits; int i; long initOptions; /* Very few conversions are required, but... */ switch (spec->format) { case AUDIO_S8: spec->format = AUDIO_U8; break; case AUDIO_U16LSB: spec->format = AUDIO_S16LSB; break; case AUDIO_U16MSB: spec->format = AUDIO_S16MSB; break; } SDL_CalculateAudioSpec(spec); /* initialize bufferCmd header */ memset(&header, 0, sizeof(header)); callback = (SndCallBackUPP) NewSndCallBackUPP(callBackProc); sample_bits = spec->size / spec->samples / spec->channels * 8; #ifdef DEBUG_AUDIO fprintf(stderr, "Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n", spec->format, spec->channels, sample_bits, spec->freq); #endif /* DEBUG_AUDIO */ header.numChannels = spec->channels; header.sampleSize = sample_bits; header.sampleRate = spec->freq << 16; header.numFrames = spec->samples; header.encode = cmpSH; /* Note that we install the 16bitLittleEndian Converter if needed. */ if (spec->format == 0x8010) { header.compressionID = fixedCompression; header.format = k16BitLittleEndianFormat; } /* allocate 2 buffers */ for (i = 0; i < 2; i++) { buffer[i] = (UInt8 *) malloc(sizeof(UInt8) * spec->size); if (buffer[i] == NULL) { SDL_OutOfMemory(); return (-1); } memset(buffer[i], 0, spec->size); } /* Create the sound manager channel */ channel = (SndChannelPtr) SDL_malloc(sizeof(*channel)); if (channel == NULL) { SDL_OutOfMemory(); return (-1); } if (spec->channels >= 2) { initOptions = initStereo; } else { initOptions = initMono; } channel->userInfo = (long) this; channel->qLength = 128; if (SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr) { SDL_SetError("Unable to create audio channel"); SDL_free(channel); channel = NULL; return (-1); } /* start playback */ { SndCommand cmd; cmd.cmd = callBackCmd; cmd.param2 = 0; running = 1; SndDoCommand(channel, &cmd, 0); } return 1; } static void Mac_CloseAudio(_THIS) { int i; running = 0; if (channel) { SndDisposeChannel(channel, true); channel = NULL; } for (i = 0; i < 2; ++i) { if (buffer[i]) { SDL_free(buffer[i]); buffer[i] = NULL; } } } #else /* !TARGET_API_MAC_CARBON && !USE_RYANS_SOUNDCODE */ static void Mac_LockAudio(_THIS) { /* no-op. */ } static void Mac_UnlockAudio(_THIS) { /* no-op. */ } /* This function is called by Sound Manager when it has exhausted one of the buffers, so we'll zero it to silence and fill it with audio if we're not paused. */ static pascal void sndDoubleBackProc(SndChannelPtr chan, SndDoubleBufferPtr newbuf) { SDL_AudioDevice *audio = (SDL_AudioDevice *) newbuf->dbUserInfo[0]; /* If audio is quitting, don't do anything */ if (!audio->enabled) { return; } memset(newbuf->dbSoundData, 0, audio->spec.size); newbuf->dbNumFrames = audio->spec.samples; if (!audio->paused) { if (audio->convert.needed) { audio->spec.callback(audio->spec.userdata, (Uint8 *) audio->convert.buf, audio->convert.len); SDL_ConvertAudio(&audio->convert); #if 0 if (audio->convert.len_cvt != audio->spec.size) { /* Uh oh... probably crashes here */ ; } #endif SDL_memcpy(newbuf->dbSoundData, audio->convert.buf, audio->convert.len_cvt); } else { audio->spec.callback(audio->spec.userdata, (Uint8 *) newbuf->dbSoundData, audio->spec.size); } } newbuf->dbFlags |= dbBufferReady; } static int DoubleBufferAudio_Available(void) { int available; NumVersion sndversion; long response; available = 0; sndversion = SndSoundManagerVersion(); if (sndversion.majorRev >= 3) { if (Gestalt(gestaltSoundAttr, &response) == noErr) { if ((response & (1 << gestaltSndPlayDoubleBuffer))) { available = 1; } } } else { if (Gestalt(gestaltSoundAttr, &response) == noErr) { if ((response & (1 << gestaltHasASC))) { available = 1; } } } return (available); } static void Mac_CloseAudio(_THIS) { int i; if (channel != NULL) { /* Clean up the audio channel */ SndDisposeChannel(channel, true); channel = NULL; } for (i = 0; i < 2; ++i) { if (audio_buf[i]) { SDL_free(audio_buf[i]); audio_buf[i] = NULL; } } } static int Mac_OpenAudio(_THIS, SDL_AudioSpec * spec) { SndDoubleBufferHeader2 audio_dbh; int i; long initOptions; int sample_bits; SndDoubleBackUPP doubleBackProc; /* Check to make sure double-buffered audio is available */ if (!DoubleBufferAudio_Available()) { SDL_SetError("Sound manager doesn't support double-buffering"); return (-1); } /* Very few conversions are required, but... */ switch (spec->format) { case AUDIO_S8: spec->format = AUDIO_U8; break; case AUDIO_U16LSB: spec->format = AUDIO_S16LSB; break; case AUDIO_U16MSB: spec->format = AUDIO_S16MSB; break; } SDL_CalculateAudioSpec(spec); /* initialize the double-back header */ SDL_memset(&audio_dbh, 0, sizeof(audio_dbh)); doubleBackProc = NewSndDoubleBackProc(sndDoubleBackProc); sample_bits = spec->size / spec->samples / spec->channels * 8; audio_dbh.dbhNumChannels = spec->channels; audio_dbh.dbhSampleSize = sample_bits; audio_dbh.dbhCompressionID = 0; audio_dbh.dbhPacketSize = 0; audio_dbh.dbhSampleRate = spec->freq << 16; audio_dbh.dbhDoubleBack = doubleBackProc; audio_dbh.dbhFormat = 0; /* Note that we install the 16bitLittleEndian Converter if needed. */ if (spec->format == 0x8010) { audio_dbh.dbhCompressionID = fixedCompression; audio_dbh.dbhFormat = k16BitLittleEndianFormat; } /* allocate the 2 double-back buffers */ for (i = 0; i < 2; ++i) { audio_buf[i] = SDL_calloc(1, sizeof(SndDoubleBuffer) + spec->size); if (audio_buf[i] == NULL) { SDL_OutOfMemory(); return (-1); } audio_buf[i]->dbNumFrames = spec->samples; audio_buf[i]->dbFlags = dbBufferReady; audio_buf[i]->dbUserInfo[0] = (long) this; audio_dbh.dbhBufferPtr[i] = audio_buf[i]; } /* Create the sound manager channel */ channel = (SndChannelPtr) SDL_malloc(sizeof(*channel)); if (channel == NULL) { SDL_OutOfMemory(); return (-1); } if (spec->channels >= 2) { initOptions = initStereo; } else { initOptions = initMono; } channel->userInfo = 0; channel->qLength = 128; if (SndNewChannel(&channel, sampledSynth, initOptions, 0L) != noErr) { SDL_SetError("Unable to create audio channel"); SDL_free(channel); channel = NULL; return (-1); } /* Start playback */ if (SndPlayDoubleBuffer(channel, (SndDoubleBufferHeaderPtr) & audio_dbh) != noErr) { SDL_SetError("Unable to play double buffered audio"); return (-1); } return 1; } #endif /* TARGET_API_MAC_CARBON || USE_RYANS_SOUNDCODE */ /* vi: set ts=4 sw=4 expandtab: */