This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/snd_dx/s_mix.c

541 lines
13 KiB
C
Raw Normal View History

2010-04-21 22:00:00 +02:00
//=======================================================================
// Copyright XashXT Group 2009 <20>
// s_mix.c - portable code to mix sounds
//=======================================================================
2009-10-02 22:00:00 +02:00
#include "sound.h"
2010-07-21 22:00:00 +02:00
#include "byteorder.h"
2009-10-02 22:00:00 +02:00
2010-06-28 22:00:00 +02:00
#define PAINTBUFFER_SIZE 512
2009-10-11 22:00:00 +02:00
portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
int snd_scaletable[32][256];
int *snd_p, snd_linear_count, snd_vol;
short *snd_out;
2009-10-02 22:00:00 +02:00
void S_WriteLinearBlastStereo16( void )
{
int i, val;
for( i = 0; i < snd_linear_count; i += 2 )
{
val = snd_p[i]>>8;
if( val > 0x7fff ) snd_out[i] = 0x7fff;
else if( val < (short)0x8000 )
snd_out[i] = (short)0x8000;
else snd_out[i] = val;
val = snd_p[i+1]>>8;
if( val > 0x7fff ) snd_out[i+1] = 0x7fff;
else if( val < (short)0x8000 )
snd_out[i+1] = (short)0x8000;
else snd_out[i+1] = val;
}
}
2010-06-27 22:00:00 +02:00
void S_TransferStereo16( dword *pbuf, int endtime )
2009-10-02 22:00:00 +02:00
{
int lpos, lpaintedtime;
2010-06-27 22:00:00 +02:00
2009-10-11 22:00:00 +02:00
snd_p = (int *)paintbuffer;
2009-10-02 22:00:00 +02:00
lpaintedtime = paintedtime;
while( lpaintedtime < endtime )
{
// handle recirculating buffer issues
lpos = lpaintedtime & ((dma.samples >> 1) - 1);
snd_out = (short *) pbuf + (lpos << 1);
snd_linear_count = (dma.samples>>1) - lpos;
if( lpaintedtime + snd_linear_count > endtime )
snd_linear_count = endtime - lpaintedtime;
snd_linear_count <<= 1;
// write a linear blast of samples
S_WriteLinearBlastStereo16();
snd_p += snd_linear_count;
lpaintedtime += (snd_linear_count >> 1);
}
}
/*
===================
S_TransferPaintBuffer
===================
*/
void S_TransferPaintBuffer( int endtime )
{
int out_idx, count;
2010-06-27 22:00:00 +02:00
int step, val;
2009-10-02 22:00:00 +02:00
int *p, out_mask;
2010-06-27 22:00:00 +02:00
dword *pbuf;
2009-10-02 22:00:00 +02:00
pbuf = (dword *)dma.buffer;
2009-10-11 22:00:00 +02:00
if( dma.samplebits == 16 && dma.channels == 2 )
{
// optimized case
2010-06-27 22:00:00 +02:00
S_TransferStereo16( pbuf, endtime );
2009-10-11 22:00:00 +02:00
}
2010-06-27 22:00:00 +02:00
else
{
// general case
p = (int *)paintbuffer;
count = (endtime - paintedtime) * dma.channels;
out_mask = dma.samples - 1;
out_idx = paintedtime * dma.channels & out_mask;
step = 3 - dma.channels;
if( dma.samplebits == 16 )
2009-10-02 22:00:00 +02:00
{
2010-06-27 22:00:00 +02:00
short *out = (short *)pbuf;
2009-10-02 22:00:00 +02:00
2010-06-27 22:00:00 +02:00
while( count-- )
2009-10-11 22:00:00 +02:00
{
2010-06-27 22:00:00 +02:00
val = *p >> 8;
p += step;
if( val > 0x7fff ) val = 0x7fff;
else if( val < (short)0x8000 )
val = (short)0x8000;
out[out_idx] = val;
out_idx = (out_idx + 1) & out_mask;
2009-10-11 22:00:00 +02:00
}
2009-10-02 22:00:00 +02:00
}
2010-06-27 22:00:00 +02:00
else if( dma.samplebits == 8 )
2009-10-02 22:00:00 +02:00
{
2010-06-27 22:00:00 +02:00
byte *out = (byte *)pbuf;
2009-10-02 22:00:00 +02:00
2010-06-27 22:00:00 +02:00
while( count-- )
{
val = *p >> 8;
p += step;
if( val > 0x7fff ) val = 0x7fff;
else if( val < (short)0x8000 )
val = (short)0x8000;
out[out_idx] = (val>>8) + 128;
out_idx = (out_idx + 1) & out_mask;
}
2009-10-02 22:00:00 +02:00
}
}
}
/*
===============================================================================
CHANNEL MIXING
===============================================================================
*/
2010-09-12 22:00:00 +02:00
void S_PaintMonoFrom8( portable_samplepair_t *pbuf, int *volume, byte *pData, int outCount )
2009-10-02 22:00:00 +02:00
{
2010-09-12 22:00:00 +02:00
int i, data;
int *lscale, *rscale;
2009-10-02 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
lscale = snd_scaletable[volume[0] >> 3];
rscale = snd_scaletable[volume[1] >> 3];
2009-10-02 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
for( i = 0; i < outCount; i++ )
2009-10-02 22:00:00 +02:00
{
2010-09-12 22:00:00 +02:00
data = pData[i];
pbuf[i].left += lscale[data];
pbuf[i].right += rscale[data];
2009-10-02 22:00:00 +02:00
}
}
2010-09-12 22:00:00 +02:00
void S_PaintStereoFrom8( portable_samplepair_t *pbuf, int *volume, byte *pData, int outCount )
2010-07-21 22:00:00 +02:00
{
2010-09-12 22:00:00 +02:00
int *lscale, *rscale;
uint left, right;
word *data;
int i;
2010-07-21 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
lscale = snd_scaletable[volume[0] >> 3];
rscale = snd_scaletable[volume[1] >> 3];
data = (word *)pData;
2010-07-21 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
for( i = 0; i < outCount; i++, data++ )
2010-07-21 22:00:00 +02:00
{
left = (byte)((*data & 0x00FF));
right = (byte)((*data & 0xFF00) >> 8);
2010-09-12 22:00:00 +02:00
pbuf[i].left += lscale[left];
pbuf[i].right += rscale[right];
2010-07-21 22:00:00 +02:00
}
}
2010-09-12 22:00:00 +02:00
void S_PaintMonoFrom16( portable_samplepair_t *pbuf, int *volume, short *pData, int outCount )
2009-10-02 22:00:00 +02:00
{
2010-09-12 22:00:00 +02:00
int i, data;
int left, right;
int lscale, rscale;
2010-04-24 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
lscale = volume[0] * snd_vol;
rscale = volume[1] * snd_vol;
2009-10-02 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
for( i = 0; i < outCount; i++ )
{
data = pData[i];
left = ( data * lscale ) >> 8;
right = (data * rscale ) >> 8;
pbuf[i].left += left;
pbuf[i].right += right;
}
}
2010-06-27 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
void S_PaintStereoFrom16( portable_samplepair_t *pbuf, int *volume, short *pData, int outCount )
{
uint *data;
int lscale, rscale;
int left, right;
int i;
lscale = volume[0] * snd_vol;
rscale = volume[1] * snd_vol;
data = (uint *)pData;
for( i = 0; i < outCount; i++, data++ )
2009-10-02 22:00:00 +02:00
{
2010-09-12 22:00:00 +02:00
left = (signed short)((*data & 0x0000FFFF));
right = (signed short)((*data & 0xFFFF0000) >> 16);
left = (left * lscale ) >> 8;
right = (right * rscale) >> 8;
pbuf[i].left += left;
pbuf[i].right += right;
2009-10-02 22:00:00 +02:00
}
}
2010-09-12 22:00:00 +02:00
void S_Mix8Mono( portable_samplepair_t *pbuf, int *volume, byte *pData, int inputOffset, uint rateScaleFix, int outCount )
2010-07-21 22:00:00 +02:00
{
2010-09-12 22:00:00 +02:00
int i, sampleIndex = 0;
uint sampleFrac = inputOffset;
int *lscale, *rscale;
2010-07-21 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
// Not using pitch shift?
if( rateScaleFix == FIX( 1 ))
{
S_PaintMonoFrom8( pbuf, volume, pData, outCount );
return;
}
2010-07-21 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
lscale = snd_scaletable[volume[0] >> 3];
rscale = snd_scaletable[volume[1] >> 3];
2010-07-21 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
for( i = 0; i < outCount; i++ )
2010-07-21 22:00:00 +02:00
{
2010-09-12 22:00:00 +02:00
pbuf[i].left += lscale[pData[sampleIndex]];
pbuf[i].right += rscale[pData[sampleIndex]];
sampleFrac += rateScaleFix;
sampleIndex += FIX_INTPART( sampleFrac );
sampleFrac = FIX_FRACPART( sampleFrac );
}
}
void S_Mix8Stereo( portable_samplepair_t *pbuf, int *volume, byte *pData, int inputOffset, uint rateScaleFix, int outCount )
{
int i, sampleIndex = 0;
uint sampleFrac = inputOffset;
int *lscale, *rscale;
// Not using pitch shift?
if( rateScaleFix == FIX( 1 ))
{
S_PaintStereoFrom8( pbuf, volume, pData, outCount );
return;
}
lscale = snd_scaletable[volume[0] >> 3];
rscale = snd_scaletable[volume[1] >> 3];
for( i = 0; i < outCount; i++ )
{
pbuf[i].left += lscale[pData[sampleIndex+0]];
pbuf[i].right += rscale[pData[sampleIndex+1]];
sampleFrac += rateScaleFix;
sampleIndex += FIX_INTPART( sampleFrac )<<1;
sampleFrac = FIX_FRACPART( sampleFrac );
}
}
2010-07-21 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
void S_Mix16Mono( portable_samplepair_t *pbuf, int *volume, short *pData, int inputOffset, uint rateScaleFix, int outCount )
{
int i, sampleIndex = 0;
uint sampleFrac = inputOffset;
int lscale, rscale;
2010-07-21 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
// Not using pitch shift?
if( rateScaleFix == FIX( 1 ))
{
S_PaintMonoFrom16( pbuf, volume, pData, outCount );
return;
}
lscale = volume[0] * snd_vol;
rscale = volume[1] * snd_vol;
for( i = 0; i < outCount; i++ )
{
pbuf[i].left += (lscale * (int)( pData[sampleIndex] ))>>8;
pbuf[i].right += (rscale * (int)( pData[sampleIndex] ))>>8;
sampleFrac += rateScaleFix;
sampleIndex += FIX_INTPART( sampleFrac );
sampleFrac = FIX_FRACPART( sampleFrac );
2010-07-21 22:00:00 +02:00
}
2010-09-12 22:00:00 +02:00
}
void S_Mix16Stereo( portable_samplepair_t *pbuf, int *volume, short *pData, int inputOffset, uint rateScaleFix, int outCount )
{
int i, sampleIndex = 0;
uint sampleFrac = inputOffset;
int lscale, rscale;
// Not using pitch shift?
if( rateScaleFix == FIX( 1 ))
{
S_PaintStereoFrom16( pbuf, volume, pData, outCount );
return;
}
lscale = volume[0] * snd_vol;
rscale = volume[1] * snd_vol;
for( i = 0; i < outCount; i++ )
{
pbuf[i].left += (lscale * (int)( pData[sampleIndex+0] ))>>8;
pbuf[i].right += (rscale * (int)( pData[sampleIndex+1] ))>>8;
sampleFrac += rateScaleFix;
sampleIndex += FIX_INTPART(sampleFrac)<<1;
sampleFrac = FIX_FRACPART(sampleFrac);
}
}
int S_MixDataToDevice( channel_t *pChannel, int sampleCount, int outputRate, int outputOffset )
{
// save this to compute total output
int startingOffset = outputOffset;
float inputRate = ( pChannel->pitch * pChannel->sfx->cache->rate );
float rate = inputRate / outputRate;
int pvol[2];
// shouldn't be playing this if finished, but return if we are
if( pChannel->pMixer.finished )
return 0;
// If we are terminating this wave prematurely, then make sure we detect the limit
if( pChannel->pMixer.forcedEndSample )
{
// how many total input samples will we need?
int samplesRequired = (int)(sampleCount * rate);
// will this hit the end?
if( pChannel->pMixer.sample + samplesRequired >= pChannel->pMixer.forcedEndSample )
{
// yes, mark finished and truncate the sample request
pChannel->pMixer.finished = true;
sampleCount = (int)(( pChannel->pMixer.forcedEndSample - pChannel->pMixer.sample ) / rate );
}
}
pvol[0] = pChannel->leftvol;
pvol[1] = pChannel->rightvol;
while( sampleCount > 0 )
{
double sampleFraction;
portable_samplepair_t *pbuf;
bool advanceSample = true;
int availableSamples, outputSampleCount;
wavdata_t *pSource = pChannel->sfx->cache;
bool use_loop = pChannel->use_loop;
char *pData = NULL;
// compute number of input samples required
double end = pChannel->pMixer.sample + rate * sampleCount;
int inputSampleCount = (int)(ceil( end ) - floor( pChannel->pMixer.sample ));
availableSamples = S_GetOutputData( pSource, &pData, pChannel->pMixer.sample, inputSampleCount, use_loop );
// none available, bail out
if( !availableSamples ) break;
sampleFraction = pChannel->pMixer.sample - floor( pChannel->pMixer.sample );
if( availableSamples < inputSampleCount )
{
// how many samples are there given the number of input samples and the rate.
outputSampleCount = (int)ceil(( availableSamples - sampleFraction ) / rate );
}
else
{
outputSampleCount = sampleCount;
}
// Verify that we won't get a buffer overrun.
ASSERT( floor( sampleFraction + rate * ( outputSampleCount - 1 )) <= availableSamples );
// mix this data to all active paintbuffers
pbuf = &paintbuffer[outputOffset];
if( pSource->channels == 1 )
{
if( pSource->width == 1 )
S_Mix8Mono( pbuf, pvol, (char *)pData, FIX_FLOAT(sampleFraction), FIX_FLOAT(rate), outputSampleCount );
else S_Mix16Mono( pbuf, pvol, (short *)pData, FIX_FLOAT(sampleFraction), FIX_FLOAT(rate), outputSampleCount );
}
else
{
if( pSource->width == 1 )
S_Mix8Stereo( pbuf, pvol, (char *)pData, FIX_FLOAT(sampleFraction), FIX_FLOAT(rate), outputSampleCount );
else S_Mix16Stereo( pbuf, pvol, (short *)pData, FIX_FLOAT(sampleFraction), FIX_FLOAT(rate), outputSampleCount );
}
if( advanceSample )
{
pChannel->pMixer.sample += outputSampleCount * rate;
}
outputOffset += outputSampleCount;
sampleCount -= outputSampleCount;
}
// Did we run out of samples? if so, mark finished
if( sampleCount > 0 )
{
pChannel->pMixer.finished = true;
}
// total number of samples mixed !!! at the output clock rate !!!
return outputOffset - startingOffset;
}
bool S_ShouldContinueMixing( channel_t *ch )
{
if( ch->isSentence )
{
if( ch->currentWord )
return true;
return false;
}
return !ch->pMixer.finished;
2010-07-21 22:00:00 +02:00
}
2009-10-11 22:00:00 +02:00
void S_MixAllChannels( int endtime, int end )
2009-10-02 22:00:00 +02:00
{
2010-04-20 22:00:00 +02:00
channel_t *ch;
wavdata_t *sc;
2010-09-12 22:00:00 +02:00
int i, count = end - paintedtime;
2009-10-02 22:00:00 +02:00
2009-10-11 22:00:00 +02:00
// paint in the channels.
for( i = 0, ch = channels; i < total_channels; i++, ch++ )
{
2010-04-21 22:00:00 +02:00
if( !ch->sfx ) continue;
2010-06-30 22:00:00 +02:00
if( !ch->leftvol && !ch->rightvol && !ch->isSentence )
2010-07-24 22:00:00 +02:00
{
// sentences must be playing even if not hearing
2010-06-27 22:00:00 +02:00
continue;
2010-07-24 22:00:00 +02:00
}
2010-06-28 22:00:00 +02:00
2010-04-21 22:00:00 +02:00
sc = S_LoadSound( ch->sfx );
if( !sc ) continue;
2010-07-24 22:00:00 +02:00
if( s_listener.inmenu && !ch->localsound )
{
// play only local sounds, keep pause for other
continue;
}
else if( !s_listener.inmenu && !s_listener.active && !ch->staticsound )
{
// play only ambient sounds, keep pause for other
continue;
}
else if( s_listener.paused )
{
// play only ambient sounds, keep pause for other
continue;
}
2010-09-12 22:00:00 +02:00
// get playback pitch
if( ch->isSentence )
ch->pitch = VOX_ModifyPitch( ch, ch->basePitch * 0.01f );
else ch->pitch = ch->basePitch * 0.01f;
// check volume
ch->leftvol = bound( 0, ch->leftvol, 255 );
ch->rightvol = bound( 0, ch->rightvol, 255 );
if( si.GetClientEdict( ch->entnum ) && ( ch->entchannel == CHAN_VOICE ))
2009-10-11 22:00:00 +02:00
{
2010-09-12 22:00:00 +02:00
SND_MoveMouth8( ch, sc, count );
}
2009-10-11 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
if( ch->isSentence )
VOX_MixDataToDevice( ch, count, dma.speed, 0 );
else S_MixDataToDevice( ch, count, dma.speed, 0 );
2010-06-27 22:00:00 +02:00
2010-09-12 22:00:00 +02:00
if( !S_ShouldContinueMixing( ch ))
{
S_FreeChannel( ch );
2009-10-11 22:00:00 +02:00
}
}
}
2009-10-10 22:00:00 +02:00
2009-10-11 22:00:00 +02:00
void S_PaintChannels( int endtime )
{
2010-04-21 22:00:00 +02:00
int end;
2009-10-11 22:00:00 +02:00
2009-12-06 22:00:00 +01:00
snd_vol = S_GetMasterVolume () * 256;
2009-10-10 22:00:00 +02:00
2009-10-02 22:00:00 +02:00
while( paintedtime < endtime )
{
// if paintbuffer is smaller than DMA buffer
end = endtime;
if( endtime - paintedtime > PAINTBUFFER_SIZE )
end = paintedtime + PAINTBUFFER_SIZE;
// clear the paint buffer
2010-06-27 22:00:00 +02:00
if( s_rawend < paintedtime )
2009-10-02 22:00:00 +02:00
{
2009-10-11 22:00:00 +02:00
Mem_Set( paintbuffer, 0, (end - paintedtime) * sizeof( portable_samplepair_t ));
2009-10-02 22:00:00 +02:00
}
else
{
2010-04-21 22:00:00 +02:00
int i, stop;
2009-10-02 22:00:00 +02:00
// copy from the streaming sound source
2010-06-27 22:00:00 +02:00
stop = (end < s_rawend) ? end : s_rawend;
2009-10-02 22:00:00 +02:00
for( i = paintedtime; i < stop; i++ )
2010-06-27 22:00:00 +02:00
paintbuffer[i - paintedtime] = s_rawsamples[i & (MAX_RAW_SAMPLES - 1)];
2009-10-02 22:00:00 +02:00
for( ; i < end; i++ )
paintbuffer[i-paintedtime].left = paintbuffer[i-paintedtime].right = 0;
}
2009-10-11 22:00:00 +02:00
S_MixAllChannels( endtime, end );
2009-10-02 22:00:00 +02:00
2010-06-27 22:00:00 +02:00
// SX_RoomFX( endtime, true, true );
2009-10-10 22:00:00 +02:00
2009-10-02 22:00:00 +02:00
// transfer out according to DMA format
S_TransferPaintBuffer( end );
paintedtime = end;
}
}
2010-06-27 22:00:00 +02:00
void S_InitScaletable( void )
2009-10-02 22:00:00 +02:00
{
int i, j;
2009-10-11 22:00:00 +02:00
int scale;
2009-10-02 22:00:00 +02:00
2009-10-11 22:00:00 +02:00
for( i = 0; i < 32; i++ )
{
2009-12-06 22:00:00 +01:00
scale = i * 8 * 256 * S_GetMasterVolume();
2010-06-27 22:00:00 +02:00
for( j = 0; j < 256; j++ ) snd_scaletable[i][j] = ((signed char)j) * scale;
2009-10-11 22:00:00 +02:00
}
2010-06-27 22:00:00 +02:00
s_volume->modified = false;
2009-10-11 22:00:00 +02:00
}