quakeforge/libs/audio/renderer/snd_resample.c

409 lines
8.9 KiB
C

/*
snd_mem.c
sound caching
Copyright (C) 1996-1997 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:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static __attribute__ ((used)) const char rcsid[] =
"$Id$";
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include "QF/cvar.h"
#include "QF/dstring.h"
#include "QF/sound.h"
#include "QF/sys.h"
#include "QF/qendian.h"
#include "QF/quakefs.h"
#include "compat.h"
#include "snd_render.h"
typedef struct {
byte left;
byte right;
} stereo8_t;
typedef struct {
short left;
short right;
} stereo16_t;
cvar_t *snd_loadas8bit;
cvar_t *snd_interp;
static inline int
snd_convert_8_to_8 (byte data)
{
return data - 128;
}
static inline int
snd_convert_8_to_16 (byte data)
{
return snd_convert_8_to_8 (data) << 8;
}
static inline int
snd_convert_16_to_16 (short data)
{
return LittleShort (data);
}
static inline int
snd_convert_16_to_8 (short data)
{
return snd_convert_16_to_16 (data) >> 8;
}
#define FAST_RESAMPLE(in,out,count,convert) \
do { \
int i; \
\
for (i = 0; i < count; i++) { \
*out++ = convert (*in++); \
} \
} while (0)
static void
check_buffer_integrity (sfxbuffer_t *sc, int width, const char *func)
{
byte *x = sc->data + sc->length * width;
if (memcmp (x, "\xde\xad\xbe\xef", 4))
Sys_Error ("%s screwed the pooch %02x%02x%02x%02x", func,
x[0], x[1], x[2], x[3]);
}
void
SND_ResampleMono (sfxbuffer_t *sc, byte *data, int length)
{
void *prev = 0;
byte *ib, *ob, *pb;
int fracstep, outcount, sample, samplefrac, srcsample, i;
float stepscale;
short *is, *os, *ps;
wavinfo_t *info = sc->sfx->wavinfo (sc->sfx);
int inwidth = info->width;
int inrate = info->rate;
int outwidth;
short zero_s[1];
byte zero_b[1];
if (sc->data != sc->sample_data)
prev = sc->data;
is = (short *) data;
os = (short *) sc->data;
ib = data;
ob = sc->data;
ps = zero_s;
pb = zero_b;
if (!prev) {
zero_s[0] = 0;
zero_b[0] = 128;
}
os += sc->head;
ob += sc->head;
stepscale = (float) inrate / snd_shm->speed;
outcount = length / stepscale;
sc->sfx->length = info->samples / stepscale;
//printf ("%s %d %g %d\n", sc->sfx->name, length, stepscale, sc->sfx->length);
if (info->loopstart != (unsigned int)-1)
sc->sfx->loopstart = info->loopstart / stepscale;
else
sc->sfx->loopstart = (unsigned int)-1;
if (snd_loadas8bit->int_val)
sc->bps = outwidth = 1;
else
sc->bps = outwidth = 2;
if (prev) {
if (inwidth == 1)
zero_b[0] = ((char *)prev)[0];
else
zero_s[0] = ((short *)prev)[0];
}
if (!length)
return;
// resample / decimate to the current source rate
if (stepscale == 1) {
if (inwidth == 1) {
if (outwidth == 1)
FAST_RESAMPLE (ib, ob, outcount, snd_convert_8_to_8);
else if (outwidth == 2)
FAST_RESAMPLE (ib, os, outcount, snd_convert_8_to_16);
else
goto general_Mono;
} else if (inwidth == 2) {
if (outwidth == 1)
FAST_RESAMPLE (is, ob, outcount, snd_convert_16_to_8);
else if (outwidth == 2)
FAST_RESAMPLE (is, os, outcount, snd_convert_16_to_16);
else
goto general_Mono;
}
} else { // general case
general_Mono:
if (snd_interp->int_val && stepscale < 1) {
int j;
int points = 1 / stepscale;
for (i = 0; i < length; i++) {
int s1, s2;
if (inwidth == 2) {
s1 = snd_convert_16_to_16 (*ps);
s2 = snd_convert_16_to_16 (*is);
ps = is++;
} else {
s1 = snd_convert_8_to_16 (*pb);
s2 = snd_convert_8_to_16 (*ib);
pb = ib++;
}
for (j = 0; j < points; j++) {
sample = s1 + (s2 - s1) * ((float) j) / points;
if (outwidth == 2) {
os[j] = sample;
} else {
ob[j] = sample >> 8;
}
}
if (outwidth == 2) {
os += points;
} else {
ob += points;
}
}
if (prev) {
if (inwidth == 1)
((char *)prev)[0] = *pb;
else
((short *)prev)[0] = *ps;
}
} else {
samplefrac = 0;
fracstep = stepscale * 256;
for (i = 0; i < outcount; i++) {
srcsample = samplefrac >> 8;
samplefrac += fracstep;
if (inwidth == 2)
sample = snd_convert_16_to_16 (is[srcsample]);
else
sample = snd_convert_8_to_16 (ib[srcsample]);
if (outwidth == 2)
os[i] = sample;
else
ob[i] = sample >> 8;
}
}
}
check_buffer_integrity (sc, outwidth, __FUNCTION__);
}
void
SND_ResampleStereo (sfxbuffer_t *sc, byte *data, int length)
{
void *prev = 0;
int fracstep, outcount, outwidth, samplefrac, srcsample, sl, sr, i;
float stepscale;
stereo8_t *ib, *ob, *pb;
stereo16_t *is, *os, *ps;
wavinfo_t *info = sc->sfx->wavinfo (sc->sfx);
int inwidth = info->width;
int inrate = info->rate;
stereo16_t zero_s;
stereo8_t zero_b;
if (sc->data != sc->sample_data)
prev = sc->data;
is = (stereo16_t *) data;
os = (stereo16_t *) sc->data;
ib = (stereo8_t *) data;
ob = (stereo8_t *) sc->data;
ps = &zero_s;
pb = &zero_b;
if (!prev) {
zero_s.left = zero_s.right = 0;
zero_b.left = zero_b.right = 128;
}
os += sc->head;
ob += sc->head;
stepscale = (float) inrate / snd_shm->speed;
outcount = length / stepscale;
sc->sfx->length = info->samples / stepscale;
if (info->loopstart != (unsigned int)-1)
sc->sfx->loopstart = info->loopstart / stepscale;
else
sc->sfx->loopstart = (unsigned int)-1;
if (snd_loadas8bit->int_val) {
outwidth = 1;
sc->bps = 2;
} else {
outwidth = 2;
sc->bps = 4;
}
if (prev) {
if (inwidth == 1) {
zero_b.left = ((char *)prev)[0];
zero_b.right = ((char *)prev)[1];
} else {
zero_s.left = ((short *)prev)[0];
zero_s.right = ((short *)prev)[1];
}
}
if (!length)
return;
// resample / decimate to the current source rate
if (stepscale == 1) {
if (inwidth == 1) {
if (outwidth == 1) {
for (i = 0; i < outcount; i++, ob++, ib++) {
ob->left = ib->left - 128;
ob->right = ib->right - 128;
}
} else if (outwidth == 2) {
for (i = 0; i < outcount; i++, os++, ib++) {
os->left = (ib->left - 128) << 8;
os->right = (ib->right - 128) << 8;
}
} else {
goto general_Stereo;
}
} else if (inwidth == 2) {
if (outwidth == 1) {
for (i = 0; i < outcount; i++, ob++, ib++) {
ob->left = LittleShort (is->left) >> 8;
ob->right = LittleShort (is->right) >> 8;
}
} else if (outwidth == 2) {
for (i = 0; i < outcount; i++, os++, is++) {
os->left = LittleShort (is->left);
os->right = LittleShort (is->right);
}
} else {
goto general_Stereo;
}
}
} else { // general case
general_Stereo:
if (snd_interp->int_val && stepscale < 1) {
int j;
int points = 1 / stepscale;
for (i = 0; i < length; i++) {
int sl1, sl2;
int sr1, sr2;
if (inwidth == 2) {
sl1 = LittleShort (ps->left);
sr1 = LittleShort (ps->right);
sl2 = LittleShort (is->left);
sr2 = LittleShort (is->right);
ps = is++;
} else {
sl1 = (pb->left - 128) << 8;
sr1 = (pb->right - 128) << 8;
sl2 = (ib->left - 128) << 8;
sr2 = (ib->right - 128) << 8;
pb = ib++;
}
for (j = 0; j < points; j++) {
sl = sl1 + (sl2 - sl1) * ((float) j) / points;
sr = sr1 + (sr2 - sr1) * ((float) j) / points;
if (outwidth == 2) {
os[j].left = sl;
os[j].right = sr;
} else {
ob[j].left = sl >> 8;
ob[j].right = sr >> 8;
}
}
if (outwidth == 2) {
os += points;
} else {
ob += points;
}
}
if (prev) {
if (inwidth == 1) {
((char *)prev)[0] = pb->left;
((char *)prev)[1] = pb->right;
} else {
((short *)prev)[0] = ps->left;
((short *)prev)[1] = ps->right;
}
}
} else {
samplefrac = 0;
fracstep = stepscale * 256;
for (i = 0; i < outcount; i++) {
srcsample = samplefrac >> 8;
samplefrac += fracstep;
if (inwidth == 2) {
sl = LittleShort (is[srcsample].left);
sr = LittleShort (is[srcsample].right);
} else {
sl = (ib[srcsample].left - 128) << 8;
sr = (ib[srcsample].right - 128) << 8;
}
if (outwidth == 2) {
os[i].left = sl;
os[i].right = sr;
} else {
ob[i].left = sl >> 8;
ob[i].right = sr >> 8;
}
}
}
}
check_buffer_integrity (sc, outwidth * 2, __FUNCTION__);
}