diff --git a/audio/dsp_filter.c b/audio/dsp_filter.c index d567720511..505e72cb28 100644 --- a/audio/dsp_filter.c +++ b/audio/dsp_filter.c @@ -222,9 +222,13 @@ static bool create_filter_graph(rarch_dsp_filter_t *dsp, float sample_rate) #if defined(HAVE_FILTERS_BUILTIN) extern const struct dspfilter_implementation *panning_dspfilter_get_implementation(dspfilter_simd_mask_t mask); +extern const struct dspfilter_implementation *iir_dspfilter_get_implementation(dspfilter_simd_mask_t mask); +extern const struct dspfilter_implementation *echo_dspfilter_get_implementation(dspfilter_simd_mask_t mask); static const dspfilter_get_implementation_t dsp_plugs_builtin[] = { panning_dspfilter_get_implementation, + iir_dspfilter_get_implementation, + echo_dspfilter_get_implementation, }; static bool append_plugs(rarch_dsp_filter_t *dsp) diff --git a/audio/filters/Echo.dsp b/audio/filters/Echo.dsp new file mode 100644 index 0000000000..e26fdb666a --- /dev/null +++ b/audio/filters/Echo.dsp @@ -0,0 +1,10 @@ +filters = 1 +filter0 = echo + +# Somewhat fancy Echo filter. Can take any number of echo channels with varying delays (ms) and feedback factors. +# Echo output from all channels can be fed back into each other to create a somewhat reverb-like effect if desired. +echo_delay = " 60 80 120 172 200 320 380" +echo_feedback = "0.5 0.5 0.4 0.3 0.5 0.3 0.2" + +# Overall echo amplification. If too high, the echo becomes unstable due to feedback. +echo_amp = "0.12" diff --git a/audio/filters/echo.c b/audio/filters/echo.c new file mode 100644 index 0000000000..4baf7f8fd5 --- /dev/null +++ b/audio/filters/echo.c @@ -0,0 +1,167 @@ +/* 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 + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +struct echo_channel +{ + float *buffer; + unsigned ptr; + unsigned frames; + float feedback; +}; + +struct echo_data +{ + struct echo_channel *channels; + unsigned num_channels; + float amp; +}; + +static void echo_free(void *data) +{ + unsigned i; + struct echo_data *echo = (struct echo_data*)data; + + for (i = 0; i < echo->num_channels; i++) + free(echo->channels[i].buffer); + free(echo->channels); + free(echo); +} + +static void echo_process(void *data, struct dspfilter_output *output, + const struct dspfilter_input *input) +{ + unsigned i, c; + struct echo_data *echo = (struct echo_data*)data; + + output->samples = input->samples; + output->frames = input->frames; + + float *out = output->samples; + + for (i = 0; i < input->frames; i++, out += 2) + { + float echo_left = 0.0f; + float echo_right = 0.0f; + + for (c = 0; c < echo->num_channels; c++) + { + echo_left += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0]; + echo_right += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1]; + } + + echo_left *= echo->amp; + echo_right *= echo->amp; + + float left = out[0] + echo_left; + float right = out[1] + echo_right; + + for (c = 0; c < echo->num_channels; c++) + { + float feedback_left = out[0] + echo->channels[c].feedback * echo_left; + float feedback_right = out[1] + echo->channels[c].feedback * echo_right; + + echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0] = feedback_left; + echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1] = feedback_right; + + echo->channels[c].ptr = (echo->channels[c].ptr + 1) % echo->channels[c].frames; + } + + out[0] = left; + out[1] = right; + } +} + +static void *echo_init(const struct dspfilter_info *info, + const struct dspfilter_config *config, void *userdata) +{ + unsigned i; + struct echo_data *echo = (struct echo_data*)calloc(1, sizeof(*echo)); + if (!echo) + return NULL; + + float *delay = NULL, *feedback = NULL; + unsigned num_delay = 0, num_feedback = 0; + + static const float default_delay[] = { 200.0f }; + static const float default_feedback[] = { 0.5f }; + + 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(userdata, "amp", &echo->amp, 0.2f); + + unsigned channels = num_feedback = num_delay = min(num_delay, num_feedback); + + echo->channels = (struct echo_channel*)calloc(channels, sizeof(*echo->channels)); + if (!echo->channels) + goto error; + + echo->num_channels = channels; + + for (i = 0; i < channels; i++) + { + unsigned frames = (unsigned)(delay[i] * info->input_rate / 1000.0f + 0.5f); + if (!frames) + goto error; + + echo->channels[i].buffer = (float*)calloc(frames, 2 * sizeof(float)); + if (!echo->channels[i].buffer) + goto error; + + echo->channels[i].frames = frames; + echo->channels[i].feedback = feedback[i]; + } + + config->free(delay); + config->free(feedback); + return echo; + +error: + config->free(delay); + config->free(feedback); + echo_free(echo); + return NULL; +} + + +static const struct dspfilter_implementation echo_plug = { + echo_init, + echo_process, + echo_free, + + DSPFILTER_API_VERSION, + "Multi-Echo", + "echo", +}; + +#ifdef HAVE_FILTERS_BUILTIN +#define dspfilter_get_implementation echo_dspfilter_get_implementation +#endif + +const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) +{ + (void)mask; + return &echo_plug; +} + +#undef dspfilter_get_implementation + diff --git a/griffin/griffin.c b/griffin/griffin.c index 0d32845dc4..bbea8c20e1 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -486,6 +486,8 @@ FILTERS #include "../gfx/filters/phosphor2x.c" #include "../audio/filters/panning.c" +#include "../audio/filters/iir.c" +#include "../audio/filters/echo.c" #endif /*============================================================ DYNAMIC