diff options
Diffstat (limited to 'distrib/sdl-1.2.15/src/audio/nto/SDL_nto_audio.c')
-rw-r--r-- | distrib/sdl-1.2.15/src/audio/nto/SDL_nto_audio.c | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/distrib/sdl-1.2.15/src/audio/nto/SDL_nto_audio.c b/distrib/sdl-1.2.15/src/audio/nto/SDL_nto_audio.c new file mode 100644 index 0000000..f951825 --- /dev/null +++ b/distrib/sdl-1.2.15/src/audio/nto/SDL_nto_audio.c @@ -0,0 +1,507 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sched.h> +#include <sys/select.h> +#include <sys/neutrino.h> +#include <sys/asoundlib.h> + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "SDL_nto_audio.h" + +/* The tag name used by NTO audio */ +#define DRIVER_NAME "qsa-nto" + +/* default channel communication parameters */ +#define DEFAULT_CPARAMS_RATE 22050 +#define DEFAULT_CPARAMS_VOICES 1 +/* FIXME: need to add in the near future flexible logic with frag_size and frags count */ +#define DEFAULT_CPARAMS_FRAG_SIZE 4096 +#define DEFAULT_CPARAMS_FRAGS_MIN 1 +#define DEFAULT_CPARAMS_FRAGS_MAX 1 + +/* Open the audio device for playback, and don't block if busy */ +#define OPEN_FLAGS SND_PCM_OPEN_PLAYBACK + +#define QSA_NO_WORKAROUNDS 0x00000000 +#define QSA_MMAP_WORKAROUND 0x00000001 + +struct BuggyCards +{ + char* cardname; + unsigned long bugtype; +}; + +#define QSA_WA_CARDS 3 + +struct BuggyCards buggycards[QSA_WA_CARDS]= +{ + {"Sound Blaster Live!", QSA_MMAP_WORKAROUND}, + {"Vortex 8820", QSA_MMAP_WORKAROUND}, + {"Vortex 8830", QSA_MMAP_WORKAROUND}, +}; + +/* Audio driver functions */ +static void NTO_ThreadInit(_THIS); +static int NTO_OpenAudio(_THIS, SDL_AudioSpec* spec); +static void NTO_WaitAudio(_THIS); +static void NTO_PlayAudio(_THIS); +static Uint8* NTO_GetAudioBuf(_THIS); +static void NTO_CloseAudio(_THIS); + +/* card names check to apply the workarounds */ +static int NTO_CheckBuggyCards(_THIS, unsigned long checkfor) +{ + char scardname[33]; + int it; + + if (snd_card_get_name(cardno, scardname, 32)<0) + { + return 0; + } + + for (it=0; it<QSA_WA_CARDS; it++) + { + if (SDL_strcmp(buggycards[it].cardname, scardname)==0) + { + if (buggycards[it].bugtype==checkfor) + { + return 1; + } + } + } + + return 0; +} + +static void NTO_ThreadInit(_THIS) +{ + int status; + struct sched_param param; + + /* increasing default 10 priority to 25 to avoid jerky sound */ + status=SchedGet(0, 0, ¶m); + param.sched_priority=param.sched_curpriority+15; + status=SchedSet(0, 0, SCHED_NOCHANGE, ¶m); +} + +/* PCM transfer channel parameters initialize function */ +static void NTO_InitAudioParams(snd_pcm_channel_params_t* cpars) +{ + SDL_memset(cpars, 0, sizeof(snd_pcm_channel_params_t)); + + cpars->channel = SND_PCM_CHANNEL_PLAYBACK; + cpars->mode = SND_PCM_MODE_BLOCK; + cpars->start_mode = SND_PCM_START_DATA; + cpars->stop_mode = SND_PCM_STOP_STOP; + cpars->format.format = SND_PCM_SFMT_S16_LE; + cpars->format.interleave = 1; + cpars->format.rate = DEFAULT_CPARAMS_RATE; + cpars->format.voices = DEFAULT_CPARAMS_VOICES; + cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE; + cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN; + cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX; +} + +static int NTO_AudioAvailable(void) +{ + /* See if we can open a nonblocking channel. + Return value '1' means we can. + Return value '0' means we cannot. */ + + int available; + int rval; + snd_pcm_t* handle; + + available = 0; + handle = NULL; + + rval = snd_pcm_open_preferred(&handle, NULL, NULL, OPEN_FLAGS); + + if (rval >= 0) + { + available = 1; + + if ((rval = snd_pcm_close(handle)) < 0) + { + SDL_SetError("NTO_AudioAvailable(): snd_pcm_close failed: %s\n", snd_strerror(rval)); + available = 0; + } + } + else + { + SDL_SetError("NTO_AudioAvailable(): there are no available audio devices.\n"); + } + + return (available); +} + +static void NTO_DeleteAudioDevice(SDL_AudioDevice *device) +{ + if ((device)&&(device->hidden)) + { + SDL_free(device->hidden); + } + if (device) + { + SDL_free(device); + } +} + +static SDL_AudioDevice* NTO_CreateAudioDevice(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(SDL_AudioDevice)); + this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(struct SDL_PrivateAudioData)); + } + if ((this == NULL) || (this->hidden == NULL)) + { + SDL_OutOfMemory(); + if (this) + { + SDL_free(this); + } + return (0); + } + SDL_memset(this->hidden, 0, sizeof(struct SDL_PrivateAudioData)); + audio_handle = NULL; + + /* Set the function pointers */ + this->ThreadInit = NTO_ThreadInit; + this->OpenAudio = NTO_OpenAudio; + this->WaitAudio = NTO_WaitAudio; + this->PlayAudio = NTO_PlayAudio; + this->GetAudioBuf = NTO_GetAudioBuf; + this->CloseAudio = NTO_CloseAudio; + + this->free = NTO_DeleteAudioDevice; + + return this; +} + +AudioBootStrap QNXNTOAUDIO_bootstrap = +{ + DRIVER_NAME, "QNX6 QSA-NTO Audio", + NTO_AudioAvailable, + NTO_CreateAudioDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void NTO_WaitAudio(_THIS) +{ + fd_set wfds; + int selectret; + + FD_ZERO(&wfds); + FD_SET(audio_fd, &wfds); + + do { + selectret=select(audio_fd + 1, NULL, &wfds, NULL, NULL); + switch (selectret) + { + case -1: + case 0: SDL_SetError("NTO_WaitAudio(): select() failed: %s\n", strerror(errno)); + return; + default: if (FD_ISSET(audio_fd, &wfds)) + { + return; + } + break; + } + } while(1); +} + +static void NTO_PlayAudio(_THIS) +{ + int written, rval; + int towrite; + void* pcmbuffer; + + if (!this->enabled) + { + return; + } + + towrite = this->spec.size; + pcmbuffer = pcm_buf; + + /* Write the audio data, checking for EAGAIN (buffer full) and underrun */ + do { + written = snd_pcm_plugin_write(audio_handle, pcm_buf, towrite); + if (written != towrite) + { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + { + /* Let a little CPU time go by and try to write again */ + SDL_Delay(1); + /* if we wrote some data */ + towrite -= written; + pcmbuffer += written * this->spec.channels; + continue; + } + else + { + if ((errno == EINVAL) || (errno == EIO)) + { + SDL_memset(&cstatus, 0, sizeof(cstatus)); + cstatus.channel = SND_PCM_CHANNEL_PLAYBACK; + if ((rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0) + { + SDL_SetError("NTO_PlayAudio(): snd_pcm_plugin_status failed: %s\n", snd_strerror(rval)); + return; + } + if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) + { + if ((rval = snd_pcm_plugin_prepare(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) + { + SDL_SetError("NTO_PlayAudio(): snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rval)); + return; + } + } + continue; + } + else + { + return; + } + } + } + else + { + /* we wrote all remaining data */ + towrite -= written; + pcmbuffer += written * this->spec.channels; + } + } while ((towrite > 0) && (this->enabled)); + + /* If we couldn't write, assume fatal error for now */ + if (towrite != 0) + { + this->enabled = 0; + } + + return; +} + +static Uint8* NTO_GetAudioBuf(_THIS) +{ + return pcm_buf; +} + +static void NTO_CloseAudio(_THIS) +{ + int rval; + + this->enabled = 0; + + if (audio_handle != NULL) + { + if ((rval = snd_pcm_plugin_flush(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) + { + SDL_SetError("NTO_CloseAudio(): snd_pcm_plugin_flush failed: %s\n", snd_strerror(rval)); + return; + } + if ((rval = snd_pcm_close(audio_handle)) < 0) + { + SDL_SetError("NTO_CloseAudio(): snd_pcm_close failed: %s\n",snd_strerror(rval)); + return; + } + audio_handle = NULL; + } +} + +static int NTO_OpenAudio(_THIS, SDL_AudioSpec* spec) +{ + int rval; + int format; + Uint16 test_format; + int found; + + audio_handle = NULL; + this->enabled = 0; + + if (pcm_buf != NULL) + { + SDL_FreeAudioMem(pcm_buf); + pcm_buf = NULL; + } + + /* initialize channel transfer parameters to default */ + NTO_InitAudioParams(&cparams); + + /* Open the audio device */ + rval = snd_pcm_open_preferred(&audio_handle, &cardno, &deviceno, OPEN_FLAGS); + if (rval < 0) + { + SDL_SetError("NTO_OpenAudio(): snd_pcm_open failed: %s\n", snd_strerror(rval)); + return (-1); + } + + if (!NTO_CheckBuggyCards(this, QSA_MMAP_WORKAROUND)) + { + /* enable count status parameter */ + if ((rval = snd_pcm_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP)) < 0) + { + SDL_SetError("snd_pcm_plugin_set_disable failed: %s\n", snd_strerror(rval)); + return (-1); + } + } + + /* Try for a closest match on audio format */ + format = 0; + /* can't use format as SND_PCM_SFMT_U8 = 0 in nto */ + found = 0; + + for (test_format=SDL_FirstAudioFormat(spec->format); !found ;) + { + /* if match found set format to equivalent ALSA format */ + switch (test_format) + { + case AUDIO_U8: + format = SND_PCM_SFMT_U8; + found = 1; + break; + case AUDIO_S8: + format = SND_PCM_SFMT_S8; + found = 1; + break; + case AUDIO_S16LSB: + format = SND_PCM_SFMT_S16_LE; + found = 1; + break; + case AUDIO_S16MSB: + format = SND_PCM_SFMT_S16_BE; + found = 1; + break; + case AUDIO_U16LSB: + format = SND_PCM_SFMT_U16_LE; + found = 1; + break; + case AUDIO_U16MSB: + format = SND_PCM_SFMT_U16_BE; + found = 1; + break; + default: + break; + } + + if (!found) + { + test_format = SDL_NextAudioFormat(); + } + } + + /* assumes test_format not 0 on success */ + if (test_format == 0) + { + SDL_SetError("NTO_OpenAudio(): Couldn't find any hardware audio formats"); + return (-1); + } + + spec->format = test_format; + + /* Set the audio format */ + cparams.format.format = format; + + /* Set mono or stereo audio (currently only two channels supported) */ + cparams.format.voices = spec->channels; + + /* Set rate */ + cparams.format.rate = spec->freq; + + /* Setup the transfer parameters according to cparams */ + rval = snd_pcm_plugin_params(audio_handle, &cparams); + if (rval < 0) + { + SDL_SetError("NTO_OpenAudio(): snd_pcm_channel_params failed: %s\n", snd_strerror(rval)); + return (-1); + } + + /* Make sure channel is setup right one last time */ + SDL_memset(&csetup, 0x00, sizeof(csetup)); + csetup.channel = SND_PCM_CHANNEL_PLAYBACK; + if (snd_pcm_plugin_setup(audio_handle, &csetup) < 0) + { + SDL_SetError("NTO_OpenAudio(): Unable to setup playback channel\n"); + return -1; + } + + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + + pcm_len = spec->size; + + if (pcm_len==0) + { + pcm_len = csetup.buf.block.frag_size * spec->channels * (snd_pcm_format_width(format)/8); + } + + /* Allocate memory to the audio buffer and initialize with silence (Note that + buffer size must be a multiple of fragment size, so find closest multiple) + */ + pcm_buf = (Uint8*)SDL_AllocAudioMem(pcm_len); + if (pcm_buf == NULL) + { + SDL_SetError("NTO_OpenAudio(): pcm buffer allocation failed\n"); + return (-1); + } + SDL_memset(pcm_buf, spec->silence, pcm_len); + + /* get the file descriptor */ + if ((audio_fd = snd_pcm_file_descriptor(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) + { + SDL_SetError("NTO_OpenAudio(): snd_pcm_file_descriptor failed with error code: %s\n", snd_strerror(rval)); + return (-1); + } + + /* Trigger audio playback */ + rval = snd_pcm_plugin_prepare(audio_handle, SND_PCM_CHANNEL_PLAYBACK); + if (rval < 0) + { + SDL_SetError("snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rval)); + return (-1); + } + + this->enabled = 1; + + /* Get the parent process id (we're the parent of the audio thread) */ + parent = getpid(); + + /* We're really ready to rock and roll. :-) */ + return (0); +} |