This commit is contained in:
twinaphex 2017-01-09 12:37:40 +01:00
parent 1b289782e6
commit 87fd380bac
28 changed files with 2881 additions and 0 deletions

324
audio/dsp_filter.c Normal file
View file

@ -0,0 +1,324 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (dsp_filter.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <retro_miscellaneous.h>
#include <compat/posix_string.h>
#include <dynamic/dylib.h>
#include <file/file_path.h>
#include <file/config_file_userdata.h>
#include <features/features_cpu.h>
#include <lists/string_list.h>
#include <string/stdstring.h>
#include <libretro_dspfilter.h>
#include <audio/dsp_filter.h>
struct rarch_dsp_plug
{
#ifdef HAVE_DYLIB
dylib_t lib;
#endif
const struct dspfilter_implementation *impl;
};
struct rarch_dsp_instance
{
const struct dspfilter_implementation *impl;
void *impl_data;
};
struct rarch_dsp_filter
{
config_file_t *conf;
struct rarch_dsp_plug *plugs;
unsigned num_plugs;
struct rarch_dsp_instance *instances;
unsigned num_instances;
};
static const struct dspfilter_implementation *find_implementation(
rarch_dsp_filter_t *dsp, const char *ident)
{
unsigned i;
for (i = 0; i < dsp->num_plugs; i++)
{
if (string_is_equal(dsp->plugs[i].impl->short_ident, ident))
return dsp->plugs[i].impl;
}
return NULL;
}
static const struct dspfilter_config dspfilter_config = {
config_userdata_get_float,
config_userdata_get_int,
config_userdata_get_float_array,
config_userdata_get_int_array,
config_userdata_get_string,
config_userdata_free,
};
static bool create_filter_graph(rarch_dsp_filter_t *dsp, float sample_rate)
{
unsigned i;
struct rarch_dsp_instance *instances = NULL;
unsigned filters = 0;
if (!config_get_uint(dsp->conf, "filters", &filters))
return false;
instances = (struct rarch_dsp_instance*)calloc(filters, sizeof(*instances));
if (!instances)
return false;
dsp->instances = instances;
dsp->num_instances = filters;
for (i = 0; i < filters; i++)
{
struct config_file_userdata userdata;
struct dspfilter_info info;
char key[64];
char name[64];
key[0] = name[0] = '\0';
info.input_rate = sample_rate;
snprintf(key, sizeof(key), "filter%u", i);
if (!config_get_array(dsp->conf, key, name, sizeof(name)))
return false;
dsp->instances[i].impl = find_implementation(dsp, name);
if (!dsp->instances[i].impl)
return false;
userdata.conf = dsp->conf;
/* Index-specific configs take priority over ident-specific. */
userdata.prefix[0] = key;
userdata.prefix[1] = dsp->instances[i].impl->short_ident;
dsp->instances[i].impl_data = dsp->instances[i].impl->init(&info,
&dspfilter_config, &userdata);
if (!dsp->instances[i].impl_data)
return false;
}
return true;
}
#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);
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,
iir_dspfilter_get_implementation,
echo_dspfilter_get_implementation,
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, struct string_list *list)
{
unsigned i;
dspfilter_simd_mask_t mask = cpu_features_get();
struct rarch_dsp_plug *plugs = (struct rarch_dsp_plug*)
calloc(ARRAY_SIZE(dsp_plugs_builtin), sizeof(*plugs));
if (!plugs)
return false;
dsp->plugs = plugs;
dsp->num_plugs = ARRAY_SIZE(dsp_plugs_builtin);
for (i = 0; i < ARRAY_SIZE(dsp_plugs_builtin); i++)
{
dsp->plugs[i].impl = dsp_plugs_builtin[i](mask);
if (!dsp->plugs[i].impl)
return false;
}
return true;
}
#elif defined(HAVE_DYLIB)
static bool append_plugs(rarch_dsp_filter_t *dsp, struct string_list *list)
{
unsigned i;
dspfilter_simd_mask_t mask = cpu_features_get();
for (i = 0; i < list->size; i++)
{
dspfilter_get_implementation_t cb;
const struct dspfilter_implementation *impl = NULL;
struct rarch_dsp_plug *new_plugs = NULL;
dylib_t lib =
dylib_load(list->elems[i].data);
if (!lib)
continue;
cb = (dspfilter_get_implementation_t)dylib_proc(lib, "dspfilter_get_implementation");
if (!cb)
{
dylib_close(lib);
continue;
}
impl = cb(mask);
if (!impl)
{
dylib_close(lib);
continue;
}
if (impl->api_version != DSPFILTER_API_VERSION)
{
dylib_close(lib);
continue;
}
new_plugs = (struct rarch_dsp_plug*)
realloc(dsp->plugs, sizeof(*dsp->plugs) * (dsp->num_plugs + 1));
if (!new_plugs)
{
dylib_close(lib);
return false;
}
/* Found plug. */
dsp->plugs = new_plugs;
dsp->plugs[dsp->num_plugs].lib = lib;
dsp->plugs[dsp->num_plugs].impl = impl;
dsp->num_plugs++;
}
return true;
}
#endif
rarch_dsp_filter_t *rarch_dsp_filter_new(
const char *filter_config,
void *string_data,
float sample_rate)
{
config_file_t *conf = NULL;
struct string_list *plugs = NULL;
rarch_dsp_filter_t *dsp = (rarch_dsp_filter_t*)calloc(1, sizeof(*dsp));
if (!dsp)
return NULL;
conf = config_file_new(filter_config);
if (!conf) /* Did not find config. */
goto error;
dsp->conf = conf;
if (string_data)
plugs = (struct string_list*)string_data;
#if defined(HAVE_DYLIB) || defined(HAVE_FILTERS_BUILTIN)
if (!append_plugs(dsp, plugs))
goto error;
#endif
if (plugs)
string_list_free(plugs);
plugs = NULL;
if (!create_filter_graph(dsp, sample_rate))
goto error;
return dsp;
error:
if (plugs)
string_list_free(plugs);
rarch_dsp_filter_free(dsp);
return NULL;
}
void rarch_dsp_filter_free(rarch_dsp_filter_t *dsp)
{
unsigned i;
if (!dsp)
return;
for (i = 0; i < dsp->num_instances; i++)
{
if (dsp->instances[i].impl_data && dsp->instances[i].impl)
dsp->instances[i].impl->free(dsp->instances[i].impl_data);
}
free(dsp->instances);
#ifdef HAVE_DYLIB
for (i = 0; i < dsp->num_plugs; i++)
{
if (dsp->plugs[i].lib)
dylib_close(dsp->plugs[i].lib);
}
free(dsp->plugs);
#endif
if (dsp->conf)
config_file_free(dsp->conf);
free(dsp);
}
void rarch_dsp_filter_process(rarch_dsp_filter_t *dsp,
struct rarch_dsp_data *data)
{
unsigned i;
struct dspfilter_output output = {0};
struct dspfilter_input input = {0};
output.samples = data->input;
output.frames = data->input_frames;
for (i = 0; i < dsp->num_instances; i++)
{
input.samples = output.samples;
input.frames = output.frames;
dsp->instances[i].impl->process(
dsp->instances[i].impl_data, &output, &input);
}
data->output = output.samples;
data->output_frames = output.frames;
}

