331 lines
11 KiB
C
331 lines
11 KiB
C
/* _______ ____ __ ___ ___
|
|
* \ _ \ \ / \ / \ \ / / ' ' '
|
|
* | | \ \ | | || | \/ | . .
|
|
* | | | | | | || ||\ /| |
|
|
* | | | | | | || || \/ | | ' ' '
|
|
* | | | | | | || || | | . .
|
|
* | |_/ / \ \__// || | |
|
|
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
|
* / \
|
|
* / . \
|
|
* resample.c - Resampling helpers. / / \ \
|
|
* | < / \_
|
|
* 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.
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include "dumb.h"
|
|
|
|
#include "internal/resampler.h"
|
|
|
|
|
|
|
|
/* Compile with -DHEAVYDEBUG if you want to make sure the pick-up function is
|
|
* called when it should be. There will be a considerable performance hit,
|
|
* since at least one condition has to be tested for every sample generated.
|
|
*/
|
|
#ifdef HEAVYDEBUG
|
|
#define HEAVYASSERT(cond) ASSERT(cond)
|
|
#else
|
|
#define HEAVYASSERT(cond)
|
|
#endif
|
|
|
|
|
|
|
|
/* Make MSVC shut the hell up about if ( upd ) UPDATE_VOLUME() conditions being constant */
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable:4127 4701)
|
|
#endif
|
|
|
|
|
|
|
|
/* A global variable for controlling resampling quality wherever a local
|
|
* specification doesn't override it. The following values are valid:
|
|
*
|
|
* 0 - DUMB_RQ_ALIASING - fastest
|
|
* 1 - DUMB_RQ_BLEP - nicer than aliasing, but slower
|
|
* 2 - DUMB_RQ_LINEAR
|
|
* 3 - DUMB_RQ_BLAM - band-limited linear interpolation, nice but slower
|
|
* 4 - DUMB_RQ_CUBIC
|
|
* 5 - DUMB_RQ_FIR - nicest
|
|
*
|
|
* Values outside the range 0-4 will behave the same as the nearest
|
|
* value within the range.
|
|
*/
|
|
int dumb_resampling_quality = DUMB_RQ_CUBIC;
|
|
|
|
|
|
|
|
//#define MULSC(a, b) ((int)((LONG_LONG)(a) * (b) >> 16))
|
|
//#define MULSC(a, b) ((a) * ((b) >> 2) >> 14)
|
|
#define MULSCV(a, b) ((int)((LONG_LONG)(a) * (b) >> 32))
|
|
#define MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32))
|
|
#define MULSC16(a, b) ((int)((LONG_LONG)((a) << 12) * ((b) << 12) >> 32))
|
|
|
|
|
|
|
|
/* Executes the content 'iterator' times.
|
|
* Clobbers the 'iterator' variable.
|
|
* The loop is unrolled by four.
|
|
*/
|
|
#if 0
|
|
#define LOOP4(iterator, CONTENT) \
|
|
{ \
|
|
if ((iterator) & 2) { \
|
|
CONTENT; \
|
|
CONTENT; \
|
|
} \
|
|
if ((iterator) & 1) { \
|
|
CONTENT; \
|
|
} \
|
|
(iterator) >>= 2; \
|
|
while (iterator) { \
|
|
CONTENT; \
|
|
CONTENT; \
|
|
CONTENT; \
|
|
CONTENT; \
|
|
(iterator)--; \
|
|
} \
|
|
}
|
|
#else
|
|
#define LOOP4(iterator, CONTENT) \
|
|
{ \
|
|
while ( (iterator)-- ) \
|
|
{ \
|
|
CONTENT; \
|
|
} \
|
|
}
|
|
#endif
|
|
|
|
#define PASTERAW(a, b) a ## b /* This does not expand macros in b ... */
|
|
#define PASTE(a, b) PASTERAW(a, b) /* ... but b is expanded during this substitution. */
|
|
|
|
#define X PASTE(x.x, SRCBITS)
|
|
|
|
|
|
|
|
void _dumb_init_cubic(void)
|
|
{
|
|
static int done = 0;
|
|
if (done) return;
|
|
|
|
resampler_init();
|
|
|
|
done = 1;
|
|
}
|
|
|
|
|
|
|
|
/* Create resamplers for 24-in-32-bit source samples. */
|
|
|
|
/* #define SUFFIX
|
|
* MSVC warns if we try to paste a null SUFFIX, so instead we define
|
|
* special macros for the function names that don't bother doing the
|
|
* corresponding paste. The more generic definitions are further down.
|
|
*/
|
|
#define process_pickup PASTE(process_pickup, SUFFIX2)
|
|
#define dumb_resample PASTE(PASTE(dumb_resample, SUFFIX2), SUFFIX3)
|
|
#define dumb_resample_get_current_sample PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX2), SUFFIX3)
|
|
|
|
#define SRCTYPE sample_t
|
|
#define SRCBITS 24
|
|
#define FIR(x) (x >> 8)
|
|
#include "resample.inc"
|
|
|
|
/* Undefine the simplified macros. */
|
|
#undef dumb_resample_get_current_sample
|
|
#undef dumb_resample
|
|
#undef process_pickup
|
|
|
|
|
|
/* Now define the proper ones that use SUFFIX. */
|
|
#define dumb_reset_resampler PASTE(dumb_reset_resampler, SUFFIX)
|
|
#define dumb_start_resampler PASTE(dumb_start_resampler, SUFFIX)
|
|
#define process_pickup PASTE(PASTE(process_pickup, SUFFIX), SUFFIX2)
|
|
#define dumb_resample PASTE(PASTE(PASTE(dumb_resample, SUFFIX), SUFFIX2), SUFFIX3)
|
|
#define dumb_resample_get_current_sample PASTE(PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX), SUFFIX2), SUFFIX3)
|
|
#define dumb_end_resampler PASTE(dumb_end_resampler, SUFFIX)
|
|
|
|
/* Create resamplers for 16-bit source samples. */
|
|
#define SUFFIX _16
|
|
#define SRCTYPE short
|
|
#define SRCBITS 16
|
|
#define FIR(x) (x * (1.0f / 32768.0f))
|
|
#include "resample.inc"
|
|
|
|
/* Create resamplers for 8-bit source samples. */
|
|
#define SUFFIX _8
|
|
#define SRCTYPE signed char
|
|
#define SRCBITS 8
|
|
#define FIR(x) (x * (1.0f / 256.0f))
|
|
#include "resample.inc"
|
|
|
|
|
|
#undef dumb_reset_resampler
|
|
#undef dumb_start_resampler
|
|
#undef process_pickup
|
|
#undef dumb_resample
|
|
#undef dumb_resample_get_current_sample
|
|
#undef dumb_end_resampler
|
|
|
|
|
|
|
|
void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, int src_channels, long pos, long start, long end, int quality)
|
|
{
|
|
if (n == 8)
|
|
dumb_reset_resampler_8(resampler, src, src_channels, pos, start, end, quality);
|
|
else if (n == 16)
|
|
dumb_reset_resampler_16(resampler, src, src_channels, pos, start, end, quality);
|
|
else
|
|
dumb_reset_resampler(resampler, src, src_channels, pos, start, end, quality);
|
|
}
|
|
|
|
|
|
|
|
DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, long pos, long start, long end, int quality)
|
|
{
|
|
if (n == 8)
|
|
return dumb_start_resampler_8(src, src_channels, pos, start, end, quality);
|
|
else if (n == 16)
|
|
return dumb_start_resampler_16(src, src_channels, pos, start, end, quality);
|
|
else
|
|
return dumb_start_resampler(src, src_channels, pos, start, end, quality);
|
|
}
|
|
|
|
|
|
|
|
long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float delta)
|
|
{
|
|
if (n == 8)
|
|
return dumb_resample_8_1_1(resampler, dst, dst_size, volume, delta);
|
|
else if (n == 16)
|
|
return dumb_resample_16_1_1(resampler, dst, dst_size, volume, delta);
|
|
else
|
|
return dumb_resample_1_1(resampler, dst, dst_size, volume, delta);
|
|
}
|
|
|
|
|
|
|
|
long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta)
|
|
{
|
|
if (n == 8)
|
|
return dumb_resample_8_1_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
|
else if (n == 16)
|
|
return dumb_resample_16_1_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
|
else
|
|
return dumb_resample_1_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
|
}
|
|
|
|
|
|
|
|
long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta)
|
|
{
|
|
if (n == 8)
|
|
return dumb_resample_8_2_1(resampler, dst, dst_size, volume_left, volume_right, delta);
|
|
else if (n == 16)
|
|
return dumb_resample_16_2_1(resampler, dst, dst_size, volume_left, volume_right, delta);
|
|
else
|
|
return dumb_resample_2_1(resampler, dst, dst_size, volume_left, volume_right, delta);
|
|
}
|
|
|
|
|
|
|
|
long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta)
|
|
{
|
|
if (n == 8)
|
|
return dumb_resample_8_2_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
|
else if (n == 16)
|
|
return dumb_resample_16_2_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
|
else
|
|
return dumb_resample_2_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
|
}
|
|
|
|
|
|
|
|
void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst)
|
|
{
|
|
if (n == 8)
|
|
dumb_resample_get_current_sample_8_1_1(resampler, volume, dst);
|
|
else if (n == 16)
|
|
dumb_resample_get_current_sample_16_1_1(resampler, volume, dst);
|
|
else
|
|
dumb_resample_get_current_sample_1_1(resampler, volume, dst);
|
|
}
|
|
|
|
|
|
|
|
void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst)
|
|
{
|
|
if (n == 8)
|
|
dumb_resample_get_current_sample_8_1_2(resampler, volume_left, volume_right, dst);
|
|
else if (n == 16)
|
|
dumb_resample_get_current_sample_16_1_2(resampler, volume_left, volume_right, dst);
|
|
else
|
|
dumb_resample_get_current_sample_1_2(resampler, volume_left, volume_right, dst);
|
|
}
|
|
|
|
|
|
|
|
void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst)
|
|
{
|
|
if (n == 8)
|
|
dumb_resample_get_current_sample_8_2_1(resampler, volume_left, volume_right, dst);
|
|
else if (n == 16)
|
|
dumb_resample_get_current_sample_16_2_1(resampler, volume_left, volume_right, dst);
|
|
else
|
|
dumb_resample_get_current_sample_2_1(resampler, volume_left, volume_right, dst);
|
|
}
|
|
|
|
|
|
|
|
void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst)
|
|
{
|
|
if (n == 8)
|
|
dumb_resample_get_current_sample_8_2_2(resampler, volume_left, volume_right, dst);
|
|
else if (n == 16)
|
|
dumb_resample_get_current_sample_16_2_2(resampler, volume_left, volume_right, dst);
|
|
else
|
|
dumb_resample_get_current_sample_2_2(resampler, volume_left, volume_right, dst);
|
|
}
|
|
|
|
|
|
|
|
void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler)
|
|
{
|
|
if (n == 8)
|
|
dumb_end_resampler_8(resampler);
|
|
else if (n == 16)
|
|
dumb_end_resampler_16(resampler);
|
|
else
|
|
dumb_end_resampler(resampler);
|
|
}
|