initial support for changing the filter length on the fly...
[speexdsp.git] / libspeex / resample.c
index 5325cb8..29ddb59 100644 (file)
@@ -65,7 +65,6 @@ void speex_free (void *ptr) {free(ptr);}
 #include "misc.h"
 #endif
 
-
 #include <math.h>
 #include "speex/speex_resampler.h"
 
@@ -81,6 +80,26 @@ void speex_free (void *ptr) {free(ptr);}
 
 #define IMAX(a,b) ((a) > (b) ? (a) : (b))
 
+struct QualityMapping {
+   int base_length;
+   int oversample;
+};
+
+
+struct QualityMapping quality_map[11] = {
+   {  8,  4}, /* 0 */
+   { 16,  4}, /* 1 */
+   { 32,  4}, /* 2 */
+   { 48,  8}, /* 3 */
+   { 64,  8}, /* 4 */
+   { 80,  8}, /* 5 */
+   { 96,  8}, /* 6 */
+   {128, 16}, /* 7 */
+   {160, 16}, /* 8 */
+   {192, 16}, /* 9 */
+   {256, 16}, /* 10 */
+};
+
 typedef enum {SPEEX_RESAMPLER_DIRECT=0, SPEEX_RESAMPLER_INTERPOLATE=1} SpeexSincType;
 
 struct SpeexResamplerState_ {
@@ -91,15 +110,18 @@ struct SpeexResamplerState_ {
    
    int    quality;
    int    nb_channels;
-   int    last_sample;
-   int    samp_frac_num;
    int    filt_len;
+   int    mem_alloc_size;
    int    int_advance;
    int    frac_advance;
    float  cutoff;
    int    oversample;
    int    initialised;
+   int    started;
    
+   /* FIXME: Need those per-channel */
+   int    last_sample;
+   int    samp_frac_num;
    
    spx_word16_t *mem;
    spx_word16_t *sinc_table;
@@ -140,11 +162,25 @@ static spx_word16_t sinc(float cutoff, float x, int N)
 static void update_filter(SpeexResamplerState *st)
 {
    int i;
+   int old_length;
    
-   st->oversample = OVERSAMPLE;
-
-   st->filt_len = 8 + 12*st->quality;
+   old_length = st->filt_len;
+   st->oversample = quality_map[st->quality].oversample;
+   st->filt_len = quality_map[st->quality].base_length;
    
+   if (st->num_rate > st->den_rate)
+   {
+      /* down-sampling */
+      st->cutoff = .92f * st->den_rate / st->num_rate;
+      /* FIXME: divide the numerator and denominator by a certain amount if they're too large */
+      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);
+   } else {
+      /* up-sampling */
+      st->cutoff = .97f;
+   }
+
    /* Choose the resampling type that requires the least amount of memory */
    if (st->den_rate <= st->oversample)
    {
@@ -182,11 +218,39 @@ static void update_filter(SpeexResamplerState *st)
    st->frac_advance = st->num_rate%st->den_rate;
 
    if (!st->mem)
+   {
       st->mem = (spx_word16_t*)speex_alloc(st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
-   else
+      for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
+         st->mem[i] = 0;
+      speex_warning("init filter");
+   } else if (!st->started)
+   {
       st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
-   for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
-      st->mem[i] = 0;
+      for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
+         st->mem[i] = 0;
+      speex_warning("reinit filter");
+   } else if (st->filt_len > old_length)
+   {
+      /* Increase the filter length */
+      speex_warning("increase filter size");
+      st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
+      for (i=0;i<st->nb_channels;i++)
+      {
+         int j;
+         /* Copy data going backward */
+         for (j=0;j<old_length-1;j++)
+            st->mem[i*(st->filt_len-1)+(st->filt_len-2-j)] = st->mem[i*(old_length-1)+(old_length-2-j)];
+         /* Then put zeros for lack of anything better */
+         for (;j<st->filt_len-1;j++)
+            st->mem[i*(st->filt_len-1)+(st->filt_len-2-j)] = 0;
+         /* Adjust last_sample */
+         st->last_sample += (st->filt_len - old_length)/2;
+      }
+   } else if (st->filt_len < old_length)
+   {
+      /* Reduce filter length */
+      speex_warning("decrease filter size (unimplemented)");
+   }
 
 }
 
@@ -195,12 +259,15 @@ SpeexResamplerState *speex_resampler_init(int nb_channels, int ratio_num, int ra
 {
    SpeexResamplerState *st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState));
    st->initialised = 0;
+   st->started = 0;
    st->in_rate = 0;
    st->out_rate = 0;
    st->num_rate = 0;
    st->den_rate = 0;
    st->quality = -1;
    st->sinc_table_length = 0;
+   st->mem_alloc_size = 0;
+   st->filt_len = 0;
    st->mem = 0;
    
    st->cutoff = 1.f;
@@ -210,8 +277,8 @@ SpeexResamplerState *speex_resampler_init(int nb_channels, int ratio_num, int ra
    st->in_stride = 1;
    st->out_stride = 1;
 
-   speex_resample_set_quality(st, quality);
-   speex_resample_set_rate(st, ratio_num, ratio_den, in_rate, out_rate);
+   speex_resampler_set_quality(st, quality);
+   speex_resampler_set_rate(st, ratio_num, ratio_den, in_rate, out_rate);
 
    
    update_filter(st);
@@ -234,6 +301,7 @@ static void speex_resampler_process_native(SpeexResamplerState *st, int channel_
    int out_sample = 0;
    spx_word16_t *mem;
    mem = st->mem + channel_index * (N-1);
+   st->started = 1;
    while (!(st->last_sample >= *in_len || out_sample >= *out_len))
    {
       int j;
@@ -371,7 +439,7 @@ void speex_resampler_process_interleaved_float(SpeexResamplerState *st, const fl
 }
 
 
-void speex_resample_set_rate(SpeexResamplerState *st, int ratio_num, int ratio_den, int in_rate, int out_rate)
+void speex_resampler_set_rate(SpeexResamplerState *st, int ratio_num, int ratio_den, int in_rate, int out_rate)
 {
    int fact;
    if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den)
@@ -390,22 +458,17 @@ void speex_resample_set_rate(SpeexResamplerState *st, int ratio_num, int ratio_d
          st->den_rate /= fact;
       }
    }
-   
-   if (ratio_num > ratio_den)
-   {
-      /* down-sampling */
-      st->cutoff = .92f * ratio_den / ratio_num;
-   } else {
-      /* up-sampling */
-      st->cutoff = .97f;
-   }
-   
+      
    if (st->initialised)
       update_filter(st);
 }
 
-void speex_resample_set_quality(SpeexResamplerState *st, int quality)
+void speex_resampler_set_quality(SpeexResamplerState *st, int quality)
 {
+   if (quality < 0)
+      quality = 0;
+   if (quality > 10)
+      quality = 10;
    if (st->quality == quality)
       return;
    st->quality = quality;
@@ -413,22 +476,22 @@ void speex_resample_set_quality(SpeexResamplerState *st, int quality)
       update_filter(st);
 }
 
-void speex_resample_set_input_stride(SpeexResamplerState *st, int stride)
+void speex_resampler_set_input_stride(SpeexResamplerState *st, int stride)
 {
    st->in_stride = stride;
 }
 
-void speex_resample_set_output_stride(SpeexResamplerState *st, int stride)
+void speex_resampler_set_output_stride(SpeexResamplerState *st, int stride)
 {
    st->out_stride = stride;
 }
 
-void speex_resample_skip_zeros(SpeexResamplerState *st)
+void speex_resampler_skip_zeros(SpeexResamplerState *st)
 {
    st->last_sample = st->filt_len/2;
 }
 
-void speex_resample_reset_mem(SpeexResamplerState *st)
+void speex_resampler_reset_mem(SpeexResamplerState *st)
 {
    int i;
    for (i=0;i<st->nb_channels*(st->filt_len-1);i++)