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

178 lines
8.9 KiB
C++

/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* resamp2.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.
*/
#define SUFFIX3 _1
/* For convenience, returns nonzero on stop. */
static int process_pickup(DUMB_RESAMPLER *resampler) {
if (resampler->overshot < 0) {
resampler->overshot = 0;
dumb_resample(resampler, NULL, 2, MONO_DEST_VOLUME_ZEROS,
1.0f); /* Doesn't matter which SUFFIX3. */
COPYSRC(resampler->X, 0, resampler->X, 1);
}
for (;;) {
SRCTYPE *src = resampler->src;
if (resampler->dir < 0) {
if (resampler->overshot >= 3 &&
resampler->pos + 3 >= resampler->start)
COPYSRC(resampler->X, 0, src, resampler->pos + 3);
if (resampler->overshot >= 2 &&
resampler->pos + 2 >= resampler->start)
COPYSRC(resampler->X, 1, src, resampler->pos + 2);
if (resampler->overshot >= 1 &&
resampler->pos + 1 >= resampler->start)
COPYSRC(resampler->X, 2, src, resampler->pos + 1);
resampler->overshot = (int)(resampler->start - resampler->pos - 1);
} else {
if (resampler->overshot >= 3 && resampler->pos - 3 < resampler->end)
COPYSRC(resampler->X, 0, src, resampler->pos - 3);
if (resampler->overshot >= 2 && resampler->pos - 2 < resampler->end)
COPYSRC(resampler->X, 1, src, resampler->pos - 2);
if (resampler->overshot >= 1 && resampler->pos - 1 < resampler->end)
COPYSRC(resampler->X, 2, src, resampler->pos - 1);
resampler->overshot = (int)(resampler->pos - resampler->end);
}
if (resampler->overshot < 0) {
resampler->overshot = 0;
return 0;
}
if (!resampler->pickup) {
resampler->dir = 0;
return 1;
}
(*resampler->pickup)(resampler, resampler->pickup_data);
if (resampler->dir == 0)
return 1;
ASSERT(resampler->dir == -1 || resampler->dir == 1);
}
}
/* Create mono destination resampler. */
/* SUFFIX3 was set above. */
#define VOLUME_PARAMETERS MONO_DEST_VOLUME_PARAMETERS
#define VOLUME_VARIABLES MONO_DEST_VOLUME_VARIABLES
#define SET_VOLUME_VARIABLES SET_MONO_DEST_VOLUME_VARIABLES
#define RETURN_VOLUME_VARIABLES RETURN_MONO_DEST_VOLUME_VARIABLES
#define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO
#define PEEK_FIR MONO_DEST_PEEK_FIR
#define MIX_FIR MONO_DEST_MIX_FIR
#define MIX_ZEROS(op) *dst++ op 0
#include "resamp3.inc"
/* Create stereo destination resampler. */
#define SUFFIX3 _2
#define VOLUME_PARAMETERS \
DUMB_VOLUME_RAMP_INFO *volume_left, DUMB_VOLUME_RAMP_INFO *volume_right
#define VOLUME_VARIABLES \
lvol, lvolr, lvold, lvolt, lvolm, rvol, rvolr, rvold, rvolt, rvolm
#define SET_VOLUME_VARIABLES \
{ \
if (volume_left) { \
lvolr = volume_left->volume; \
lvold = volume_left->delta; \
lvolt = volume_left->target; \
lvolm = volume_left->mix; \
lvol = lvolr * lvolm; \
if (lvolr == lvolt) \
volume_left = NULL; \
} else { \
lvol = 0; \
lvold = 0; \
lvolt = 0; \
lvolm = 0; \
} \
if (volume_right) { \
rvolr = volume_right->volume; \
rvold = volume_right->delta; \
rvolt = volume_right->target; \
rvolm = volume_right->mix; \
rvol = rvolr * rvolm; \
if (rvolr == rvolt) \
volume_right = NULL; \
} else { \
rvol = 0; \
rvold = 0; \
rvolt = 0; \
rvolm = 0; \
} \
}
#define RETURN_VOLUME_VARIABLES \
{ \
if (volume_left) \
volume_left->volume = lvolr; \
if (volume_right) \
volume_right->volume = rvolr; \
}
#define VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0)
#define PEEK_FIR STEREO_DEST_PEEK_FIR
#define MIX_FIR STEREO_DEST_MIX_FIR
#define MIX_ZEROS(op) \
{ \
*dst++ op 0; \
*dst++ op 0; \
}
#include "resamp3.inc"
#undef MONO_DEST_VOLUMES_ARE_ZERO
#undef SET_MONO_DEST_VOLUME_VARIABLES
#undef RETURN_MONO_DEST_VOLUME_VARIABLES
#undef MONO_DEST_VOLUME_ZEROS
#undef MONO_DEST_VOLUME_VARIABLES
#undef MONO_DEST_VOLUME_PARAMETERS
#undef MONO_DEST_PEEK_FIR
#undef STEREO_DEST_PEEK_FIR
#undef MONO_DEST_MIX_FIR
#undef STEREO_DEST_MIX_FIR
#undef ADVANCE_FIR
#undef POKE_FIR
#undef COPYSRC2
#undef COPYSRC
#undef DIVIDE_BY_SRC_CHANNELS
#undef SRC_CHANNELS
#undef SUFFIX2