ref: 52b415ace8dee10d26984da1eabf36fc08f07f4c
parent: 2fa3b87ae99fc62a03ec26c81c81ae23f3ab7cec
author: Chris Moeller <kode54@gmail.com>
date: Fri Jan 11 14:27:12 EST 2013
- Implemented FIR resampler - Implemented one-time LPC extension of samples without permanent loops, to better facilitate declicking, and also to help flush the resamplers
--- a/dumb/include/dumb.h
+++ b/dumb/include/dumb.h
@@ -654,7 +654,8 @@
#define DUMB_RQ_ALIASING 0
#define DUMB_RQ_LINEAR 1
#define DUMB_RQ_CUBIC 2
-#define DUMB_RQ_N_LEVELS 3
+#define DUMB_RQ_FIR 3
+#define DUMB_RQ_N_LEVELS 4
extern int dumb_resampling_quality;
typedef struct DUMB_RESAMPLER DUMB_RESAMPLER;
@@ -664,6 +665,7 @@
typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data);
#include "internal/blip_buf.h"
+#include "internal/fir_resampler.h"
struct DUMB_RESAMPLER
{
@@ -685,6 +687,8 @@
int last_clock;
int last_amp[2];
blip_t* blip_buffer[2];
+ double fir_resampler_ratio;
+ void* fir_resampler[2];
};
struct DUMB_VOLUME_RAMP_INFO
--- /dev/null
+++ b/dumb/include/internal/fir_resampler.h
@@ -1,0 +1,18 @@
+#ifndef _FIR_RESAMPLER_H_
+#define _FIR_RESAMPLER_H_
+
+void fir_init();
+
+void * fir_resampler_create();
+void fir_resampler_delete(void *);
+void * fir_resampler_dup(void *);
+
+int fir_resampler_get_free_count(void *);
+void fir_resampler_write_sample(void *, short sample);
+void fir_resampler_set_rate( void *, double new_factor );
+int fir_resampler_ready(void *);
+void fir_resampler_clear(void *);
+int fir_resampler_get_sample(void *);
+void fir_resampler_remove_sample(void *);
+
+#endif
--- a/dumb/include/internal/it.h
+++ b/dumb/include/internal/it.h
@@ -414,6 +414,8 @@
#define IT_WAS_AN_STM 4096
+#define IT_WAS_PROCESSED 8192 /* Will be set the first time a sigdata passes through a sigrenderer */
+
#define IT_ORDER_END 255
#define IT_ORDER_SKIP 254
--- /dev/null
+++ b/dumb/include/internal/lpc.h
@@ -1,0 +1,30 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 *
+ * by the Xiph.Org Foundation http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: LPC low level routines
+ last mod: $Id: lpc.h 16037 2009-05-26 21:10:58Z xiphmont $
+
+ ********************************************************************/
+
+#ifndef _V_LPC_H_
+#define _V_LPC_H_
+
+/* simple linear scale LPC code */
+extern float vorbis_lpc_from_data(float *data,float *lpc,int n,int m);
+
+extern void vorbis_lpc_predict(float *coeff,float *prime,int m,
+ float *data,long n);
+
+struct DUMB_IT_SIGDATA;
+extern void dumb_it_add_lpc(struct DUMB_IT_SIGDATA *sigdata);
+
+#endif
--- /dev/null
+++ b/dumb/include/internal/stack_alloc.h
@@ -1,0 +1,113 @@
+/* Copyright (C) 2002 Jean-Marc Valin */
+/**
+ @file stack_alloc.h
+ @brief Temporary memory allocation on stack
+*/
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ - Neither the name of the Xiph.org Foundation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef STACK_ALLOC_H
+#define STACK_ALLOC_H
+
+#ifdef WIN32
+# include <malloc.h>
+#else
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# include <stdlib.h>
+# endif
+#endif
+
+/**
+ * @def ALIGN(stack, size)
+ *
+ * Aligns the stack to a 'size' boundary
+ *
+ * @param stack Stack
+ * @param size New size boundary
+ */
+
+/**
+ * @def PUSH(stack, size, type)
+ *
+ * Allocates 'size' elements of type 'type' on the stack
+ *
+ * @param stack Stack
+ * @param size Number of elements
+ * @param type Type of element
+ */
+
+/**
+ * @def VARDECL(var)
+ *
+ * Declare variable on stack
+ *
+ * @param var Variable to declare
+ */
+
+/**
+ * @def ALLOC(var, size, type)
+ *
+ * Allocate 'size' elements of 'type' on stack
+ *
+ * @param var Name of variable to allocate
+ * @param size Number of elements
+ * @param type Type of element
+ */
+
+#ifdef ENABLE_VALGRIND
+
+#include <valgrind/memcheck.h>
+
+#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
+
+#define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
+
+#else
+
+#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
+
+#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
+
+#endif
+
+#if defined(VAR_ARRAYS)
+#define VARDECL(var)
+#define ALLOC(var, size, type) type var[size]
+#elif defined(USE_ALLOCA)
+#define VARDECL(var) var
+#define ALLOC(var, size, type) var = alloca(sizeof(type)*(size))
+#else
+#define VARDECL(var) var
+#define ALLOC(var, size, type) var = PUSH(stack, size, type)
+#endif
+
+
+#endif
--- a/dumb/prj/dumb/dumb.pro
+++ b/dumb/prj/dumb/dumb.pro
@@ -97,7 +97,9 @@
../../src/it/readany.c \
../../src/it/loadany2.c \
../../src/it/loadany.c \
- ../../src/it/readany2.c
+ ../../src/it/readany2.c \
+ ../../src/helpers/fir_resampler.c \
+ ../../src/helpers/lpc.c
HEADERS += \
../../include/dumb.h \
@@ -106,7 +108,10 @@
../../include/internal/dumb.h \
../../include/internal/blip_buf.h \
../../include/internal/barray.h \
- ../../include/internal/aldumb.h
+ ../../include/internal/aldumb.h \
+ ../../include/internal/fir_resampler.h \
+ ../../include/internal/stack_alloc.h \
+ ../../include/internal/lpc.h
unix:!symbian {
maemo5 {
target.path = /opt/usr/lib
--- /dev/null
+++ b/dumb/src/helpers/fir_resampler.c
@@ -1,0 +1,258 @@
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "internal/fir_resampler.h"
+
+enum { fir_width = 16 };
+
+enum { fir_max_res = 1024 };
+enum { fir_min_width = (fir_width < 4 ? 4 : fir_width) };
+enum { fir_adj_width = fir_min_width / 4 * 4 + 2 };
+enum { fir_stereo = 1 }; /* channel count, not boolean value */
+enum { fir_write_offset = fir_adj_width * fir_stereo };
+
+enum { fir_buffer_size = fir_width * 2 };
+
+typedef short fir_impulse[fir_adj_width];
+
+static fir_impulse fir_impulses[fir_max_res];
+
+#undef PI
+#define PI 3.1415926535897932384626433832795029
+
+static void gen_sinc( double rolloff, int width, double offset, double spacing, double scale,
+ int count, short* out )
+{
+ double const maxh = 256;
+ double const step = PI / maxh * spacing;
+ double const to_w = maxh * 2 / width;
+ double const pow_a_n = pow( rolloff, maxh );
+ scale /= maxh * 2;
+
+ double angle = (count / 2 - 1 + offset) * -step;
+ while ( count-- )
+ {
+ *out++ = 0;
+ double w = angle * to_w;
+ if ( fabs( w ) < PI )
+ {
+ double rolloff_cos_a = rolloff * cos( angle );
+ double num = 1 - rolloff_cos_a -
+ pow_a_n * cos( maxh * angle ) +
+ pow_a_n * rolloff * cos( (maxh - 1) * angle );
+ double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
+ double sinc = scale * num / den - scale;
+
+ out [-1] = (short) (cos( w ) * sinc + sinc);
+ }
+ angle += step;
+ }
+}
+
+typedef struct fir_resampler
+{
+ int write_pos, write_filled;
+ int read_pos, read_filled;
+ unsigned short phase;
+ unsigned int phase_inc;
+ short buffer_in[fir_buffer_size * 2];
+ int buffer_out[fir_buffer_size];
+} fir_resampler;
+
+void * fir_resampler_create()
+{
+ fir_resampler * r = ( fir_resampler * ) malloc( sizeof(fir_resampler) );
+ if ( !r ) return 0;
+
+ r->write_pos = 0;
+ r->write_filled = 0;
+ r->read_pos = 0;
+ r->read_filled = 0;
+ r->phase = 0;
+ r->phase_inc = 0;
+ memset( r->buffer_in, 0, sizeof(r->buffer_in) );
+ memset( r->buffer_out, 0, sizeof(r->buffer_out) );
+
+ return r;
+}
+
+void fir_resampler_delete(void * _r)
+{
+ free( _r );
+}
+
+void * fir_resampler_dup(void * _r)
+{
+ fir_resampler * r_in = ( fir_resampler * ) _r;
+ fir_resampler * r_out = ( fir_resampler * ) malloc( sizeof(fir_resampler) );
+ if ( !r_out ) return 0;
+
+ r_out->write_pos = r_in->write_pos;
+ r_out->write_filled = r_in->write_filled;
+ r_out->read_pos = r_in->read_pos;
+ r_out->read_filled = r_in->read_filled;
+ r_out->phase = r_in->phase;
+ r_out->phase_inc = r_in->phase_inc;
+ memcpy( r_out->buffer_in, r_in->buffer_in, sizeof(r_in->buffer_in) );
+ memcpy( r_out->buffer_out, r_in->buffer_out, sizeof(r_in->buffer_out) );
+
+ return r_out;
+}
+
+int fir_resampler_get_free_count(void *_r)
+{
+ fir_resampler * r = ( fir_resampler * ) _r;
+ return fir_buffer_size - r->write_filled;
+}
+
+int fir_resampler_ready(void *_r)
+{
+ fir_resampler * r = ( fir_resampler * ) _r;
+ return r->write_filled > fir_adj_width;
+}
+
+void fir_resampler_clear(void *_r)
+{
+ fir_resampler * r = ( fir_resampler * ) _r;
+ r->write_pos = 0;
+ r->write_filled = 0;
+ r->read_pos = 0;
+ r->read_filled = 0;
+ r->phase = 0;
+ memset( r->buffer_in, 0, sizeof(r->buffer_in) );
+}
+
+void fir_resampler_write_sample(void *_r, short s)
+{
+ fir_resampler * r = ( fir_resampler * ) _r;
+
+ if ( r->write_filled < fir_buffer_size )
+ {
+ r->buffer_in[ r->write_pos ] = s;
+ r->buffer_in[ r->write_pos + fir_buffer_size ] = s;
+
+ ++r->write_filled;
+
+ r->write_pos = ( r->write_pos + 1 ) % fir_buffer_size;
+ }
+}
+
+void fir_resampler_set_rate(void *_r, double new_factor)
+{
+ fir_resampler * r = ( fir_resampler * ) _r;
+ r->phase_inc = (int)( new_factor * 65536.0 );
+}
+
+void fir_init()
+{
+ double const rolloff = 0.999;
+ double const gain = 1.0;
+
+ int const res = fir_max_res;
+
+ double const ratio_ = 1.0 / (double)fir_max_res;
+
+ double fraction = fmod( ratio_, 1.0 );
+
+ double const filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_;
+ double pos = 0.0;
+ //int input_per_cycle = 0;
+ short* out = fir_impulses;
+ int n;
+ for ( n = res; --n >= 0; )
+ {
+ gen_sinc( rolloff, (int) (fir_adj_width * filter + 1) & ~1, pos, filter,
+ (double) (0x7FFF * gain * filter), (int) fir_adj_width, out );
+ out += fir_adj_width;
+
+ pos += fraction;
+ }
+}
+
+int fir_resampler_run(void *_r, int ** out_, int * out_end)
+{
+ fir_resampler * r = ( fir_resampler * ) _r;
+ int in_size = r->write_filled;
+ short const* in_ = r->buffer_in + fir_buffer_size + r->write_pos - r->write_filled;
+ int used = 0;
+ in_size -= fir_write_offset;
+ if ( in_size > 0 )
+ {
+ int* out = *out_;
+ short const* in = in_;
+ short const* const in_end = in + in_size;
+ int phase = r->phase;
+ int phase_inc = r->phase_inc;
+
+ do
+ {
+ // accumulate in extended precision
+ short const* imp = fir_impulses[(phase & 0xFFC0) >> 6];
+ int pt = imp [0];
+ int s = pt * in [0];
+ int n;
+ if ( out >= out_end )
+ break;
+ for ( n = (fir_adj_width - 2) / 2; n; --n )
+ {
+ pt = imp [1];
+ s += pt * in [1];
+
+ // pre-increment more efficient on some RISC processors
+ imp += 2;
+ pt = imp [0];
+ in += 2;
+ s += pt * in [0];
+ }
+ pt = imp [1];
+ s += pt * in [1];
+
+ phase += phase_inc;
+
+ in += (phase >> 16) - fir_adj_width + 2;
+
+ phase &= 65535;
+
+ *out++ = (int) (s >> 7);
+ }
+ while ( in < in_end );
+
+ r->phase = phase;
+ *out_ = out;
+
+ used = in - in_;
+
+ r->write_filled -= used;
+ }
+
+ return used;
+}
+
+int fir_resampler_get_sample(void *_r)
+{
+ fir_resampler * r = ( fir_resampler * ) _r;
+ if ( r->read_filled < 1 )
+ {
+ int write_pos = ( r->read_pos + r->read_filled ) % fir_buffer_size;
+ int write_size = fir_buffer_size - write_pos;
+ int * out = r->buffer_out + write_pos;
+ if ( write_size > ( fir_buffer_size - r->read_filled ) )
+ write_size = fir_buffer_size - r->read_filled;
+ fir_resampler_run( r, &out, out + write_size );
+ r->read_filled += out - r->buffer_out - write_pos;
+ }
+ if ( r->read_filled < 1 )
+ return 0;
+ return r->buffer_out[ r->read_pos ];
+}
+
+void fir_resampler_remove_sample(void *_r)
+{
+ fir_resampler * r = ( fir_resampler * ) _r;
+ if ( r->read_filled > 0 )
+ {
+ --r->read_filled;
+ r->read_pos = ( r->read_pos + 1 ) % fir_buffer_size;
+ }
+}
--- /dev/null
+++ b/dumb/src/helpers/lpc.c
@@ -1,0 +1,297 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 *
+ * by the Xiph.Org Foundation http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: LPC low level routines
+ last mod: $Id: lpc.c 16227 2009-07-08 06:58:46Z xiphmont $
+
+ ********************************************************************/
+
+/* Some of these routines (autocorrelator, LPC coefficient estimator)
+ are derived from code written by Jutta Degener and Carsten Bormann;
+ thus we include their copyright below. The entirety of this file
+ is freely redistributable on the condition that both of these
+ copyright notices are preserved without modification. */
+
+/* Preserved Copyright: *********************************************/
+
+/* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
+Technische Universita"t Berlin
+
+Any use of this software is permitted provided that this notice is not
+removed and that neither the authors nor the Technische Universita"t
+Berlin are deemed to have made any representations as to the
+suitability of this software for any purpose nor are held responsible
+for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR
+THIS SOFTWARE.
+
+As a matter of courtesy, the authors request to be informed about uses
+this software has found, about bugs in this software, and about any
+improvements that may be of general interest.
+
+Berlin, 28.11.1994
+Jutta Degener
+Carsten Bormann
+
+*********************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "internal/stack_alloc.h"
+#include "internal/lpc.h"
+
+/* Autocorrelation LPC coeff generation algorithm invented by
+ N. Levinson in 1947, modified by J. Durbin in 1959. */
+
+/* Input : n elements of time doamin data
+ Output: m lpc coefficients, excitation energy */
+
+float vorbis_lpc_from_data(float *data,float *lpci,int n,int m){
+ double *aut=alloca(sizeof(*aut)*(m+1));
+ double *lpc=alloca(sizeof(*lpc)*(m));
+ double error;
+ double epsilon;
+ int i,j;
+
+ /* autocorrelation, p+1 lag coefficients */
+ j=m+1;
+ while(j--){
+ double d=0; /* double needed for accumulator depth */
+ for(i=j;i<n;i++)d+=(double)data[i]*data[(i-j)];
+ aut[j]=d;
+ }
+
+ /* Generate lpc coefficients from autocorr values */
+
+ /* set our noise floor to about -100dB */
+ error=aut[0] * (1. + 1e-10);
+ epsilon=1e-9*aut[0]+1e-10;
+
+ for(i=0;i<m;i++){
+ double r= -aut[i+1];
+
+ if(error<epsilon){
+ memset(lpc+i,0,(m-i)*sizeof(*lpc));
+ goto done;
+ }
+
+ /* Sum up this iteration's reflection coefficient; note that in
+ Vorbis we don't save it. If anyone wants to recycle this code
+ and needs reflection coefficients, save the results of 'r' from
+ each iteration. */
+
+ for(j=0;j<i;j++)r-=lpc[j]*aut[i-j];
+ r/=error;
+
+ /* Update LPC coefficients and total error */
+
+ lpc[i]=r;
+ for(j=0;j<i/2;j++){
+ double tmp=lpc[j];
+
+ lpc[j]+=r*lpc[i-1-j];
+ lpc[i-1-j]+=r*tmp;
+ }
+ if(i&1)lpc[j]+=lpc[j]*r;
+
+ error*=1.-r*r;
+
+ }
+
+ done:
+
+ /* slightly damp the filter */
+ {
+ double g = .99;
+ double damp = g;
+ for(j=0;j<m;j++){
+ lpc[j]*=damp;
+ damp*=g;
+ }
+ }
+
+ for(j=0;j<m;j++)lpci[j]=(float)lpc[j];
+
+ /* we need the error value to know how big an impulse to hit the
+ filter with later */
+
+ return error;
+}
+
+void vorbis_lpc_predict(float *coeff,float *prime,int m,
+ float *data,long n){
+
+ /* in: coeff[0...m-1] LPC coefficients
+ prime[0...m-1] initial values (allocated size of n+m-1)
+ out: data[0...n-1] data samples */
+
+ long i,j,o,p;
+ float y;
+ float *work=alloca(sizeof(*work)*(m+n));
+
+ if(!prime)
+ for(i=0;i<m;i++)
+ work[i]=0.f;
+ else
+ for(i=0;i<m;i++)
+ work[i]=prime[i];
+
+ for(i=0;i<n;i++){
+ y=0;
+ o=i;
+ p=m;
+ for(j=0;j<m;j++)
+ y-=work[o++]*coeff[--p];
+
+ data[i]=work[o]=y;
+ }
+}
+
+#include "dumb.h"
+#include "internal/dumb.h"
+#include "internal/it.h"
+
+void dumb_it_add_lpc(struct DUMB_IT_SIGDATA *sigdata){
+ const int lpc_max = 256;
+ const int lpc_order = 32;
+ const int lpc_extra = 64;
+
+ float lpc[lpc_order * 2];
+ float lpc_input[lpc_max * 2];
+ float lpc_output[lpc_extra * 2];
+
+ signed char * s8;
+ signed short * s16;
+
+ int n, o, offset, lpc_samples;
+
+ for ( n = 0; n < sigdata->n_samples; n++ ) {
+ IT_SAMPLE * sample = sigdata->sample + n;
+ if ( ( ( sample->flags & ( IT_SAMPLE_EXISTS | IT_SAMPLE_LOOP) ) == IT_SAMPLE_EXISTS ) &&
+ sample->length >= lpc_order ) {
+ lpc_samples = sample->length;
+ if (lpc_samples > lpc_max) lpc_samples = lpc_max;
+ offset = sample->length - lpc_samples;
+
+ if ( sample->flags & IT_SAMPLE_STEREO )
+ {
+ if ( sample->flags & IT_SAMPLE_16BIT )
+ {
+ s16 = ( signed short * ) sample->data;
+ s16 += offset * 2;
+ for ( o = 0; o < lpc_samples; o++ )
+ {
+ lpc_input[ o ] = s16[ o * 2 + 0 ];
+ lpc_input[ o + lpc_max ] = s16[ o * 2 + 1 ];
+ }
+ }
+ else
+ {
+ s8 = ( signed char * ) sample->data;
+ s8 += offset * 2;
+ for ( o = 0; o < lpc_samples; o++ )
+ {
+ lpc_input[ o ] = s8[ o * 2 + 0 ];
+ lpc_input[ o + lpc_max ] = s8[ o * 2 + 1 ];
+ }
+ }
+
+ vorbis_lpc_from_data( lpc_input, lpc, lpc_samples, lpc_order );
+ vorbis_lpc_from_data( lpc_input + lpc_max, lpc + lpc_order, lpc_samples, lpc_order );
+
+ vorbis_lpc_predict( lpc, lpc_input + lpc_samples - lpc_order, lpc_order, lpc_output, lpc_extra );
+ vorbis_lpc_predict( lpc + lpc_order, lpc_input + lpc_max + lpc_samples - lpc_order, lpc_order, lpc_output + lpc_extra, lpc_extra );
+
+ if ( sample->flags & IT_SAMPLE_16BIT )
+ {
+ s16 = ( signed short * ) realloc( sample->data, ( sample->length + lpc_extra ) * 2 * sizeof(short) );
+ sample->data = s16;
+
+ s16 += sample->length * 2;
+ sample->length += lpc_extra;
+
+ for ( o = 0; o < lpc_extra; o++ )
+ {
+ s16[ o * 2 + 0 ] = lpc_output[ o ];
+ s16[ o * 2 + 1 ] = lpc_output[ o + lpc_extra ];
+ }
+ }
+ else
+ {
+ s8 = ( signed char * ) realloc( sample->data, ( sample->length + lpc_extra ) * 2 );
+ sample->data = s8;
+
+ s8 += sample->length * 2;
+ sample->length += lpc_extra;
+
+ for ( o = 0; o < lpc_extra; o++ )
+ {
+ s8[ o * 2 + 0 ] = lpc_output[ o ];
+ s8[ o * 2 + 1 ] = lpc_output[ o + lpc_extra ];
+ }
+ }
+ }
+ else
+ {
+ if ( sample->flags & IT_SAMPLE_16BIT )
+ {
+ s16 = ( signed short * ) sample->data;
+ s16 += offset;
+ for ( o = 0; o < lpc_samples; o++ )
+ {
+ lpc_input[ o ] = s16[ o ];
+ }
+ }
+ else
+ {
+ s8 = ( signed char * ) sample->data;
+ s8 += offset;
+ for ( o = 0; o < lpc_samples; o++ )
+ {
+ lpc_input[ o ] = s8[ o ];
+ }
+ }
+
+ vorbis_lpc_from_data( lpc_input, lpc, lpc_samples, lpc_order );
+
+ vorbis_lpc_predict( lpc, lpc_input + lpc_samples - lpc_order, lpc_order, lpc_output, lpc_extra );
+
+ if ( sample->flags & IT_SAMPLE_16BIT )
+ {
+ s16 = ( signed short * ) realloc( sample->data, ( sample->length + lpc_extra ) * sizeof(short) );
+ sample->data = s16;
+
+ s16 += sample->length;
+ sample->length += lpc_extra;
+
+ for ( o = 0; o < lpc_extra; o++ )
+ {
+ s16[ o ] = lpc_output[ o ];
+ }
+ }
+ else
+ {
+ s8 = ( signed char * ) realloc( sample->data, sample->length + lpc_extra );
+ sample->data = s8;
+
+ s8 += sample->length;
+ sample->length += lpc_extra;
+
+ for ( o = 0; o < lpc_extra; o++ )
+ {
+ s8[ o ] = lpc_output[ o ];
+ }
+ }
+ }
+ }
+ }
+}
--- a/dumb/src/helpers/resamp2.inc
+++ b/dumb/src/helpers/resamp2.inc
@@ -96,6 +96,8 @@
#define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO
#define MIX_ALIAS(count) MONO_DEST_MIX_ALIAS(count)
#define PEEK_ALIAS MONO_DEST_PEEK_ALIAS
+#define PEEK_FIR MONO_DEST_PEEK_FIR
+#define MIX_FIR MONO_DEST_MIX_FIR
#define MIX_LINEAR(op, upd, o0, o1) MONO_DEST_MIX_LINEAR(op, upd, o0, o1)
#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3)
#define MIX_ZEROS(op) *dst++ op 0
@@ -136,6 +138,8 @@
#define VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0)
#define MIX_ALIAS(count) STEREO_DEST_MIX_ALIAS(count)
#define PEEK_ALIAS STEREO_DEST_PEEK_ALIAS
+#define PEEK_FIR STEREO_DEST_PEEK_FIR
+#define MIX_FIR STEREO_DEST_MIX_FIR
#define MIX_LINEAR(op, upd, o0, o1) STEREO_DEST_MIX_LINEAR(op, upd, o0, o1)
#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3)
#define MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; }
@@ -158,6 +162,12 @@
#undef STEREO_DEST_PEEK_ALIAS
#undef MONO_DEST_PEEK_ALIAS
#undef POKE_ALIAS
+#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
--- a/dumb/src/helpers/resamp3.inc
+++ b/dumb/src/helpers/resamp3.inc
@@ -60,7 +60,7 @@
done = 0;
dt = (int)(delta * 65536.0 + 0.5);
if (dt == 0 || dt == 0x80000000) return 0;
- inv_dt = (int)(1.0 / delta * 65536.0 + 0.5);
+ inv_dt = (int)(1.0 / delta * 65536.0 + 0.5);
SET_VOLUME_VARIABLES;
if (VOLUMES_ARE_ZERO) dst = NULL;
@@ -70,7 +70,7 @@
quality = resampler->quality;
while (done < dst_size) {
- if (process_pickup(resampler)) {
+ if (process_pickup(resampler)) {
RETURN_VOLUME_VARIABLES;
return done;
}
@@ -159,7 +159,7 @@
x += (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
);
- } else {
+ } else if (quality <= DUMB_RQ_CUBIC) {
/* Cubic interpolation, backwards */
SRCTYPE xbuf[6*SRC_CHANNELS];
SRCTYPE *x = &xbuf[3*SRC_CHANNELS];
@@ -187,7 +187,40 @@
x += (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
);
- }
+ } else {
+ /* FIR resampling, backwards */
+ SRCTYPE xbuf[2*SRC_CHANNELS];
+ SRCTYPE *x = &xbuf[0];
+ COPYSRC(xbuf, 0, resampler->X, 1);
+ COPYSRC(xbuf, 1, resampler->X, 2);
+ if ( resampler->fir_resampler_ratio != delta ) {
+ fir_resampler_set_rate( resampler->fir_resampler[0], delta );
+ fir_resampler_set_rate( resampler->fir_resampler[1], delta );
+ resampler->fir_resampler_ratio = delta;
+ }
+ while (fir_resampler_get_free_count( resampler->fir_resampler[0] ) && x < &xbuf[2*SRC_CHANNELS]) {
+ // TODO: check what happens when multiple tempo slides occur per row
+ HEAVYASSERT(pos >= resampler->start);
+ POKE_FIR(0);
+ pos--;
+ x += SRC_CHANNELS;
+ }
+ x = &src[pos*SRC_CHANNELS];
+ while ( todo ) {
+ while ( fir_resampler_get_free_count( resampler->fir_resampler[0] ) &&
+ pos >= resampler->start )
+ {
+ POKE_FIR(2);
+ pos--;
+ x -= SRC_CHANNELS;
+ }
+ if ( !fir_resampler_ready( resampler->fir_resampler[0] ) ) break;
+ MIX_FIR;
+ ADVANCE_FIR;
+ --todo;
+ }
+ done -= todo;
+ }
diff = diff - pos;
overshot = resampler->start - pos - 1;
if (diff >= 3) {
@@ -262,7 +295,7 @@
x += (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
);
- } else {
+ } else if (quality <= DUMB_RQ_CUBIC) {
/* Cubic interpolation, forwards */
SRCTYPE xbuf[6*SRC_CHANNELS];
SRCTYPE *x = &xbuf[3*SRC_CHANNELS];
@@ -290,7 +323,39 @@
x += (subpos >> 16) * SRC_CHANNELS;
subpos &= 65535;
);
- }
+ } else {
+ /* FIR resampling, forwards */
+ SRCTYPE xbuf[2*SRC_CHANNELS];
+ SRCTYPE *x = &xbuf[0];
+ COPYSRC(xbuf, 0, resampler->X, 1);
+ COPYSRC(xbuf, 1, resampler->X, 2);
+ if ( resampler->fir_resampler_ratio != delta ) {
+ fir_resampler_set_rate( resampler->fir_resampler[0], delta );
+ fir_resampler_set_rate( resampler->fir_resampler[1], delta );
+ resampler->fir_resampler_ratio = delta;
+ }
+ while (fir_resampler_get_free_count( resampler->fir_resampler[0] ) && x < &xbuf[2*SRC_CHANNELS]) {
+ HEAVYASSERT(pos < resampler->end);
+ POKE_FIR(0);
+ pos++;
+ x += SRC_CHANNELS;
+ }
+ x = &src[pos*SRC_CHANNELS];
+ while ( todo ) {
+ while ( fir_resampler_get_free_count( resampler->fir_resampler[0] ) &&
+ pos < resampler->end )
+ {
+ POKE_FIR(-2);
+ pos++;
+ x += SRC_CHANNELS;
+ }
+ if ( !fir_resampler_ready( resampler->fir_resampler[0] ) ) break;
+ MIX_FIR;
+ ADVANCE_FIR;
+ --todo;
+ }
+ done -= todo;
+ }
diff = pos - diff;
overshot = pos - resampler->end;
if (diff >= 3) {
@@ -353,10 +418,13 @@
} else if (quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, backwards */
MIX_LINEAR(=, 0, 2, 1);
- } else {
+ } else if (quality <= DUMB_RQ_CUBIC) {
/* Cubic interpolation, backwards */
MIX_CUBIC(=, 0, src, x, pos, 2, 1, 0);
- }
+ } else {
+ /* FIR resampling, backwards */
+ PEEK_FIR;
+ }
} else {
HEAVYASSERT(pos < resampler->end);
if (quality <= DUMB_RQ_ALIASING) {
@@ -365,10 +433,13 @@
} else if (quality <= DUMB_RQ_LINEAR) {
/* Linear interpolation, forwards */
MIX_LINEAR(=, 0, 1, 2);
- } else {
+ } else if (quality <= DUMB_RQ_CUBIC) {
/* Cubic interpolation, forwards */
MIX_CUBIC(=, 0, x, src, 0, 1, 2, pos);
- }
+ } else {
+ /* FIR resampling, forwards */
+ PEEK_FIR;
+ }
}
}
--- a/dumb/src/helpers/resample.c
+++ b/dumb/src/helpers/resample.c
@@ -71,9 +71,10 @@
*
* 0 - DUMB_RQ_ALIASING - fastest
* 1 - DUMB_RQ_LINEAR
- * 2 - DUMB_RQ_CUBIC - nicest
+ * 2 - DUMB_RQ_CUBIC
+ * 3 - DUMB_RQ_FIR - nicest
*
- * Values outside the range 0-2 will behave the same as the nearest
+ * Values outside the range 0-3 will behave the same as the nearest
* value within the range.
*/
int dumb_resampling_quality = DUMB_RQ_CUBIC;
@@ -167,6 +168,8 @@
cubicA0[t] = -(int)( t*t*t >> 17) + (int)( t*t >> 6) - (int)(t << 3);
cubicA1[t] = (int)(3*t*t*t >> 17) - (int)(5*t*t >> 7) + (int)(1 << 14);
}
+
+ fir_init();
}
@@ -185,6 +188,7 @@
#define SRCTYPE sample_t
#define SRCBITS 24
#define ALIAS(x) (x >> 8)
+#define FIR(x) (x >> 8)
#define LINEAR(x0, x1) (x0 + MULSC(x1 - x0, subpos))
/*
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
@@ -221,6 +225,7 @@
#define SRCTYPE short
#define SRCBITS 16
#define ALIAS(x) (x)
+#define FIR(x) (x)
#define LINEAR(x0, x1) ((x0 << 8) + MULSC16(x1 - x0, subpos))
/*
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
@@ -243,6 +248,7 @@
#define SRCTYPE signed char
#define SRCBITS 8
#define ALIAS(x) (x << 8)
+#define FIR(x) (x << 8)
#define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos)
/*
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
--- a/dumb/src/helpers/resample.inc
+++ b/dumb/src/helpers/resample.inc
@@ -74,6 +74,9 @@
resampler->last_amp[1] = 0;
blip_clear(resampler->blip_buffer[0]);
blip_clear(resampler->blip_buffer[1]);
+ resampler->fir_resampler_ratio = 0;
+ fir_resampler_clear(resampler->fir_resampler[0]);
+ fir_resampler_clear(resampler->fir_resampler[1]);
}
@@ -149,7 +152,16 @@
if ( delta ) blip_add_delta( resampler->blip_buffer[0], resampler->last_clock, delta ); \
resampler->last_clock += inv_dt; \
}
+#define POKE_FIR(offset) { \
+ fir_resampler_write_sample( resampler->fir_resampler[0], FIR(x[offset]) ); \
+}
#define MONO_DEST_PEEK_ALIAS *dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), vol )
+#define MONO_DEST_PEEK_FIR *dst = MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), vol )
+#define MONO_DEST_MIX_FIR { \
+ *dst++ += MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), vol ); \
+ UPDATE_VOLUME( volume, vol ); \
+}
+#define ADVANCE_FIR fir_resampler_remove_sample( resampler->fir_resampler[0] )
#define MONO_DEST_MIX_ALIAS(count) { \
int n = 0; \
resampler->last_clock -= count * 65536; \
@@ -166,6 +178,18 @@
*dst++ = MULSC( sample, lvol ); \
*dst++ = MULSC( sample, rvol ); \
}
+#define STEREO_DEST_PEEK_FIR { \
+ int sample = fir_resampler_get_sample( resampler->fir_resampler[0] ); \
+ *dst++ = MULSC( sample, lvol ); \
+ *dst++ = MULSC( sample, rvol ); \
+}
+#define STEREO_DEST_MIX_FIR { \
+ int sample = fir_resampler_get_sample( resampler->fir_resampler[0] ); \
+ *dst++ += MULSC( sample, lvol ); \
+ *dst++ += MULSC( sample, rvol ); \
+ UPDATE_VOLUME( volume_left, lvol ); \
+ UPDATE_VOLUME( volume_right, rvol ); \
+}
#define STEREO_DEST_MIX_ALIAS(count) { \
int sample, n = 0; \
resampler->last_clock -= count * 65536; \
@@ -262,10 +286,28 @@
if ( deltar ) blip_add_delta( resampler->blip_buffer[1], resampler->last_clock, deltar ); \
resampler->last_clock += inv_dt; \
}
+#define POKE_FIR(offset) { \
+ fir_resampler_write_sample( resampler->fir_resampler[0], FIR(x[(offset)*2+0]) ); \
+ fir_resampler_write_sample( resampler->fir_resampler[1], FIR(x[(offset)*2+1]) ); \
+}
#define MONO_DEST_PEEK_ALIAS { \
*dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ) + \
MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \
}
+#define MONO_DEST_PEEK_FIR { \
+ *dst = MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \
+ MULSC( fir_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
+}
+#define MONO_DEST_MIX_FIR { \
+ *dst++ += MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \
+ MULSC( fir_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
+ UPDATE_VOLUME( volume_left, lvol ); \
+ UPDATE_VOLUME( volume_right, rvol ); \
+}
+#define ADVANCE_FIR { \
+ fir_resampler_remove_sample( resampler->fir_resampler[0] ); \
+ fir_resampler_remove_sample( resampler->fir_resampler[1] ); \
+}
#define MONO_DEST_MIX_ALIAS(count) { \
int n = 0; \
resampler->last_clock -= count * 65536; \
@@ -284,6 +326,16 @@
*dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ); \
*dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \
}
+#define STEREO_DEST_PEEK_FIR { \
+ *dst++ = MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \
+ *dst++ = MULSC( fir_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
+}
+#define STEREO_DEST_MIX_FIR { \
+ *dst++ += MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \
+ *dst++ += MULSC( fir_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
+ UPDATE_VOLUME( volume_left, lvol ); \
+ UPDATE_VOLUME( volume_right, rvol ); \
+}
#define STEREO_DEST_MIX_ALIAS(count) { \
int n = 0; \
resampler->last_clock -= count * 65536; \
@@ -339,6 +391,7 @@
#undef CUBIC
#undef LINEAR
#undef ALIAS
+#undef FIR
#undef SRCBITS
#undef SRCTYPE
#undef SUFFIX
--- a/dumb/src/it/itrender.c
+++ b/dumb/src/it/itrender.c
@@ -23,6 +23,7 @@
#include "dumb.h"
#include "internal/dumb.h"
#include "internal/it.h"
+#include "internal/lpc.h"
// #define BIT_ARRAY_BULLSHIT
@@ -49,12 +50,32 @@
}
blip_set_rates(r->resampler.blip_buffer[0], 65536, 1);
blip_set_rates(r->resampler.blip_buffer[1], 65536, 1);
- }
+ r->resampler.fir_resampler_ratio = 0.0;
+ r->resampler.fir_resampler[0] = fir_resampler_create();
+ if ( !r->resampler.fir_resampler[0] )
+ {
+ free( r->resampler.blip_buffer[1] );
+ free( r->resampler.blip_buffer[0] );
+ free( r );
+ return NULL;
+ }
+ r->resampler.fir_resampler[1] = fir_resampler_create();
+ if ( !r->resampler.fir_resampler[1] )
+ {
+ fir_resampler_delete( r->resampler.fir_resampler[0] );
+ free( r->resampler.blip_buffer[1] );
+ free( r->resampler.blip_buffer[0] );
+ free( r );
+ return NULL;
+ }
+ }
return r;
}
static void free_playing(IT_PLAYING * r)
{
+ fir_resampler_delete( r->resampler.fir_resampler[1] );
+ fir_resampler_delete( r->resampler.fir_resampler[0] );
blip_delete( r->resampler.blip_buffer[1] );
blip_delete( r->resampler.blip_buffer[0] );
free( r );
@@ -162,6 +183,24 @@
free( dst );
return NULL;
}
+ dst->resampler.fir_resampler_ratio = src->resampler.fir_resampler_ratio;
+ dst->resampler.fir_resampler[0] = fir_resampler_dup( src->resampler.fir_resampler[0] );
+ if ( !dst->resampler.fir_resampler[0] )
+ {
+ blip_delete( dst->resampler.blip_buffer[1] );
+ blip_delete( dst->resampler.blip_buffer[0] );
+ free( dst );
+ return NULL;
+ }
+ dst->resampler.fir_resampler[1] = fir_resampler_dup( src->resampler.fir_resampler[1] );
+ if ( !dst->resampler.fir_resampler[1] )
+ {
+ fir_resampler_delete( dst->resampler.fir_resampler[0] );
+ blip_delete( dst->resampler.blip_buffer[1] );
+ blip_delete( dst->resampler.blip_buffer[0] );
+ free( dst );
+ return NULL;
+ }
dst->time_lost = src->time_lost;
//dst->output = src->output;
@@ -841,11 +880,11 @@
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
@@ -1806,7 +1845,7 @@
if (channel->playing)
free_playing(channel->playing);
- channel->playing = new_playing();
+ channel->playing = new_playing();
if (!channel->playing)
return;
@@ -3438,7 +3477,7 @@
channel->destnote = IT_NOTE_OFF;
if (!channel->playing) {
- channel->playing = new_playing();
+ channel->playing = new_playing();
if (!channel->playing) {
if (playing) free_playing(playing);
return;
@@ -5187,7 +5226,7 @@
}
}
- destroy_sample_buffer(samples_to_filter);
+ destroy_sample_buffer(samples_to_filter);
for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
if (sigrenderer->channel[i].playing) {
@@ -5379,6 +5418,12 @@
sigrenderer->gvz_sub_time = 0;
//sigrenderer->max_output = 0;
+
+ if ( !(sigdata->flags & IT_WAS_PROCESSED) ) {
+ dumb_it_add_lpc( sigdata );
+
+ sigdata->flags |= IT_WAS_PROCESSED;
+ }
return sigrenderer;
}