Cog/Frameworks/Dumb/dumb/src/helpers/resamp3.inc
2017-09-26 16:11:54 -07:00

278 lines
10 KiB
C++

/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* resamp3.inc - Resampling helper template. / / \ \
* | < / \_
* By Bob and entheh. | \/ /\ /
* \_ / > /
* In order to find a good trade-off between | \ / /
* speed and accuracy in this code, some tests | ' /
* were carried out regarding the behaviour of \__/
* long long ints with gcc. The following code
* was tested:
*
* int a, b, c;
* c = ((long long)a * b) >> 16;
*
* DJGPP GCC Version 3.0.3 generated the following assembly language code for
* the multiplication and scaling, leaving the 32-bit result in EAX.
*
* movl -8(%ebp), %eax ; read one int into EAX
* imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX
* shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX
*
* Note that a 32*32->64 multiplication is performed, allowing for high
* accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally),
* so it is a minor concern when four multiplications are being performed
* (the cubic resampler). On the Pentium MMX and earlier, it takes four or
* more cycles, so this method is unsuitable for use in the low-quality
* resamplers.
*
* Since "long long" is a gcc-specific extension, we use LONG_LONG instead,
* defined in dumb.h. We may investigate later what code MSVC generates, but
* if it seems too slow then we suggest you use a good compiler.
*
* FIXME: these comments are somewhat out of date now.
*/
long dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size,
VOLUME_PARAMETERS, float delta) {
int dt, inv_dt;
float VOLUME_VARIABLES;
long done;
long todo;
LONG_LONG todo64;
int quality;
if (!resampler || resampler->dir == 0)
return 0;
ASSERT(resampler->dir == -1 || resampler->dir == 1);
done = 0;
dt = (int)(delta * 65536.0 + 0.5);
if (dt == 0 || dt == (int)-0x80000000)
return 0;
inv_dt = (int)(1.0 / delta * 65536.0 + 0.5);
SET_VOLUME_VARIABLES;
if (VOLUMES_ARE_ZERO)
dst = NULL;
_dumb_init_cubic();
quality = resampler->quality;
while (done < dst_size) {
if (process_pickup(resampler)) {
RETURN_VOLUME_VARIABLES;
return done;
}
if ((resampler->dir ^ dt) < 0)
dt = -dt;
if (resampler->dir < 0)
todo64 = ((((LONG_LONG)(resampler->pos - resampler->start) << 16) +
resampler->subpos - dt) /
-dt);
else
todo64 = ((((LONG_LONG)(resampler->end - resampler->pos) << 16) -
resampler->subpos - 1 + dt) /
dt);
if (todo64 < 0)
todo = 0;
else if (todo64 > dst_size - done)
todo = dst_size - done;
else
todo = (long)todo64;
done += todo;
{
SRCTYPE *src = resampler->src;
long pos = resampler->pos;
int subpos = resampler->subpos;
long diff = pos;
long overshot;
if (resampler->dir < 0) {
if (!dst) {
/* Silence or simulation */
LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo;
pos += (long)(new_subpos >> 16);
subpos = (long)new_subpos & 65535;
} else {
/* FIR resampling, backwards */
SRCTYPE *x;
if (resampler->fir_resampler_ratio != delta) {
resampler_set_rate(resampler->fir_resampler[0], delta);
resampler_set_rate(resampler->fir_resampler[1], delta);
resampler->fir_resampler_ratio = delta;
}
x = &src[pos * SRC_CHANNELS];
while (todo) {
while ((resampler_get_free_count(
resampler->fir_resampler[0]) ||
(!resampler_get_sample_count(
resampler->fir_resampler[0])
#if SRC_CHANNELS == 2
&& !resampler_get_sample_count(
resampler->fir_resampler[1])
#endif
)) &&
pos >= resampler->start) {
POKE_FIR(0);
pos--;
x -= SRC_CHANNELS;
}
if (!resampler_get_sample_count(
resampler->fir_resampler[0]))
break;
MIX_FIR;
ADVANCE_FIR;
--todo;
}
done -= todo;
}
diff = diff - pos;
overshot = resampler->start - pos - 1;
if (diff >= 3) {
COPYSRC2(resampler->X, 0, overshot < 3, src, pos + 3);
COPYSRC2(resampler->X, 1, overshot < 2, src, pos + 2);
COPYSRC2(resampler->X, 2, overshot < 1, src, pos + 1);
} else if (diff >= 2) {
COPYSRC(resampler->X, 0, resampler->X, 2);
COPYSRC2(resampler->X, 1, overshot < 2, src, pos + 2);
COPYSRC2(resampler->X, 2, overshot < 1, src, pos + 1);
} else if (diff >= 1) {
COPYSRC(resampler->X, 0, resampler->X, 1);
COPYSRC(resampler->X, 1, resampler->X, 2);
COPYSRC2(resampler->X, 2, overshot < 1, src, pos + 1);
}
} else {
if (!dst) {
/* Silence or simulation */
LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo;
pos += (long)(new_subpos >> 16);
subpos = (long)new_subpos & 65535;
} else {
/* FIR resampling, forwards */
SRCTYPE *x;
if (resampler->fir_resampler_ratio != delta) {
resampler_set_rate(resampler->fir_resampler[0], delta);
resampler_set_rate(resampler->fir_resampler[1], delta);
resampler->fir_resampler_ratio = delta;
}
x = &src[pos * SRC_CHANNELS];
while (todo) {
while ((resampler_get_free_count(
resampler->fir_resampler[0]) ||
(!resampler_get_sample_count(
resampler->fir_resampler[0])
#if SRC_CHANNELS == 2
&& !resampler_get_sample_count(
resampler->fir_resampler[1])
#endif
)) &&
pos < resampler->end) {
POKE_FIR(0);
pos++;
x += SRC_CHANNELS;
}
if (!resampler_get_sample_count(
resampler->fir_resampler[0]))
break;
MIX_FIR;
ADVANCE_FIR;
--todo;
}
done -= todo;
}
diff = pos - diff;
overshot = pos - resampler->end;
if (diff >= 3) {
COPYSRC2(resampler->X, 0, overshot < 3, src, pos - 3);
COPYSRC2(resampler->X, 1, overshot < 2, src, pos - 2);
COPYSRC2(resampler->X, 2, overshot < 1, src, pos - 1);
} else if (diff >= 2) {
COPYSRC(resampler->X, 0, resampler->X, 2);
COPYSRC2(resampler->X, 1, overshot < 2, src, pos - 2);
COPYSRC2(resampler->X, 2, overshot < 1, src, pos - 1);
} else if (diff >= 1) {
COPYSRC(resampler->X, 0, resampler->X, 1);
COPYSRC(resampler->X, 1, resampler->X, 2);
COPYSRC2(resampler->X, 2, overshot < 1, src, pos - 1);
}
}
resampler->pos = pos;
resampler->subpos = subpos;
}
}
RETURN_VOLUME_VARIABLES;
return done;
}
void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler,
VOLUME_PARAMETERS, sample_t *dst) {
float VOLUME_VARIABLES;
SRCTYPE *src;
long pos;
int subpos;
int quality;
SRCTYPE *x;
if (!resampler || resampler->dir == 0) {
MIX_ZEROS(=);
return;
}
ASSERT(resampler->dir == -1 || resampler->dir == 1);
if (process_pickup(resampler)) {
MIX_ZEROS(=);
return;
}
SET_VOLUME_VARIABLES;
if (VOLUMES_ARE_ZERO) {
MIX_ZEROS(=);
return;
}
_dumb_init_cubic();
quality = resampler->quality;
src = resampler->src;
pos = resampler->pos;
subpos = resampler->subpos;
x = resampler->X;
if (resampler->dir < 0) {
HEAVYASSERT(pos >= resampler->start);
/* FIR resampling, backwards */
PEEK_FIR;
} else {
HEAVYASSERT(pos < resampler->end);
/* FIR resampling, forwards */
PEEK_FIR;
}
}
#undef MIX_ZEROS
#undef MIX_FIR
#undef PEEK_FIR
#undef VOLUMES_ARE_ZERO
#undef SET_VOLUME_VARIABLES
#undef RETURN_VOLUME_VARIABLES
#undef VOLUME_VARIABLES
#undef VOLUME_PARAMETERS
#undef SUFFIX3