mirror of
https://github.com/libretro/libretro-common.git
synced 2024-06-22 06:02:24 -04:00
Update
This commit is contained in:
parent
1b289782e6
commit
87fd380bac
324
audio/dsp_filter.c
Normal file
324
audio/dsp_filter.c
Normal 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;
|
||||
}
|
12
audio/dsp_filters/BassBoost.dsp
Normal file
12
audio/dsp_filters/BassBoost.dsp
Normal 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"
|
||||
|
22
audio/dsp_filters/ChipTuneEnhance.dsp
Normal file
22
audio/dsp_filters/ChipTuneEnhance.dsp
Normal 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"
|
14
audio/dsp_filters/Chorus.dsp
Normal file
14
audio/dsp_filters/Chorus.dsp
Normal 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
41
audio/dsp_filters/EQ.dsp
Normal 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
|
19
audio/dsp_filters/Echo.dsp
Normal file
19
audio/dsp_filters/Echo.dsp
Normal 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"
|
12
audio/dsp_filters/EchoReverb.dsp
Normal file
12
audio/dsp_filters/EchoReverb.dsp
Normal 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
|
7
audio/dsp_filters/HighShelfDampen.dsp
Normal file
7
audio/dsp_filters/HighShelfDampen.dsp
Normal 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
23
audio/dsp_filters/IIR.dsp
Normal 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
|
||||
|
47
audio/dsp_filters/LowPassCPS.dsp
Normal file
47
audio/dsp_filters/LowPassCPS.dsp
Normal 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
|
12
audio/dsp_filters/Mono.dsp
Normal file
12
audio/dsp_filters/Mono.dsp
Normal 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"
|
23
audio/dsp_filters/Panning.dsp
Normal file
23
audio/dsp_filters/Panning.dsp
Normal 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"
|
||||
|
11
audio/dsp_filters/Phaser.dsp
Normal file
11
audio/dsp_filters/Phaser.dsp
Normal 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
|
||||
|
10
audio/dsp_filters/Reverb.dsp
Normal file
10
audio/dsp_filters/Reverb.dsp
Normal 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
|
||||
|
10
audio/dsp_filters/WahWah.dsp
Normal file
10
audio/dsp_filters/WahWah.dsp
Normal 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
161
audio/dsp_filters/chorus.c
Normal 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
182
audio/dsp_filters/echo.c
Normal 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
352
audio/dsp_filters/eq.c
Normal 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
205
audio/dsp_filters/fft/fft.c
Normal 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);
|
||||
}
|
||||
|
46
audio/dsp_filters/fft/fft.h
Normal file
46
audio/dsp_filters/fft/fft.h
Normal 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
371
audio/dsp_filters/iir.c
Normal 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
4
audio/dsp_filters/link.T
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
global: dspfilter_get_implementation;
|
||||
local: *;
|
||||
};
|
112
audio/dsp_filters/panning.c
Normal file
112
audio/dsp_filters/panning.c
Normal 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
146
audio/dsp_filters/phaser.c
Normal 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
327
audio/dsp_filters/reverb.c
Normal 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
148
audio/dsp_filters/wahwah.c
Normal 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
|
||||
|
53
include/audio/dsp_filter.h
Normal file
53
include/audio/dsp_filter.h
Normal 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
|
||||
|
187
include/libretro_dspfilter.h
Normal file
187
include/libretro_dspfilter.h
Normal 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
|
Loading…
Reference in a new issue