| /* Resampling library |
| * Copyright (C) <2001> David A. Schleef <ds@schleef.org> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <string.h> |
| #include <math.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "private.h" |
| #include <gst/gstplugin.h> |
| #include <gst/gstversion.h> |
| |
| inline double sinc(double x) |
| { |
| if(x==0)return 1; |
| return sin(x) / x; |
| } |
| |
| inline double window_func(double x) |
| { |
| x = 1 - x*x; |
| return x*x; |
| } |
| |
| signed short double_to_s16(double x) |
| { |
| if(x<-32768){ |
| printf("clipped\n"); |
| return -32768; |
| } |
| if(x>32767){ |
| printf("clipped\n"); |
| return -32767; |
| } |
| return rint(x); |
| } |
| |
| signed short double_to_s16_ppcasm(double x) |
| { |
| if(x<-32768){ |
| return -32768; |
| } |
| if(x>32767){ |
| return -32767; |
| } |
| return rint(x); |
| } |
| |
| void resample_init(resample_t * r) |
| { |
| r->i_start = 0; |
| if(r->filter_length&1){ |
| r->o_start = 0; |
| }else{ |
| r->o_start = r->o_inc * 0.5; |
| } |
| |
| memset(r->acc, 0, sizeof(r->acc)); |
| |
| resample_reinit(r); |
| } |
| |
| void resample_reinit(resample_t * r) |
| { |
| /* i_inc is the number of samples that the output increments for |
| * each input sample. o_inc is the opposite. */ |
| r->i_inc = (double) r->o_rate / r->i_rate; |
| r->o_inc = (double) r->i_rate / r->o_rate; |
| |
| r->halftaps = (r->filter_length - 1.0) * 0.5; |
| |
| if (r->format == RESAMPLE_S16) { |
| switch (r->method) { |
| default: |
| case RESAMPLE_NEAREST: |
| r->scale = resample_nearest_s16; |
| break; |
| case RESAMPLE_BILINEAR: |
| r->scale = resample_bilinear_s16; |
| break; |
| case RESAMPLE_SINC_SLOW: |
| r->scale = resample_sinc_s16; |
| break; |
| case RESAMPLE_SINC: |
| r->scale = resample_sinc_ft_s16; |
| break; |
| } |
| } else if (r->format == RESAMPLE_FLOAT) { |
| switch (r->method) { |
| default: |
| case RESAMPLE_NEAREST: |
| r->scale = resample_nearest_float; |
| break; |
| case RESAMPLE_BILINEAR: |
| r->scale = resample_bilinear_float; |
| break; |
| case RESAMPLE_SINC_SLOW: |
| r->scale = resample_sinc_float; |
| break; |
| case RESAMPLE_SINC: |
| r->scale = resample_sinc_ft_float; |
| break; |
| } |
| } else { |
| fprintf (stderr, "resample: Unexpected format \"%d\"\n", r->format); |
| } |
| } |
| |
| /* |
| * Prepare to be confused. |
| * |
| * We keep a "timebase" that is based on output samples. The zero |
| * of the timebase cooresponds to the next output sample that will |
| * be written. |
| * |
| * i_start is the "time" that corresponds to the first input sample |
| * in an incoming buffer. Since the output depends on input samples |
| * ahead in time, i_start will tend to be around halftaps. |
| * |
| * i_start_buf is the time of the first sample in the temporary |
| * buffer. |
| */ |
| void resample_scale(resample_t * r, void *i_buf, unsigned int i_size) |
| { |
| int o_size; |
| |
| r->i_buf = i_buf; |
| |
| r->i_samples = i_size / 2 / r->channels; |
| |
| r->i_start_buf = r->i_start - r->filter_length * r->i_inc; |
| |
| /* i_start is the offset (in a given output sample) that is the |
| * beginning of the current input buffer */ |
| r->i_end = r->i_start + r->i_inc * r->i_samples; |
| |
| r->o_samples = floor(r->i_end - r->halftaps * r->i_inc); |
| |
| o_size = r->o_samples * r->channels * 2; |
| r->o_buf = r->get_buffer(r->priv, o_size); |
| |
| if(r->verbose){ |
| printf("resample_scale: i_buf=%p i_size=%d\n", |
| i_buf,i_size); |
| printf("resample_scale: i_samples=%d o_samples=%d i_inc=%g o_buf=%p\n", |
| r->i_samples, r->o_samples, r->i_inc, r->o_buf); |
| printf("resample_scale: i_start=%g i_end=%g o_start=%g\n", |
| r->i_start, r->i_end, r->o_start); |
| } |
| |
| if ((r->filter_length + r->i_samples)*sizeof(double)*2 > r->buffer_len) { |
| int size = (r->filter_length + r->i_samples) * sizeof(double) * 2; |
| |
| if(r->verbose){ |
| printf("resample temp buffer size=%d\n",size); |
| } |
| if(r->buffer)free(r->buffer); |
| r->buffer_len = size; |
| r->buffer = malloc(size); |
| memset(r->buffer, 0, size); |
| } |
| |
| if (r->format==RESAMPLE_S16) { |
| if(r->channels==2){ |
| conv_double_short( |
| r->buffer + r->filter_length * sizeof(double) * 2, |
| r->i_buf, r->i_samples * 2); |
| } else { |
| conv_double_short_dstr( |
| r->buffer + r->filter_length * sizeof(double) * 2, |
| r->i_buf, r->i_samples, sizeof(double) * 2); |
| } |
| } else if (r->format==RESAMPLE_FLOAT) { |
| if(r->channels==2){ |
| conv_double_float( |
| r->buffer + r->filter_length * sizeof(double) * 2, |
| r->i_buf, r->i_samples * 2); |
| } else { |
| conv_double_float_dstr( |
| r->buffer + r->filter_length * sizeof(double) * 2, |
| r->i_buf, r->i_samples, sizeof(double) * 2); |
| } |
| } |
| |
| r->scale(r); |
| |
| memcpy(r->buffer, |
| r->buffer + r->i_samples * sizeof(double) * 2, |
| r->filter_length * sizeof(double) * 2); |
| |
| /* updating times */ |
| r->i_start += r->i_samples * r->i_inc; |
| r->o_start += r->o_samples * r->o_inc - r->i_samples; |
| |
| /* adjusting timebase zero */ |
| r->i_start -= r->o_samples; |
| } |
| |
| void resample_nearest_s16(resample_t * r) |
| { |
| signed short *i_ptr, *o_ptr; |
| int i_count = 0; |
| double a; |
| int i; |
| |
| i_ptr = (signed short *) r->i_buf; |
| o_ptr = (signed short *) r->o_buf; |
| |
| a = r->o_start; |
| i_count = 0; |
| #define SCALE_LOOP(COPY,INC) \ |
| for (i = 0; i < r->o_samples; i++) { \ |
| COPY; \ |
| a += r->o_inc; \ |
| while (a >= 1) { \ |
| a -= 1; \ |
| i_ptr+=INC; \ |
| i_count++; \ |
| } \ |
| o_ptr+=INC; \ |
| } |
| |
| switch (r->channels) { |
| case 1: |
| SCALE_LOOP(o_ptr[0] = i_ptr[0], 1); |
| break; |
| case 2: |
| SCALE_LOOP(o_ptr[0] = i_ptr[0]; |
| o_ptr[1] = i_ptr[1], 2); |
| break; |
| default: |
| { |
| int n, n_chan = r->channels; |
| |
| SCALE_LOOP(for (n = 0; n < n_chan; n++) o_ptr[n] = |
| i_ptr[n], n_chan); |
| } |
| } |
| if (i_count != r->i_samples) { |
| printf("handled %d in samples (expected %d)\n", i_count, |
| r->i_samples); |
| } |
| } |
| |
| void resample_bilinear_s16(resample_t * r) |
| { |
| signed short *i_ptr, *o_ptr; |
| int o_count = 0; |
| double b; |
| int i; |
| double acc0, acc1; |
| |
| i_ptr = (signed short *) r->i_buf; |
| o_ptr = (signed short *) r->o_buf; |
| |
| acc0 = r->acc[0]; |
| acc1 = r->acc[1]; |
| b = r->i_start; |
| for (i = 0; i < r->i_samples; i++) { |
| b += r->i_inc; |
| /*printf("in %d\n",i_ptr[0]); */ |
| if(b>=2){ |
| printf("not expecting b>=2\n"); |
| } |
| if (b >= 1) { |
| acc0 += (1.0 - (b-r->i_inc)) * i_ptr[0]; |
| acc1 += (1.0 - (b-r->i_inc)) * i_ptr[1]; |
| |
| o_ptr[0] = rint(acc0); |
| /*printf("out %d\n",o_ptr[0]); */ |
| o_ptr[1] = rint(acc1); |
| o_ptr += 2; |
| o_count++; |
| |
| b -= 1.0; |
| |
| acc0 = b * i_ptr[0]; |
| acc1 = b * i_ptr[1]; |
| } else { |
| acc0 += i_ptr[0] * r->i_inc; |
| acc1 += i_ptr[1] * r->i_inc; |
| } |
| i_ptr += 2; |
| } |
| r->acc[0] = acc0; |
| r->acc[1] = acc1; |
| |
| if (o_count != r->o_samples) { |
| printf("handled %d out samples (expected %d)\n", o_count, |
| r->o_samples); |
| } |
| } |
| |
| void resample_sinc_slow_s16(resample_t * r) |
| { |
| signed short *i_ptr, *o_ptr; |
| int i, j; |
| double c0, c1; |
| double a; |
| int start; |
| double center; |
| double weight; |
| |
| if (!r->buffer) { |
| int size = r->filter_length * 2 * r->channels; |
| |
| printf("resample temp buffer\n"); |
| r->buffer = malloc(size); |
| memset(r->buffer, 0, size); |
| } |
| |
| i_ptr = (signed short *) r->i_buf; |
| o_ptr = (signed short *) r->o_buf; |
| |
| a = r->i_start; |
| #define GETBUF(index,chan) (((index)<0) \ |
| ? ((short *)(r->buffer))[((index)+r->filter_length)*2+(chan)] \ |
| : i_ptr[(index)*2+(chan)]) |
| { |
| double sinx, cosx, sind, cosd; |
| double x, d; |
| double t; |
| |
| for (i = 0; i < r->o_samples; i++) { |
| start = floor(a) - r->filter_length; |
| center = a - r->halftaps; |
| x = M_PI * (start - center) * r->o_inc; |
| sinx = sin(M_PI * (start - center) * r->o_inc); |
| cosx = cos(M_PI * (start - center) * r->o_inc); |
| d = M_PI * r->o_inc; |
| sind = sin(M_PI * r->o_inc); |
| cosd = cos(M_PI * r->o_inc); |
| c0 = 0; |
| c1 = 0; |
| for (j = 0; j < r->filter_length; j++) { |
| weight = (x==0)?1:(sinx/x); |
| /*printf("j %d sin %g cos %g\n",j,sinx,cosx); */ |
| /*printf("j %d sin %g x %g sinc %g\n",j,sinx,x,weight); */ |
| c0 += weight * GETBUF((start + j), 0); |
| c1 += weight * GETBUF((start + j), 1); |
| t = cosx * cosd - sinx * sind; |
| sinx = cosx * sind + sinx * cosd; |
| cosx = t; |
| x += d; |
| } |
| o_ptr[0] = rint(c0); |
| o_ptr[1] = rint(c1); |
| o_ptr += 2; |
| a += r->o_inc; |
| } |
| } |
| #undef GETBUF |
| |
| memcpy(r->buffer, |
| i_ptr + (r->i_samples - r->filter_length) * r->channels, |
| r->filter_length * 2 * r->channels); |
| } |
| |
| /* only works for channels == 2 ???? */ |
| void resample_sinc_s16(resample_t * r) |
| { |
| double *ptr; |
| signed short *o_ptr; |
| int i, j; |
| double c0, c1; |
| double a; |
| int start; |
| double center; |
| double weight; |
| double x0, x, d; |
| double scale; |
| |
| ptr = (double *) r->buffer; |
| o_ptr = (signed short *) r->o_buf; |
| |
| /* scale provides a cutoff frequency for the low |
| * pass filter aspects of sinc(). scale=M_PI |
| * will cut off at the input frequency, which is |
| * good for up-sampling, but will cause aliasing |
| * for downsampling. Downsampling needs to be |
| * cut off at o_rate, thus scale=M_PI*r->i_inc. */ |
| /* actually, it needs to be M_PI*r->i_inc*r->i_inc. |
| * Need to research why. */ |
| scale = M_PI*r->i_inc; |
| for (i = 0; i < r->o_samples; i++) { |
| a = r->o_start + i * r->o_inc; |
| start = floor(a - r->halftaps); |
| /*printf("%d: a=%g start=%d end=%d\n",i,a,start,start+r->filter_length-1); */ |
| center = a; |
| /*x = M_PI * (start - center) * r->o_inc; */ |
| /*d = M_PI * r->o_inc; */ |
| /*x = (start - center) * r->o_inc; */ |
| x0 = (start - center) * r->o_inc; |
| d = r->o_inc; |
| c0 = 0; |
| c1 = 0; |
| for (j = 0; j < r->filter_length; j++) { |
| x = x0 + d * j; |
| weight = sinc(x*scale*r->i_inc)*scale/M_PI; |
| weight *= window_func(x/r->halftaps*r->i_inc); |
| c0 += weight * ptr[(start + j + r->filter_length)*2 + 0]; |
| c1 += weight * ptr[(start + j + r->filter_length)*2 + 1]; |
| } |
| o_ptr[0] = double_to_s16(c0); |
| o_ptr[1] = double_to_s16(c1); |
| o_ptr += 2; |
| } |
| } |
| |
| /* |
| * Resampling audio is best done using a sinc() filter. |
| * |
| * |
| * out[t] = Sum( in[t'] * sinc((t-t')/delta_t), all t') |
| * |
| * The immediate problem with this algorithm is that it involves a |
| * sum over an infinite number of input samples, both in the past |
| * and future. Note that even though sinc(x) is bounded by 1/x, |
| * and thus decays to 0 for large x, since sum(x,{x=0,1..,n}) diverges |
| * as log(n), we need to be careful about convergence. This is |
| * typically done by using a windowing function, which also makes |
| * the sum over a finite number of input samples. |
| * |
| * The next problem is computational: sinc(), and especially |
| * sinc() multiplied by a non-trivial windowing function is expensive |
| * to calculate, and also difficult to find SIMD optimizations. Since |
| * the time increment on input and output is different, it is not |
| * possible to use a FIR filter, because the taps would have to be |
| * recalculated for every t. |
| * |
| * To get around the expense of calculating sinc() for every point, |
| * we pre-calculate sinc() at a number of points, and then interpolate |
| * for the values we want in calculations. The interpolation method |
| * chosen is bi-cubic, which requires both the evalated function and |
| * its derivative at every pre-sampled point. Also, if the sampled |
| * points are spaced commensurate with the input delta_t, we notice |
| * that the interpolating weights are the same for every input point. |
| * This decreases the number of operations to 4 multiplies and 4 adds |
| * for each tap, regardless of the complexity of the filtering function. |
| * |
| * At this point, it is possible to rearrange the problem as the sum |
| * of 4 properly weghted FIR filters. Typical SIMD computation units |
| * are highly optimized for FIR filters, making long filter lengths |
| * reasonable. |
| */ |
| |
| static functable_t *ft; |
| |
| double out_tmp[10000]; |
| |
| void resample_sinc_ft_s16(resample_t * r) |
| { |
| double *ptr; |
| signed short *o_ptr; |
| int i; |
| /*int j; */ |
| double c0, c1; |
| /*double a; */ |
| double start_f, start_x; |
| int start; |
| double center; |
| /*double weight; */ |
| double x, d; |
| double scale; |
| int n = 4; |
| |
| scale = r->i_inc; /* cutoff at 22050 */ |
| /*scale = 1.0; // cutoff at 24000 */ |
| /*scale = r->i_inc * 0.5; // cutoff at 11025 */ |
| |
| if(!ft){ |
| ft = malloc(sizeof(*ft)); |
| memset(ft,0,sizeof(*ft)); |
| |
| ft->len = (r->filter_length + 2) * n; |
| ft->offset = 1.0 / n; |
| ft->start = - ft->len * 0.5 * ft->offset; |
| |
| ft->func_x = functable_sinc; |
| ft->func_dx = functable_dsinc; |
| ft->scale = M_PI * scale; |
| |
| ft->func2_x = functable_window_std; |
| ft->func2_dx = functable_window_dstd; |
| ft->scale2 = 1.0 / r->halftaps; |
| |
| functable_init(ft); |
| |
| /*printf("len=%d offset=%g start=%g\n",ft->len,ft->offset,ft->start); */ |
| } |
| |
| ptr = r->buffer; |
| o_ptr = (signed short *) r->o_buf; |
| |
| center = r->o_start; |
| start_x = center - r->halftaps; |
| start_f = floor(start_x); |
| start_x -= start_f; |
| start = start_f; |
| for (i = 0; i < r->o_samples; i++) { |
| /*start_f = floor(center - r->halftaps); */ |
| /*printf("%d: a=%g start=%d end=%d\n",i,a,start,start+r->filter_length-1); */ |
| x = start_f - center; |
| d = 1; |
| c0 = 0; |
| c1 = 0; |
| /*#define slow */ |
| #ifdef slow |
| for (j = 0; j < r->filter_length; j++) { |
| weight = functable_eval(ft,x)*scale; |
| /*weight = sinc(M_PI * scale * x)*scale*r->i_inc; */ |
| /*weight *= window_func(x / r->halftaps); */ |
| c0 += weight * ptr[(start + j + r->filter_length)*2 + 0]; |
| c1 += weight * ptr[(start + j + r->filter_length)*2 + 1]; |
| x += d; |
| } |
| #else |
| functable_fir2(ft, |
| &c0,&c1, |
| x, n, |
| ptr+(start + r->filter_length)*2, |
| r->filter_length); |
| c0 *= scale; |
| c1 *= scale; |
| #endif |
| |
| out_tmp[2 * i + 0] = c0; |
| out_tmp[2 * i + 1] = c1; |
| center += r->o_inc; |
| start_x += r->o_inc; |
| while(start_x>=1.0){ |
| start_f++; |
| start_x -= 1.0; |
| start++; |
| } |
| } |
| |
| if(r->channels==2){ |
| conv_short_double(r->o_buf,out_tmp,2 * r->o_samples); |
| }else{ |
| conv_short_double_sstr(r->o_buf,out_tmp,r->o_samples,2 * sizeof(double)); |
| } |
| } |
| |
| /******** |
| ** float code below |
| ********/ |
| |
| |
| void resample_nearest_float(resample_t * r) |
| { |
| float *i_ptr, *o_ptr; |
| int i_count = 0; |
| double a; |
| int i; |
| |
| i_ptr = (float *) r->i_buf; |
| o_ptr = (float *) r->o_buf; |
| |
| a = r->o_start; |
| i_count = 0; |
| #define SCALE_LOOP(COPY,INC) \ |
| for (i = 0; i < r->o_samples; i++) { \ |
| COPY; \ |
| a += r->o_inc; \ |
| while (a >= 1) { \ |
| a -= 1; \ |
| i_ptr+=INC; \ |
| i_count++; \ |
| } \ |
| o_ptr+=INC; \ |
| } |
| |
| switch (r->channels) { |
| case 1: |
| SCALE_LOOP(o_ptr[0] = i_ptr[0], 1); |
| break; |
| case 2: |
| SCALE_LOOP(o_ptr[0] = i_ptr[0]; |
| o_ptr[1] = i_ptr[1], 2); |
| break; |
| default: |
| { |
| int n, n_chan = r->channels; |
| |
| SCALE_LOOP(for (n = 0; n < n_chan; n++) o_ptr[n] = |
| i_ptr[n], n_chan); |
| } |
| } |
| if (i_count != r->i_samples) { |
| printf("handled %d in samples (expected %d)\n", i_count, |
| r->i_samples); |
| } |
| } |
| |
| void resample_bilinear_float(resample_t * r) |
| { |
| float *i_ptr, *o_ptr; |
| int o_count = 0; |
| double b; |
| int i; |
| double acc0, acc1; |
| |
| i_ptr = (float *) r->i_buf; |
| o_ptr = (float *) r->o_buf; |
| |
| acc0 = r->acc[0]; |
| acc1 = r->acc[1]; |
| b = r->i_start; |
| for (i = 0; i < r->i_samples; i++) { |
| b += r->i_inc; |
| /*printf("in %d\n",i_ptr[0]); */ |
| if(b>=2){ |
| printf("not expecting b>=2\n"); |
| } |
| if (b >= 1) { |
| acc0 += (1.0 - (b-r->i_inc)) * i_ptr[0]; |
| acc1 += (1.0 - (b-r->i_inc)) * i_ptr[1]; |
| |
| o_ptr[0] = acc0; |
| /*printf("out %d\n",o_ptr[0]); */ |
| o_ptr[1] = acc1; |
| o_ptr += 2; |
| o_count++; |
| |
| b -= 1.0; |
| |
| acc0 = b * i_ptr[0]; |
| acc1 = b * i_ptr[1]; |
| } else { |
| acc0 += i_ptr[0] * r->i_inc; |
| acc1 += i_ptr[1] * r->i_inc; |
| } |
| i_ptr += 2; |
| } |
| r->acc[0] = acc0; |
| r->acc[1] = acc1; |
| |
| if (o_count != r->o_samples) { |
| printf("handled %d out samples (expected %d)\n", o_count, |
| r->o_samples); |
| } |
| } |
| |
| void resample_sinc_slow_float(resample_t * r) |
| { |
| float *i_ptr, *o_ptr; |
| int i, j; |
| double c0, c1; |
| double a; |
| int start; |
| double center; |
| double weight; |
| |
| if (!r->buffer) { |
| int size = r->filter_length * sizeof(float) * r->channels; |
| |
| printf("resample temp buffer\n"); |
| r->buffer = malloc(size); |
| memset(r->buffer, 0, size); |
| } |
| |
| i_ptr = (float *) r->i_buf; |
| o_ptr = (float *) r->o_buf; |
| |
| a = r->i_start; |
| #define GETBUF(index,chan) (((index)<0) \ |
| ? ((float *)(r->buffer))[((index)+r->filter_length)*2+(chan)] \ |
| : i_ptr[(index)*2+(chan)]) |
| { |
| double sinx, cosx, sind, cosd; |
| double x, d; |
| double t; |
| |
| for (i = 0; i < r->o_samples; i++) { |
| start = floor(a) - r->filter_length; |
| center = a - r->halftaps; |
| x = M_PI * (start - center) * r->o_inc; |
| sinx = sin(M_PI * (start - center) * r->o_inc); |
| cosx = cos(M_PI * (start - center) * r->o_inc); |
| d = M_PI * r->o_inc; |
| sind = sin(M_PI * r->o_inc); |
| cosd = cos(M_PI * r->o_inc); |
| c0 = 0; |
| c1 = 0; |
| for (j = 0; j < r->filter_length; j++) { |
| weight = (x==0)?1:(sinx/x); |
| /*printf("j %d sin %g cos %g\n",j,sinx,cosx); */ |
| /*printf("j %d sin %g x %g sinc %g\n",j,sinx,x,weight); */ |
| c0 += weight * GETBUF((start + j), 0); |
| c1 += weight * GETBUF((start + j), 1); |
| t = cosx * cosd - sinx * sind; |
| sinx = cosx * sind + sinx * cosd; |
| cosx = t; |
| x += d; |
| } |
| o_ptr[0] = c0; |
| o_ptr[1] = c1; |
| o_ptr += 2; |
| a += r->o_inc; |
| } |
| } |
| #undef GETBUF |
| |
| memcpy(r->buffer, |
| i_ptr + (r->i_samples - r->filter_length) * r->channels, |
| r->filter_length * sizeof(float) * r->channels); |
| } |
| |
| /* only works for channels == 2 ???? */ |
| void resample_sinc_float(resample_t * r) |
| { |
| double *ptr; |
| float *o_ptr; |
| int i, j; |
| double c0, c1; |
| double a; |
| int start; |
| double center; |
| double weight; |
| double x0, x, d; |
| double scale; |
| |
| ptr = (double *) r->buffer; |
| o_ptr = (float *) r->o_buf; |
| |
| /* scale provides a cutoff frequency for the low |
| * pass filter aspects of sinc(). scale=M_PI |
| * will cut off at the input frequency, which is |
| * good for up-sampling, but will cause aliasing |
| * for downsampling. Downsampling needs to be |
| * cut off at o_rate, thus scale=M_PI*r->i_inc. */ |
| /* actually, it needs to be M_PI*r->i_inc*r->i_inc. |
| * Need to research why. */ |
| scale = M_PI*r->i_inc; |
| for (i = 0; i < r->o_samples; i++) { |
| a = r->o_start + i * r->o_inc; |
| start = floor(a - r->halftaps); |
| /*printf("%d: a=%g start=%d end=%d\n",i,a,start,start+r->filter_length-1); */ |
| center = a; |
| /*x = M_PI * (start - center) * r->o_inc; */ |
| /*d = M_PI * r->o_inc; */ |
| /*x = (start - center) * r->o_inc; */ |
| x0 = (start - center) * r->o_inc; |
| d = r->o_inc; |
| c0 = 0; |
| c1 = 0; |
| for (j = 0; j < r->filter_length; j++) { |
| x = x0 + d * j; |
| weight = sinc(x*scale*r->i_inc)*scale/M_PI; |
| weight *= window_func(x/r->halftaps*r->i_inc); |
| c0 += weight * ptr[(start + j + r->filter_length)*2 + 0]; |
| c1 += weight * ptr[(start + j + r->filter_length)*2 + 1]; |
| } |
| o_ptr[0] = c0; |
| o_ptr[1] = c1; |
| o_ptr += 2; |
| } |
| } |
| |
| void resample_sinc_ft_float(resample_t * r) |
| { |
| double *ptr; |
| float *o_ptr; |
| int i; |
| /*int j; */ |
| double c0, c1; |
| /*double a; */ |
| double start_f, start_x; |
| int start; |
| double center; |
| /*double weight; */ |
| double x, d; |
| double scale; |
| int n = 4; |
| |
| scale = r->i_inc; /* cutoff at 22050 */ |
| /*scale = 1.0; // cutoff at 24000 */ |
| /*scale = r->i_inc * 0.5; // cutoff at 11025 */ |
| |
| if(!ft){ |
| ft = malloc(sizeof(*ft)); |
| memset(ft,0,sizeof(*ft)); |
| |
| ft->len = (r->filter_length + 2) * n; |
| ft->offset = 1.0 / n; |
| ft->start = - ft->len * 0.5 * ft->offset; |
| |
| ft->func_x = functable_sinc; |
| ft->func_dx = functable_dsinc; |
| ft->scale = M_PI * scale; |
| |
| ft->func2_x = functable_window_std; |
| ft->func2_dx = functable_window_dstd; |
| ft->scale2 = 1.0 / r->halftaps; |
| |
| functable_init(ft); |
| |
| /*printf("len=%d offset=%g start=%g\n",ft->len,ft->offset,ft->start); */ |
| } |
| |
| ptr = r->buffer; |
| o_ptr = (float *) r->o_buf; |
| |
| center = r->o_start; |
| start_x = center - r->halftaps; |
| start_f = floor(start_x); |
| start_x -= start_f; |
| start = start_f; |
| for (i = 0; i < r->o_samples; i++) { |
| /*start_f = floor(center - r->halftaps); */ |
| /*printf("%d: a=%g start=%d end=%d\n",i,a,start,start+r->filter_length-1); */ |
| x = start_f - center; |
| d = 1; |
| c0 = 0; |
| c1 = 0; |
| /*#define slow */ |
| #ifdef slow |
| for (j = 0; j < r->filter_length; j++) { |
| weight = functable_eval(ft,x)*scale; |
| /*weight = sinc(M_PI * scale * x)*scale*r->i_inc; */ |
| /*weight *= window_func(x / r->halftaps); */ |
| c0 += weight * ptr[(start + j + r->filter_length)*2 + 0]; |
| c1 += weight * ptr[(start + j + r->filter_length)*2 + 1]; |
| x += d; |
| } |
| #else |
| functable_fir2(ft, |
| &c0,&c1, |
| x, n, |
| ptr+(start + r->filter_length)*2, |
| r->filter_length); |
| c0 *= scale; |
| c1 *= scale; |
| #endif |
| |
| out_tmp[2 * i + 0] = c0; |
| out_tmp[2 * i + 1] = c1; |
| center += r->o_inc; |
| start_x += r->o_inc; |
| while(start_x>=1.0){ |
| start_f++; |
| start_x -= 1.0; |
| start++; |
| } |
| } |
| |
| if(r->channels==2){ |
| conv_float_double(r->o_buf,out_tmp,2 * r->o_samples); |
| }else{ |
| conv_float_double_sstr(r->o_buf,out_tmp,r->o_samples,2 * sizeof(double)); |
| } |
| } |
| |
| static gboolean |
| plugin_init (GstPlugin *plugin) |
| { |
| return TRUE; |
| } |
| |
| GST_PLUGIN_DEFINE ( |
| GST_VERSION_MAJOR, |
| GST_VERSION_MINOR, |
| "gstresample", |
| "Resampling routines for use in audio plugins", |
| plugin_init, |
| VERSION, |
| GST_LICENSE, |
| GST_PACKAGE, |
| GST_ORIGIN |
| ); |
| |