View file

@ -0,0 +1,12 @@
filters = 2
filter0 = iir
filter1 = panning
iir_gain = 10.0
iir_type = BBOOST
iir_frequency = 200.0
# Avoids clipping.
panning_left_mix = "0.3 0.0"
panning_right_mix = "0.0 0.3"

View file

@ -0,0 +1,22 @@
filters = 4
filter0 = eq
filter1 = reverb
filter2 = iir
filter3 = panning
eq_frequencies = "32 64 125 250 500 1000 2000 4000 8000 16000 20000"
eq_gains = "6 9 12 7 6 5 7 9 11 6 0"
# Reverb - slight reverb
reverb_drytime = 0.5
reverb_wettime = 0.15
reverb_damping = 0.8
reverb_roomwidth = 0.25
reverb_roomsize = 0.25
# IIR - filters out some harsh sounds on the upper end
iir_type = RIAA_CD
# Panning - cut the volume a bit
panning_left_mix = "0.75 0.0"
panning_right_mix = "0.0 0.75"

View file

@ -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

41
audio/dsp_filters/EQ.dsp Normal file
View file

@ -0,0 +1,41 @@
filters = 1
filter0 = eq
# Defaults
# Beta factor for Kaiser window.
# Lower values will allow better frequency resolution, but more ripple.
# eq_window_beta = 4.0
# The block size on which FFT is done.
# Too high value requires more processing as well as longer latency but
# allows finer-grained control over the spectrum.
# eq_block_size_log2 = 8
# An array of which frequencies to control.
# You can create an arbitrary amount of these sampling points.
# The EQ will try to create a frequency response which fits well to these points.
# The filter response is linearly interpolated between sampling points here.
#
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
#
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
# eq_frequencies = "500 1000 2000"
# eq_gains = "0 3 0"
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.
# By default, this filter has a flat frequency response.
# Dumps the impulse response generated by the EQ as a plain-text file
# with one coefficient per line.
# eq_impulse_response_output = "eq_impulse.txt"
#
# Using GNU Octave or Matlab, you can plot the response with:
#
# f = fopen('/path/to/eq_impulse.txt');
# l = textscan(f, '%f');
# res = l{1};
# freqz(res, 1, 4096, 48000);
#
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch

View file

@ -0,0 +1,19 @@
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.
# Defaults, 200 ms delay echo with feedback:
# Delay in ms. Takes an array with multiple channels.
# echo_delay = "200"
# Feedback factor for echo.
# echo_feedback = "0.5"
# Overall echo amplification. If too high, the echo becomes unstable due to feedback.
# echo_amp = "0.2"
# Reverby preset.
# 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"
# echo_amp = "0.12"

View file

@ -0,0 +1,12 @@
filters = 2
filter0 = echo
filter1 = reverb
echo_delay = "200"
echo_feedback = "0.6"
echo_amp = "0.25"
reverb_roomwidth = 0.75
reverb_roomsize = 0.75
reverb_damping = 1.0
reverb_wettime = 0.3

View file

@ -0,0 +1,7 @@
filters = 1
filter0 = iir
iir_gain = -12.0
iir_type = HSH
iir_frequency = 8000.0

23
audio/dsp_filters/IIR.dsp Normal file
View file

@ -0,0 +1,23 @@
filters = 1
filter0 = iir
# Defaults.
#iir_frequency = 1024.0
#iir_quality = 0.707
#iir_gain = 0.0
#iir_type = LPF
# Filter types:
# LPF: Low-pass
# HPF: High-pass
# BPCSGF: Band-pass #1
# BPZPGF: Band-pass #2
# APF: Allpass
# NOTCH: Notch filter
# RIAA_phono: RIAA record/tape deemphasis
# PEQ: peaking band EQ
# BBOOST: Bassboost
# LSH: Low-shelf
# HSH: High-shelf
# RIAA_CD: CD de-emphasis

View file

@ -0,0 +1,47 @@
filters = 1
filter0 = eq
eq_frequencies = "8000 10000 12500 16000 20000"
eq_gains = "0 -30 -30 -30 -30"
# Low pass filter for the QSound chip from CPS-1/2.
# Some games have aliasing due low quality samples, so you can hear some annoying noisy near 11 kHz
# Defaults
# Beta factor for Kaiser window.
# Lower values will allow better frequency resolution, but more ripple.
# eq_window_beta = 4.0
# The block size on which FFT is done.
# Too high value requires more processing as well as longer latency but
# allows finer-grained control over the spectrum.
# eq_block_size_log2 = 8
# An array of which frequencies to control.
# You can create an arbitrary amount of these sampling points.
# The EQ will try to create a frequency response which fits well to these points.
# The filter response is linearly interpolated between sampling points here.
#
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
#
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
# eq_frequencies = "500 1000 2000"
# eq_gains = "0 3 0"
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.
# By default, this filter has a low pass response with cuttof frequency at ~8600 Hz.
# Dumps the impulse response generated by the EQ as a plain-text file
# with one coefficient per line.
# eq_impulse_response_output = "eq_impulse.txt"
#
# Using GNU Octave or Matlab, you can plot the response with:
#
# f = fopen('/path/to/eq_impulse.txt');
# l = textscan(f, '%f');
# res = l{1};
# freqz(res, 1, 4096, 48000);
#
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch

View file

@ -0,0 +1,12 @@
filters = 1
filter0 = panning
# Gains are linear.
# Stereo Mono:
panning_left_mix = "0.5 0.5"
panning_right_mix = "0.5 0.5"
# Mono on one speaker:
# panning_left_mix = "0.5 0.5"
# panning_right_mix = "0.0 0.0"

View file

@ -0,0 +1,23 @@
filters = 1
filter0 = panning
# Gains are linear.
# The default. Left and right channels map to each other.
panning_left_mix = "1.0 0.0"
panning_right_mix = "0.0 1.0"
# Some examples:
#
# Mono:
# panning_left_mix = "0.5 0.5"
# panning_right_mix = "0.5 0.5"
# Swap left and right channels:
# panning_left_mix = "0.0 1.0"
# panning_right_mix = "1.0 0.0"
#
# Mono on one speaker:
# panning_left_mix = "0.5 0.5"
# panning_right_mix = "0.0 0.0"

View file

@ -0,0 +1,11 @@
filters = 1
filter0 = phaser
# Defaults.
# phaser_lfo_freq = 0.4
# phaser_lfo_start_phase = 0.0
# phaser_feedback = 0.0
# phaser_depth = 0.4
# phaser_dry_wet = 0.5
# phaser_stages = 2

View file

@ -0,0 +1,10 @@
filters = 1
filter0 = reverb
# Defaults.
# reverb_drytime = 0.43
# reverb_wettime = 0.4
# reverb_damping = 0.8
# reverb_roomwidth = 0.56
# reverb_roomsize = 0.56

View file

