/* FIR filter * * (C) 2017 by Andreas Eversberg * All Rights Reserved * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "../libsample/sample.h" #include "fir_filter.h" //#define DEBUG_TAPS static void kernel(double *taps, int M, double cutoff, int invert) { int i; double sum; for (i = 0; i <= M; i++) { /* gen sinc */ if (i == M / 2) taps[i] = 2.0 * M_PI * cutoff; else taps[i] = sin(2.0 * M_PI * cutoff * (double)(i - M / 2)) / (double)(i - M / 2); /* blackman window */ taps[i] *= 0.42 - 0.50 * cos(2 * M_PI * (double)(i / M)) + 0.08 * cos(4 * M_PI * (double)(i / M)); } /* normalize */ sum = 0; for (i = 0; i <= M; i++) sum += taps[i]; for (i = 0; i <= M; i++) taps[i] /= sum; /* invert */ if (invert) { for (i = 0; i <= M; i++) taps[i] = -taps[i]; taps[M / 2] += 1.0; } #ifdef DEBUG_TAPS puts("start"); for (i = 0; i <= M; i++) puts(debug_amplitude(taps[i])); #endif } static fir_filter_t *fir_init(double samplerate, double transition_bandwidth) { fir_filter_t *fir; int M; /* alloc struct */ fir = calloc(1, sizeof(*fir)); if (!fir) { fprintf(stderr, "No memory creating FIR filter!\n"); return NULL; } /* transition bandwidth */ M = ceil(1.0 / (transition_bandwidth / samplerate)); if ((M & 1)) M++; // printf("cutoff=%.4f\n", cutoff / samplerate); // printf("tb=%.4f\n", transition_bandwidth / samplerate); fir->ntaps = M + 1; fir->delay = M / 2; /* alloc taps */ fir->taps = calloc(fir->ntaps, sizeof(*fir->taps)); if (!fir->taps) { fprintf(stderr, "No memory creating FIR filter!\n"); fir_exit(fir); return NULL; } /* alloc ring buffer */ fir->buffer = calloc(fir->ntaps, sizeof(*fir->buffer)); if (!fir->buffer) { fprintf(stderr, "No memory creating FIR filter!\n"); fir_exit(fir); return NULL; } return fir; } fir_filter_t *fir_lowpass_init(double samplerate, double cutoff, double transition_bandwidth) { /* calculate kernel */ fir_filter_t *fir = fir_init(samplerate, transition_bandwidth); if (!fir) return NULL; kernel(fir->taps, fir->ntaps - 1, cutoff / samplerate, 0); return fir; } fir_filter_t *fir_highpass_init(double samplerate, double cutoff, double transition_bandwidth) { fir_filter_t *fir = fir_init(samplerate, transition_bandwidth); if (!fir) return NULL; kernel(fir->taps, fir->ntaps - 1, cutoff / samplerate, 1); return fir; } fir_filter_t *fir_allpass_init(double samplerate, double transition_bandwidth) { fir_filter_t *fir = fir_init(samplerate, transition_bandwidth); if (!fir) return NULL; fir->taps[(fir->ntaps - 1) / 2] = 1.0; return fir; } fir_filter_t *fir_twopass_init(double samplerate, double cutoff_low, double cutoff_high, double transition_bandwidth) { int i; double sum; fir_filter_t *fir = fir_init(samplerate, transition_bandwidth); if (!fir) return NULL; double lp_taps[fir->ntaps], hp_taps[fir->ntaps]; kernel(lp_taps, fir->ntaps - 1, cutoff_low / samplerate, 0); kernel(hp_taps, fir->ntaps - 1, cutoff_high / samplerate, 1); sum = 0; printf("#warning does not work as expected\n"); abort(); for (i = 0; i < fir->ntaps; i++) { fir->taps[i] = lp_taps[i] + hp_taps[i]; sum += fir->taps[i]; } /* hp will die */ // for (i = 0; i < fir->ntaps; i++) // fir->taps[i] /= sum; return fir; } void fir_exit(fir_filter_t *fir) { if (!fir) return; free(fir->taps); free(fir->buffer); free(fir); } void fir_process(fir_filter_t *fir, sample_t *samples, int num) { int i, j; double y; for (i = 0; i < num; i++) { /* put sample in ring buffer */ fir->buffer[fir->buffer_pos] = samples[i]; if (++fir->buffer_pos == fir->ntaps) fir->buffer_pos = 0; /* convolve samples */ y = 0; for (j = 0; j < fir->ntaps; j++) { /* convolve sample from ring buffer, starting with oldest */ y += fir->buffer[fir->buffer_pos] * fir->taps[j]; if (++fir->buffer_pos == fir->ntaps) fir->buffer_pos = 0; } samples[i] = y; } } int fir_get_delay(fir_filter_t *fir) { return fir->delay; }