spingle/source/sdl/snd_sdl.c

219 lines
5.1 KiB
C

/*
* 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 <sezero@users.sourceforge.net>
* 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 <SDL.h>
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(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);
}