@ -0,0 +1,10 @@
filters = 1
filter0 = wahwah
# Defaults.
# wahwah_lfo_freq = 1.5
# wahwah_lfo_start_phase = 0.0
# wahwah_freq_offset = 0.3
# wahwah_depth = 0.7
# wahwah_resonance = 2.5

161
audio/dsp_filters/chorus.c Normal file
View file

@ -0,0 +1,161 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (chorus.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
#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)
{
if (data)
free(data);
}
static void chorus_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
float *out = NULL;
struct chorus_data *ch = (struct chorus_data*)data;
output->samples = input->samples;
output->frames = input->frames;
out = output->samples;
for (i = 0; i < input->frames; i++, out += 2)
{
unsigned delay_int;
float delay_frac, l_a, l_b, r_a, r_b;
float chorus_l, chorus_r;
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;
delay_int = (unsigned)delay;
if (delay_int >= CHORUS_MAX_DELAY - 1)
delay_int = CHORUS_MAX_DELAY - 2;
delay_frac = delay - delay_int;
ch->old[0][ch->old_ptr] = in[0];
ch->old[1][ch->old_ptr] = in[1];
l_a = ch->old[0][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK];
l_b = ch->old[0][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK];
r_a = ch->old[1][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK];
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. */
chorus_l = l_a * (1.0f - delay_frac) + l_b * delay_frac;
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;
}
}
static void *chorus_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float delay, depth, lfo_freq, drywet;
struct chorus_data *ch = (struct chorus_data*)calloc(1, sizeof(*ch));
if (!ch)
return NULL;
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

182
audio/dsp_filters/echo.c Normal file
View file

@ -0,0 +1,182 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (echo.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
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;
float *out = NULL;
struct echo_data *echo = (struct echo_data*)data;
output->samples = input->samples;
output->frames = input->frames;
out = output->samples;
for (i = 0; i < input->frames; i++, out += 2)
{
float left, right;
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;
left = out[0] + echo_left;
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, channels;
struct echo_channel *echo_channels = NULL;
float *delay = NULL;
float *feedback = NULL;
unsigned num_delay = 0;
unsigned num_feedback = 0;
static const float default_delay[] = { 200.0f };
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, "feedback", &feedback,
&num_feedback, default_feedback, 1);
config->get_float(userdata, "amp", &echo->amp, 0.2f);
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->channels = echo_channels;
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

352
audio/dsp_filters/eq.c Normal file
View file

@ -0,0 +1,352 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (eq.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <retro_inline.h>
#include <retro_miscellaneous.h>
#include <filters.h>
#include <libretro_dspfilter.h>
#include "fft/fft.c"
struct eq_data
{
fft_t *fft;
float buffer[8 * 1024];
float *save;
float *block;
fft_complex_t *filter;
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;
if (!eq)
return;
fft_free(eq->fft);
free(eq->save);
free(eq->block);
free(eq->fftblock);
free(eq->filter);
free(eq);
}
static void eq_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
float *out;
const float *in;
unsigned input_frames;
struct eq_data *eq = (struct eq_data*)data;
output->samples = eq->buffer;
output->frames = 0;
out = eq->buffer;
in = input->samples;
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++)
{
fft_process_forward(eq->fft, eq->fftblock, eq->block + c, 2);
for (i = 0; i < 2 * eq->block_size; i++)
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.
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;
if (a->freq > b->freq)
return 1;
return 0;
}
static void generate_response(fft_complex_t *response,
const struct eq_gain *gains, unsigned num_gains, unsigned samples)
{
unsigned i;
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 = 0; i <= samples; i++)
{
float gain;
float lerp = 0.5f;
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
{
start_freq = end_freq;
start_gain = end_gain;
end_freq = 1.0f;
end_gain = 1.0f;
break;
}
}
/* Edge case where i == samples. */
if (end_freq > start_freq)
lerp = (freq - start_freq) / (end_freq - start_freq);
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 = 0.0f;
}
}
static void create_filter(struct eq_data *eq, unsigned size_log2,
struct eq_gain *gains, unsigned num_gains, double beta, const char *filter_path)
{
int i;
int half_block_size = eq->block_size >> 1;
double window_mod = 1.0 / kaiser_window_function(0.0, beta);
fft_t *fft = fft_new(size_log2);
float *time_filter = (float*)calloc(eq->block_size * 2 + 1, sizeof(*time_filter));
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. */
generate_response(eq->filter, gains, num_gains, half_block_size);
/* Get equivalent time-domain filter. */
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++)
{
/* Kaiser window. */
double phase = (double)i / eq->block_size;
phase = 2.0 * (phase - 0.5);
time_filter[i] *= window_mod * kaiser_window_function(phase, beta);
}
/* Debugging. */
if (filter_path)
{
FILE *file = fopen(filter_path, "w");
if (file)
{
for (i = 0; i < (int)eq->block_size - 1; i++)
fprintf(file, "%.8f\n", time_filter[i + 1]);
fclose(file);
}
}
/* Padded FFT to create our FFT filter.
* 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.
*/
fft_process_forward(eq->fft, eq->filter, time_filter + 1, 1);
end:
fft_free(fft);
free(time_filter);
}
static void *eq_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
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));
if (!eq)
return NULL;
config->get_float(userdata, "window_beta", &beta, 4.0f);
config->get_int(userdata, "block_size_log2", &size_log2, 8);
size = 1 << size_log2;
config->get_float_array(userdata, "frequencies", &frequencies, &num_freq, default_freq, 2);
config->get_float_array(userdata, "gains", &gain, &num_gain, default_gain, 2);
if (!config->get_string(userdata, "impulse_response_output", &filter_path, ""))
{
config->free(filter_path);
filter_path = NULL;
}
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 = frequencies[i] / (0.5f * 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));
eq->block = (float*)calloc(2 * size, 2 * sizeof(*eq->block));
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 = fft_new(size_log2 + 1);
if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter)
goto error;
create_filter(eq, size_log2, gains, num_gain, beta, filter_path);
config->free(filter_path);
filter_path = NULL;
free(gains);
return eq;
error:
free(gains);
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 *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &eq_plug;
}
#undef dspfilter_get_implementation

205
audio/dsp_filters/fft/fft.c Normal file
View file

