diff --git a/audio/dsp_filter.c b/audio/dsp_filter.c index 48ecaf2690..d9cdcc14fa 100644 --- a/audio/dsp_filter.c +++ b/audio/dsp_filter.c @@ -227,6 +227,7 @@ extern const struct dspfilter_implementation *echo_dspfilter_get_implementation( 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); +extern const struct dspfilter_implementation *chorus_dspfilter_get_implementation(dspfilter_simd_mask_t mask); static const dspfilter_get_implementation_t dsp_plugs_builtin[] = { panning_dspfilter_get_implementation, @@ -235,6 +236,7 @@ static const dspfilter_get_implementation_t dsp_plugs_builtin[] = { phaser_dspfilter_get_implementation, wahwah_dspfilter_get_implementation, eq_dspfilter_get_implementation, + chorus_dspfilter_get_implementation, }; static bool append_plugs(rarch_dsp_filter_t *dsp) diff --git a/audio/filters/Chorus.dsp b/audio/filters/Chorus.dsp new file mode 100644 index 0000000000..f48b3c3897 --- /dev/null +++ b/audio/filters/Chorus.dsp @@ -0,0 +1,14 @@ +filters = 1 +filter0 = chorus + +# Controls the base delay of the chorus (milliseconds). +# chorus_delay_ms = 25.0 +# +# Controls the depth of the delay. The delay will vary between delay_ms +/- depth_ms. +# chorus_depth_ms = 1.0 +# +# Frequency of LFO which controls delay. +# chorus_lfo_freq = 0.5 +# +# Controls dry/wet-ness of effect. 1.0 = full chorus, 0.0 = no chorus. +# chorus_drywet = 0.8 diff --git a/audio/filters/chorus.c b/audio/filters/chorus.c new file mode 100644 index 0000000000..99a98ada05 --- /dev/null +++ b/audio/filters/chorus.c @@ -0,0 +1,148 @@ +/* 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 + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795 +#endif + +#define CHORUS_MAX_DELAY 4096 +#define CHORUS_DELAY_MASK (CHORUS_MAX_DELAY - 1) + +struct chorus_data +{ + float old[2][CHORUS_MAX_DELAY]; + unsigned old_ptr; + + float delay; + float depth; + float input_rate; + float mix_dry; + float mix_wet; + unsigned lfo_ptr; + unsigned lfo_period; +}; + +static void chorus_free(void *data) +{ + free(data); +} + +static void chorus_process(void *data, struct dspfilter_output *output, + const struct dspfilter_input *input) +{ + unsigned i; + struct chorus_data *ch = (struct chorus_data*)data; + + output->samples = input->samples; + output->frames = input->frames; + float *out = output->samples; + + for (i = 0; i < input->frames; i++, out += 2) + { + float in[2] = { out[0], out[1] }; + + float delay = ch->delay + ch->depth * sin((2.0 * M_PI * ch->lfo_ptr++) / ch->lfo_period); + delay *= ch->input_rate; + if (ch->lfo_ptr >= ch->lfo_period) + ch->lfo_ptr = 0; + + unsigned delay_int = (unsigned)delay; + if (delay_int >= CHORUS_MAX_DELAY - 1) + delay_int = CHORUS_MAX_DELAY - 2; + float delay_frac = delay - delay_int; + + ch->old[0][ch->old_ptr] = in[0]; + ch->old[1][ch->old_ptr] = in[1]; + + float l_a = ch->old[0][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK]; + float l_b = ch->old[0][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK]; + float r_a = ch->old[1][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK]; + float r_b = ch->old[1][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK]; + + // Lerp introduces aliasing of the chorus component, but doing full polyphase here is probably overkill. + float chorus_l = l_a * (1.0f - delay_frac) + l_b * delay_frac; + float chorus_r = r_a * (1.0f - delay_frac) + r_b * delay_frac; + + out[0] = ch->mix_dry * in[0] + ch->mix_wet * chorus_l; + out[1] = ch->mix_dry * in[1] + ch->mix_wet * chorus_r; + + ch->old_ptr = (ch->old_ptr + 1) & CHORUS_DELAY_MASK; + ch->lfo_ptr = (ch->lfo_ptr + 1) % ch->lfo_period; + } +} + +static void *chorus_init(const struct dspfilter_info *info, + const struct dspfilter_config *config, void *userdata) +{ + struct chorus_data *ch = (struct chorus_data*)calloc(1, sizeof(*ch)); + if (!ch) + return NULL; + + float delay, depth, lfo_freq, drywet; + config->get_float(userdata, "delay_ms", &delay, 25.0f); + config->get_float(userdata, "depth_ms", &depth, 1.0f); + config->get_float(userdata, "lfo_freq", &lfo_freq, 0.5f); + config->get_float(userdata, "drywet", &drywet, 0.8f); + + delay /= 1000.0f; + depth /= 1000.0f; + + if (depth > delay) + depth = delay; + + if (drywet < 0.0f) + drywet = 0.0f; + else if (drywet > 1.0f) + drywet = 1.0f; + + ch->mix_dry = 1.0f - 0.5f * drywet; + ch->mix_wet = 0.5f * drywet; + + ch->delay = delay; + ch->depth = depth; + ch->lfo_period = (1.0f / lfo_freq) * info->input_rate; + ch->input_rate = info->input_rate; + if (!ch->lfo_period) + ch->lfo_period = 1; + return ch; +} + +static const struct dspfilter_implementation chorus_plug = { + chorus_init, + chorus_process, + chorus_free, + + DSPFILTER_API_VERSION, + "Chorus", + "chorus", +}; + +#ifdef HAVE_FILTERS_BUILTIN +#define dspfilter_get_implementation chorus_dspfilter_get_implementation +#endif + +const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) +{ + (void)mask; + return &chorus_plug; +} + +#undef dspfilter_get_implementation + diff --git a/griffin/griffin.c b/griffin/griffin.c index 2494f23cfe..a55ba815cd 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -481,6 +481,7 @@ FILTERS #include "../audio/filters/echo.c" #include "../audio/filters/eq.c" +#include "../audio/filters/chorus.c" #include "../audio/filters/iir.c" #include "../audio/filters/panning.c" #include "../audio/filters/phaser.c"