diff options
Diffstat (limited to 'pph/resample.c')
-rw-r--r-- | pph/resample.c | 213 |
1 files changed, 155 insertions, 58 deletions
diff --git a/pph/resample.c b/pph/resample.c index 281cb32..1cc4d49 100644 --- a/pph/resample.c +++ b/pph/resample.c @@ -56,9 +56,9 @@ TODO list: #ifdef OUTSIDE_SPEEX #include <stdlib.h> -void *speex_alloc (int size) {return calloc(size,1);} -void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);} -void speex_free (void *ptr) {free(ptr);} +static void *speex_alloc (int size) {return calloc(size,1);} +static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);} +static void speex_free (void *ptr) {free(ptr);} #include "speex_resampler.h" #include "arch.h" #else /* OUTSIDE_SPEEX */ @@ -84,7 +84,11 @@ void speex_free (void *ptr) {free(ptr);} #define OVERSAMPLE 8 #define IMAX(a,b) ((a) > (b) ? (a) : (b)) +#define IMIN(a,b) ((a) < (b) ? (a) : (b)) +#ifndef NULL +#define NULL 0 +#endif typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *); @@ -205,12 +209,12 @@ static const struct QualityMapping quality_map[11] = { { 32, 4, 0.882f, 0.910f, KAISER6 }, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */ { 48, 8, 0.895f, 0.917f, KAISER8 }, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */ { 64, 8, 0.921f, 0.940f, KAISER8 }, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */ - { 80, 8, 0.922f, 0.940f, KAISER10}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */ - { 96, 8, 0.940f, 0.945f, KAISER10}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */ + { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */ + { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */ {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */ {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */ - {192, 16, 0.968f, 0.968f, KAISER12}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */ - {256, 16, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */ + {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */ + {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */ }; /*8,24,40,56,80,104,128,160,200,256,320*/ static double compute_func(float x, struct FuncDef *func) @@ -545,16 +549,23 @@ static void update_filter(SpeexResamplerState *st) st->filt_len = st->filt_len*st->num_rate / st->den_rate; /* Round down to make sure we have a multiple of 4 */ st->filt_len &= (~0x3); + if (2*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (4*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (8*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (16*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (st->oversample < 1) + st->oversample = 1; } else { /* up-sampling */ st->cutoff = quality_map[st->quality].upsample_bandwidth; } /* Choose the resampling type that requires the least amount of memory */ - /* FIXME: The direct sinc table can be noisy in some conditions. - * Disable it as a temporary workaround. - */ - if (0 /*st->den_rate <= st->oversample*/) + if (st->den_rate <= st->oversample) { spx_uint32_t i; if (!st->sinc_table) @@ -566,10 +577,10 @@ static void update_filter(SpeexResamplerState *st) } for (i=0;i<st->den_rate;i++) { - spx_uint32_t j; + spx_int32_t j; for (j=0;j<st->filt_len;j++) { - st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func); + st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func); } } #ifdef FIXED_POINT @@ -605,6 +616,10 @@ static void update_filter(SpeexResamplerState *st) st->int_advance = st->num_rate/st->den_rate; st->frac_advance = st->num_rate%st->den_rate; + + /* Here's the place where we update the filter memory to take into account + the change in filter length. It's probably the messiest part of the code + due to handling of lots of corner cases. */ if (!st->mem) { spx_uint32_t i; @@ -623,7 +638,7 @@ static void update_filter(SpeexResamplerState *st) /*speex_warning("reinit filter");*/ } else if (st->filt_len > old_length) { - spx_uint32_t i; + spx_int32_t i; /* Increase the filter length */ /*speex_warning("increase filter size");*/ int old_alloc_size = st->mem_alloc_size; @@ -632,46 +647,76 @@ static void update_filter(SpeexResamplerState *st) st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t)); st->mem_alloc_size = st->filt_len-1; } - for (i=0;i<st->nb_channels;i++) + for (i=st->nb_channels-1;i>=0;i--) { - spx_uint32_t j; - /* Copy data going backward */ - for (j=0;j<old_length-1;j++) - st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*old_alloc_size+(old_length-2-j)]; - /* Then put zeros for lack of anything better */ - for (;j<st->filt_len-1;j++) - st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0; - /* Adjust last_sample */ - st->last_sample[i] += (st->filt_len - old_length)/2; + spx_int32_t j; + spx_uint32_t olen = old_length; + /*if (st->magic_samples[i])*/ + { + /* Try and remove the magic samples as if nothing had happened */ + + /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */ + olen = old_length + 2*st->magic_samples[i]; + for (j=old_length-2+st->magic_samples[i];j>=0;j--) + st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j]; + for (j=0;j<st->magic_samples[i];j++) + st->mem[i*st->mem_alloc_size+j] = 0; + st->magic_samples[i] = 0; + } + if (st->filt_len > olen) + { + /* If the new filter length is still bigger than the "augmented" length */ + /* Copy data going backward */ + for (j=0;j<olen-1;j++) + st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)]; + /* Then put zeros for lack of anything better */ + for (;j<st->filt_len-1;j++) + st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0; + /* Adjust last_sample */ + st->last_sample[i] += (st->filt_len - olen)/2; + } else { + /* Put back some of the magic! */ + st->magic_samples[i] = (olen - st->filt_len)/2; + for (j=0;j<st->filt_len-1+st->magic_samples[i];j++) + st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; + } } } else if (st->filt_len < old_length) { spx_uint32_t i; - /* Reduce filter length, this a bit tricky */ - /*speex_warning("decrease filter size (unimplemented)");*/ - /* Adjust last_sample (which will likely end up negative) */ - /*st->last_sample += (st->filt_len - old_length)/2;*/ + /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic" + samples so they can be used directly as input the next time(s) */ for (i=0;i<st->nb_channels;i++) { spx_uint32_t j; + spx_uint32_t old_magic = st->magic_samples[i]; st->magic_samples[i] = (old_length - st->filt_len)/2; + /* We must copy some of the memory that's no longer used */ /* Copy data going backward */ - for (j=0;j<st->filt_len-1+st->magic_samples[i];j++) + for (j=0;j<st->filt_len-1+st->magic_samples[i]+old_magic;j++) st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; + st->magic_samples[i] += old_magic; } } } -SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality) +SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) { - return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality); + return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err); } -SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality) +SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) { spx_uint32_t i; - SpeexResamplerState *st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState)); + SpeexResamplerState *st; + if (quality > 10 || quality < 0) + { + if (err) + *err = RESAMPLER_ERR_INVALID_ARG; + return NULL; + } + st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState)); st->initialised = 0; st->started = 0; st->in_rate = 0; @@ -708,6 +753,9 @@ SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uin update_filter(st); st->initialised = 1; + if (err) + *err = RESAMPLER_ERR_SUCCESS; + return st; } @@ -723,7 +771,7 @@ void speex_resampler_destroy(SpeexResamplerState *st) -static void speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) { int j=0; int N = st->filt_len; @@ -736,15 +784,19 @@ static void speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t /* Handle the case where we have samples left from a reduction in filter length */ if (st->magic_samples[channel_index]) { + int istride_save; spx_uint32_t tmp_in_len; spx_uint32_t tmp_magic; + + istride_save = st->in_stride; tmp_in_len = st->magic_samples[channel_index]; tmp_out_len = *out_len; - /* FIXME: Need to handle the case where the out array is too small */ /* magic_samples needs to be set to zero to avoid infinite recursion */ tmp_magic = st->magic_samples[channel_index]; st->magic_samples[channel_index] = 0; + st->in_stride = 1; speex_resampler_process_native(st, channel_index, mem+N-1, &tmp_in_len, out, &tmp_out_len); + st->in_stride = istride_save; /*speex_warning_int("extra samples:", tmp_out_len);*/ /* If we couldn't process all "magic" input samples, save the rest for next time */ if (tmp_in_len < tmp_magic) @@ -754,7 +806,8 @@ static void speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t for (i=0;i<st->magic_samples[channel_index];i++) mem[N-1+i]=mem[N-1+i+tmp_in_len]; } - out += tmp_out_len; + out += tmp_out_len*st->out_stride; + *out_len -= tmp_out_len; } /* Call the right resampler through the function ptr */ @@ -770,12 +823,13 @@ static void speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t for (;j<N-1;j++) mem[j] = in[st->in_stride*(j+*in_len-N+1)]; + return RESAMPLER_ERR_SUCCESS; } #define FIXED_STACK_ALLOC 1024 #ifdef FIXED_POINT -void speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) { spx_uint32_t i; int istride_save, ostride_save; @@ -827,17 +881,18 @@ void speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel *in_len -= ilen; *out_len -= olen; #endif + return RESAMPLER_ERR_SUCCESS; } -void speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) { - speex_resampler_process_native(st, channel_index, in, in_len, out, out_len); + return speex_resampler_process_native(st, channel_index, in, in_len, out, out_len); } #else -void speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) { - speex_resampler_process_native(st, channel_index, in, in_len, out, out_len); + return speex_resampler_process_native(st, channel_index, in, in_len, out, out_len); } -void speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) { spx_uint32_t i; int istride_save, ostride_save; @@ -889,42 +944,50 @@ void speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_i *in_len -= ilen; *out_len -= olen; #endif + return RESAMPLER_ERR_SUCCESS; } #endif -void speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) { spx_uint32_t i; int istride_save, ostride_save; + spx_uint32_t bak_len = *out_len; istride_save = st->in_stride; ostride_save = st->out_stride; st->in_stride = st->out_stride = st->nb_channels; for (i=0;i<st->nb_channels;i++) { + *out_len = bak_len; speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len); } st->in_stride = istride_save; st->out_stride = ostride_save; + return RESAMPLER_ERR_SUCCESS; } -void speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) + +int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) { spx_uint32_t i; int istride_save, ostride_save; + spx_uint32_t bak_len = *out_len; istride_save = st->in_stride; ostride_save = st->out_stride; st->in_stride = st->out_stride = st->nb_channels; for (i=0;i<st->nb_channels;i++) { + *out_len = bak_len; speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len); } st->in_stride = istride_save; st->out_stride = ostride_save; + return RESAMPLER_ERR_SUCCESS; } -void speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate) +int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate) { - speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate); + return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate); } void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate) @@ -933,18 +996,21 @@ void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, sp *out_rate = st->out_rate; } -void speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate) +int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate) { - int fact; + spx_uint32_t fact; + spx_uint32_t old_den; + spx_uint32_t i; if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den) - return; + return RESAMPLER_ERR_SUCCESS; + old_den = st->den_rate; st->in_rate = in_rate; st->out_rate = out_rate; st->num_rate = ratio_num; st->den_rate = ratio_den; /* FIXME: This is terribly inefficient, but who cares (at least for now)? */ - for (fact=2;fact<=sqrt(IMAX(in_rate, out_rate));fact++) + for (fact=2;fact<=IMIN(st->num_rate, st->den_rate);fact++) { while ((st->num_rate % fact == 0) && (st->den_rate % fact == 0)) { @@ -953,8 +1019,20 @@ void speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_n } } + if (old_den > 0) + { + for (i=0;i<st->nb_channels;i++) + { + st->samp_frac_num[i]=st->samp_frac_num[i]*st->den_rate/old_den; + /* Safety net */ + if (st->samp_frac_num[i] >= st->den_rate) + st->samp_frac_num[i] = st->den_rate-1; + } + } + if (st->initialised) update_filter(st); + return RESAMPLER_ERR_SUCCESS; } void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den) @@ -963,17 +1041,16 @@ void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, *ratio_den = st->den_rate; } -void speex_resampler_set_quality(SpeexResamplerState *st, int quality) +int speex_resampler_set_quality(SpeexResamplerState *st, int quality) { - if (quality < 0) - quality = 0; - if (quality > 10) - quality = 10; + if (quality > 10 || quality < 0) + return RESAMPLER_ERR_INVALID_ARG; if (st->quality == quality) - return; + return RESAMPLER_ERR_SUCCESS; st->quality = quality; if (st->initialised) update_filter(st); + return RESAMPLER_ERR_SUCCESS; } void speex_resampler_get_quality(SpeexResamplerState *st, int *quality) @@ -1001,17 +1078,37 @@ void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *st *stride = st->out_stride; } -void speex_resampler_skip_zeros(SpeexResamplerState *st) +int speex_resampler_skip_zeros(SpeexResamplerState *st) { spx_uint32_t i; for (i=0;i<st->nb_channels;i++) st->last_sample[i] = st->filt_len/2; + return RESAMPLER_ERR_SUCCESS; } -void speex_resampler_reset_mem(SpeexResamplerState *st) +int speex_resampler_reset_mem(SpeexResamplerState *st) { spx_uint32_t i; for (i=0;i<st->nb_channels*(st->filt_len-1);i++) st->mem[i] = 0; + return RESAMPLER_ERR_SUCCESS; } +const char *speex_resampler_strerror(int err) +{ + switch (err) + { + case RESAMPLER_ERR_SUCCESS: + return "Success."; + case RESAMPLER_ERR_ALLOC_FAILED: + return "Memory allocation failed."; + case RESAMPLER_ERR_BAD_STATE: + return "Bad resampler state."; + case RESAMPLER_ERR_INVALID_ARG: + return "Invalid argument."; + case RESAMPLER_ERR_PTR_OVERLAP: + return "Input and output buffers overlap."; + default: + return "Unknown error. Bad error code or strange version mismatch."; + } +} |