@ -0,0 +1,205 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (fft.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include "fft.h"
#include <retro_miscellaneous.h>
struct fft
{
fft_complex_t *interleave_buffer;
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 fft_complex_t exp_imag(double phase)
{
fft_complex_t out = { cos(phase), sin(phase) };
return out;
}
static void build_phase_lut(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,
fft_complex_t *out, const fft_complex_t *in,
unsigned samples, unsigned step)
{
unsigned i;
for (i = 0; i < samples; i++, in += step)
out[bitinverse[i]] = *in;
}
static void interleave_float(const unsigned *bitinverse,
fft_complex_t *out, const float *in,
unsigned samples, unsigned step)
{
unsigned i;
for (i = 0; i < samples; i++, in += step)
{
unsigned inv_i = bitinverse[i];
out[inv_i].real = *in;
out[inv_i].imag = 0.0f;
}
}
static void resolve_float(float *out, const fft_complex_t *in, unsigned samples,
float gain, unsigned step)
{
unsigned i;
for (i = 0; i < samples; i++, in++, out += step)
*out = gain * in->real;
}
fft_t *fft_new(unsigned block_size_log2)
{
fft_t *fft = (fft_t*)calloc(1, sizeof(*fft));
if (!fft)
return NULL;
unsigned size = 1 << block_size_log2;
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 = (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:
fft_free(fft);
return NULL;
}
void fft_free(fft_t *fft)
{
if (!fft)
return;
free(fft->interleave_buffer);
free(fft->bitinverse_buffer);
free(fft->phase_lut);
free(fft);
}
static void butterfly(fft_complex_t *a, fft_complex_t *b, fft_complex_t mod)
{
mod = fft_complex_mul(mod, *b);
*b = fft_complex_sub(*a, mod);
*a = fft_complex_add(*a, mod);
}
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;
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 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;
interleave_complex(fft->bitinverse_buffer, out, in, samples, step);
for (step_size = 1; step_size < samples; step_size <<= 1)
{
butterflies(out,
fft->phase_lut + samples,
-1, step_size, samples);
}
}
void fft_process_forward(fft_t *fft,
fft_complex_t *out, const float *in, unsigned step)
{
unsigned step_size;
unsigned samples = fft->size;
interleave_float(fft->bitinverse_buffer, out, in, samples, step);
for (step_size = 1; step_size < fft->size; step_size <<= 1)
{
butterflies(out,
fft->phase_lut + samples,
-1, step_size, samples);
}
}
void fft_process_inverse(fft_t *fft,
float *out, const fft_complex_t *in, unsigned step)
{
unsigned step_size;
unsigned samples = fft->size;
interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer,
in, samples, 1);
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, samples, 1.0f / samples, step);
}

View file

@ -0,0 +1,46 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (fft.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef RARCH_FFT_H__
#define RARCH_FFT_H__
#include <retro_inline.h>
#include <math/complex.h>
typedef struct fft fft_t;
fft_t *fft_new(unsigned block_size_log2);
void fft_free(fft_t *fft);
void fft_process_forward_complex(fft_t *fft,
fft_complex_t *out, const fft_complex_t *in, unsigned step);
void fft_process_forward(fft_t *fft,
fft_complex_t *out, const float *in, unsigned step);
void fft_process_inverse(fft_t *fft,
float *out, const fft_complex_t *in, unsigned step);
#endif

371
audio/dsp_filters/iir.c Normal file
View file

@ -0,0 +1,371 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (iir.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
#define sqr(a) ((a) * (a))
/* filter types */
enum IIRFilter
{
LPF, /* low pass filter */
HPF, /* High pass filter */
BPCSGF, /* band pass filter 1 */
BPZPGF, /* band pass filter 2 */
APF, /* Allpass filter*/
NOTCH, /* Notch Filter */
RIAA_phono, /* RIAA record/tape deemphasis */
PEQ, /* Peaking band EQ filter */
BBOOST, /* Bassboost filter */
LSH, /* Low shelf filter */
HSH, /* High shelf filter */
RIAA_CD /* CD de-emphasis */
};
struct iir_data
{
float b0, b1, b2;
float a0, a1, a2;
struct
{
float xn1, xn2;
float yn1, yn2;
} l, r;
};
static void iir_free(void *data)
{
free(data);
}
static void iir_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
struct iir_data *iir = (struct iir_data*)data;
float *out = output->samples;
float b0 = iir->b0;
float b1 = iir->b1;
float b2 = iir->b2;
float a0 = iir->a0;
float a1 = iir->a1;
float a2 = iir->a2;
float xn1_l = iir->l.xn1;
float xn2_l = iir->l.xn2;
float yn1_l = iir->l.yn1;
float yn2_l = iir->l.yn2;
float xn1_r = iir->r.xn1;
float xn2_r = iir->r.xn2;
float yn1_r = iir->r.yn1;
float yn2_r = iir->r.yn2;
output->samples = input->samples;
output->frames = input->frames;
for (i = 0; i < input->frames; i++, out += 2)
{
float in_l = out[0];
float in_r = out[1];
float l = (b0 * in_l + b1 * xn1_l + b2 * xn2_l - a1 * yn1_l - a2 * yn2_l) / a0;
float r = (b0 * in_r + b1 * xn1_r + b2 * xn2_r - a1 * yn1_r - a2 * yn2_r) / a0;
xn2_l = xn1_l;
xn1_l = in_l;
yn2_l = yn1_l;
yn1_l = l;
xn2_r = xn1_r;
xn1_r = in_r;
yn2_r = yn1_r;
yn1_r = r;
out[0] = l;
out[1] = r;
}
iir->l.xn1 = xn1_l;
iir->l.xn2 = xn2_l;
iir->l.yn1 = yn1_l;
iir->l.yn2 = yn2_l;
iir->r.xn1 = xn1_r;
iir->r.xn2 = xn2_r;
iir->r.yn1 = yn1_r;
iir->r.yn2 = yn2_r;
}
#define CHECK(x) if (!strcmp(str, #x)) return x
static enum IIRFilter str_to_type(const char *str)
{
CHECK(LPF);
CHECK(HPF);
CHECK(BPCSGF);
CHECK(BPZPGF);
CHECK(APF);
CHECK(NOTCH);
CHECK(RIAA_phono);
CHECK(PEQ);
CHECK(BBOOST);
CHECK(LSH);
CHECK(HSH);
CHECK(RIAA_CD);
return LPF; /* Fallback. */
}
static void make_poly_from_roots(
const double *roots, unsigned num_roots, float *poly)
{
unsigned i, j;
poly[0] = 1;
poly[1] = -roots[0];
memset(poly + 2, 0, (num_roots + 1 - 2) * sizeof(*poly));
for (i = 1; i < num_roots; i++)
for (j = num_roots; j > 0; j--)
poly[j] -= poly[j - 1] * roots[i];
}
static void iir_filter_init(struct iir_data *iir,
float sample_rate, float freq, float qual, float gain, enum IIRFilter filter_type)
{
double omega = 2.0 * M_PI * freq / sample_rate;
double cs = cos(omega);
double sn = sin(omega);
double a1pha = sn / (2.0 * qual);
double A = exp(log(10.0) * gain / 40.0);
double beta = sqrt(A + A);
float b0 = 0.0, b1 = 0.0, b2 = 0.0, a0 = 0.0, a1 = 0.0, a2 = 0.0;
/* Set up filter coefficients according to type */
switch (filter_type)
{
case LPF:
b0 = (1.0 - cs) / 2.0;
b1 = 1.0 - cs ;
b2 = (1.0 - cs) / 2.0;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case HPF:
b0 = (1.0 + cs) / 2.0;
b1 = -(1.0 + cs);
b2 = (1.0 + cs) / 2.0;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case APF:
b0 = 1.0 - a1pha;
b1 = -2.0 * cs;
b2 = 1.0 + a1pha;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case BPZPGF:
b0 = a1pha;
b1 = 0.0;
b2 = -a1pha;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case BPCSGF:
b0 = sn / 2.0;
b1 = 0.0;
b2 = -sn / 2.0;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case NOTCH:
b0 = 1.0;
b1 = -2.0 * cs;
b2 = 1.0;
a0 = 1.0 + a1pha;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha;
break;
case RIAA_phono: /* http://www.dsprelated.com/showmessage/73300/3.php */
{
double y, b_re, a_re, b_im, a_im, g;
float b[3], a[3];
if ((int)sample_rate == 44100)
{
static const double zeros[] = {-0.2014898, 0.9233820};
static const double poles[] = {0.7083149, 0.9924091};
make_poly_from_roots(zeros, 2, b);
make_poly_from_roots(poles, 2, a);
}
else if ((int)sample_rate == 48000)
{
static const double zeros[] = {-0.1766069, 0.9321590};
static const double poles[] = {0.7396325, 0.9931330};
make_poly_from_roots(zeros, 2, b);
make_poly_from_roots(poles, 2, a);
}
else if ((int)sample_rate == 88200)
{
static const double zeros[] = {-0.1168735, 0.9648312};
static const double poles[] = {0.8590646, 0.9964002};
make_poly_from_roots(zeros, 2, b);
make_poly_from_roots(poles, 2, a);
}
else if ((int)sample_rate == 96000)
{
static const double zeros[] = {-0.1141486, 0.9676817};
static const double poles[] = {0.8699137, 0.9966946};
make_poly_from_roots(zeros, 2, b);
make_poly_from_roots(poles, 2, a);
}
b0 = b[0];
b1 = b[1];
b2 = b[2];
a0 = a[0];
a1 = a[1];
a2 = a[2];
/* Normalise to 0dB at 1kHz (Thanks to Glenn Davis) */
y = 2.0 * M_PI * 1000.0 / sample_rate;
b_re = b0 + b1 * cos(-y) + b2 * cos(-2.0 * y);
a_re = a0 + a1 * cos(-y) + a2 * cos(-2.0 * y);
b_im = b1 * sin(-y) + b2 * sin(-2.0 * y);
a_im = a1 * sin(-y) + a2 * sin(-2.0 * y);
g = 1.0 / sqrt((sqr(b_re) + sqr(b_im)) / (sqr(a_re) + sqr(a_im)));
b0 *= g; b1 *= g; b2 *= g;
break;
}
case PEQ:
b0 = 1.0 + a1pha * A;
b1 = -2.0 * cs;
b2 = 1.0 - a1pha * A;
a0 = 1.0 + a1pha / A;
a1 = -2.0 * cs;
a2 = 1.0 - a1pha / A;
break;
case BBOOST:
beta = sqrt((A * A + 1) / 1.0 - (pow((A - 1), 2)));
b0 = A * ((A + 1) - (A - 1) * cs + beta * sn);
b1 = 2 * A * ((A - 1) - (A + 1) * cs);
b2 = A * ((A + 1) - (A - 1) * cs - beta * sn);
a0 = ((A + 1) + (A - 1) * cs + beta * sn);
a1 = -2 * ((A - 1) + (A + 1) * cs);
a2 = (A + 1) + (A - 1) * cs - beta * sn;
break;
case LSH:
b0 = A * ((A + 1) - (A - 1) * cs + beta * sn);
b1 = 2 * A * ((A - 1) - (A + 1) * cs);
b2 = A * ((A + 1) - (A - 1) * cs - beta * sn);
a0 = (A + 1) + (A - 1) * cs + beta * sn;
a1 = -2 * ((A - 1) + (A + 1) * cs);
a2 = (A + 1) + (A - 1) * cs - beta * sn;
break;
case RIAA_CD:
omega = 2.0 * M_PI * 5283.0 / sample_rate;
cs = cos(omega);
sn = sin(omega);
a1pha = sn / (2.0 * 0.4845);
A = exp(log(10.0) * -9.477 / 40.0);
beta = sqrt(A + A);
(void)a1pha;
case HSH:
b0 = A * ((A + 1.0) + (A - 1.0) * cs + beta * sn);
b1 = -2.0 * A * ((A - 1.0) + (A + 1.0) * cs);
b2 = A * ((A + 1.0) + (A - 1.0) * cs - beta * sn);
a0 = (A + 1.0) - (A - 1.0) * cs + beta * sn;
a1 = 2.0 * ((A - 1.0) - (A + 1.0) * cs);
a2 = (A + 1.0) - (A - 1.0) * cs - beta * sn;
break;
default:
break;
}
iir->b0 = b0;
iir->b1 = b1;
iir->b2 = b2;
iir->a0 = a0;
iir->a1 = a1;
iir->a2 = a2;
}
static void *iir_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float freq, qual, gain;
enum IIRFilter filter = LPF;
char *type = NULL;
struct iir_data *iir = (struct iir_data*)calloc(1, sizeof(*iir));
if (!iir)
return NULL;
config->get_float(userdata, "frequency", &freq, 1024.0f);
config->get_float(userdata, "quality", &qual, 0.707f);
config->get_float(userdata, "gain", &gain, 0.0f);
config->get_string(userdata, "type", &type, "LPF");
filter = str_to_type(type);
config->free(type);
iir_filter_init(iir, info->input_rate, freq, qual, gain, filter);
return iir;
}
static const struct dspfilter_implementation iir_plug = {
iir_init,
iir_process,
iir_free,
DSPFILTER_API_VERSION,
"IIR",
"iir",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation iir_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &iir_plug;
}
#undef dspfilter_get_implementation

