mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2025-02-19 02:22:01 +00:00
Cleanup/refactor of resampling code.
Use resampler for background music. Should work with 8 or 16 bit output.
This commit is contained in:
parent
a0f74edc77
commit
f2de795e5e
4 changed files with 141 additions and 137 deletions
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue