Mercurial > sdl-ios-xcode
diff src/audio/dma/SDL_dmaaudio.c @ 3795:589bc3d060cd SDL-ryan-multiple-audio-device
More 1.3 audio work...moved dsp and dma drivers over to new model. Untested!
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Wed, 04 Oct 2006 06:00:10 +0000 |
parents | 3b4ce57c6215 |
children | c8b3d3d13ed1 |
line wrap: on
line diff
--- a/src/audio/dma/SDL_dmaaudio.c Tue Oct 03 23:45:36 2006 +0000 +++ b/src/audio/dma/SDL_dmaaudio.c Wed Oct 04 06:00:10 2006 +0000 @@ -21,6 +21,8 @@ */ #include "SDL_config.h" +/* !!! FIXME: merge this driver with "dsp". */ + /* Allow access to a raw mixing buffer */ #include <stdio.h> @@ -60,17 +62,23 @@ #define OPEN_FLAGS (O_RDWR|O_NONBLOCK) /* Audio driver functions */ -static int DMA_OpenAudio(_THIS, SDL_AudioSpec * spec); -static void DMA_WaitAudio(_THIS); -static void DMA_PlayAudio(_THIS); -static Uint8 *DMA_GetAudioBuf(_THIS); -static void DMA_CloseAudio(_THIS); +static int DMA_DetectDevices(int iscapture); +static const char *DMA_GetDeviceName(int index, int iscapture); +static int DMA_OpenDevice(_THIS, const char *devname, int iscapture); +static void DMA_WaitDevice(_THIS); +static void DMA_PlayDevice(_THIS); +static Uint8 *DMA_GetDeviceBuf(_THIS); +static void DMA_CloseDevice(_THIS); /* Audio driver bootstrap functions */ static int -Audio_Available(void) +DMA_Available(void) { + /* + * !!! FIXME: maybe change this to always available, and move this to + * !!! FIXME: to device enumeration and opening? + */ int available; int fd; @@ -91,55 +99,276 @@ return (available); } -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)); - audio_fd = -1; +static int +DMA_Init(SDL_AudioDriverImpl *impl) +{ + /* Set the function pointers */ + impl->DetectDevices = DMA_DetectDevices; + impl->GetDeviceName = DMA_GetDeviceName; + impl->OpenDevice = DMA_OpenDevice; + impl->WaitDevice = DMA_WaitDevice; + impl->PlayDevice = DMA_PlayDevice; + impl->GetDeviceBuf = DMA_GetDeviceBuf; + impl->CloseDevice = DMA_CloseDevice; - /* Set the function pointers */ - this->OpenAudio = DMA_OpenAudio; - this->WaitAudio = DMA_WaitAudio; - this->PlayAudio = DMA_PlayAudio; - this->GetAudioBuf = DMA_GetAudioBuf; - this->CloseAudio = DMA_CloseAudio; - - this->free = Audio_DeleteDevice; - - return this; + return 1; } AudioBootStrap DMA_bootstrap = { DMA_DRIVER_NAME, "OSS /dev/dsp DMA audio", - Audio_Available, Audio_CreateDevice + DMA_Available, DMA_Init }; + +static int +DMA_DetectDevices(int iscapture) +{ + return -1; /* !!! FIXME */ +} + + +static const char * +DMA_GetDeviceName(int index, int iscapture) +{ + SDL_SetError("No such device"); /* !!! FIXME */ + return NULL; +} + + +static int +DMA_ReopenAudio(_THIS, const char *audiodev, int format, int stereo) +{ + int frag_spec; + int value; + + /* Close and then reopen the audio device */ + close(audio_fd); + audio_fd = open(audiodev, O_RDWR, 0); + if (audio_fd < 0) { + SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); + return (-1); + } + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(&this->spec); + + /* Determine the power of two of the fragment size */ + for (frag_spec = 0; (0x01 << frag_spec) < this->spec.size; ++frag_spec); + if ((0x01 << frag_spec) != this->spec.size) { + SDL_SetError("Fragment size must be a power of two"); + return (-1); + } + + /* Set the audio buffering parameters */ + if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) { + SDL_SetError("Couldn't set audio fragment spec"); + return (-1); + } + + /* Set the audio format */ + value = format; + if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) { + SDL_SetError("Couldn't set audio format"); + return (-1); + } + + /* Set mono or stereo audio */ + value = (this->spec.channels > 1); + if ((ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) || + (value != stereo)) { + SDL_SetError("Couldn't set audio channels"); + return (-1); + } + + /* Set the DSP frequency */ + value = this->spec.freq; + if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0) { + SDL_SetError("Couldn't set audio frequency"); + return (-1); + } + this->spec.freq = value; + + /* We successfully re-opened the audio */ + return (0); +} + + + +static int +open_device_internal(_THIS, const char *devname, int iscapture) +{ + char audiodev[1024]; + int format; + int stereo; + int value; + SDL_AudioFormat test_format; + struct audio_buf_info info; + + /* Initialize all variables that we clean on shutdown */ + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + if (this->hidden == NULL) { + SDL_OutOfMemory(); + return 0; + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* !!! FIXME: handle devname */ + /* !!! FIXME: handle iscapture */ + + /* Open the audio device */ + audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); + if (audio_fd < 0) { + SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); + return 0; + } + dma_buf = NULL; + ioctl(audio_fd, SNDCTL_DSP_RESET, 0); + + /* Get a list of supported hardware formats */ + if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) { + SDL_SetError("Couldn't get audio format list"); + return 0; + } + + /* Try for a closest match on audio format */ + format = 0; + for (test_format = SDL_FirstAudioFormat(this->spec.format); + !format && test_format;) { +#ifdef DEBUG_AUDIO + fprintf(stderr, "Trying format 0x%4.4x\n", test_format); +#endif + switch (test_format) { + case AUDIO_U8: + if (value & AFMT_U8) { + format = AFMT_U8; + } + break; + case AUDIO_S8: + if (value & AFMT_S8) { + format = AFMT_S8; + } + break; + case AUDIO_S16LSB: + if (value & AFMT_S16_LE) { + format = AFMT_S16_LE; + } + break; + case AUDIO_S16MSB: + if (value & AFMT_S16_BE) { + format = AFMT_S16_BE; + } + break; + case AUDIO_U16LSB: + if (value & AFMT_U16_LE) { + format = AFMT_U16_LE; + } + break; + case AUDIO_U16MSB: + if (value & AFMT_U16_BE) { + format = AFMT_U16_BE; + } + break; + default: + format = 0; + break; + } + if (!format) { + test_format = SDL_NextAudioFormat(); + } + } + if (format == 0) { + SDL_SetError("Couldn't find any hardware audio formats"); + return 0; + } + this->spec.format = test_format; + + /* Set the audio format */ + value = format; + if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) { + SDL_SetError("Couldn't set audio format"); + return 0; + } + + /* Set mono or stereo audio (currently only two channels supported) */ + stereo = (this->spec.channels > 1); + ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo); + if (stereo) { + this->spec.channels = 2; + } else { + this->spec.channels = 1; + } + + /* Because some drivers don't allow setting the buffer size + after setting the format, we must re-open the audio device + once we know what format and channels are supported + */ + if (DMA_ReopenAudio(this, audiodev, format, stereo) < 0) { + /* Error is set by DMA_ReopenAudio() */ + return 0; + } + + /* Memory map the audio buffer */ + if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { + SDL_SetError("Couldn't get OSPACE parameters"); + return 0; + } + this->spec.size = info.fragsize; + this->spec.samples = this->spec.size / ((this->spec.format & 0xFF) / 8); + this->spec.samples /= this->spec.channels; + num_buffers = info.fragstotal; + dma_len = num_buffers * this->spec.size; + dma_buf = (Uint8 *) mmap(NULL, dma_len, PROT_WRITE, MAP_SHARED, + audio_fd, 0); + if (dma_buf == MAP_FAILED) { + SDL_SetError("DMA memory map failed"); + dma_buf = NULL; + return 0; + } + SDL_memset(dma_buf, this->spec.silence, dma_len); + + /* Check to see if we need to use select() workaround */ + { + char *workaround; + workaround = SDL_getenv("SDL_DSP_NOSELECT"); + if (workaround) { + frame_ticks = (float) (this->spec.samples*1000) / this->spec.freq; + next_frame = SDL_GetTicks() + frame_ticks; + } + } + + /* Trigger audio playback */ + value = 0; + ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value); + value = PCM_ENABLE_OUTPUT; + if (ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value) < 0) { + SDL_SetError("Couldn't trigger audio output"); + return 0; + } + + /* Get the parent process id (we're the parent of the audio thread) */ + parent = getpid(); + + /* We're ready to rock and roll. :-) */ + return 1; +} + +static int +DMA_OpenDevice(_THIS, const char *devname, int iscapture) +{ + int retval = open_device_internal(this, devname, iscapture); + if (!retval) + DMA_CloseDevice(this); /* !!! FIXME: do this at higher level. */ + return retval; +} + + + + /* This function waits until it is possible to write a full sound buffer */ static void -DMA_WaitAudio(_THIS) +DMA_WaitDevice(_THIS) { fd_set fdset; @@ -189,8 +418,8 @@ fprintf(stderr, "SDL: %s\n", message); #ifdef AUDIO_OSPACE_HACK /* We may be able to use GET_OSPACE trick */ - frame_ticks = (float) (this->spec->samples * 1000) / - this->spec->freq; + frame_ticks = (float) (this->spec.samples * 1000) / + this->spec.freq; next_frame = SDL_GetTicks() + frame_ticks; #else this->enabled = 0; @@ -208,7 +437,7 @@ } static void -DMA_PlayAudio(_THIS) +DMA_PlayDevice(_THIS) { /* If timer synchronization is enabled, set the next write frame */ if (frame_ticks) { @@ -218,7 +447,7 @@ } static Uint8 * -DMA_GetAudioBuf(_THIS) +DMA_GetDeviceBuf(_THIS) { count_info info; int playing; @@ -244,224 +473,20 @@ } static void -DMA_CloseAudio(_THIS) +DMA_CloseDevice(_THIS) { - if (dma_buf != NULL) { - munmap(dma_buf, dma_len); - dma_buf = NULL; - } - if (audio_fd >= 0) { - close(audio_fd); - audio_fd = -1; + if (this->hidden != NULL) { + if (dma_buf != NULL) { + munmap(dma_buf, dma_len); + dma_buf = NULL; + } + if (audio_fd >= 0) { + close(audio_fd); + audio_fd = -1; + } + SDL_free(this->hidden); + this->hidden = NULL; } } -static int -DMA_ReopenAudio(_THIS, const char *audiodev, int format, int stereo, - SDL_AudioSpec * spec) -{ - int frag_spec; - int value; - - /* Close and then reopen the audio device */ - close(audio_fd); - audio_fd = open(audiodev, O_RDWR, 0); - if (audio_fd < 0) { - SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); - return (-1); - } - - /* Calculate the final parameters for this audio specification */ - SDL_CalculateAudioSpec(spec); - - /* Determine the power of two of the fragment size */ - for (frag_spec = 0; (0x01 << frag_spec) < spec->size; ++frag_spec); - if ((0x01 << frag_spec) != spec->size) { - SDL_SetError("Fragment size must be a power of two"); - return (-1); - } - - /* Set the audio buffering parameters */ - if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) { - SDL_SetError("Couldn't set audio fragment spec"); - return (-1); - } - - /* Set the audio format */ - value = format; - if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) { - SDL_SetError("Couldn't set audio format"); - return (-1); - } - - /* Set mono or stereo audio */ - value = (spec->channels > 1); - if ((ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) || - (value != stereo)) { - SDL_SetError("Couldn't set audio channels"); - return (-1); - } - - /* Set the DSP frequency */ - value = spec->freq; - if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0) { - SDL_SetError("Couldn't set audio frequency"); - return (-1); - } - spec->freq = value; - - /* We successfully re-opened the audio */ - return (0); -} - -static int -DMA_OpenAudio(_THIS, SDL_AudioSpec * spec) -{ - char audiodev[1024]; - int format; - int stereo; - int value; - SDL_AudioFormat test_format; - struct audio_buf_info info; - - /* Reset the timer synchronization flag */ - frame_ticks = 0.0; - - /* Open the audio device */ - audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); - if (audio_fd < 0) { - SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); - return (-1); - } - dma_buf = NULL; - ioctl(audio_fd, SNDCTL_DSP_RESET, 0); - - /* Get a list of supported hardware formats */ - if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) { - SDL_SetError("Couldn't get audio format list"); - return (-1); - } - - /* Try for a closest match on audio format */ - format = 0; - for (test_format = SDL_FirstAudioFormat(spec->format); - !format && test_format;) { -#ifdef DEBUG_AUDIO - fprintf(stderr, "Trying format 0x%4.4x\n", test_format); -#endif - switch (test_format) { - case AUDIO_U8: - if (value & AFMT_U8) { - format = AFMT_U8; - } - break; - case AUDIO_S8: - if (value & AFMT_S8) { - format = AFMT_S8; - } - break; - case AUDIO_S16LSB: - if (value & AFMT_S16_LE) { - format = AFMT_S16_LE; - } - break; - case AUDIO_S16MSB: - if (value & AFMT_S16_BE) { - format = AFMT_S16_BE; - } - break; - case AUDIO_U16LSB: - if (value & AFMT_U16_LE) { - format = AFMT_U16_LE; - } - break; - case AUDIO_U16MSB: - if (value & AFMT_U16_BE) { - format = AFMT_U16_BE; - } - break; - default: - format = 0; - break; - } - if (!format) { - test_format = SDL_NextAudioFormat(); - } - } - if (format == 0) { - SDL_SetError("Couldn't find any hardware audio formats"); - return (-1); - } - spec->format = test_format; - - /* Set the audio format */ - value = format; - if ((ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) { - SDL_SetError("Couldn't set audio format"); - return (-1); - } - - /* Set mono or stereo audio (currently only two channels supported) */ - stereo = (spec->channels > 1); - ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo); - if (stereo) { - spec->channels = 2; - } else { - spec->channels = 1; - } - - /* Because some drivers don't allow setting the buffer size - after setting the format, we must re-open the audio device - once we know what format and channels are supported - */ - if (DMA_ReopenAudio(this, audiodev, format, stereo, spec) < 0) { - /* Error is set by DMA_ReopenAudio() */ - return (-1); - } - - /* Memory map the audio buffer */ - if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { - SDL_SetError("Couldn't get OSPACE parameters"); - return (-1); - } - spec->size = info.fragsize; - spec->samples = spec->size / ((spec->format & 0xFF) / 8); - spec->samples /= spec->channels; - num_buffers = info.fragstotal; - dma_len = num_buffers * spec->size; - dma_buf = (Uint8 *) mmap(NULL, dma_len, PROT_WRITE, MAP_SHARED, - audio_fd, 0); - if (dma_buf == MAP_FAILED) { - SDL_SetError("DMA memory map failed"); - dma_buf = NULL; - return (-1); - } - SDL_memset(dma_buf, spec->silence, dma_len); - - /* Check to see if we need to use select() workaround */ - { - char *workaround; - workaround = SDL_getenv("SDL_DSP_NOSELECT"); - if (workaround) { - frame_ticks = (float) (spec->samples * 1000) / spec->freq; - next_frame = SDL_GetTicks() + frame_ticks; - } - } - - /* Trigger audio playback */ - value = 0; - ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value); - value = PCM_ENABLE_OUTPUT; - if (ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value) < 0) { - SDL_SetError("Couldn't trigger audio output"); - return (-1); - } - - /* Get the parent process id (we're the parent of the audio thread) */ - parent = getpid(); - - /* We're ready to rock and roll. :-) */ - return (0); -} - /* vi: set ts=4 sw=4 expandtab: */