4
audio/dsp_filters/link.T Normal file
View file

@ -0,0 +1,4 @@
{
global: dspfilter_get_implementation;
local: *;
};

112
audio/dsp_filters/panning.c Normal file
View file

@ -0,0 +1,112 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (panning.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <libretro_dspfilter.h>
struct panning_data
{
float left[2];
float right[2];
};
static void panning_free(void *data)
{
free(data);
}
static void panning_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
struct panning_data *pan = (struct panning_data*)data;
float *out = output->samples;
output->samples = input->samples;
output->frames = input->frames;
for (i = 0; i < input->frames; i++, out += 2)
{
float left = out[0];
float right = out[1];
out[0] = left * pan->left[0] + right * pan->left[1];
out[1] = left * pan->right[0] + right * pan->right[1];
}
}
static void *panning_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
static const float default_left[] = { 1.0f, 0.0f };
static const float default_right[] = { 0.0f, 1.0f };
float *left = NULL;
float *right = NULL;
unsigned num_left = 0;
unsigned num_right = 0;
struct panning_data *pan = (struct panning_data*)
calloc(1, sizeof(*pan));
if (!pan)
return NULL;
config->get_float_array(userdata, "left_mix",
&left, &num_left, default_left, 2);
config->get_float_array(userdata, "right_mix",
&right, &num_right, default_right, 2);
memcpy(pan->left, (num_left == 2) ?
left : default_left, sizeof(pan->left));
memcpy(pan->right, (num_right == 2) ?
right : default_right, sizeof(pan->right));
config->free(left);
config->free(right);
return pan;
}
static const struct dspfilter_implementation panning = {
panning_init,
panning_process,
panning_free,
DSPFILTER_API_VERSION,
"Panning",
"panning",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation panning_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &panning;
}
#undef dspfilter_get_implementation

