This commit is contained in:
twinaphex 2015-09-13 06:40:29 +02:00
parent d6809537b0
commit e45cd48547
3 changed files with 48 additions and 77 deletions

View File

@ -60,6 +60,7 @@ static void echo_process(void *data, struct dspfilter_output *output,
for (i = 0; i < input->frames; i++, out += 2) for (i = 0; i < input->frames; i++, out += 2)
{ {
float left, right;
float echo_left = 0.0f; float echo_left = 0.0f;
float echo_right = 0.0f; float echo_right = 0.0f;
@ -72,8 +73,8 @@ static void echo_process(void *data, struct dspfilter_output *output,
echo_left *= echo->amp; echo_left *= echo->amp;
echo_right *= echo->amp; echo_right *= echo->amp;
float left = out[0] + echo_left; left = out[0] + echo_left;
float right = out[1] + echo_right; right = out[1] + echo_right;
for (c = 0; c < echo->num_channels; c++) for (c = 0; c < echo->num_channels; c++)
{ {
@ -94,22 +95,21 @@ static void echo_process(void *data, struct dspfilter_output *output,
static void *echo_init(const struct dspfilter_info *info, static void *echo_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata) const struct dspfilter_config *config, void *userdata)
{ {
unsigned i; unsigned i, channels;
struct echo_data *echo = (struct echo_data*)calloc(1, sizeof(*echo));
if (!echo)
return NULL;
float *delay = NULL, *feedback = NULL; float *delay = NULL, *feedback = NULL;
unsigned num_delay = 0, num_feedback = 0; unsigned num_delay = 0, num_feedback = 0;
static const float default_delay[] = { 200.0f }; static const float default_delay[] = { 200.0f };
static const float default_feedback[] = { 0.5f }; static const float default_feedback[] = { 0.5f };
struct echo_data *echo = (struct echo_data*)calloc(1, sizeof(*echo));
if (!echo)
return NULL;
config->get_float_array(userdata, "delay", &delay, &num_delay, default_delay, 1); config->get_float_array(userdata, "delay", &delay, &num_delay, default_delay, 1);
config->get_float_array(userdata, "feedback", &feedback, &num_feedback, default_feedback, 1); config->get_float_array(userdata, "feedback", &feedback, &num_feedback, default_feedback, 1);
config->get_float(userdata, "amp", &echo->amp, 0.2f); config->get_float(userdata, "amp", &echo->amp, 0.2f);
unsigned channels = num_feedback = num_delay = min(num_delay, num_feedback); channels = num_feedback = num_delay = min(num_delay, num_feedback);
echo->channels = (struct echo_channel*)calloc(channels, sizeof(*echo->channels)); echo->channels = (struct echo_channel*)calloc(channels, sizeof(*echo->channels));
if (!echo->channels) if (!echo->channels)

View File

@ -12,12 +12,14 @@
* You should have received a copy of the GNU General Public License along with RetroArch. * You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>. * If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <stdio.h>
#include "dspfilter.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h>
#include <retro_inline.h> #include <retro_inline.h>
#include <filters.h>
#include "dspfilter.h"
#include "fft/fft.c" #include "fft/fft.c"
@ -45,7 +47,7 @@ struct eq_data
struct eq_gain struct eq_gain
{ {
float freq; float freq;
float gain; // Linear. float gain; /* Linear. */
}; };
static void eq_free(void *data) static void eq_free(void *data)
@ -80,6 +82,7 @@ static void eq_process(void *data, struct dspfilter_output *output,
while (input_frames) while (input_frames)
{ {
unsigned write_avail = eq->block_size - eq->block_ptr; unsigned write_avail = eq->block_size - eq->block_ptr;
if (input_frames < write_avail) if (input_frames < write_avail)
write_avail = input_frames; write_avail = input_frames;
@ -135,8 +138,8 @@ static void generate_response(fft_complex_t *response,
float start_freq = 0.0f; float start_freq = 0.0f;
float start_gain = 1.0f; float start_gain = 1.0f;
float end_freq = 1.0f; float end_freq = 1.0f;
float end_gain = 1.0f; float end_gain = 1.0f;
if (num_gains) if (num_gains)
{ {
@ -149,6 +152,8 @@ static void generate_response(fft_complex_t *response,
// Create a response by linear interpolation between known frequency sample points. // Create a response by linear interpolation between known frequency sample points.
for (i = 0; i <= samples; i++) for (i = 0; i <= samples; i++)
{ {
float gain;
float lerp = 0.5f;
float freq = (float)i / samples; float freq = (float)i / samples;
while (freq >= end_freq) while (freq >= end_freq)
@ -173,11 +178,10 @@ static void generate_response(fft_complex_t *response,
} }
} }
float lerp = 0.5f; /* Edge case where i == samples. */
// Edge case where i == samples.
if (end_freq > start_freq) if (end_freq > start_freq)
lerp = (freq - start_freq) / (end_freq - start_freq); lerp = (freq - start_freq) / (end_freq - start_freq);
float gain = (1.0f - lerp) * start_gain + lerp * end_gain; gain = (1.0f - lerp) * start_gain + lerp * end_gain;
response[i].real = gain; response[i].real = gain;
response[i].imag = 0.0f; response[i].imag = 0.0f;
@ -186,58 +190,25 @@ static void generate_response(fft_complex_t *response,
} }
} }
// Modified Bessel function of first order.
// Check Wiki for mathematical definition ...
static INLINE double kaiser_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 kaiser_besseli0(beta * sqrt(1 - index * index));
}
static void create_filter(struct eq_data *eq, unsigned size_log2, static void create_filter(struct eq_data *eq, unsigned size_log2,
struct eq_gain *gains, unsigned num_gains, double beta, const char *filter_path) struct eq_gain *gains, unsigned num_gains, double beta, const char *filter_path)
{ {
int i; int i;
int half_block_size = eq->block_size >> 1; int half_block_size = eq->block_size >> 1;
double window_mod = 1.0 / kaiser_window(0.0, beta); double window_mod = 1.0 / kaiser_window_function(0.0, beta);
fft_t *fft = fft_new(size_log2); fft_t *fft = fft_new(size_log2);
float *time_filter = (float*)calloc(eq->block_size * 2 + 1, sizeof(*time_filter)); float *time_filter = (float*)calloc(eq->block_size * 2 + 1, sizeof(*time_filter));
if (!fft || !time_filter) if (!fft || !time_filter)
goto end; goto end;
// Make sure bands are in correct order. /* Make sure bands are in correct order. */
qsort(gains, num_gains, sizeof(*gains), gains_cmp); qsort(gains, num_gains, sizeof(*gains), gains_cmp);
// Compute desired filter response. /* Compute desired filter response. */
generate_response(eq->filter, gains, num_gains, half_block_size); generate_response(eq->filter, gains, num_gains, half_block_size);
// Get equivalent time-domain filter. /* Get equivalent time-domain filter. */
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. // ifftshift() to create the correct linear phase filter.
@ -250,16 +221,16 @@ static void create_filter(struct eq_data *eq, unsigned size_log2,
time_filter[i] = tmp; time_filter[i] = tmp;
} }
// Apply a window to smooth out the frequency repsonse. /* Apply a window to smooth out the frequency repsonse. */
for (i = 0; i < (int)eq->block_size; i++) for (i = 0; i < (int)eq->block_size; i++)
{ {
// Kaiser window. /* Kaiser window. */
double phase = (double)i / eq->block_size; double phase = (double)i / eq->block_size;
phase = 2.0 * (phase - 0.5); phase = 2.0 * (phase - 0.5);
time_filter[i] *= window_mod * kaiser_window(phase, beta); time_filter[i] *= window_mod * kaiser_window_function(phase, beta);
} }
// Debugging. /* Debugging. */
if (filter_path) if (filter_path)
{ {
FILE *file = fopen(filter_path, "w"); FILE *file = fopen(filter_path, "w");
@ -271,9 +242,10 @@ static void create_filter(struct eq_data *eq, unsigned size_log2,
} }
} }
// Padded FFT to create our FFT filter. /* Padded FFT to create our FFT filter.
// Make our even-length filter odd by discarding the first coefficient. * Make our even-length filter odd by discarding the first coefficient.
// For some interesting reason, this allows us to design an odd-length linear phase filter. * For some interesting reason, this allows us to design an odd-length linear phase filter.
*/
fft_process_forward(eq->fft, eq->filter, time_filter + 1, 1); fft_process_forward(eq->fft, eq->filter, time_filter + 1, 1);
end: end:
@ -284,28 +256,27 @@ end:
static void *eq_init(const struct dspfilter_info *info, static void *eq_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata) const struct dspfilter_config *config, void *userdata)
{ {
unsigned i; float *frequencies, *gain;
unsigned num_freq, num_gain, i, size;
int size_log2;
float beta;
struct eq_gain *gains = NULL;
char *filter_path = NULL;
const float default_freq[] = { 0.0f, info->input_rate };
const float default_gain[] = { 0.0f, 0.0f };
struct eq_data *eq = (struct eq_data*)calloc(1, sizeof(*eq)); struct eq_data *eq = (struct eq_data*)calloc(1, sizeof(*eq));
if (!eq) if (!eq)
return NULL; return NULL;
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); config->get_float(userdata, "window_beta", &beta, 4.0f);
int size_log2;
config->get_int(userdata, "block_size_log2", &size_log2, 8); config->get_int(userdata, "block_size_log2", &size_log2, 8);
unsigned size = 1 << size_log2; 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, "frequencies", &frequencies, &num_freq, default_freq, 2);
config->get_float_array(userdata, "gains", &gain, &num_gain, default_gain, 2); config->get_float_array(userdata, "gains", &gain, &num_gain, default_gain, 2);
char *filter_path = NULL;
if (!config->get_string(userdata, "impulse_response_output", &filter_path, "")) if (!config->get_string(userdata, "impulse_response_output", &filter_path, ""))
{ {
config->free(filter_path); config->free(filter_path);
@ -333,8 +304,9 @@ static void *eq_init(const struct dspfilter_info *info,
eq->fftblock = (fft_complex_t*)calloc(2 * size, sizeof(*eq->fftblock)); eq->fftblock = (fft_complex_t*)calloc(2 * size, sizeof(*eq->fftblock));
eq->filter = (fft_complex_t*)calloc(2 * size, sizeof(*eq->filter)); eq->filter = (fft_complex_t*)calloc(2 * size, sizeof(*eq->filter));
// Use an FFT which is twice the block size with zero-padding /* Use an FFT which is twice the block size with zero-padding
// to make circular convolution => proper convolution. * to make circular convolution => proper convolution.
*/
eq->fft = fft_new(size_log2 + 1); eq->fft = fft_new(size_log2 + 1);
if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter) if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter)

View File

@ -53,12 +53,12 @@ static void phaser_process(void *data, struct dspfilter_output *output,
{ {
unsigned i, c; unsigned i, c;
int s; int s;
float m[2], tmp[2]; float m[2], tmp[2], *out;
struct phaser_data *ph = (struct phaser_data*)data; struct phaser_data *ph = (struct phaser_data*)data;
output->samples = input->samples; output->samples = input->samples;
output->frames = input->frames; output->frames = input->frames;
float *out = output->samples; out = output->samples;
for (i = 0; i < input->frames; i++, out += 2) for (i = 0; i < input->frames; i++, out += 2)
{ {
@ -95,12 +95,11 @@ static void phaser_process(void *data, struct dspfilter_output *output,
static void *phaser_init(const struct dspfilter_info *info, static void *phaser_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata) const struct dspfilter_config *config, void *userdata)
{ {
float lfo_freq, lfo_start_phase;
struct phaser_data *ph = (struct phaser_data*)calloc(1, sizeof(*ph)); struct phaser_data *ph = (struct phaser_data*)calloc(1, sizeof(*ph));
if (!ph) if (!ph)
return NULL; return NULL;
float lfo_freq, lfo_start_phase;
config->get_float(userdata, "lfo_freq", &lfo_freq, 0.4f); config->get_float(userdata, "lfo_freq", &lfo_freq, 0.4f);
config->get_float(userdata, "lfo_start_phase", &lfo_start_phase, 0.0f); config->get_float(userdata, "lfo_start_phase", &lfo_start_phase, 0.0f);
config->get_float(userdata, "feedback", &ph->fb, 0.0f); config->get_float(userdata, "feedback", &ph->fb, 0.0f);