/* * snd_sdl.c - SDL audio driver for Hexen II: Hammer of Thyrion (uHexen2) * based on implementations found in the quakeforge and ioq3 projects. * * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 2005-2012 O.Sezer * Copyright (C) 2010-2014 QuakeSpasm developers * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q_defs.h" #include static int32_t buffersize; static void SDLCALL paint_audio(void *unused, Uint8 *stream, int32_t len) { int32_t pos, tobufend; int32_t len1, len2; (void)unused; if(!shm) { /* shouldn't happen, but just in case */ memset(stream, 0, len); return; } pos = (shm->samplepos * (shm->samplebits / 8)); if(pos >= buffersize) shm->samplepos = pos = 0; tobufend = buffersize - pos; /* bytes to buffer's end. */ len1 = len; len2 = 0; if(len1 > tobufend) { len1 = tobufend; len2 = len - len1; } memcpy(stream, shm->buffer + pos, len1); if(len2 <= 0) { shm->samplepos += (len1 / (shm->samplebits / 8)); } else { /* wraparound? */ memcpy(stream + len1, shm->buffer, len2); shm->samplepos = (len2 / (shm->samplebits / 8)); } if(shm->samplepos >= buffersize) shm->samplepos = 0; } bool SNDDMA_Init(dma_t *dma) { SDL_AudioSpec desired, obtained; int32_t tmp, val; char drivername[128]; if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { Con_Printf("Couldn't init SDL audio: %s\n", SDL_GetError()); return false; } /* Set up the desired format */ desired.freq = tmp = snd_mixspeed.value; desired.format = (loadas8bit.value) ? AUDIO_U8 : AUDIO_S16SYS; desired.channels = 2; /* = desired_channels; */ if(desired.freq <= 11025) desired.samples = 256; else if(desired.freq <= 22050) desired.samples = 512; else if(desired.freq <= 44100) desired.samples = 1024; else if(desired.freq <= 56000) desired.samples = 2048; /* for 48 kHz */ else desired.samples = 4096; /* for 96 kHz */ desired.callback = paint_audio; desired.userdata = NULL; /* Open the audio device */ if(SDL_OpenAudio(&desired, &obtained) == -1) { Con_Printf("Couldn't open SDL audio: %s\n", SDL_GetError()); SDL_QuitSubSystem(SDL_INIT_AUDIO); return false; } /* Make sure we can support the audio format */ switch(obtained.format) { case AUDIO_S8: /* maybe needed by AHI */ case AUDIO_U8: case AUDIO_S16SYS: /* Supported */ break; default: Con_Printf("Unsupported audio format received (%" PRIu32 ")\n", obtained.format); SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); return false; } memset((void *) dma, 0, sizeof(dma_t)); shm = dma; /* Fill the audio DMA information block */ shm->samplebits = (obtained.format & 0xFF); /* first byte of format is bits */ shm->signed8 = (obtained.format == AUDIO_S8); if(obtained.freq != tmp) Con_Printf("Warning: Rate set (%" PRIi32 ") didn't match requested rate (%" PRIi32 ")!\n", obtained.freq, tmp); shm->speed = obtained.freq; shm->channels = obtained.channels; tmp = (obtained.samples * obtained.channels) * 10; if(tmp & (tmp - 1)) { /* make it a power of two */ val = 1; while(val < tmp) val <<= 1; tmp = val; } shm->samples = tmp; shm->samplepos = 0; shm->submission_chunk = 1; Con_Printf("SDL Audio Spec : %" PRIi32 " Hz, %" PRIi32 " samples, %" PRIi32 " channels\n", obtained.freq, obtained.samples, obtained.channels); { const char *driver = SDL_GetCurrentAudioDriver(); const char *device = SDL_GetAudioDeviceName(0, SDL_FALSE); q_snprintf(drivername, sizeof(drivername), "%s - %s", driver != NULL ? driver : "(UNKNOWN)", device != NULL ? device : "(UNKNOWN)"); } buffersize = shm->samples * (shm->samplebits / 8); Con_Printf("SDL Audio Driver: %s, %" PRIi32 " bytes buffer\n", drivername, buffersize); shm->buffer = (uint8_t *) calloc(1, buffersize); if(!shm->buffer) { SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); shm = NULL; Con_Printf("Failed allocating memory for SDL audio\n"); return false; } SDL_PauseAudio(0); return true; } int32_t SNDDMA_GetDMAPos(void) { return shm->samplepos; } void SNDDMA_Shutdown(void) { if(shm) { Con_Printf("Shutting down SDL sound\n"); SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); if(shm->buffer) free(shm->buffer); shm->buffer = NULL; shm = NULL; } } void SNDDMA_LockBuffer(void) { SDL_LockAudio(); } void SNDDMA_Submit(void) { SDL_UnlockAudio(); } void SNDDMA_BlockSound(void) { SDL_PauseAudio(1); } void SNDDMA_UnblockSound(void) { SDL_PauseAudio(0); }