146
audio/dsp_filters/phaser.c Normal file
View file

@ -0,0 +1,146 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (phaser.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
#define phaserlfoshape 4.0
#define phaserlfoskipsamples 20
struct phaser_data
{
float freq;
float startphase;
float fb;
float depth;
float drywet;
float old[2][24];
float gain;
float fbout[2];
float lfoskip;
float phase;
int stages;
unsigned long skipcount;
};
static void phaser_free(void *data)
{
free(data);
}
static void phaser_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i, c;
int s;
float m[2], tmp[2];
struct phaser_data *ph = (struct phaser_data*)data;
float *out = output->samples;
output->samples = input->samples;
output->frames = input->frames;
for (i = 0; i < input->frames; i++, out += 2)
{
float in[2] = { out[0], out[1] };
for (c = 0; c < 2; c++)
m[c] = in[c] + ph->fbout[c] * ph->fb * 0.01f;
if ((ph->skipcount++ % phaserlfoskipsamples) == 0)
{
ph->gain = 0.5 * (1.0 + cos(ph->skipcount * ph->lfoskip + ph->phase));
ph->gain = (exp(ph->gain * phaserlfoshape) - 1.0) / (exp(phaserlfoshape) - 1);
ph->gain = 1.0 - ph->gain * ph->depth;
}
for (s = 0; s < ph->stages; s++)
{
for (c = 0; c < 2; c++)
{
tmp[c] = ph->old[c][s];
ph->old[c][s] = ph->gain * tmp[c] + m[c];
m[c] = tmp[c] - ph->gain * ph->old[c][s];
}
}
for (c = 0; c < 2; c++)
{
ph->fbout[c] = m[c];
out[c] = m[c] * ph->drywet + in[c] * (1.0f - ph->drywet);
}
}
}
static void *phaser_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float lfo_freq, lfo_start_phase;
struct phaser_data *ph = (struct phaser_data*)calloc(1, sizeof(*ph));
if (!ph)
return NULL;
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, "feedback", &ph->fb, 0.0f);
config->get_float(userdata, "depth", &ph->depth, 0.4f);
config->get_float(userdata, "dry_wet", &ph->drywet, 0.5f);
config->get_int(userdata, "stages", &ph->stages, 2);
if (ph->stages < 1)
ph->stages = 1;
else if (ph->stages > 24)
ph->stages = 24;
ph->lfoskip = lfo_freq * 2.0 * M_PI / info->input_rate;
ph->phase = lfo_start_phase * M_PI / 180.0;
return ph;
}
static const struct dspfilter_implementation phaser_plug = {
phaser_init,
phaser_process,
phaser_free,
DSPFILTER_API_VERSION,
"Phaser",
"phaser",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation phaser_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &phaser_plug;
}
#undef dspfilter_get_implementation

327
audio/dsp_filters/reverb.c Normal file
View file

@ -0,0 +1,327 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (reverb.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_inline.h>
#include <libretro_dspfilter.h>
struct comb
{
float *buffer;
unsigned bufsize;
unsigned bufidx;
float feedback;
float filterstore;
float damp1, damp2;
};
struct allpass
{
float *buffer;
float feedback;
unsigned bufsize;
unsigned bufidx;
};
static INLINE float comb_process(struct comb *c, float input)
{
float output = c->buffer[c->bufidx];
c->filterstore = (output * c->damp2) + (c->filterstore * c->damp1);
c->buffer[c->bufidx] = input + (c->filterstore * c->feedback);
c->bufidx++;
if (c->bufidx >= c->bufsize)
c->bufidx = 0;
return output;
}
static INLINE float allpass_process(struct allpass *a, float input)
{
float bufout = a->buffer[a->bufidx];
float output = -input + bufout;
a->buffer[a->bufidx] = input + bufout * a->feedback;
a->bufidx++;
if (a->bufidx >= a->bufsize)
a->bufidx = 0;
return output;
}
#define numcombs 8
#define numallpasses 4
static const float muted = 0;
static const float fixedgain = 0.015f;
static const float scalewet = 3;
static const float scaledry = 2;
static const float scaledamp = 0.4f;
static const float scaleroom = 0.28f;
static const float offsetroom = 0.7f;
static const float initialroom = 0.5f;
static const float initialdamp = 0.5f;
static const float initialwet = 1.0f / 3.0f;
static const float initialdry = 0;
static const float initialwidth = 1;
static const float initialmode = 0;
static const float freezemode = 0.5f;
#define combtuningL1 1116
#define combtuningL2 1188
#define combtuningL3 1277
#define combtuningL4 1356
#define combtuningL5 1422
#define combtuningL6 1491
#define combtuningL7 1557
#define combtuningL8 1617
#define allpasstuningL1 556
#define allpasstuningL2 441
#define allpasstuningL3 341
#define allpasstuningL4 225
struct revmodel
{
struct comb combL[numcombs];
struct allpass allpassL[numallpasses];
float bufcombL1[combtuningL1];
float bufcombL2[combtuningL2];
float bufcombL3[combtuningL3];
float bufcombL4[combtuningL4];
float bufcombL5[combtuningL5];
float bufcombL6[combtuningL6];
float bufcombL7[combtuningL7];
float bufcombL8[combtuningL8];
float bufallpassL1[allpasstuningL1];
float bufallpassL2[allpasstuningL2];
float bufallpassL3[allpasstuningL3];
float bufallpassL4[allpasstuningL4];
float gain;
float roomsize, roomsize1;
float damp, damp1;
float wet, wet1, wet2;
float dry;
float width;
float mode;
};
static float revmodel_process(struct revmodel *rev, float in)
{
int i;
float mono_out = 0.0f;
float mono_in = in;
float input = mono_in * rev->gain;
for (i = 0; i < numcombs; i++)
mono_out += comb_process(&rev->combL[i], input);
for (i = 0; i < numallpasses; i++)
mono_out = allpass_process(&rev->allpassL[i], mono_out);
return mono_in * rev->dry + mono_out * rev->wet1;
}
static void revmodel_update(struct revmodel *rev)
{
int i;
rev->wet1 = rev->wet * (rev->width / 2.0f + 0.5f);
if (rev->mode >= freezemode)
{
rev->roomsize1 = 1.0f;
rev->damp1 = 0.0f;
rev->gain = muted;
}
else
{
rev->roomsize1 = rev->roomsize;
rev->damp1 = rev->damp;
rev->gain = fixedgain;
}
for (i = 0; i < numcombs; i++)
{
rev->combL[i].feedback = rev->roomsize1;
rev->combL[i].damp1 = rev->damp1;
rev->combL[i].damp2 = 1.0f - rev->damp1;
}
}
static void revmodel_setroomsize(struct revmodel *rev, float value)
{
rev->roomsize = value * scaleroom + offsetroom;
revmodel_update(rev);
}
static void revmodel_setdamp(struct revmodel *rev, float value)
{
rev->damp = value * scaledamp;
revmodel_update(rev);
}
static void revmodel_setwet(struct revmodel *rev, float value)
{
rev->wet = value * scalewet;
revmodel_update(rev);
}
static void revmodel_setdry(struct revmodel *rev, float value)
{
rev->dry = value * scaledry;
revmodel_update(rev);
}
static void revmodel_setwidth(struct revmodel *rev, float value)
{
rev->width = value;
revmodel_update(rev);
}
static void revmodel_setmode(struct revmodel *rev, float value)
{
rev->mode = value;
revmodel_update(rev);
}
static void revmodel_init(struct revmodel *rev)
{
rev->combL[0].buffer = rev->bufcombL1; rev->combL[0].bufsize = combtuningL1;
rev->combL[1].buffer = rev->bufcombL2; rev->combL[1].bufsize = combtuningL2;
rev->combL[2].buffer = rev->bufcombL3; rev->combL[2].bufsize = combtuningL3;
rev->combL[3].buffer = rev->bufcombL4; rev->combL[3].bufsize = combtuningL4;
rev->combL[4].buffer = rev->bufcombL5; rev->combL[4].bufsize = combtuningL5;
rev->combL[5].buffer = rev->bufcombL6; rev->combL[5].bufsize = combtuningL6;
rev->combL[6].buffer = rev->bufcombL7; rev->combL[6].bufsize = combtuningL7;
rev->combL[7].buffer = rev->bufcombL8; rev->combL[7].bufsize = combtuningL8;
rev->allpassL[0].buffer = rev->bufallpassL1; rev->allpassL[0].bufsize = allpasstuningL1;
rev->allpassL[1].buffer = rev->bufallpassL2; rev->allpassL[1].bufsize = allpasstuningL2;
rev->allpassL[2].buffer = rev->bufallpassL3; rev->allpassL[2].bufsize = allpasstuningL3;
rev->allpassL[3].buffer = rev->bufallpassL4; rev->allpassL[3].bufsize = allpasstuningL4;
rev->allpassL[0].feedback = 0.5f;
rev->allpassL[1].feedback = 0.5f;
rev->allpassL[2].feedback = 0.5f;
rev->allpassL[3].feedback = 0.5f;
revmodel_setwet(rev, initialwet);
revmodel_setroomsize(rev, initialroom);
revmodel_setdry(rev, initialdry);
revmodel_setdamp(rev, initialdamp);
revmodel_setwidth(rev, initialwidth);
revmodel_setmode(rev, initialmode);
}
struct reverb_data
{
struct revmodel left, right;
};
static void reverb_free(void *data)
{
free(data);
}
static void reverb_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
float *out;
struct reverb_data *rev = (struct reverb_data*)data;
output->samples = input->samples;
output->frames = input->frames;
out = output->samples;
for (i = 0; i < input->frames; i++, out += 2)
{
float in[2] = { out[0], out[1] };
out[0] = revmodel_process(&rev->left, in[0]);
out[1] = revmodel_process(&rev->right, in[1]);
}
}
static void *reverb_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
float drytime, wettime, damping, roomwidth, roomsize;
struct reverb_data *rev = (struct reverb_data*)
calloc(1, sizeof(*rev));
if (!rev)
return NULL;
config->get_float(userdata, "drytime", &drytime, 0.43f);
config->get_float(userdata, "wettime", &wettime, 0.4f);
config->get_float(userdata, "damping", &damping, 0.8f);
config->get_float(userdata, "roomwidth", &roomwidth, 0.56f);
config->get_float(userdata, "roomsize", &roomsize, 0.56f);
revmodel_init(&rev->left);
revmodel_init(&rev->right);
revmodel_setdamp(&rev->left, damping);
revmodel_setdry(&rev->left, drytime);
revmodel_setwet(&rev->left, wettime);
revmodel_setwidth(&rev->left, roomwidth);
revmodel_setroomsize(&rev->left, roomsize);
revmodel_setdamp(&rev->right, damping);
revmodel_setdry(&rev->right, drytime);
revmodel_setwet(&rev->right, wettime);
revmodel_setwidth(&rev->right, roomwidth);
revmodel_setroomsize(&rev->right, roomsize);
return rev;
}
static const struct dspfilter_implementation reverb_plug = {
reverb_init,
reverb_process,
reverb_free,
DSPFILTER_API_VERSION,
"Reverb",
"reverb",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation reverb_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &reverb_plug;
}
#undef dspfilter_get_implementation

