2001-12-28 19:56:33 +00:00
|
|
|
/*
|
|
|
|
Copyright (C) 1997-2001 Id Software, Inc.
|
|
|
|
|
|
|
|
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
*/
|
2001-12-22 04:27:19 +00:00
|
|
|
#include <dmedia/dmedia.h>
|
|
|
|
#include <dmedia/audio.h>
|
|
|
|
|
|
|
|
#include "../client/client.h"
|
|
|
|
#include "../client/snd_loc.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
SNDDM_Init
|
|
|
|
|
|
|
|
Try to find a sound device to mix for.
|
|
|
|
Returns false if nothing is found.
|
|
|
|
Returns true and fills in the "dma" structure with information for the mixer.
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
|
|
|
|
// must be power of two!
|
|
|
|
#define QSND_SKID 2
|
|
|
|
#define QSND_BUFFER_FRAMES 8192
|
|
|
|
#define QSND_BUFFER_SIZE (QSND_BUFFER_FRAMES*2)
|
|
|
|
|
|
|
|
#define UST_TO_BUFFPOS(ust) ((int)((ust) & (QSND_BUFFER_FRAMES - 1)) << 1)
|
|
|
|
|
|
|
|
cvar_t *s_loadas8bit;
|
|
|
|
cvar_t *s_khz;
|
|
|
|
cvar_t *sndchannels;
|
|
|
|
|
|
|
|
short int dma_buffer[QSND_BUFFER_SIZE];
|
|
|
|
ALport sgisnd_aport = NULL;
|
|
|
|
long long sgisnd_startframe;
|
|
|
|
double sgisnd_frames_per_ns;
|
|
|
|
long long sgisnd_lastframewritten = 0;
|
|
|
|
|
|
|
|
qboolean SNDDMA_Init(void)
|
|
|
|
{
|
|
|
|
ALconfig ac = NULL;
|
|
|
|
ALpv pvbuf[2];
|
|
|
|
|
|
|
|
s_loadas8bit = Cvar_Get("s_loadas8bit", "16", CVAR_ARCHIVE);
|
|
|
|
if ((int)s_loadas8bit->value)
|
|
|
|
dma.samplebits = 8;
|
|
|
|
else
|
|
|
|
dma.samplebits = 16;
|
|
|
|
|
|
|
|
if (dma.samplebits != 16) {
|
|
|
|
Com_Printf("Don't currently support %i-bit data. Forcing 16-bit.\n",
|
|
|
|
dma.samplebits);
|
|
|
|
dma.samplebits = 16;
|
|
|
|
Cvar_SetValue( "s_loadas8bit", false );
|
|
|
|
}
|
|
|
|
|
|
|
|
s_khz = Cvar_Get("s_khz", "0", CVAR_ARCHIVE);
|
|
|
|
switch ((int)s_khz->value) {
|
|
|
|
case 48:
|
|
|
|
dma.speed = AL_RATE_48000;
|
|
|
|
break;
|
|
|
|
case 44:
|
|
|
|
dma.speed = AL_RATE_44100;
|
|
|
|
break;
|
|
|
|
case 32:
|
|
|
|
dma.speed = AL_RATE_32000;
|
|
|
|
break;
|
|
|
|
case 22:
|
|
|
|
dma.speed = AL_RATE_22050;
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
dma.speed = AL_RATE_16000;
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
dma.speed = AL_RATE_11025;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
dma.speed = AL_RATE_8000;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dma.speed = AL_RATE_22050;
|
|
|
|
Com_Printf("Don't currently support %i kHz sample rate. Using %i.\n",
|
|
|
|
(int)s_khz->value, (int)(dma.speed/1000));
|
|
|
|
}
|
|
|
|
|
|
|
|
sndchannels = Cvar_Get("sndchannels", "2", CVAR_ARCHIVE);
|
|
|
|
dma.channels = (int)sndchannels->value;
|
|
|
|
if (dma.channels != 2)
|
|
|
|
Com_Printf("Don't currently support %i sound channels. Try 2.\n",
|
|
|
|
sndchannels);
|
|
|
|
|
|
|
|
/***********************/
|
|
|
|
|
|
|
|
ac = alNewConfig();
|
|
|
|
alSetChannels( ac, AL_STEREO );
|
|
|
|
alSetSampFmt( ac, AL_SAMPFMT_TWOSCOMP );
|
|
|
|
alSetQueueSize( ac, QSND_BUFFER_FRAMES );
|
|
|
|
if (dma.samplebits == 8)
|
|
|
|
alSetWidth( ac, AL_SAMPLE_8 );
|
|
|
|
else
|
|
|
|
alSetWidth( ac, AL_SAMPLE_16 );
|
|
|
|
|
|
|
|
sgisnd_aport = alOpenPort( "Quake", "w", ac );
|
|
|
|
if (!sgisnd_aport)
|
|
|
|
{
|
|
|
|
printf( "failed to open audio port!\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
// set desired sample rate
|
|
|
|
pvbuf[0].param = AL_MASTER_CLOCK;
|
|
|
|
pvbuf[0].value.i = AL_CRYSTAL_MCLK_TYPE;
|
|
|
|
pvbuf[1].param = AL_RATE;
|
|
|
|
pvbuf[1].value.ll = alIntToFixed( dma.speed );
|
|
|
|
alSetParams( alGetResource( sgisnd_aport ), pvbuf, 2 );
|
|
|
|
if (pvbuf[1].sizeOut < 0)
|
|
|
|
printf( "illegal sample rate %d\n", dma.speed );
|
|
|
|
|
|
|
|
sgisnd_frames_per_ns = dma.speed * 1.0e-9;
|
|
|
|
|
|
|
|
dma.samples = sizeof(dma_buffer)/(dma.samplebits/8);
|
|
|
|
dma.submission_chunk = 1;
|
|
|
|
|
|
|
|
dma.buffer = (unsigned char *)dma_buffer;
|
|
|
|
|
|
|
|
dma.samplepos = 0;
|
|
|
|
|
|
|
|
alFreeConfig( ac );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
SNDDMA_GetDMAPos
|
|
|
|
|
|
|
|
return the current sample position (in mono samples, not stereo)
|
|
|
|
inside the recirculating dma buffer, so the mixing code will know
|
|
|
|
how many sample are required to fill it up.
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
int SNDDMA_GetDMAPos(void)
|
|
|
|
{
|
|
|
|
long long ustFuture, ustNow;
|
|
|
|
if (!sgisnd_aport) return( 0 );
|
|
|
|
alGetFrameTime( sgisnd_aport, &sgisnd_startframe, &ustFuture );
|
|
|
|
dmGetUST( (unsigned long long *)&ustNow );
|
|
|
|
sgisnd_startframe -= (long long)((ustFuture - ustNow) * sgisnd_frames_per_ns);
|
|
|
|
sgisnd_startframe += 100;
|
|
|
|
//printf( "frame %ld pos %d\n", frame, UST_TO_BUFFPOS( sgisnd_startframe ) );
|
|
|
|
return( UST_TO_BUFFPOS( sgisnd_startframe ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
SNDDMA_Shutdown
|
|
|
|
|
|
|
|
Reset the sound device for exiting
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
void SNDDMA_Shutdown(void)
|
|
|
|
{
|
|
|
|
if (sgisnd_aport) alClosePort( sgisnd_aport ), sgisnd_aport = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
SNDDMA_Submit
|
|
|
|
|
|
|
|
Send sound to device if buffer isn't really the dma buffer
|
|
|
|
===============
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern int soundtime;
|
|
|
|
|
|
|
|
void SNDDMA_Submit(void)
|
|
|
|
{
|
|
|
|
int nFillable, nFilled, nPos;
|
|
|
|
int nFrames, nFramesLeft;
|
|
|
|
unsigned endtime;
|
|
|
|
|
|
|
|
if (!sgisnd_aport) return;
|
|
|
|
|
|
|
|
nFillable = alGetFillable( sgisnd_aport );
|
|
|
|
nFilled = QSND_BUFFER_FRAMES - nFillable;
|
|
|
|
|
|
|
|
nFrames = dma.samples >> (dma.channels - 1);
|
|
|
|
|
|
|
|
if (paintedtime - soundtime < nFrames)
|
|
|
|
nFrames = paintedtime - soundtime;
|
|
|
|
|
|
|
|
if (nFrames <= QSND_SKID) return;
|
|
|
|
|
|
|
|
nPos = UST_TO_BUFFPOS( sgisnd_startframe );
|
|
|
|
|
|
|
|
// dump re-written contents of the buffer
|
|
|
|
if (sgisnd_lastframewritten > sgisnd_startframe)
|
|
|
|
{
|
|
|
|
alDiscardFrames( sgisnd_aport, sgisnd_lastframewritten - sgisnd_startframe );
|
|
|
|
}
|
|
|
|
else if ((int)(sgisnd_startframe - sgisnd_lastframewritten) >= QSND_BUFFER_FRAMES)
|
|
|
|
{
|
|
|
|
// blow away everything if we've underflowed
|
|
|
|
alDiscardFrames( sgisnd_aport, QSND_BUFFER_FRAMES );
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't block
|
|
|
|
if (nFrames > nFillable) nFrames = nFillable;
|
|
|
|
|
|
|
|
// account for stereo
|
|
|
|
nFramesLeft = nFrames;
|
|
|
|
if (nPos + nFrames * dma.channels > QSND_BUFFER_SIZE)
|
|
|
|
{
|
|
|
|
int nFramesAtEnd = (QSND_BUFFER_SIZE - nPos) >> (dma.channels - 1);
|
|
|
|
|
|
|
|
alWriteFrames( sgisnd_aport, &dma_buffer[nPos], nFramesAtEnd );
|
|
|
|
nPos = 0;
|
|
|
|
nFramesLeft -= nFramesAtEnd;
|
|
|
|
}
|
|
|
|
alWriteFrames( sgisnd_aport, &dma_buffer[nPos], nFramesLeft );
|
|
|
|
|
|
|
|
sgisnd_lastframewritten = sgisnd_startframe + nFrames;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SNDDMA_BeginPainting (void)
|
|
|
|
{
|
|
|
|
}
|