ref: b8a5d88fa56fb7432298be19059a88fc2a4e7750
dir: /src/helpers/resamp3.inc/
/* _______ ____ __ ___ ___ * \ _ \ \ / \ / \ \ / / ' ' ' * | | \ \ | | || | \/ | . . * | | | | | | || ||\ /| | * | | | | | | || || \/ | | ' ' ' * | | | | | | || || | | . . * | |_/ / \ \__// || | | * /_______/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