From 8f913e8e9b2d2aaef4ddb6f4425b788c4cbb971e Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 27 May 2014 18:02:38 +0200 Subject: [PATCH 1/8] Add new FFT implementation. To be used for EQ implementation. --- audio/fft/fft.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++++ audio/fft/fft.h | 43 +++++++++ 2 files changed, 269 insertions(+) create mode 100644 audio/fft/fft.c create mode 100644 audio/fft/fft.h diff --git a/audio/fft/fft.c b/audio/fft/fft.c new file mode 100644 index 0000000000..1a9996fead --- /dev/null +++ b/audio/fft/fft.c @@ -0,0 +1,226 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * + * RetroArch 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#include "fft.h" +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265 +#endif + +struct rarch_fft +{ + rarch_fft_complex_t *interleave_buffer; + rarch_fft_complex_t *phase_lut; + unsigned *bitinverse_buffer; + unsigned size; +}; + +static unsigned bitswap(unsigned x, unsigned size_log2) +{ + unsigned i; + unsigned ret = 0; + for (i = 0; i < size_log2; i++) + ret |= ((x >> i) & 1) << (size_log2 - i - 1); + return ret; +} + +static void build_bitinverse(unsigned *bitinverse, unsigned size_log2) +{ + unsigned i; + unsigned size = 1 << size_log2; + for (i = 0; i < size; i++) + bitinverse[i] = bitswap(i, size_log2); +} + +static rarch_fft_complex_t exp_imag(double phase) +{ + rarch_fft_complex_t out = { cos(phase), sin(phase) }; + return out; +} + +static void build_phase_lut(rarch_fft_complex_t *out, int size) +{ + int i; + out += size; + for (i = -size; i <= size; i++) + out[i] = exp_imag((M_PI * i) / size); +} + +static void interleave_complex(const unsigned *bitinverse, + rarch_fft_complex_t *out, const rarch_fft_complex_t *in, + unsigned samples) +{ + unsigned i; + for (i = 0; i < samples; i++) + out[bitinverse[i]] = in[i]; +} + +static void interleave_float(const unsigned *bitinverse, + rarch_fft_complex_t *out, const float *in, + unsigned samples) +{ + unsigned i; + for (i = 0; i < samples; i++) + { + unsigned inv_i = bitinverse[i]; + out[inv_i].real = in[i]; + out[inv_i].imag = 0.0f; + } +} + +static void resolve_float(float *out, const rarch_fft_complex_t *in, unsigned samples, float gain) +{ + unsigned i; + for (i = 0; i < samples; i++) + out[i] = gain * in[i].real; +} + +rarch_fft_t *rarch_fft_new(unsigned block_size_log2) +{ + rarch_fft_t *fft = (rarch_fft_t*)calloc(1, sizeof(*fft)); + if (!fft) + return NULL; + + unsigned size = 1 << block_size_log2; + + fft->interleave_buffer = (rarch_fft_complex_t*)calloc(size, sizeof(*fft->interleave_buffer)); + fft->bitinverse_buffer = (unsigned*)calloc(size, sizeof(*fft->bitinverse_buffer)); + fft->phase_lut = (rarch_fft_complex_t*)calloc(2 * size + 1, sizeof(*fft->phase_lut)); + + if (!fft->interleave_buffer || !fft->bitinverse_buffer || !fft->phase_lut) + goto error; + + fft->size = size; + + build_bitinverse(fft->bitinverse_buffer, block_size_log2); + build_phase_lut(fft->phase_lut, size); + return fft; + +error: + rarch_fft_free(fft); + return NULL; +} + +void rarch_fft_free(rarch_fft_t *fft) +{ + if (!fft) + return; + + free(fft->interleave_buffer); + free(fft->bitinverse_buffer); + free(fft->phase_lut); + free(fft); +} + +static inline rarch_fft_complex_t complex_mul(rarch_fft_complex_t a, rarch_fft_complex_t b) +{ + rarch_fft_complex_t out = { + a.real * b.real - a.imag * b.imag, + a.imag * b.real + a.real * b.imag, + }; + + return out; +} + +static inline rarch_fft_complex_t complex_add(rarch_fft_complex_t a, rarch_fft_complex_t b) +{ + rarch_fft_complex_t out = { + a.real + b.real, + a.imag + b.imag, + }; + + return out; +} + +static inline rarch_fft_complex_t complex_sub(rarch_fft_complex_t a, rarch_fft_complex_t b) +{ + rarch_fft_complex_t out = { + a.real - b.real, + a.imag - b.imag, + }; + + return out; +} + + +static void butterfly(rarch_fft_complex_t *a, rarch_fft_complex_t *b, rarch_fft_complex_t mod) +{ + mod = complex_mul(mod, *b); + *b = complex_sub(*a, mod); + *a = complex_add(*a, mod); +} + +static void butterflies(rarch_fft_complex_t *butterfly_buf, + const rarch_fft_complex_t *phase_lut, + int phase_dir, unsigned step_size, unsigned samples) +{ + unsigned i, j; + for (i = 0; i < samples; i += step_size << 1) + { + int phase_step = (int)samples * phase_dir / (int)step_size; + for (j = i; j < i + step_size; j++) + butterfly(&butterfly_buf[j], &butterfly_buf[j + step_size], phase_lut[phase_step * (int)(j - i)]); + } +} + +void rarch_fft_process_forward_complex(rarch_fft_t *fft, + rarch_fft_complex_t *out, const rarch_fft_complex_t *in) +{ + unsigned step_size; + unsigned samples = fft->size; + interleave_complex(fft->bitinverse_buffer, out, in, fft->size); + + for (step_size = 1; step_size < samples; step_size <<= 1) + { + butterflies(out, + fft->phase_lut + samples, + -1, step_size, samples); + } +} + +void rarch_fft_process_forward(rarch_fft_t *fft, + rarch_fft_complex_t *out, const float *in) +{ + unsigned step_size; + unsigned samples = fft->size; + interleave_float(fft->bitinverse_buffer, out, in, fft->size); + + for (step_size = 1; step_size < fft->size; step_size <<= 1) + { + butterflies(out, + fft->phase_lut + samples, + -1, step_size, samples); + } +} + +void rarch_fft_process_inverse(rarch_fft_t *fft, + float *out, const rarch_fft_complex_t *in) +{ + unsigned step_size; + unsigned samples = fft->size; + interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer, in, fft->size); + + for (step_size = 1; step_size < samples; step_size <<= 1) + { + butterflies(fft->interleave_buffer, + fft->phase_lut + samples, + 1, step_size, samples); + } + + resolve_float(out, fft->interleave_buffer, fft->size, 1.0f / fft->size); +} + diff --git a/audio/fft/fft.h b/audio/fft/fft.h new file mode 100644 index 0000000000..3fc7ca8d49 --- /dev/null +++ b/audio/fft/fft.h @@ -0,0 +1,43 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * + * RetroArch 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#ifndef RARCH_FFT_H__ +#define RARCH_FFT_H__ + +typedef struct rarch_fft rarch_fft_t; + +// C99 would be nice. +typedef struct +{ + float real; + float imag; +} rarch_fft_complex_t; + +rarch_fft_t *rarch_fft_new(unsigned block_size_log2); + +void rarch_fft_free(rarch_fft_t *fft); + +void rarch_fft_process_forward_complex(rarch_fft_t *fft, + rarch_fft_complex_t *out, const rarch_fft_complex_t *in); + +void rarch_fft_process_forward(rarch_fft_t *fft, + rarch_fft_complex_t *out, const float *in); + +void rarch_fft_process_inverse(rarch_fft_t *fft, + float *out, const rarch_fft_complex_t *in); + + +#endif + From ce14f2f517c6c9000dc7a20ef9b6b96ef6001562 Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 27 May 2014 18:38:25 +0200 Subject: [PATCH 2/8] Begin implementing EQ. --- audio/fft/fft.c | 41 +++--------------- audio/fft/fft.h | 45 +++++++++++++++++++ audio/filters/eq.c | 105 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 36 deletions(-) create mode 100644 audio/filters/eq.c diff --git a/audio/fft/fft.c b/audio/fft/fft.c index 1a9996fead..d091c1730b 100644 --- a/audio/fft/fft.c +++ b/audio/fft/fft.c @@ -18,7 +18,7 @@ #include #ifndef M_PI -#define M_PI 3.14159265 +#define M_PI 3.1415926535897932384626433832795 #endif struct rarch_fft @@ -126,42 +126,11 @@ void rarch_fft_free(rarch_fft_t *fft) free(fft); } -static inline rarch_fft_complex_t complex_mul(rarch_fft_complex_t a, rarch_fft_complex_t b) -{ - rarch_fft_complex_t out = { - a.real * b.real - a.imag * b.imag, - a.imag * b.real + a.real * b.imag, - }; - - return out; -} - -static inline rarch_fft_complex_t complex_add(rarch_fft_complex_t a, rarch_fft_complex_t b) -{ - rarch_fft_complex_t out = { - a.real + b.real, - a.imag + b.imag, - }; - - return out; -} - -static inline rarch_fft_complex_t complex_sub(rarch_fft_complex_t a, rarch_fft_complex_t b) -{ - rarch_fft_complex_t out = { - a.real - b.real, - a.imag - b.imag, - }; - - return out; -} - - static void butterfly(rarch_fft_complex_t *a, rarch_fft_complex_t *b, rarch_fft_complex_t mod) { - mod = complex_mul(mod, *b); - *b = complex_sub(*a, mod); - *a = complex_add(*a, mod); + mod = rarch_fft_complex_mul(mod, *b); + *b = rarch_fft_complex_sub(*a, mod); + *a = rarch_fft_complex_add(*a, mod); } static void butterflies(rarch_fft_complex_t *butterfly_buf, @@ -221,6 +190,6 @@ void rarch_fft_process_inverse(rarch_fft_t *fft, 1, step_size, samples); } - resolve_float(out, fft->interleave_buffer, fft->size, 1.0f / fft->size); + resolve_float(out, fft->interleave_buffer, samples, 1.0f / samples); } diff --git a/audio/fft/fft.h b/audio/fft/fft.h index 3fc7ca8d49..6b68526e6b 100644 --- a/audio/fft/fft.h +++ b/audio/fft/fft.h @@ -25,6 +25,51 @@ typedef struct float imag; } rarch_fft_complex_t; +static inline rarch_fft_complex_t rarch_fft_complex_mul(rarch_fft_complex_t a, + rarch_fft_complex_t b) +{ + rarch_fft_complex_t out = { + a.real * b.real - a.imag * b.imag, + a.imag * b.real + a.real * b.imag, + }; + + return out; + +} + +static inline rarch_fft_complex_t rarch_fft_complex_add(rarch_fft_complex_t a, + rarch_fft_complex_t b) +{ + rarch_fft_complex_t out = { + a.real + b.real, + a.imag + b.imag, + }; + + return out; + +} + +static inline rarch_fft_complex_t rarch_fft_complex_sub(rarch_fft_complex_t a, + rarch_fft_complex_t b) +{ + rarch_fft_complex_t out = { + a.real - b.real, + a.imag - b.imag, + }; + + return out; + +} + +static inline rarch_fft_complex_t rarch_fft_complex_conj(rarch_fft_complex_t a) +{ + rarch_fft_complex_t out = { + a.real, -a.imag, + }; + + return out; +} + rarch_fft_t *rarch_fft_new(unsigned block_size_log2); void rarch_fft_free(rarch_fft_t *fft); diff --git a/audio/filters/eq.c b/audio/filters/eq.c new file mode 100644 index 0000000000..7cc736cd7a --- /dev/null +++ b/audio/filters/eq.c @@ -0,0 +1,105 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * + * RetroArch 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#include "dspfilter.h" +#include +#include + +#include "../fft/fft.c" + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795 +#endif + +struct eq_data +{ + rarch_fft_t *fft; + float buffer[8 * 1024]; + + float *save; + float *block; + rarch_fft_complex_t *filter; + unsigned block_size; + unsigned block_ptr; +}; + +static void eq_free(void *data) +{ + struct eq_data *eq = (struct eq_data*)data; + if (!eq) + return; + + rarch_fft_free(eq->fft); + free(eq->save); + free(eq->block); + free(eq->filter); + free(eq); +} + +static void eq_process(void *data, struct dspfilter_output *output, + const struct dspfilter_input *input) +{ + struct eq_data *eq = (struct eq_data*)data; +} + +static void *eq_init(const struct dspfilter_info *info, + const struct dspfilter_config *config, void *userdata) +{ + struct eq_data *eq = (struct eq_data*)calloc(1, sizeof(*eq)); + if (!eq) + return NULL; + + unsigned size_log2 = 8; + unsigned size = 1 << size_log2; + + eq->block_size = size; + + eq->save = (float*)calloc(2 * size, sizeof(*eq->save)); + eq->block = (float*)calloc(2 * size, 2 * sizeof(*eq->block)); + eq->filter = (rarch_fft_complex_t*)calloc(2 * size, sizeof(*eq->filter)); + eq->fft = rarch_fft_new(size_log2 + 1); + + if (!eq->fft || !eq->save || !eq->block || !eq->filter) + goto error; + + return eq; + +error: + eq_free(eq); + return NULL; +} + +static const struct dspfilter_implementation eq_plug = { + eq_init, + eq_process, + eq_free, + + DSPFILTER_API_VERSION, + "Linear-Phase FFT Equalizer", + "eq", +}; + +#ifdef HAVE_FILTERS_BUILTIN +#define dspfilter_get_implementation eq_dspfilter_get_implementation +#endif + +const struct dspfilter_implementation *eq_get_implementation(dspfilter_simd_mask_t mask) +{ + (void)mask; + return &eq_plug; +} + +#undef dspfilter_get_implementation + From b99b288980938e82e8042d1b393ad23a7a528154 Mon Sep 17 00:00:00 2001 From: Themaister Date: Tue, 27 May 2014 22:26:16 +0200 Subject: [PATCH 3/8] Implement more of EQ. --- audio/fft/fft.c | 33 ++++---- audio/fft/fft.h | 6 +- audio/filters/eq.c | 199 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 214 insertions(+), 24 deletions(-) diff --git a/audio/fft/fft.c b/audio/fft/fft.c index d091c1730b..e4a33cbd99 100644 --- a/audio/fft/fft.c +++ b/audio/fft/fft.c @@ -62,31 +62,32 @@ static void build_phase_lut(rarch_fft_complex_t *out, int size) static void interleave_complex(const unsigned *bitinverse, rarch_fft_complex_t *out, const rarch_fft_complex_t *in, - unsigned samples) + unsigned samples, unsigned step) { unsigned i; - for (i = 0; i < samples; i++) - out[bitinverse[i]] = in[i]; + for (i = 0; i < samples; i++, in += step) + out[bitinverse[i]] = *in; } static void interleave_float(const unsigned *bitinverse, rarch_fft_complex_t *out, const float *in, - unsigned samples) + unsigned samples, unsigned step) { unsigned i; - for (i = 0; i < samples; i++) + for (i = 0; i < samples; i++, in += step) { unsigned inv_i = bitinverse[i]; - out[inv_i].real = in[i]; + out[inv_i].real = *in; out[inv_i].imag = 0.0f; } } -static void resolve_float(float *out, const rarch_fft_complex_t *in, unsigned samples, float gain) +static void resolve_float(float *out, const rarch_fft_complex_t *in, unsigned samples, + float gain, unsigned step) { unsigned i; - for (i = 0; i < samples; i++) - out[i] = gain * in[i].real; + for (i = 0; i < samples; i++, in++, out += step) + *out = gain * in->real; } rarch_fft_t *rarch_fft_new(unsigned block_size_log2) @@ -147,11 +148,11 @@ static void butterflies(rarch_fft_complex_t *butterfly_buf, } void rarch_fft_process_forward_complex(rarch_fft_t *fft, - rarch_fft_complex_t *out, const rarch_fft_complex_t *in) + rarch_fft_complex_t *out, const rarch_fft_complex_t *in, unsigned step) { unsigned step_size; unsigned samples = fft->size; - interleave_complex(fft->bitinverse_buffer, out, in, fft->size); + interleave_complex(fft->bitinverse_buffer, out, in, fft->size, step); for (step_size = 1; step_size < samples; step_size <<= 1) { @@ -162,11 +163,11 @@ void rarch_fft_process_forward_complex(rarch_fft_t *fft, } void rarch_fft_process_forward(rarch_fft_t *fft, - rarch_fft_complex_t *out, const float *in) + rarch_fft_complex_t *out, const float *in, unsigned step) { unsigned step_size; unsigned samples = fft->size; - interleave_float(fft->bitinverse_buffer, out, in, fft->size); + interleave_float(fft->bitinverse_buffer, out, in, fft->size, step); for (step_size = 1; step_size < fft->size; step_size <<= 1) { @@ -177,11 +178,11 @@ void rarch_fft_process_forward(rarch_fft_t *fft, } void rarch_fft_process_inverse(rarch_fft_t *fft, - float *out, const rarch_fft_complex_t *in) + float *out, const rarch_fft_complex_t *in, unsigned step) { unsigned step_size; unsigned samples = fft->size; - interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer, in, fft->size); + interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer, in, fft->size, step); for (step_size = 1; step_size < samples; step_size <<= 1) { @@ -190,6 +191,6 @@ void rarch_fft_process_inverse(rarch_fft_t *fft, 1, step_size, samples); } - resolve_float(out, fft->interleave_buffer, samples, 1.0f / samples); + resolve_float(out, fft->interleave_buffer, samples, 1.0f / samples, step); } diff --git a/audio/fft/fft.h b/audio/fft/fft.h index 6b68526e6b..96d18cd9b5 100644 --- a/audio/fft/fft.h +++ b/audio/fft/fft.h @@ -75,13 +75,13 @@ rarch_fft_t *rarch_fft_new(unsigned block_size_log2); void rarch_fft_free(rarch_fft_t *fft); void rarch_fft_process_forward_complex(rarch_fft_t *fft, - rarch_fft_complex_t *out, const rarch_fft_complex_t *in); + rarch_fft_complex_t *out, const rarch_fft_complex_t *in, unsigned step); void rarch_fft_process_forward(rarch_fft_t *fft, - rarch_fft_complex_t *out, const float *in); + rarch_fft_complex_t *out, const float *in, unsigned step); void rarch_fft_process_inverse(rarch_fft_t *fft, - float *out, const rarch_fft_complex_t *in); + float *out, const rarch_fft_complex_t *in, unsigned step); #endif diff --git a/audio/filters/eq.c b/audio/filters/eq.c index 7cc736cd7a..772de6fc95 100644 --- a/audio/filters/eq.c +++ b/audio/filters/eq.c @@ -16,6 +16,7 @@ #include "dspfilter.h" #include #include +#include #include "../fft/fft.c" @@ -31,10 +32,17 @@ struct eq_data float *save; float *block; rarch_fft_complex_t *filter; + rarch_fft_complex_t *fftblock; unsigned block_size; unsigned block_ptr; }; +struct eq_gain +{ + float freq; + float gain; // Linear. +}; + static void eq_free(void *data) { struct eq_data *eq = (struct eq_data*)data; @@ -44,6 +52,7 @@ static void eq_free(void *data) rarch_fft_free(eq->fft); free(eq->save); free(eq->block); + free(eq->fftblock); free(eq->filter); free(eq); } @@ -52,6 +61,178 @@ static void eq_process(void *data, struct dspfilter_output *output, const struct dspfilter_input *input) { struct eq_data *eq = (struct eq_data*)data; + + output->samples = eq->buffer; + output->frames = 0; + + float *out = eq->buffer; + const float *in = input->samples; + unsigned input_frames = input->frames; + + while (input_frames) + { + unsigned write_avail = eq->block_size - eq->block_ptr; + if (input_frames < write_avail) + write_avail = input_frames; + + memcpy(eq->block + eq->block_ptr * 2, in, write_avail * 2 * sizeof(float)); + in += write_avail * 2; + input_frames -= write_avail; + eq->block_ptr += write_avail; + + // Convolve a new block. + if (eq->block_ptr == eq->block_size) + { + unsigned i, c; + + for (c = 0; c < 2; c++) + { + rarch_fft_process_forward(eq->fft, eq->fftblock, eq->block + c, 2); + for (i = 0; i < 2 * eq->block_size; i++) + eq->fftblock[i] = rarch_fft_complex_mul(eq->fftblock[i], eq->filter[i]); + rarch_fft_process_inverse(eq->fft, out + c, eq->fftblock, 2); + } + + // Overlap add method, so add in saved block now. + for (i = 0; i < 2 * eq->block_size; i++) + out[i] += eq->save[i]; + + // Save block for later. + memcpy(eq->save, out + 2 * eq->block_size, 2 * eq->block_size * sizeof(float)); + + out += eq->block_size * 2; + output->frames += eq->block_size; + eq->block_ptr = 0; + } + } +} + +static int gains_cmp(const void *a_, const void *b_) +{ + const struct eq_gain *a = (const struct eq_gain*)a_; + const struct eq_gain *b = (const struct eq_gain*)b_; + if (a->freq < b->freq) + return -1; + else if (a->freq > b->freq) + return 1; + else + return 0; +} + +static void generate_response(rarch_fft_complex_t *response, + const struct eq_gain *gains, unsigned num_gains, unsigned samples) +{ + unsigned i; + + // DC and Nyquist get 0 gain. (This will get smeared out good with windowing later though ...) + response[0].real = 0.0f; + response[0].imag = 0.0f; + response[samples].real = 0.0f; + response[samples].imag = 0.0f; + + float start_freq = 0.0f; + float start_gain = 1.0f; + + float end_freq = 1.0f; + float end_gain = 1.0f; + + if (num_gains) + { + end_freq = gains->freq; + end_gain = gains->gain; + num_gains--; + gains++; + } + + // Create a response by linear interpolation between known frequency sample points. + for (i = 1; i < samples; i++) + { + float freq = (float)i / samples; + + while (freq > end_freq) + { + if (num_gains) + { + start_freq = end_freq; + start_gain = end_gain; + + end_freq = gains->freq; + end_gain = gains->gain; + gains++; + num_gains--; + } + else + { + end_freq = 1.0f; + end_gain = 1.0f; + } + } + + float lerp = (freq - start_freq) / (end_freq - start_freq); + float gain = (1.0f - lerp) * start_gain + lerp * end_gain; + + response[i].real = gain; + response[i].imag = 0.0f; + response[2 * samples - i].real = gain; + response[2 * samples - i].imag = gain; + } +} + +static void create_filter(struct eq_data *eq, unsigned size_log2, + struct eq_gain *gains, unsigned num_gains) +{ + int i; + int half_block_size = eq->block_size >> 1; + + rarch_fft_t *fft = rarch_fft_new(size_log2); + float *time_filter = (float*)calloc(eq->block_size * 2, sizeof(*time_filter)); + if (!fft || !time_filter) + goto end; + + qsort(gains, num_gains, sizeof(*gains), gains_cmp); + + // Compute desired filter response. + generate_response(eq->filter, gains, num_gains, half_block_size); + + // Get equivalent time-domain filter. + rarch_fft_process_inverse(fft, time_filter, eq->filter, 1); + + // ifftshift() to create the correct linear phase filter. + // The filter response was designed with zero phase, which won't work unless we compensate + // for the repeating property of the FFT here by flipping left and right blocks. + for (i = 0; i < half_block_size; i++) + { + float tmp = time_filter[i + half_block_size]; + time_filter[i + half_block_size] = time_filter[i]; + time_filter[i] = tmp; + } + + // Apply a window to smooth out the frequency repsonse. + for (i = 0; i < (int)eq->block_size; i++) + { + // Simple cosine window. + double phase = (double)i / (eq->block_size - 1); + phase = 2.0 * (phase - 0.5); + time_filter[i] *= cos(phase * M_PI); + } + + // Debugging. +#if 1 + FILE *file = fopen("/tmp/test.txt", "w"); + if (file) + { + for (i = 0; i < (int)eq->block_size; i++) + fprintf(file, "%.6f", time_filter[i]); + fclose(file); + } +#endif + + // Padded FFT to create our FFT filter. + rarch_fft_process_forward(eq->fft, eq->filter, time_filter, 1); + +end: + rarch_fft_free(fft); + free(time_filter); } static void *eq_init(const struct dspfilter_info *info, @@ -66,14 +247,22 @@ static void *eq_init(const struct dspfilter_info *info, eq->block_size = size; - eq->save = (float*)calloc(2 * size, sizeof(*eq->save)); - eq->block = (float*)calloc(2 * size, 2 * sizeof(*eq->block)); - eq->filter = (rarch_fft_complex_t*)calloc(2 * size, sizeof(*eq->filter)); - eq->fft = rarch_fft_new(size_log2 + 1); + eq->save = (float*)calloc( size, 2 * sizeof(*eq->save)); + eq->block = (float*)calloc(2 * size, 2 * sizeof(*eq->block)); + eq->fftblock = (rarch_fft_complex_t*)calloc(2 * size, sizeof(*eq->fftblock)); + eq->filter = (rarch_fft_complex_t*)calloc(2 * size, sizeof(*eq->filter)); - if (!eq->fft || !eq->save || !eq->block || !eq->filter) + // Use an FFT which is twice the block size with zero-padding + // to make circular convolution => proper convolution. + eq->fft = rarch_fft_new(size_log2 + 1); + + if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter) goto error; + struct eq_gain *gains = NULL; + unsigned num_gains = 0; + create_filter(eq, size_log2, gains, num_gains); + return eq; error: From 95d1582f3cf232f59b8d8b9583884def6cdd6481 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 28 May 2014 00:07:14 +0200 Subject: [PATCH 4/8] Equalizer works. --- audio/fft/fft.c | 6 ++-- audio/filters/eq.c | 84 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/audio/fft/fft.c b/audio/fft/fft.c index e4a33cbd99..a036c7fb09 100644 --- a/audio/fft/fft.c +++ b/audio/fft/fft.c @@ -152,7 +152,7 @@ void rarch_fft_process_forward_complex(rarch_fft_t *fft, { unsigned step_size; unsigned samples = fft->size; - interleave_complex(fft->bitinverse_buffer, out, in, fft->size, step); + interleave_complex(fft->bitinverse_buffer, out, in, samples, step); for (step_size = 1; step_size < samples; step_size <<= 1) { @@ -167,7 +167,7 @@ void rarch_fft_process_forward(rarch_fft_t *fft, { unsigned step_size; unsigned samples = fft->size; - interleave_float(fft->bitinverse_buffer, out, in, fft->size, step); + interleave_float(fft->bitinverse_buffer, out, in, samples, step); for (step_size = 1; step_size < fft->size; step_size <<= 1) { @@ -182,7 +182,7 @@ void rarch_fft_process_inverse(rarch_fft_t *fft, { unsigned step_size; unsigned samples = fft->size; - interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer, in, fft->size, step); + interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer, in, samples, 1); for (step_size = 1; step_size < samples; step_size <<= 1) { diff --git a/audio/filters/eq.c b/audio/filters/eq.c index 772de6fc95..cfdfb231be 100644 --- a/audio/filters/eq.c +++ b/audio/filters/eq.c @@ -76,6 +76,7 @@ static void eq_process(void *data, struct dspfilter_output *output, write_avail = input_frames; memcpy(eq->block + eq->block_ptr * 2, in, write_avail * 2 * sizeof(float)); + in += write_avail * 2; input_frames -= write_avail; eq->block_ptr += write_avail; @@ -124,12 +125,6 @@ static void generate_response(rarch_fft_complex_t *response, { unsigned i; - // DC and Nyquist get 0 gain. (This will get smeared out good with windowing later though ...) - response[0].real = 0.0f; - response[0].imag = 0.0f; - response[samples].real = 0.0f; - response[samples].imag = 0.0f; - float start_freq = 0.0f; float start_gain = 1.0f; @@ -145,44 +140,83 @@ static void generate_response(rarch_fft_complex_t *response, } // Create a response by linear interpolation between known frequency sample points. - for (i = 1; i < samples; i++) + for (i = 0; i <= samples; i++) { float freq = (float)i / samples; - while (freq > end_freq) + while (freq >= end_freq) { if (num_gains) { start_freq = end_freq; start_gain = end_gain; - end_freq = gains->freq; end_gain = gains->gain; + gains++; num_gains--; } else { + start_freq = end_freq; + start_gain = end_gain; end_freq = 1.0f; end_gain = 1.0f; + break; } } - float lerp = (freq - start_freq) / (end_freq - start_freq); + float lerp = 0.5f; + if (end_freq > start_freq) + lerp = (freq - start_freq) / (end_freq - start_freq); float gain = (1.0f - lerp) * start_gain + lerp * end_gain; response[i].real = gain; response[i].imag = 0.0f; response[2 * samples - i].real = gain; - response[2 * samples - i].imag = gain; + response[2 * samples - i].imag = 0.0f; } } +// Modified Bessel function of first order. +// Check Wiki for mathematical definition ... +static inline double besseli0(double x) +{ + unsigned i; + double sum = 0.0; + + double factorial = 1.0; + double factorial_mult = 0.0; + double x_pow = 1.0; + double two_div_pow = 1.0; + double x_sqr = x * x; + + // Approximate. This is an infinite sum. + // Luckily, it converges rather fast. + for (i = 0; i < 18; i++) + { + sum += x_pow * two_div_pow / (factorial * factorial); + + factorial_mult += 1.0; + x_pow *= x_sqr; + two_div_pow *= 0.25; + factorial *= factorial_mult; + } + + return sum; +} + +static inline double kaiser_window(double index, double beta) +{ + return besseli0(beta * sqrt(1 - index * index)); +} + static void create_filter(struct eq_data *eq, unsigned size_log2, - struct eq_gain *gains, unsigned num_gains) + struct eq_gain *gains, unsigned num_gains, double beta) { int i; int half_block_size = eq->block_size >> 1; + double window_mod = 1.0 / kaiser_window(0.0, beta); rarch_fft_t *fft = rarch_fft_new(size_log2); float *time_filter = (float*)calloc(eq->block_size * 2, sizeof(*time_filter)); @@ -213,7 +247,7 @@ static void create_filter(struct eq_data *eq, unsigned size_log2, // Simple cosine window. double phase = (double)i / (eq->block_size - 1); phase = 2.0 * (phase - 0.5); - time_filter[i] *= cos(phase * M_PI); + time_filter[i] *= window_mod * kaiser_window(phase, beta); } // Debugging. @@ -222,7 +256,7 @@ static void create_filter(struct eq_data *eq, unsigned size_log2, if (file) { for (i = 0; i < (int)eq->block_size; i++) - fprintf(file, "%.6f", time_filter[i]); + fprintf(file, "%.6f\n", time_filter[i]); fclose(file); } #endif @@ -259,9 +293,23 @@ static void *eq_init(const struct dspfilter_info *info, if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter) goto error; - struct eq_gain *gains = NULL; - unsigned num_gains = 0; - create_filter(eq, size_log2, gains, num_gains); + //struct eq_gain *gains = NULL; + //unsigned num_gains = 0; + struct eq_gain gains[] = { + { 0.00f, 2.0f }, + { 0.04f, 1.0f }, + { 0.05f, 0.4f }, + { 0.06f, 1.0f }, + { 0.08f, 0.1f }, + { 0.15f, 0.1f }, + { 0.3f, 3.0f }, + { 0.5f, 0.25f }, + { 0.7f, 2.0f }, + }; + unsigned num_gains = sizeof(gains) / sizeof(gains[0]); + double beta = 5.0; + + create_filter(eq, size_log2, gains, num_gains, beta); return eq; @@ -284,7 +332,7 @@ static const struct dspfilter_implementation eq_plug = { #define dspfilter_get_implementation eq_dspfilter_get_implementation #endif -const struct dspfilter_implementation *eq_get_implementation(dspfilter_simd_mask_t mask) +const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) { (void)mask; return &eq_plug; From 902c92acfcd9914a618b55efe586fb820d91923c Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 28 May 2014 00:30:03 +0200 Subject: [PATCH 5/8] Read config in EQ. --- audio/filters/eq.c | 58 +++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/audio/filters/eq.c b/audio/filters/eq.c index cfdfb231be..5defc3c9d5 100644 --- a/audio/filters/eq.c +++ b/audio/filters/eq.c @@ -24,6 +24,10 @@ #define M_PI 3.1415926535897932384626433832795 #endif +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + struct eq_data { rarch_fft_t *fft; @@ -167,6 +171,7 @@ static void generate_response(rarch_fft_complex_t *response, } float lerp = 0.5f; + // Edge case where i == samples. if (end_freq > start_freq) lerp = (freq - start_freq) / (end_freq - start_freq); float gain = (1.0f - lerp) * start_gain + lerp * end_gain; @@ -223,6 +228,7 @@ static void create_filter(struct eq_data *eq, unsigned size_log2, if (!fft || !time_filter) goto end; + // Make sure bands are in correct order. qsort(gains, num_gains, sizeof(*gains), gains_cmp); // Compute desired filter response. @@ -251,7 +257,7 @@ static void create_filter(struct eq_data *eq, unsigned size_log2, } // Debugging. -#if 1 +#if 0 FILE *file = fopen("/tmp/test.txt", "w"); if (file) { @@ -272,13 +278,41 @@ end: static void *eq_init(const struct dspfilter_info *info, const struct dspfilter_config *config, void *userdata) { + unsigned i; struct eq_data *eq = (struct eq_data*)calloc(1, sizeof(*eq)); if (!eq) return NULL; - unsigned size_log2 = 8; + const float default_freq[] = { 0.0f, info->input_rate }; + const float default_gain[] = { 0.0f, 0.0f }; + + float beta; + config->get_float(userdata, "window_beta", &beta, 4.0f); + + int size_log2; + config->get_int(userdata, "block_size_log2", &size_log2, 8); unsigned size = 1 << size_log2; + struct eq_gain *gains = NULL; + float *frequencies, *gain; + unsigned num_freq, num_gain; + config->get_float_array(userdata, "frequencies", &frequencies, &num_freq, default_freq, 2); + config->get_float_array(userdata, "gains", &gain, &num_gain, default_gain, 2); + + num_gain = num_freq = min(num_gain, num_freq); + + gains = (struct eq_gain*)calloc(num_gain, sizeof(*gains)); + if (!gains) + goto error; + + for (i = 0; i < num_gain; i++) + { + gains[i].freq = 0.5f * frequencies[i] / info->input_rate; + gains[i].gain = pow(10.0, gain[i] / 20.0); + } + config->free(frequencies); + config->free(gain); + eq->block_size = size; eq->save = (float*)calloc( size, 2 * sizeof(*eq->save)); @@ -293,27 +327,13 @@ static void *eq_init(const struct dspfilter_info *info, if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter) goto error; - //struct eq_gain *gains = NULL; - //unsigned num_gains = 0; - struct eq_gain gains[] = { - { 0.00f, 2.0f }, - { 0.04f, 1.0f }, - { 0.05f, 0.4f }, - { 0.06f, 1.0f }, - { 0.08f, 0.1f }, - { 0.15f, 0.1f }, - { 0.3f, 3.0f }, - { 0.5f, 0.25f }, - { 0.7f, 2.0f }, - }; - unsigned num_gains = sizeof(gains) / sizeof(gains[0]); - double beta = 5.0; - - create_filter(eq, size_log2, gains, num_gains, beta); + create_filter(eq, size_log2, gains, num_gain, beta); + free(gains); return eq; error: + free(gains); eq_free(eq); return NULL; } From 31249ab9daaa8576b9013e815773860fc89c3acc Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 28 May 2014 10:36:13 +0200 Subject: [PATCH 6/8] Some fixes to EQ. --- audio/filters/eq.c | 34 +++++++++++----------- audio/{ => filters}/fft/fft.c | 54 +++++++++++++++++------------------ audio/{ => filters}/fft/fft.h | 42 +++++++++++++-------------- 3 files changed, 65 insertions(+), 65 deletions(-) rename audio/{ => filters}/fft/fft.c (72%) rename audio/{ => filters}/fft/fft.h (53%) diff --git a/audio/filters/eq.c b/audio/filters/eq.c index 5defc3c9d5..04c050f03e 100644 --- a/audio/filters/eq.c +++ b/audio/filters/eq.c @@ -18,7 +18,7 @@ #include #include -#include "../fft/fft.c" +#include "fft/fft.c" #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 @@ -30,13 +30,13 @@ struct eq_data { - rarch_fft_t *fft; + fft_t *fft; float buffer[8 * 1024]; float *save; float *block; - rarch_fft_complex_t *filter; - rarch_fft_complex_t *fftblock; + fft_complex_t *filter; + fft_complex_t *fftblock; unsigned block_size; unsigned block_ptr; }; @@ -53,7 +53,7 @@ static void eq_free(void *data) if (!eq) return; - rarch_fft_free(eq->fft); + fft_free(eq->fft); free(eq->save); free(eq->block); free(eq->fftblock); @@ -92,10 +92,10 @@ static void eq_process(void *data, struct dspfilter_output *output, for (c = 0; c < 2; c++) { - rarch_fft_process_forward(eq->fft, eq->fftblock, eq->block + c, 2); + fft_process_forward(eq->fft, eq->fftblock, eq->block + c, 2); for (i = 0; i < 2 * eq->block_size; i++) - eq->fftblock[i] = rarch_fft_complex_mul(eq->fftblock[i], eq->filter[i]); - rarch_fft_process_inverse(eq->fft, out + c, eq->fftblock, 2); + eq->fftblock[i] = fft_complex_mul(eq->fftblock[i], eq->filter[i]); + fft_process_inverse(eq->fft, out + c, eq->fftblock, 2); } // Overlap add method, so add in saved block now. @@ -124,7 +124,7 @@ static int gains_cmp(const void *a_, const void *b_) return 0; } -static void generate_response(rarch_fft_complex_t *response, +static void generate_response(fft_complex_t *response, const struct eq_gain *gains, unsigned num_gains, unsigned samples) { unsigned i; @@ -223,7 +223,7 @@ static void create_filter(struct eq_data *eq, unsigned size_log2, int half_block_size = eq->block_size >> 1; double window_mod = 1.0 / kaiser_window(0.0, beta); - rarch_fft_t *fft = rarch_fft_new(size_log2); + fft_t *fft = fft_new(size_log2); float *time_filter = (float*)calloc(eq->block_size * 2, sizeof(*time_filter)); if (!fft || !time_filter) goto end; @@ -235,7 +235,7 @@ static void create_filter(struct eq_data *eq, unsigned size_log2, generate_response(eq->filter, gains, num_gains, half_block_size); // Get equivalent time-domain filter. - rarch_fft_process_inverse(fft, time_filter, eq->filter, 1); + fft_process_inverse(fft, time_filter, eq->filter, 1); // ifftshift() to create the correct linear phase filter. // The filter response was designed with zero phase, which won't work unless we compensate @@ -268,10 +268,10 @@ static void create_filter(struct eq_data *eq, unsigned size_log2, #endif // Padded FFT to create our FFT filter. - rarch_fft_process_forward(eq->fft, eq->filter, time_filter, 1); + fft_process_forward(eq->fft, eq->filter, time_filter, 1); end: - rarch_fft_free(fft); + fft_free(fft); free(time_filter); } @@ -307,7 +307,7 @@ static void *eq_init(const struct dspfilter_info *info, for (i = 0; i < num_gain; i++) { - gains[i].freq = 0.5f * frequencies[i] / info->input_rate; + gains[i].freq = frequencies[i] / (0.5f * info->input_rate); gains[i].gain = pow(10.0, gain[i] / 20.0); } config->free(frequencies); @@ -317,12 +317,12 @@ static void *eq_init(const struct dspfilter_info *info, eq->save = (float*)calloc( size, 2 * sizeof(*eq->save)); eq->block = (float*)calloc(2 * size, 2 * sizeof(*eq->block)); - eq->fftblock = (rarch_fft_complex_t*)calloc(2 * size, sizeof(*eq->fftblock)); - eq->filter = (rarch_fft_complex_t*)calloc(2 * size, sizeof(*eq->filter)); + eq->fftblock = (fft_complex_t*)calloc(2 * size, sizeof(*eq->fftblock)); + eq->filter = (fft_complex_t*)calloc(2 * size, sizeof(*eq->filter)); // Use an FFT which is twice the block size with zero-padding // to make circular convolution => proper convolution. - eq->fft = rarch_fft_new(size_log2 + 1); + eq->fft = fft_new(size_log2 + 1); if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter) goto error; diff --git a/audio/fft/fft.c b/audio/filters/fft/fft.c similarity index 72% rename from audio/fft/fft.c rename to audio/filters/fft/fft.c index a036c7fb09..aac3427ee9 100644 --- a/audio/fft/fft.c +++ b/audio/filters/fft/fft.c @@ -21,10 +21,10 @@ #define M_PI 3.1415926535897932384626433832795 #endif -struct rarch_fft +struct fft { - rarch_fft_complex_t *interleave_buffer; - rarch_fft_complex_t *phase_lut; + fft_complex_t *interleave_buffer; + fft_complex_t *phase_lut; unsigned *bitinverse_buffer; unsigned size; }; @@ -46,13 +46,13 @@ static void build_bitinverse(unsigned *bitinverse, unsigned size_log2) bitinverse[i] = bitswap(i, size_log2); } -static rarch_fft_complex_t exp_imag(double phase) +static fft_complex_t exp_imag(double phase) { - rarch_fft_complex_t out = { cos(phase), sin(phase) }; + fft_complex_t out = { cos(phase), sin(phase) }; return out; } -static void build_phase_lut(rarch_fft_complex_t *out, int size) +static void build_phase_lut(fft_complex_t *out, int size) { int i; out += size; @@ -61,7 +61,7 @@ static void build_phase_lut(rarch_fft_complex_t *out, int size) } static void interleave_complex(const unsigned *bitinverse, - rarch_fft_complex_t *out, const rarch_fft_complex_t *in, + fft_complex_t *out, const fft_complex_t *in, unsigned samples, unsigned step) { unsigned i; @@ -70,7 +70,7 @@ static void interleave_complex(const unsigned *bitinverse, } static void interleave_float(const unsigned *bitinverse, - rarch_fft_complex_t *out, const float *in, + fft_complex_t *out, const float *in, unsigned samples, unsigned step) { unsigned i; @@ -82,7 +82,7 @@ static void interleave_float(const unsigned *bitinverse, } } -static void resolve_float(float *out, const rarch_fft_complex_t *in, unsigned samples, +static void resolve_float(float *out, const fft_complex_t *in, unsigned samples, float gain, unsigned step) { unsigned i; @@ -90,17 +90,17 @@ static void resolve_float(float *out, const rarch_fft_complex_t *in, unsigned sa *out = gain * in->real; } -rarch_fft_t *rarch_fft_new(unsigned block_size_log2) +fft_t *fft_new(unsigned block_size_log2) { - rarch_fft_t *fft = (rarch_fft_t*)calloc(1, sizeof(*fft)); + fft_t *fft = (fft_t*)calloc(1, sizeof(*fft)); if (!fft) return NULL; unsigned size = 1 << block_size_log2; - fft->interleave_buffer = (rarch_fft_complex_t*)calloc(size, sizeof(*fft->interleave_buffer)); + fft->interleave_buffer = (fft_complex_t*)calloc(size, sizeof(*fft->interleave_buffer)); fft->bitinverse_buffer = (unsigned*)calloc(size, sizeof(*fft->bitinverse_buffer)); - fft->phase_lut = (rarch_fft_complex_t*)calloc(2 * size + 1, sizeof(*fft->phase_lut)); + fft->phase_lut = (fft_complex_t*)calloc(2 * size + 1, sizeof(*fft->phase_lut)); if (!fft->interleave_buffer || !fft->bitinverse_buffer || !fft->phase_lut) goto error; @@ -112,11 +112,11 @@ rarch_fft_t *rarch_fft_new(unsigned block_size_log2) return fft; error: - rarch_fft_free(fft); + fft_free(fft); return NULL; } -void rarch_fft_free(rarch_fft_t *fft) +void fft_free(fft_t *fft) { if (!fft) return; @@ -127,15 +127,15 @@ void rarch_fft_free(rarch_fft_t *fft) free(fft); } -static void butterfly(rarch_fft_complex_t *a, rarch_fft_complex_t *b, rarch_fft_complex_t mod) +static void butterfly(fft_complex_t *a, fft_complex_t *b, fft_complex_t mod) { - mod = rarch_fft_complex_mul(mod, *b); - *b = rarch_fft_complex_sub(*a, mod); - *a = rarch_fft_complex_add(*a, mod); + mod = fft_complex_mul(mod, *b); + *b = fft_complex_sub(*a, mod); + *a = fft_complex_add(*a, mod); } -static void butterflies(rarch_fft_complex_t *butterfly_buf, - const rarch_fft_complex_t *phase_lut, +static void butterflies(fft_complex_t *butterfly_buf, + const fft_complex_t *phase_lut, int phase_dir, unsigned step_size, unsigned samples) { unsigned i, j; @@ -147,8 +147,8 @@ static void butterflies(rarch_fft_complex_t *butterfly_buf, } } -void rarch_fft_process_forward_complex(rarch_fft_t *fft, - rarch_fft_complex_t *out, const rarch_fft_complex_t *in, unsigned step) +void fft_process_forward_complex(fft_t *fft, + fft_complex_t *out, const fft_complex_t *in, unsigned step) { unsigned step_size; unsigned samples = fft->size; @@ -162,8 +162,8 @@ void rarch_fft_process_forward_complex(rarch_fft_t *fft, } } -void rarch_fft_process_forward(rarch_fft_t *fft, - rarch_fft_complex_t *out, const float *in, unsigned step) +void fft_process_forward(fft_t *fft, + fft_complex_t *out, const float *in, unsigned step) { unsigned step_size; unsigned samples = fft->size; @@ -177,8 +177,8 @@ void rarch_fft_process_forward(rarch_fft_t *fft, } } -void rarch_fft_process_inverse(rarch_fft_t *fft, - float *out, const rarch_fft_complex_t *in, unsigned step) +void fft_process_inverse(fft_t *fft, + float *out, const fft_complex_t *in, unsigned step) { unsigned step_size; unsigned samples = fft->size; diff --git a/audio/fft/fft.h b/audio/filters/fft/fft.h similarity index 53% rename from audio/fft/fft.h rename to audio/filters/fft/fft.h index 96d18cd9b5..3e54ddcfa5 100644 --- a/audio/fft/fft.h +++ b/audio/filters/fft/fft.h @@ -16,19 +16,19 @@ #ifndef RARCH_FFT_H__ #define RARCH_FFT_H__ -typedef struct rarch_fft rarch_fft_t; +typedef struct fft fft_t; // C99 would be nice. typedef struct { float real; float imag; -} rarch_fft_complex_t; +} fft_complex_t; -static inline rarch_fft_complex_t rarch_fft_complex_mul(rarch_fft_complex_t a, - rarch_fft_complex_t b) +static inline fft_complex_t fft_complex_mul(fft_complex_t a, + fft_complex_t b) { - rarch_fft_complex_t out = { + fft_complex_t out = { a.real * b.real - a.imag * b.imag, a.imag * b.real + a.real * b.imag, }; @@ -37,10 +37,10 @@ static inline rarch_fft_complex_t rarch_fft_complex_mul(rarch_fft_complex_t a, } -static inline rarch_fft_complex_t rarch_fft_complex_add(rarch_fft_complex_t a, - rarch_fft_complex_t b) +static inline fft_complex_t fft_complex_add(fft_complex_t a, + fft_complex_t b) { - rarch_fft_complex_t out = { + fft_complex_t out = { a.real + b.real, a.imag + b.imag, }; @@ -49,10 +49,10 @@ static inline rarch_fft_complex_t rarch_fft_complex_add(rarch_fft_complex_t a, } -static inline rarch_fft_complex_t rarch_fft_complex_sub(rarch_fft_complex_t a, - rarch_fft_complex_t b) +static inline fft_complex_t fft_complex_sub(fft_complex_t a, + fft_complex_t b) { - rarch_fft_complex_t out = { + fft_complex_t out = { a.real - b.real, a.imag - b.imag, }; @@ -61,27 +61,27 @@ static inline rarch_fft_complex_t rarch_fft_complex_sub(rarch_fft_complex_t a, } -static inline rarch_fft_complex_t rarch_fft_complex_conj(rarch_fft_complex_t a) +static inline fft_complex_t fft_complex_conj(fft_complex_t a) { - rarch_fft_complex_t out = { + fft_complex_t out = { a.real, -a.imag, }; return out; } -rarch_fft_t *rarch_fft_new(unsigned block_size_log2); +fft_t *fft_new(unsigned block_size_log2); -void rarch_fft_free(rarch_fft_t *fft); +void fft_free(fft_t *fft); -void rarch_fft_process_forward_complex(rarch_fft_t *fft, - rarch_fft_complex_t *out, const rarch_fft_complex_t *in, unsigned step); +void fft_process_forward_complex(fft_t *fft, + fft_complex_t *out, const fft_complex_t *in, unsigned step); -void rarch_fft_process_forward(rarch_fft_t *fft, - rarch_fft_complex_t *out, const float *in, unsigned step); +void fft_process_forward(fft_t *fft, + fft_complex_t *out, const float *in, unsigned step); -void rarch_fft_process_inverse(rarch_fft_t *fft, - float *out, const rarch_fft_complex_t *in, unsigned step); +void fft_process_inverse(fft_t *fft, + float *out, const fft_complex_t *in, unsigned step); #endif From 7bf6554661d30415a746d8eb188fa09f6e4d10d2 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 28 May 2014 10:37:21 +0200 Subject: [PATCH 7/8] Add EQ to griffin. --- audio/dsp_filter.c | 2 ++ griffin/griffin.c | 1 + 2 files changed, 3 insertions(+) diff --git a/audio/dsp_filter.c b/audio/dsp_filter.c index 939c6ce455..48ecaf2690 100644 --- a/audio/dsp_filter.c +++ b/audio/dsp_filter.c @@ -226,6 +226,7 @@ extern const struct dspfilter_implementation *iir_dspfilter_get_implementation(d extern const struct dspfilter_implementation *echo_dspfilter_get_implementation(dspfilter_simd_mask_t mask); extern const struct dspfilter_implementation *phaser_dspfilter_get_implementation(dspfilter_simd_mask_t mask); extern const struct dspfilter_implementation *wahwah_dspfilter_get_implementation(dspfilter_simd_mask_t mask); +extern const struct dspfilter_implementation *eq_dspfilter_get_implementation(dspfilter_simd_mask_t mask); static const dspfilter_get_implementation_t dsp_plugs_builtin[] = { panning_dspfilter_get_implementation, @@ -233,6 +234,7 @@ static const dspfilter_get_implementation_t dsp_plugs_builtin[] = { echo_dspfilter_get_implementation, phaser_dspfilter_get_implementation, wahwah_dspfilter_get_implementation, + eq_dspfilter_get_implementation, }; static bool append_plugs(rarch_dsp_filter_t *dsp) diff --git a/griffin/griffin.c b/griffin/griffin.c index e0b6c7c8e5..8a17af699b 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -483,6 +483,7 @@ FILTERS #include "../audio/filters/panning.c" #include "../audio/filters/iir.c" #include "../audio/filters/echo.c" +#include "../audio/filters/eq.c" #endif /*============================================================ DYNAMIC From 244b072ab8d67604a8a2f1671dd72984ef2ba565 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 28 May 2014 10:40:24 +0200 Subject: [PATCH 8/8] Fix Android build. --- audio/filters/eq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio/filters/eq.c b/audio/filters/eq.c index 04c050f03e..0b4a5d24f7 100644 --- a/audio/filters/eq.c +++ b/audio/filters/eq.c @@ -185,7 +185,7 @@ static void generate_response(fft_complex_t *response, // Modified Bessel function of first order. // Check Wiki for mathematical definition ... -static inline double besseli0(double x) +static inline double kaiser_besseli0(double x) { unsigned i; double sum = 0.0; @@ -213,7 +213,7 @@ static inline double besseli0(double x) static inline double kaiser_window(double index, double beta) { - return besseli0(beta * sqrt(1 - index * index)); + return kaiser_besseli0(beta * sqrt(1 - index * index)); } static void create_filter(struct eq_data *eq, unsigned size_log2,