/*
===========================================================================
Return to Castle Wolfenstein single player GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
This file is part of the Return to Castle Wolfenstein single player GPL Source Code (RTCW SP Source Code).
RTCW SP Source Code 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 3 of the License, or
(at your option) any later version.
RTCW SP Source Code 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 RTCW SP Source Code. If not, see .
In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __linux__ // rb0101023 - guard this
#include
#endif
#ifdef __FreeBSD__ // rb0101023 - added
#include
#endif
#include
#include "../game/q_shared.h"
#include "../client/snd_local.h"
int audio_fd;
int snd_inited = 0;
cvar_t *sndbits;
cvar_t *sndspeed;
cvar_t *sndchannels;
cvar_t *snddevice;
/* Some devices may work only with 48000 */
static int tryrates[] = { 22050, 11025, 44100, 48000, 8000 };
static qboolean use_custom_memset = qfalse;
// show_bug.cgi?id=371
void Snd_Memset( void* dest, const int val, const size_t count ) {
int *pDest;
int i, iterate;
if ( !use_custom_memset ) {
Com_Memset( dest,val,count );
return;
}
iterate = count / sizeof( int );
pDest = (int*)dest;
for ( i = 0; i < iterate; i++ )
{
pDest[i] = val;
}
}
qboolean SNDDMA_Init( void ) {
int rc;
int fmt;
int tmp;
int i;
// char *s; // bk001204 - unused
struct audio_buf_info info;
int caps;
extern uid_t saved_euid;
if ( snd_inited ) {
return 1;
}
if ( !snddevice ) {
sndbits = Cvar_Get( "sndbits", "16", CVAR_ARCHIVE );
sndspeed = Cvar_Get( "sndspeed", "0", CVAR_ARCHIVE );
sndchannels = Cvar_Get( "sndchannels", "2", CVAR_ARCHIVE );
snddevice = Cvar_Get( "snddevice", "/dev/dsp", CVAR_ARCHIVE );
}
// open /dev/dsp, confirm capability to mmap, and get size of dma buffer
if ( !audio_fd ) {
seteuid( saved_euid );
audio_fd = open( snddevice->string, O_RDWR );
seteuid( getuid() );
if ( audio_fd < 0 ) {
perror( snddevice->string );
Com_Printf( "Could not open %s\n", snddevice->string );
return 0;
}
}
if ( ioctl( audio_fd, SNDCTL_DSP_GETCAPS, &caps ) == -1 ) {
perror( snddevice->string );
Com_Printf( "Sound driver too old\n" );
close( audio_fd );
return 0;
}
if ( !( caps & DSP_CAP_TRIGGER ) || !( caps & DSP_CAP_MMAP ) ) {
Com_Printf( "Sorry but your soundcard can't do this\n" );
close( audio_fd );
return 0;
}
/* SNDCTL_DSP_GETOSPACE moved to be called later */
// set sample bits & speed
dma.samplebits = (int)sndbits->value;
if ( dma.samplebits != 16 && dma.samplebits != 8 ) {
ioctl( audio_fd, SNDCTL_DSP_GETFMTS, &fmt );
if ( fmt & AFMT_S16_LE ) {
dma.samplebits = 16;
} else if ( fmt & AFMT_U8 ) {
dma.samplebits = 8;
}
}
dma.speed = (int)sndspeed->value;
if ( !dma.speed ) {
for ( i = 0 ; i < sizeof( tryrates ) / 4 ; i++ )
if ( !ioctl( audio_fd, SNDCTL_DSP_SPEED, &tryrates[i] ) ) {
break;
}
dma.speed = tryrates[i];
}
dma.channels = (int)sndchannels->value;
if ( dma.channels < 1 || dma.channels > 2 ) {
dma.channels = 2;
}
/* mmap() call moved forward */
tmp = 0;
if ( dma.channels == 2 ) {
tmp = 1;
}
rc = ioctl( audio_fd, SNDCTL_DSP_STEREO, &tmp );
if ( rc < 0 ) {
perror( snddevice->string );
Com_Printf( "Could not set %s to stereo=%d", snddevice->string, dma.channels );
close( audio_fd );
return 0;
}
if ( tmp ) {
dma.channels = 2;
} else {
dma.channels = 1;
}
rc = ioctl( audio_fd, SNDCTL_DSP_SPEED, &dma.speed );
if ( rc < 0 ) {
perror( snddevice->string );
Com_Printf( "Could not set %s speed to %d", snddevice->string, dma.speed );
close( audio_fd );
return 0;
}
if ( dma.samplebits == 16 ) {
rc = AFMT_S16_LE;
rc = ioctl( audio_fd, SNDCTL_DSP_SETFMT, &rc );
if ( rc < 0 ) {
perror( snddevice->string );
Com_Printf( "Could not support 16-bit data. Try 8-bit.\n" );
close( audio_fd );
return 0;
}
} else if ( dma.samplebits == 8 ) {
rc = AFMT_U8;
rc = ioctl( audio_fd, SNDCTL_DSP_SETFMT, &rc );
if ( rc < 0 ) {
perror( snddevice->string );
Com_Printf( "Could not support 8-bit data.\n" );
close( audio_fd );
return 0;
}
} else {
perror( snddevice->string );
Com_Printf( "%d-bit sound not supported.", dma.samplebits );
close( audio_fd );
return 0;
}
if ( ioctl( audio_fd, SNDCTL_DSP_GETOSPACE, &info ) == -1 ) {
perror( "GETOSPACE" );
Com_Printf( "Um, can't do GETOSPACE?\n" );
close( audio_fd );
return 0;
}
dma.samples = info.fragstotal * info.fragsize / ( dma.samplebits / 8 );
dma.submission_chunk = 1;
// memory map the dma buffer
// TTimo PROT_READ
// show_bug.cgi?id=371
// checking Alsa bug, doesn't allow dma alloc with PROT_READ?
// 1-17-2002
// use local C implementation of memset in all cases
// propagated from MP code to be safe with sound stuff once and for all
/*
if (!dma.buffer)
dma.buffer = (unsigned char *) mmap(NULL, info.fragstotal
* info.fragsize, PROT_WRITE|PROT_READ, MAP_FILE|MAP_SHARED, audio_fd, 0);
*/
dma.buffer = NULL;
if ( dma.buffer == NULL ) {
// Com_Printf("Could not mmap dma buffer PROT_WRITE|PROT_READ\n");
// Com_Printf("trying mmap PROT_WRITE (with associated better compatibility / less performance code)\n");
dma.buffer = (unsigned char *) mmap( NULL, info.fragstotal
* info.fragsize, PROT_WRITE, MAP_FILE | MAP_SHARED, audio_fd, 0 );
// LordHavoc MAP_FAILED is a bad value to have outside init code
if (dma.buffer == MAP_FAILED)
dma.buffer = NULL;
// NOTE TTimo could add a variable to force using regular memset on systems that are known to be safe
use_custom_memset = qtrue;
}
if ( dma.buffer == NULL ) {
perror( snddevice->string );
Com_Printf( "Could not mmap %s\n", snddevice->string );
close( audio_fd );
return 0;
}
// toggle the trigger & start her up
tmp = 0;
rc = ioctl( audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp );
if ( rc < 0 ) {
perror( snddevice->string );
Com_Printf( "Could not toggle.\n" );
close( audio_fd );
return 0;
}
tmp = PCM_ENABLE_OUTPUT;
rc = ioctl( audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp );
if ( rc < 0 ) {
perror( snddevice->string );
Com_Printf( "Could not toggle.\n" );
close( audio_fd );
return 0;
}
snd_inited = 1;
return 1;
}
int SNDDMA_GetDMAPos( void ) {
struct count_info count;
if ( !snd_inited ) {
return 0;
}
if ( ioctl( audio_fd, SNDCTL_DSP_GETOPTR, &count ) == -1 ) {
perror( snddevice->string );
Com_Printf( "Uh, sound dead.\n" );
close( audio_fd );
snd_inited = 0;
return 0;
}
return count.ptr / ( dma.samplebits / 8 );
}
void SNDDMA_Shutdown( void ) {
}
/*
==============
SNDDMA_Submit
Send sound to device if buffer isn't really the dma buffer
===============
*/
void SNDDMA_Submit( void ) {
}
void SNDDMA_BeginPainting( void ) {
}