Cleanup/refactor of resampling code.

Use resampler for background music.
Should work with 8 or 16 bit output.
This commit is contained in:
Eric Wasylishen 2011-01-19 13:13:09 -07:00
parent a0f74edc77
commit f2de795e5e
4 changed files with 141 additions and 137 deletions

View File

@ -71,7 +71,7 @@ qboolean sound_started = false;
int s_rawend[MAX_RAW_STREAMS];
portable_samplepair_t s_rawsamples[MAX_RAW_STREAMS][MAX_RAW_SAMPLES];
static void *s_rawresampler[MAX_RAW_STREAMS];
cvar_t bgmvolume = {"bgmvolume", "1", true};
cvar_t sfxvolume = {"volume", "0.7", true};
@ -221,6 +221,13 @@ void S_Shutdown (void)
SNDDMA_Shutdown();
shm = NULL;
int i;
for (i = 0; i<MAX_RAW_STREAMS; i++)
{
if (s_rawresampler[i] != NULL)
Snd_ResamplerClose(s_rawresampler[i]);
}
}
@ -620,6 +627,8 @@ void S_Base_RawSamples( int stream, int samples, int rate, int width, int s_chan
float scale;
int intVolume;
portable_samplepair_t *rawsamples;
void *resampler;
int resampledNumSamples;
if ( !sound_started ) {
return;
@ -630,6 +639,13 @@ void S_Base_RawSamples( int stream, int samples, int rate, int width, int s_chan
}
rawsamples = s_rawsamples[stream];
// Set up resampler
if (s_rawresampler[stream] == NULL)
{
s_rawresampler[stream] = Snd_ResamplerInit();
}
resampler = s_rawresampler[stream];
intVolume = 256 * volume;
if ( s_rawend[stream] < soundtime ) {
@ -637,78 +653,39 @@ void S_Base_RawSamples( int stream, int samples, int rate, int width, int s_chan
s_rawend[stream] = soundtime;
}
scale = (float)rate / shm->speed;
void *resampled = Snd_Resample(resampler,
rate, width, samples, s_channels, data,
shm->speed, 2, &resampledNumSamples);
// old:
//Con_Printf ("%i < %i < %i\n", soundtime, s_paintedtime, s_rawend[stream]);
if (s_channels == 2 && width == 2)
if (s_channels == 2)
{
if (scale == 1.0)
{ // optimized case
for (i=0 ; i<samples ; i++)
{
dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
s_rawend[stream]++;
rawsamples[dst].left = ((short *)data)[i*2] * intVolume;
rawsamples[dst].right = ((short *)data)[i*2+1] * intVolume;
}
}
else
for (i=0 ; i<resampledNumSamples ; i++)
{
for (i=0 ; ; i++)
{
src = i*scale;
if (src >= samples)
break;
dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
s_rawend[stream]++;
rawsamples[dst].left = ((short *)data)[src*2] * intVolume;
rawsamples[dst].right = ((short *)data)[src*2+1] * intVolume;
}
}
}
else if (s_channels == 1 && width == 2)
{
for (i=0 ; ; i++)
{
src = i*scale;
if (src >= samples)
break;
dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
s_rawend[stream]++;
rawsamples[dst].left = ((short *)data)[src] * intVolume;
rawsamples[dst].right = ((short *)data)[src] * intVolume;
rawsamples[dst].left = ((short *)data)[i*2] * intVolume;
rawsamples[dst].right = ((short *)data)[i*2+1] * intVolume;
}
}
else if (s_channels == 2 && width == 1)
else if (s_channels == 1)
{
intVolume *= 256;
for (i=0 ; ; i++)
for (i=0 ; i<resampledNumSamples; i++)
{
src = i*scale;
if (src >= samples)
break;
dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
s_rawend[stream]++;
rawsamples[dst].left = ((char *)data)[src*2] * intVolume;
rawsamples[dst].right = ((char *)data)[src*2+1] * intVolume;
rawsamples[dst].left = ((short *)data)[i] * intVolume;
rawsamples[dst].right = ((short *)data)[i] * intVolume;
}
}
else if (s_channels == 1 && width == 1)
else
{
intVolume *= 256;
for (i=0 ; ; i++)
{
src = i*scale;
if (src >= samples)
break;
dst = s_rawend[stream]&(MAX_RAW_SAMPLES-1);
s_rawend[stream]++;
rawsamples[dst].left = (((byte *)data)[src]-128) * intVolume;
rawsamples[dst].right = (((byte *)data)[src]-128) * intVolume;
}
Con_Printf( "S_Base_RawSamples: unsupported number of channels %d\n", s_channels );
}
if ( s_rawend[stream] > soundtime + MAX_RAW_SAMPLES ) {
Con_DPrintf( "S_Base_RawSamples: overflowed %i > %i\n", s_rawend[stream], soundtime );

View File

@ -23,6 +23,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "quakedef.h"
#include "snd_codec.h"
static void *resampler = NULL;
/*
==============
S_LoadSound
@ -62,9 +64,22 @@ sfxcache_t *S_LoadSound (sfx_t *s)
return NULL;
}
// set up the resampler
if (resampler == NULL)
{
resampler = Snd_ResamplerInit();
}
else
{
Snd_ResamplerReset(resampler);
}
int resampledNumSamples;
void *resampled = Snd_Resample(info.rate, info.width, info.samples, info.channels, data, shm->speed, shm->samplebits/8, &resampledNumSamples);
void *resampled = Snd_Resample(resampler,
info.rate, info.width, info.samples, info.channels, data,
shm->speed, shm->samplebits/8, &resampledNumSamples);
// FIXME: remove
if (info.rate == shm->speed && info.width == shm->samplebits/8)
{
if (resampledNumSamples != info.samples) exit(5);
@ -73,6 +88,8 @@ sfxcache_t *S_LoadSound (sfx_t *s)
len = resampledNumSamples * (shm->samplebits/8) * info.channels;
// set up the sample struct
sc = (sfxcache_t *) Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name);
if (!sc)
return NULL;

View File

@ -22,15 +22,51 @@
#include "sound.h"
#include "speex_resampler.h"
void *Snd_Resample(int inrate, int inwidth, int innumsamples, int channels, const void *indata,
int outrate, int outwidth, int *outnumsamples)
#define Snd_ResamplerQuality 0
struct resampler {
int channels;
SpeexResamplerState *st;
};
void *Snd_ResamplerInit()
{
struct resampler *data = malloc(sizeof(struct resampler *));
data->channels = 1;
data->st = speex_resampler_init(1, 44100, 44100, Snd_ResamplerQuality, NULL);
return data;
}
void Snd_ResamplerClose(void *handle)
{
struct resampler *data = (struct resampler *)handle;
speex_resampler_destroy(data->st);
free(data);
}
void Snd_ResamplerReset(void *handle)
{
struct resampler *data = (struct resampler *)handle;
speex_resampler_reset_mem(data->st);
}
void *Snd_Resample(void *handle,
int inrate, int inwidth, int innumsamples, int channels, const void *indata,
int outrate, int outwidth, int *outnumsamples)
{
// check params
if ( !(inwidth == 1 || inwidth == 2) || !(outwidth == 1 || outwidth == 2) )
{
Sys_Error("Snd_ResampleStream only supports 1 or 2 bytes per sample");
}
const float frac = ((float)inrate) / ((float)outrate);
const int maxsamples = (innumsamples / frac) + 10;
short *outdata = malloc(maxsamples * channels * outwidth);
// Convert input to 16-bit if necessary
short *in16bit;
short *out16bit = malloc(maxsamples * channels * 2);
// Convert input to 16-bit if necessary
if (inwidth == 2)
{
in16bit = (short*)indata;
@ -42,65 +78,44 @@ void *Snd_Resample(int inrate, int inwidth, int innumsamples, int channels, cons
for (i=0; i<innumsamples; i++)
{
unsigned char sample = ((unsigned char *)indata)[i];
if (sample == 255)
{
//Con_Printf("8-bit clipping\n");
}
in16bit[i] = (((short)sample) - 128) << 8;
}
}
else
{
exit(5);
}
// See if we need to resample
// Call the resampler
if (inrate == outrate)
{
memcpy(outdata, in16bit, innumsamples * channels * 2);
memcpy(out16bit, in16bit, innumsamples * channels * 2);
*outnumsamples = innumsamples;
}
else
{
struct resampler *data = (struct resampler *)handle;
// Call the resampler
static SpeexResamplerState *st = NULL;
if (st == NULL)
int old_inrate, old_outrate;
speex_resampler_get_rate(data->st, &old_inrate, &old_outrate);
if (data->channels != channels)
{
st = speex_resampler_init(channels, inrate, outrate, 0, NULL);
speex_resampler_destroy(data->st);
data->st = speex_resampler_init(channels, inrate, outrate, Snd_ResamplerQuality, NULL);
data->channels = channels;
}
else
{
speex_resampler_reset_mem(st);
speex_resampler_set_rate(data->st, inrate, outrate);
}
speex_resampler_set_rate(st, inrate, outrate);
*outnumsamples = 0;
unsigned int consumedtotal = 0;
unsigned int outputtotal = 0;
unsigned int loops = 0;
unsigned int consumed, output;
while (consumedtotal < innumsamples)
{
int roomToConsume, roomToOutput;
consumed = innumsamples - consumedtotal;
output = maxsamples - outputtotal;
roomToConsume = consumed;
roomToOutput = output;
speex_resampler_process_interleaved_int(st, in16bit + consumedtotal, &consumed, outdata + outputtotal, &output);
unsigned int consumed = innumsamples - consumedtotal;
unsigned int output = maxsamples - outputtotal;
speex_resampler_process_interleaved_int(data->st, in16bit + consumedtotal, &consumed, out16bit + outputtotal, &output);
consumedtotal += consumed;
outputtotal += output;
loops++;
if (loops > 100)
{
Con_Printf("Infinite loop\n");
}
}
*outnumsamples = outputtotal;
@ -109,37 +124,34 @@ void *Snd_Resample(int inrate, int inwidth, int innumsamples, int channels, cons
{
Con_Printf("Output %d, predicted %d\n", *outnumsamples, (innumsamples / frac));
}
//speex_resampler_destroy(resampler);
}
// Check for clipping.
{
int i;
for (i=0; i<*outnumsamples; i++)
{
short sample = outdata[i];
if (sample == 32767)
{
//Con_Printf("16-bit clipping\n");
}
}
}
// Prepare to return
if (in16bit != indata)
{
free(in16bit);
}
if(outwidth != 2) exit(5);
void *outdata;
if (outwidth == 2)
{
outdata = out16bit;
}
else // (outputwidth == 1)
{
int i;
int len = (*outnumsamples) * channels;
outdata = malloc(len);
for (i = 0; i<len; i++)
{
int s16sample = out16bit[i];
unsigned char u8sample = ((s16sample + 32768) >> 8);
((unsigned char *)outdata)[i] = u8sample;
}
free(out16bit);
}
return outdata;
}
void *Snd_ResamplerInit(int inrate, int inwidth, int outrate, int outwidth, int channels) { return NULL; }
void Snd_ResamplerClose(void *resampler) {}
void Snd_ResampleStream(void *resampler,
int *innumsamples, void *indata,
int *outnumsamples, void *outdata) {}
}

View File

@ -151,23 +151,21 @@ qboolean S_BackgroundTrackIsPlaying( void );
qboolean S_BackgroundTrackIsPaused( void );
qboolean S_BackgroundTrackIsLooping( void );
/* resamples a whole file. return value must be freed with free() */
void *Snd_Resample(int inrate, int inwidth, int innumsamples, int channels, const void *indata,
int outrate, int outwidth, int *outnumsamples);
/* creates a new stream resampler for the specified rates and number of channels. returns a handle */
void *Snd_ResamplerInit(int inrate, int inwidth, int outrate, int outwidth, int channels);
/* creates a new stream resampler. returns a handle */
void *Snd_ResamplerInit();
/* closes a resampler handle */
void Snd_ResamplerClose(void *resampler);
/* performs resampling on samples in the given channel. numinsamples takes
the number of samples to process, and returns the number actually processed.
*/
void Snd_ResampleStream(void *resampler,
int *innumsamples, void *indata,
int *outnumsamples, void *outdata);
/* prepare resampler for processing an unrelated sound */
void Snd_ResamplerReset(void *resampler);
/* performs resampling on samples in the given channel.
return value must be freed with free()
*/
void *Snd_Resample(void *resampler,
int inrate, int inwidth, int innumsamples, int channels, const void *indata,
int outrate, int outwidth, int *outnumsamples);
// ====================================================================
// User-setable variables