148
audio/dsp_filters/wahwah.c Normal file
View file

@ -0,0 +1,148 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (wahwah.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
#define WAHWAH_LFO_SKIP_SAMPLES 30
struct wahwah_data
{
float phase;
float lfoskip;
float b0, b1, b2, a0, a1, a2;
float freq, startphase;
float depth, freqofs, res;
unsigned long skipcount;
struct
{
float xn1, xn2, yn1, yn2;
} l, r;
};
static void wahwah_free(void *data)
{
if (data)
free(data);
}
static void wahwah_process(void *data, struct dspfilter_output *output,
const struct dspfilter_input *input)
{
unsigned i;
struct wahwah_data *wah = (struct wahwah_data*)data;
float *out = output->samples;
output->samples = input->samples;
output->frames = input->frames;
for (i = 0; i < input->frames; i++, out += 2)
{
float out_l, out_r;
float in[2] = { out[0], out[1] };
if ((wah->skipcount++ % WAHWAH_LFO_SKIP_SAMPLES) == 0)
{
float omega, sn, cs, alpha;
float frequency = (1.0 + cos(wah->skipcount * wah->lfoskip + wah->phase)) / 2.0;
frequency = frequency * wah->depth * (1.0 - wah->freqofs) + wah->freqofs;
frequency = exp((frequency - 1.0) * 6.0);
omega = M_PI * frequency;
sn = sin(omega);
cs = cos(omega);
alpha = sn / (2.0 * wah->res);
wah->b0 = (1.0 - cs) / 2.0;
wah->b1 = 1.0 - cs;
wah->b2 = (1.0 - cs) / 2.0;
wah->a0 = 1.0 + alpha;
wah->a1 = -2.0 * cs;
wah->a2 = 1.0 - alpha;
}
out_l = (wah->b0 * in[0] + wah->b1 * wah->l.xn1 + wah->b2 * wah->l.xn2 - wah->a1 * wah->l.yn1 - wah->a2 * wah->l.yn2) / wah->a0;
out_r = (wah->b0 * in[1] + wah->b1 * wah->r.xn1 + wah->b2 * wah->r.xn2 - wah->a1 * wah->r.yn1 - wah->a2 * wah->r.yn2) / wah->a0;
wah->l.xn2 = wah->l.xn1;
wah->l.xn1 = in[0];
wah->l.yn2 = wah->l.yn1;
wah->l.yn1 = out_l;
wah->r.xn2 = wah->r.xn1;
wah->r.xn1 = in[1];
wah->r.yn2 = wah->r.yn1;
wah->r.yn1 = out_r;
out[0] = out_l;
out[1] = out_r;
}
}
static void *wahwah_init(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata)
{
struct wahwah_data *wah = (struct wahwah_data*)calloc(1, sizeof(*wah));
if (!wah)
return NULL;
config->get_float(userdata, "lfo_freq", &wah->freq, 1.5f);
config->get_float(userdata, "lfo_start_phase", &wah->startphase, 0.0f);
config->get_float(userdata, "freq_offset", &wah->freqofs, 0.3f);
config->get_float(userdata, "depth", &wah->depth, 0.7f);
config->get_float(userdata, "resonance", &wah->res, 2.5f);
wah->lfoskip = wah->freq * 2.0 * M_PI / info->input_rate;
wah->phase = wah->startphase * M_PI / 180.0;
return wah;
}
static const struct dspfilter_implementation wahwah_plug = {
wahwah_init,
wahwah_process,
wahwah_free,
DSPFILTER_API_VERSION,
"Wah-Wah",
"wahwah",
};
#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation wahwah_dspfilter_get_implementation
#endif
const struct dspfilter_implementation *
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
(void)mask;
return &wahwah_plug;
}
#undef dspfilter_get_implementation

