float<->int conversion with a few less bugs (actually works for the first time)
[speexdsp.git] / libspeex / resample.c
1 /* Copyright (C) 2007 Jean-Marc Valin
2       
3    File: resample.c
4    Arbitrary resampling code
5
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions are
8    met:
9
10    1. Redistributions of source code must retain the above copyright notice,
11    this list of conditions and the following disclaimer.
12
13    2. Redistributions in binary form must reproduce the above copyright
14    notice, this list of conditions and the following disclaimer in the
15    documentation and/or other materials provided with the distribution.
16
17    3. The name of the author may not be used to endorse or promote products
18    derived from this software without specific prior written permission.
19
20    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23    DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30    POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34    The design goals of this code are:
35       - Very fast algorithm
36       - SIMD-friendly algorithm
37       - Low memory requirement
38       - Good *perceptual* quality (and not best SNR)
39
40    The code is working, but it's in a very early stage, so it may have
41    artifacts, noise or subliminal messages from satan. Also, the API 
42    isn't stable and I can actually promise that I *will* change the API
43    some time in the future.
44
45 TODO list:
46       - Variable calculation resolution depending on quality setting
47          - Single vs double in float mode
48          - 16-bit vs 32-bit (sinc only) in fixed-point mode
49       - Make sure the filter update works even when changing params 
50              after only a few samples procesed
51       - Fix multi-channel (need one sample pos per channel)
52 */
53
54 #ifdef HAVE_CONFIG_H
55 #include "config.h"
56 #endif
57
58 #ifdef OUTSIDE_SPEEX
59 #include <stdlib.h>
60 void *speex_alloc (int size) {return calloc(size,1);}
61 void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);}
62 void speex_free (void *ptr) {free(ptr);}
63 #else
64 #include "misc.h"
65 #endif
66
67 #include <math.h>
68 #include "speex/speex_resampler.h"
69
70 #ifdef FIXED_POINT
71 #define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))  
72 #else
73 #define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x))))  
74 #endif
75                
76 /*#define float double*/
77 #define FILTER_SIZE 64
78 #define OVERSAMPLE 8
79
80 #define IMAX(a,b) ((a) > (b) ? (a) : (b))
81
82 struct QualityMapping {
83    int base_length;
84    int oversample;
85 };
86
87
88 struct QualityMapping quality_map[11] = {
89    {  8,  4}, /* 0 */
90    { 16,  4}, /* 1 */
91    { 32,  4}, /* 2 */
92    { 48,  8}, /* 3 */
93    { 64,  8}, /* 4 */
94    { 80,  8}, /* 5 */
95    { 96,  8}, /* 6 */
96    {128, 16}, /* 7 */
97    {160, 16}, /* 8 */
98    {192, 16}, /* 9 */
99    {256, 16}, /* 10 */
100 };
101
102 typedef enum {SPEEX_RESAMPLER_DIRECT_SINGLE=0, SPEEX_RESAMPLER_INTERPOLATE_SINGLE=1} SpeexSincType;
103
104 typedef int (*resampler_basic_func)(SpeexResamplerState *, int , const spx_word16_t *, int *, spx_word16_t *, int *);
105
106 struct SpeexResamplerState_ {
107    int    in_rate;
108    int    out_rate;
109    int    num_rate;
110    int    den_rate;
111    
112    int    quality;
113    int    nb_channels;
114    int    filt_len;
115    int    mem_alloc_size;
116    int    int_advance;
117    int    frac_advance;
118    float  cutoff;
119    int    oversample;
120    int    initialised;
121    int    started;
122    
123    /* FIXME: Need those per-channel */
124    int    *last_sample;
125    int    *samp_frac_num;
126    int    *magic_samples;
127    
128    spx_word16_t *mem;
129    spx_word16_t *sinc_table;
130    int    sinc_table_length;
131    resampler_basic_func resampler_ptr;
132          
133    int    in_stride;
134    int    out_stride;
135    SpeexSincType type;
136 } ;
137
138 #ifdef FIXED_POINT
139 /* The slow way of computing a sinc for the table. Should improve that some day */
140 static spx_word16_t sinc(float cutoff, float x, int N)
141 {
142    /*fprintf (stderr, "%f ", x);*/
143    x *= cutoff;
144    if (fabs(x)<1e-6f)
145       return WORD2INT(32768.*cutoff);
146    else if (fabs(x) > .5f*N)
147       return 0;
148    /*FIXME: Can it really be any slower than this? */
149    return WORD2INT(32768.*cutoff*sin(M_PI*x)/(M_PI*x) * (.42+.5*cos(2*x*M_PI/N)+.08*cos(4*x*M_PI/N)));
150 }
151 #else
152 /* The slow way of computing a sinc for the table. Should improve that some day */
153 static spx_word16_t sinc(float cutoff, float x, int N)
154 {
155    /*fprintf (stderr, "%f ", x);*/
156    x *= cutoff;
157    if (fabs(x)<1e-6)
158       return cutoff;
159    else if (fabs(x) > .5*N)
160       return 0;
161    /*FIXME: Can it really be any slower than this? */
162    return cutoff*sin(M_PI*x)/(M_PI*x) * (.42+.5*cos(2*x*M_PI/N)+.08*cos(4*x*M_PI/N));
163 }
164 #endif
165
166 static int resampler_basic_direct_single(SpeexResamplerState *st, int channel_index, const spx_word16_t *in, int *in_len, spx_word16_t *out, int *out_len)
167 {
168    int N = st->filt_len;
169    int out_sample = 0;
170    spx_word16_t *mem;
171    int last_sample = st->last_sample[channel_index];
172    int samp_frac_num = st->samp_frac_num[channel_index];
173    mem = st->mem + channel_index * st->mem_alloc_size;
174    while (!(last_sample >= *in_len || out_sample >= *out_len))
175    {
176       int j;
177       spx_word32_t sum=0;
178       
179       /* We already have all the filter coefficients pre-computed in the table */
180       const spx_word16_t *ptr;
181       /* Do the memory part */
182       for (j=0;last_sample-N+1+j < 0;j++)
183       {
184          sum += MULT16_16(mem[last_sample+j],st->sinc_table[samp_frac_num*st->filt_len+j]);
185       }
186       
187       /* Do the new part */
188       ptr = in+st->in_stride*(last_sample-N+1+j);
189       for (;j<N;j++)
190       {
191          sum += MULT16_16(*ptr,st->sinc_table[samp_frac_num*st->filt_len+j]);
192          ptr += st->in_stride;
193       }
194    
195       *out = PSHR32(sum,15);
196       out += st->out_stride;
197       out_sample++;
198       last_sample += st->int_advance;
199       samp_frac_num += st->frac_advance;
200       if (samp_frac_num >= st->den_rate)
201       {
202          samp_frac_num -= st->den_rate;
203          last_sample++;
204       }
205    }
206    st->last_sample[channel_index] = last_sample;
207    st->samp_frac_num[channel_index] = samp_frac_num;
208    return out_sample;
209 }
210
211 static int resampler_basic_interpolate_single(SpeexResamplerState *st, int channel_index, const spx_word16_t *in, int *in_len, spx_word16_t *out, int *out_len)
212 {
213    int N = st->filt_len;
214    int out_sample = 0;
215    spx_word16_t *mem;
216    int last_sample = st->last_sample[channel_index];
217    int samp_frac_num = st->samp_frac_num[channel_index];
218    mem = st->mem + channel_index * st->mem_alloc_size;
219    while (!(last_sample >= *in_len || out_sample >= *out_len))
220    {
221       int j;
222       spx_word32_t sum=0;
223       
224       /* We need to interpolate the sinc filter */
225       spx_word32_t accum[4] = {0.f,0.f, 0.f, 0.f};
226       float interp[4];
227       const spx_word16_t *ptr;
228       float alpha = ((float)samp_frac_num)/st->den_rate;
229       int offset = samp_frac_num*st->oversample/st->den_rate;
230       float frac = alpha*st->oversample - offset;
231          /* This code is written like this to make it easy to optimise with SIMD.
232       For most DSPs, it would be best to split the loops in two because most DSPs 
233       have only two accumulators */
234       for (j=0;last_sample-N+1+j < 0;j++)
235       {
236          spx_word16_t curr_mem = mem[last_sample+j];
237          accum[0] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
238          accum[1] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
239          accum[2] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset]);
240          accum[3] += MULT16_16(curr_mem,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
241       }
242       ptr = in+st->in_stride*(last_sample-N+1+j);
243       /* Do the new part */
244       for (;j<N;j++)
245       {
246          spx_word16_t curr_in = *ptr;
247          ptr += st->in_stride;
248          accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]);
249          accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]);
250          accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]);
251          accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]);
252       }
253          /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation
254       but I know it's MMSE-optimal on a sinc */
255       interp[0] =  -0.16667f*frac + 0.16667f*frac*frac*frac;
256       interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac;
257       /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/
258       interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac;
259       /* Just to make sure we don't have rounding problems */
260       interp[2] = 1.f-interp[0]-interp[1]-interp[3];
261       /*sum = frac*accum[1] + (1-frac)*accum[2];*/
262       sum = interp[0]*accum[0] + interp[1]*accum[1] + interp[2]*accum[2] + interp[3]*accum[3];
263    
264       *out = PSHR32(sum,15);
265       out += st->out_stride;
266       out_sample++;
267       last_sample += st->int_advance;
268       samp_frac_num += st->frac_advance;
269       if (samp_frac_num >= st->den_rate)
270       {
271          samp_frac_num -= st->den_rate;
272          last_sample++;
273       }
274    }
275    st->last_sample[channel_index] = last_sample;
276    st->samp_frac_num[channel_index] = samp_frac_num;
277    return out_sample;
278 }
279
280
281 static void update_filter(SpeexResamplerState *st)
282 {
283    int i;
284    int old_length;
285    
286    old_length = st->filt_len;
287    st->oversample = quality_map[st->quality].oversample;
288    st->filt_len = quality_map[st->quality].base_length;
289    
290    if (st->num_rate > st->den_rate)
291    {
292       /* down-sampling */
293       st->cutoff = .92f * st->den_rate / st->num_rate;
294       /* FIXME: divide the numerator and denominator by a certain amount if they're too large */
295       st->filt_len = st->filt_len*st->num_rate / st->den_rate;
296       /* Round down to make sure we have a multiple of 4 */
297       st->filt_len &= (~0x3);
298    } else {
299       /* up-sampling */
300       st->cutoff = .97f;
301    }
302
303    /* Choose the resampling type that requires the least amount of memory */
304    if (st->den_rate <= st->oversample)
305    {
306       if (!st->sinc_table)
307          st->sinc_table = (spx_word16_t *)speex_alloc(st->filt_len*st->den_rate*sizeof(spx_word16_t));
308       else if (st->sinc_table_length < st->filt_len*st->den_rate)
309       {
310          st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,st->filt_len*st->den_rate*sizeof(spx_word16_t));
311          st->sinc_table_length = st->filt_len*st->den_rate;
312       }
313       for (i=0;i<st->den_rate;i++)
314       {
315          int j;
316          for (j=0;j<st->filt_len;j++)
317          {
318             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);
319          }
320       }
321       st->type = SPEEX_RESAMPLER_DIRECT_SINGLE;
322       st->resampler_ptr = resampler_basic_direct_single;
323       /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/
324    } else {
325       if (!st->sinc_table)
326          st->sinc_table = (spx_word16_t *)speex_alloc((st->filt_len*st->oversample+8)*sizeof(spx_word16_t));
327       else if (st->sinc_table_length < st->filt_len*st->oversample+8)
328       {
329          st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,(st->filt_len*st->oversample+8)*sizeof(spx_word16_t));
330          st->sinc_table_length = st->filt_len*st->oversample+8;
331       }
332       for (i=-4;i<st->oversample*st->filt_len+4;i++)
333          st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len);
334       st->type = SPEEX_RESAMPLER_INTERPOLATE_SINGLE;
335       st->resampler_ptr = resampler_basic_interpolate_single;
336       /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/
337    }
338    st->int_advance = st->num_rate/st->den_rate;
339    st->frac_advance = st->num_rate%st->den_rate;
340
341    if (!st->mem)
342    {
343       st->mem = (spx_word16_t*)speex_alloc(st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
344       for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
345          st->mem[i] = 0;
346       st->mem_alloc_size = st->filt_len-1;
347       /*speex_warning("init filter");*/
348    } else if (!st->started)
349    {
350       st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
351       for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
352          st->mem[i] = 0;
353       st->mem_alloc_size = st->filt_len-1;
354       /*speex_warning("reinit filter");*/
355    } else if (st->filt_len > old_length)
356    {
357       /* Increase the filter length */
358       /*speex_warning("increase filter size");*/
359       int old_alloc_size = st->mem_alloc_size;
360       if (st->filt_len-1 > st->mem_alloc_size)
361       {
362          st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*(st->filt_len-1) * sizeof(spx_word16_t));
363          st->mem_alloc_size = st->filt_len-1;
364       }
365       for (i=0;i<st->nb_channels;i++)
366       {
367          int j;
368          /* Copy data going backward */
369          for (j=0;j<old_length-1;j++)
370             st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*old_alloc_size+(old_length-2-j)];
371          /* Then put zeros for lack of anything better */
372          for (;j<st->filt_len-1;j++)
373             st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
374          /* Adjust last_sample */
375          st->last_sample[i] += (st->filt_len - old_length)/2;
376       }
377    } else if (st->filt_len < old_length)
378    {
379       /* Reduce filter length, this a bit tricky */
380       /*speex_warning("decrease filter size (unimplemented)");*/
381       /* Adjust last_sample (which will likely end up negative) */
382       /*st->last_sample += (st->filt_len - old_length)/2;*/
383       for (i=0;i<st->nb_channels;i++)
384       {
385          int j;
386          st->magic_samples[i] = (old_length - st->filt_len)/2;
387          /* Copy data going backward */
388          for (j=0;j<st->filt_len-1+st->magic_samples[i];j++)
389             st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
390       }
391    }
392
393 }
394
395
396 SpeexResamplerState *speex_resampler_init(int nb_channels, int ratio_num, int ratio_den, int in_rate, int out_rate, int quality)
397 {
398    int i;
399    SpeexResamplerState *st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState));
400    st->initialised = 0;
401    st->started = 0;
402    st->in_rate = 0;
403    st->out_rate = 0;
404    st->num_rate = 0;
405    st->den_rate = 0;
406    st->quality = -1;
407    st->sinc_table_length = 0;
408    st->mem_alloc_size = 0;
409    st->filt_len = 0;
410    st->mem = 0;
411    st->resampler_ptr = 0;
412          
413    st->cutoff = 1.f;
414    st->nb_channels = nb_channels;
415    st->in_stride = 1;
416    st->out_stride = 1;
417    
418    /* Per channel data */
419    st->last_sample = (int*)speex_alloc(nb_channels*sizeof(int));
420    st->magic_samples = (int*)speex_alloc(nb_channels*sizeof(int));
421    st->samp_frac_num = (int*)speex_alloc(nb_channels*sizeof(int));
422    for (i=0;i<nb_channels;i++)
423    {
424       st->last_sample[i] = 0;
425       st->magic_samples[i] = 0;
426       st->samp_frac_num[i] = 0;
427    }
428
429    speex_resampler_set_quality(st, quality);
430    speex_resampler_set_rate(st, ratio_num, ratio_den, in_rate, out_rate);
431
432    
433    update_filter(st);
434    
435    st->initialised = 1;
436    return st;
437 }
438
439 void speex_resampler_destroy(SpeexResamplerState *st)
440 {
441    speex_free(st->mem);
442    speex_free(st->sinc_table);
443    speex_free(st->last_sample);
444    speex_free(st->magic_samples);
445    speex_free(st->samp_frac_num);
446    speex_free(st);
447 }
448
449
450
451 static void speex_resampler_process_native(SpeexResamplerState *st, int channel_index, const spx_word16_t *in, int *in_len, spx_word16_t *out, int *out_len)
452 {
453    int j=0;
454    int N = st->filt_len;
455    int out_sample = 0;
456    spx_word16_t *mem;
457    int tmp_out_len = 0;
458    mem = st->mem + channel_index * st->mem_alloc_size;
459    st->started = 1;
460    
461    /* Handle the case where we have samples left from a reduction in filter length */
462    if (st->magic_samples)
463    {
464       int tmp_in_len;
465       tmp_in_len = st->magic_samples[channel_index];
466       tmp_out_len = *out_len;
467       /* FIXME: Need to handle the case where the out array is too small */
468       /* magic_samples needs to be set to zero to avoid infinite recursion */
469       st->magic_samples = 0;
470       speex_resampler_process_native(st, channel_index, mem+N-1, &tmp_in_len, out, &tmp_out_len);
471       /*speex_warning_int("extra samples:", tmp_out_len);*/
472       out += tmp_out_len;
473    }
474    
475    /* Call the right resampler through the function ptr */
476    out_sample = st->resampler_ptr(st, channel_index, in, in_len, out, out_len);
477    
478    if (st->last_sample[channel_index] < *in_len)
479       *in_len = st->last_sample[channel_index];
480    *out_len = out_sample+tmp_out_len;
481    st->last_sample[channel_index] -= *in_len;
482    
483    for (j=0;j<N-1-*in_len;j++)
484       mem[j] = mem[j+*in_len];
485    for (;j<N-1;j++)
486       mem[j] = in[st->in_stride*(j+*in_len-N+1)];
487    
488 }
489
490 #ifdef FIXED_POINT
491 void speex_resampler_process_float(SpeexResamplerState *st, int channel_index, const float *in, int *in_len, float *out, int *out_len)
492 {
493    int i;
494    int istride_save, ostride_save;
495    spx_word16_t x[*in_len];
496    spx_word16_t y[*out_len];
497    istride_save = st->in_stride;
498    ostride_save = st->out_stride;
499    for (i=0;i<*in_len;i++)
500       x[i] = WORD2INT(in[i*st->in_stride]);
501    st->in_stride = st->out_stride = 1;
502    speex_resampler_process_native(st, channel_index, x, in_len, y, out_len);
503    st->in_stride = istride_save;
504    st->out_stride = ostride_save;
505    for (i=0;i<*out_len;i++)
506       out[i*st->out_stride] = y[i];
507 }
508 void speex_resampler_process_int(SpeexResamplerState *st, int channel_index, const spx_int16_t *in, int *in_len, spx_int16_t *out, int *out_len)
509 {
510    speex_resampler_process_native(st, channel_index, in, in_len, out, out_len);
511 }
512 #else
513 void speex_resampler_process_float(SpeexResamplerState *st, int channel_index, const float *in, int *in_len, float *out, int *out_len)
514 {
515    speex_resampler_process_native(st, channel_index, in, in_len, out, out_len);
516 }
517 void speex_resampler_process_int(SpeexResamplerState *st, int channel_index, const spx_int16_t *in, int *in_len, spx_int16_t *out, int *out_len)
518 {
519    int i;
520    int istride_save, ostride_save;
521    spx_word16_t x[*in_len];
522    spx_word16_t y[*out_len];
523    istride_save = st->in_stride;
524    ostride_save = st->out_stride;
525    for (i=0;i<*in_len;i++)
526       x[i] = in[i+st->in_stride];
527    st->in_stride = st->out_stride = 1;
528    speex_resampler_process_native(st, channel_index, x, in_len, y, out_len);
529    st->in_stride = istride_save;
530    st->out_stride = ostride_save;
531    for (i=0;i<*out_len;i++)
532       out[i+st->out_stride] = WORD2INT(y[i]);
533 }
534 #endif
535
536 void speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, int *in_len, float *out, int *out_len)
537 {
538    int i;
539    int istride_save, ostride_save;
540    istride_save = st->in_stride;
541    ostride_save = st->out_stride;
542    st->in_stride = st->out_stride = st->nb_channels;
543    for (i=0;i<st->nb_channels;i++)
544    {
545       speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len);
546    }
547    st->in_stride = istride_save;
548    st->out_stride = ostride_save;
549 }
550
551
552 void speex_resampler_set_rate(SpeexResamplerState *st, int ratio_num, int ratio_den, int in_rate, int out_rate)
553 {
554    int fact;
555    if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den)
556       return;
557    
558    st->in_rate = in_rate;
559    st->out_rate = out_rate;
560    st->num_rate = ratio_num;
561    st->den_rate = ratio_den;
562    /* FIXME: This is terribly inefficient, but who cares (at least for now)? */
563    for (fact=2;fact<=sqrt(IMAX(in_rate, out_rate));fact++)
564    {
565       while ((st->num_rate % fact == 0) && (st->den_rate % fact == 0))
566       {
567          st->num_rate /= fact;
568          st->den_rate /= fact;
569       }
570    }
571       
572    if (st->initialised)
573       update_filter(st);
574 }
575
576 void speex_resampler_set_quality(SpeexResamplerState *st, int quality)
577 {
578    if (quality < 0)
579       quality = 0;
580    if (quality > 10)
581       quality = 10;
582    if (st->quality == quality)
583       return;
584    st->quality = quality;
585    if (st->initialised)
586       update_filter(st);
587 }
588
589 void speex_resampler_set_input_stride(SpeexResamplerState *st, int stride)
590 {
591    st->in_stride = stride;
592 }
593
594 void speex_resampler_set_output_stride(SpeexResamplerState *st, int stride)
595 {
596    st->out_stride = stride;
597 }
598
599 void speex_resampler_skip_zeros(SpeexResamplerState *st)
600 {
601    int i;
602    for (i=0;i<st->nb_channels;i++)
603       st->last_sample[i] = st->filt_len/2;
604 }
605
606 void speex_resampler_reset_mem(SpeexResamplerState *st)
607 {
608    int i;
609    for (i=0;i<st->nb_channels*(st->filt_len-1);i++)
610       st->mem[i] = 0;
611 }
612