View file

@ -0,0 +1,53 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (dsp_filter.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_AUDIO_DSP_FILTER_H
#define __LIBRETRO_SDK_AUDIO_DSP_FILTER_H
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
typedef struct rarch_dsp_filter rarch_dsp_filter_t;
rarch_dsp_filter_t *rarch_dsp_filter_new(const char *filter_config,
void *string_data, float sample_rate);
void rarch_dsp_filter_free(rarch_dsp_filter_t *dsp);
struct rarch_dsp_data
{
float *input;
unsigned input_frames;
/* Set by rarch_dsp_filter_process(). */
float *output;
unsigned output_frames;
};
void rarch_dsp_filter_process(rarch_dsp_filter_t *dsp,
struct rarch_dsp_data *data);
RETRO_END_DECLS
#endif

View file

@ -0,0 +1,187 @@
/* Copyright (C) 2010-2016 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this libretro API header (libretro_dspfilter.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRETRO_DSPFILTER_API_H__
#define LIBRETRO_DSPFILTER_API_H__
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
#define DSPFILTER_SIMD_SSE (1 << 0)
#define DSPFILTER_SIMD_SSE2 (1 << 1)
#define DSPFILTER_SIMD_VMX (1 << 2)
#define DSPFILTER_SIMD_VMX128 (1 << 3)
#define DSPFILTER_SIMD_AVX (1 << 4)
#define DSPFILTER_SIMD_NEON (1 << 5)
#define DSPFILTER_SIMD_SSE3 (1 << 6)
#define DSPFILTER_SIMD_SSSE3 (1 << 7)
#define DSPFILTER_SIMD_MMX (1 << 8)
#define DSPFILTER_SIMD_MMXEXT (1 << 9)
#define DSPFILTER_SIMD_SSE4 (1 << 10)
#define DSPFILTER_SIMD_SSE42 (1 << 11)
#define DSPFILTER_SIMD_AVX2 (1 << 12)
#define DSPFILTER_SIMD_VFPU (1 << 13)
#define DSPFILTER_SIMD_PS (1 << 14)
/* A bit-mask of all supported SIMD instruction sets.
* Allows an implementation to pick different
* dspfilter_implementation structs.
*/
typedef unsigned dspfilter_simd_mask_t;
/* Dynamic library endpoint. */
typedef const struct dspfilter_implementation *(
*dspfilter_get_implementation_t)(dspfilter_simd_mask_t mask);
/* The same SIMD mask argument is forwarded to create() callback
* as well to avoid having to keep lots of state around. */
const struct dspfilter_implementation *dspfilter_get_implementation(
dspfilter_simd_mask_t mask);
#define DSPFILTER_API_VERSION 1
struct dspfilter_info
{
/* Input sample rate that the DSP plugin receives. */
float input_rate;
};
struct dspfilter_output
{
/* The DSP plugin has to provide the buffering for the
* output samples or reuse the input buffer directly.
*
* The samples are laid out in interleaving order: LRLRLRLR
* The range of the samples are [-1.0, 1.0].
*
* It is not necessary to manually clip values. */
float *samples;
/* Frames which the DSP plugin outputted for the current process.
*
* One frame is here defined as a combined sample of
* left and right channels.
*
* (I.e. 44.1kHz, 16bit stereo will have
* 88.2k samples/sec and 44.1k frames/sec.)
*/
unsigned frames;
};
struct dspfilter_input
{
/* Input data for the DSP. The samples are interleaved in order: LRLRLRLR
*
* It is valid for a DSP plug to use this buffer for output as long as
* the output size is less or equal to the input.
*
* This is useful for filters which can output one sample for each
* input sample and do not need to maintain its own buffers.
*
* Block based filters must provide their own buffering scheme.
*
* The input size is not bound, but it can be safely assumed that it
* will not exceed ~100ms worth of audio at a time. */
float *samples;
/* Number of frames for input data.
* One frame is here defined as a combined sample of
* left and right channels.
*
* (I.e. 44.1kHz, 16bit stereo will have
* 88.2k samples/sec and 44.1k frames/sec.)
*/
unsigned frames;
};
/* Returns true if config key was found. Otherwise,
* returns false, and sets value to default value.
*/
typedef int (*dspfilter_config_get_float_t)(void *userdata,
const char *key, float *value, float default_value);
typedef int (*dspfilter_config_get_int_t)(void *userdata,
const char *key, int *value, int default_value);
/* Allocates an array with values. free() with dspfilter_config_free_t. */
typedef int (*dspfilter_config_get_float_array_t)(void *userdata,
const char *key, float **values, unsigned *out_num_values,
const float *default_values, unsigned num_default_values);
typedef int (*dspfilter_config_get_int_array_t)(void *userdata,
const char *key, int **values, unsigned *out_num_values,
const int *default_values, unsigned num_default_values);
typedef int (*dspfilter_config_get_string_t)(void *userdata,
const char *key, char **output, const char *default_output);
/* Calls free() in host runtime. Sometimes needed on Windows.
* free() on NULL is fine. */
typedef void (*dspfilter_config_free_t)(void *ptr);
struct dspfilter_config
{
dspfilter_config_get_float_t get_float;
dspfilter_config_get_int_t get_int;
dspfilter_config_get_float_array_t get_float_array;
dspfilter_config_get_int_array_t get_int_array;
dspfilter_config_get_string_t get_string;
/* Avoid problems where DSP plug and host are
* linked against different C runtimes. */
dspfilter_config_free_t free;
};
/* Creates a handle of the plugin. Returns NULL if failed. */
typedef void *(*dspfilter_init_t)(const struct dspfilter_info *info,
const struct dspfilter_config *config, void *userdata);
/* Frees the handle. */
typedef void (*dspfilter_free_t)(void *data);
/* Processes input data.
* The plugin is allowed to return variable sizes for output data. */
typedef void (*dspfilter_process_t)(void *data,
struct dspfilter_output *output, const struct dspfilter_input *input);
struct dspfilter_implementation
{
dspfilter_init_t init;
dspfilter_process_t process;
dspfilter_free_t free;
/* Must be DSPFILTER_API_VERSION */
unsigned api_version;
/* Human readable identifier of implementation. */
const char *ident;
/* Computer-friendly short version of ident.
* Lower case, no spaces and special characters, etc. */
const char *short_ident;
};
RETRO_END_DECLS
#endif