1 /* Copyright (C) 2010-2020 The RetroArch team
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (dsp_filter.c).
5 * ---------------------------------------------------------------------------------------
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 #include <retro_miscellaneous.h>
27 #include <compat/posix_string.h>
28 #include <dynamic/dylib.h>
30 #include <file/file_path.h>
31 #include <file/config_file_userdata.h>
32 #include <features/features_cpu.h>
33 #include <lists/string_list.h>
34 #include <string/stdstring.h>
35 #include <libretro_dspfilter.h>
37 #include <audio/dsp_filter.h>
44 const struct dspfilter_implementation *impl;
47 struct retro_dsp_instance
49 const struct dspfilter_implementation *impl;
53 struct retro_dsp_filter
57 struct retro_dsp_plug *plugs;
60 struct retro_dsp_instance *instances;
61 unsigned num_instances;
64 static const struct dspfilter_implementation *find_implementation(
65 retro_dsp_filter_t *dsp, const char *ident)
68 for (i = 0; i < dsp->num_plugs; i++)
70 if (string_is_equal(dsp->plugs[i].impl->short_ident, ident))
71 return dsp->plugs[i].impl;
77 static const struct dspfilter_config dspfilter_config = {
78 config_userdata_get_float,
79 config_userdata_get_int,
80 config_userdata_get_float_array,
81 config_userdata_get_int_array,
82 config_userdata_get_string,
86 static bool create_filter_graph(retro_dsp_filter_t *dsp, float sample_rate)
89 struct retro_dsp_instance *instances = NULL;
92 if (!config_get_uint(dsp->conf, "filters", &filters))
95 instances = (struct retro_dsp_instance*)calloc(filters, sizeof(*instances));
99 dsp->instances = instances;
100 dsp->num_instances = filters;
102 for (i = 0; i < filters; i++)
104 struct config_file_userdata userdata;
105 struct dspfilter_info info;
109 key[0] = name[0] = '\0';
111 info.input_rate = sample_rate;
113 snprintf(key, sizeof(key), "filter%u", i);
115 if (!config_get_array(dsp->conf, key, name, sizeof(name)))
118 dsp->instances[i].impl = find_implementation(dsp, name);
119 if (!dsp->instances[i].impl)
122 userdata.conf = dsp->conf;
123 /* Index-specific configs take priority over ident-specific. */
124 userdata.prefix[0] = key;
125 userdata.prefix[1] = dsp->instances[i].impl->short_ident;
127 dsp->instances[i].impl_data = dsp->instances[i].impl->init(&info,
128 &dspfilter_config, &userdata);
129 if (!dsp->instances[i].impl_data)
136 #if defined(HAVE_FILTERS_BUILTIN)
137 extern const struct dspfilter_implementation *panning_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
138 extern const struct dspfilter_implementation *iir_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
139 extern const struct dspfilter_implementation *echo_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
140 extern const struct dspfilter_implementation *phaser_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
141 extern const struct dspfilter_implementation *wahwah_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
142 extern const struct dspfilter_implementation *eq_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
143 extern const struct dspfilter_implementation *chorus_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
145 static const dspfilter_get_implementation_t dsp_plugs_builtin[] = {
146 panning_dspfilter_get_implementation,
147 iir_dspfilter_get_implementation,
148 echo_dspfilter_get_implementation,
149 phaser_dspfilter_get_implementation,
150 wahwah_dspfilter_get_implementation,
151 eq_dspfilter_get_implementation,
152 chorus_dspfilter_get_implementation,
155 static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
158 dspfilter_simd_mask_t mask = (dspfilter_simd_mask_t)cpu_features_get();
159 struct retro_dsp_plug *plugs = (struct retro_dsp_plug*)
160 calloc(ARRAY_SIZE(dsp_plugs_builtin), sizeof(*plugs));
166 dsp->num_plugs = ARRAY_SIZE(dsp_plugs_builtin);
168 for (i = 0; i < ARRAY_SIZE(dsp_plugs_builtin); i++)
170 dsp->plugs[i].impl = dsp_plugs_builtin[i](mask);
171 if (!dsp->plugs[i].impl)
177 #elif defined(HAVE_DYLIB)
178 static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
181 dspfilter_simd_mask_t mask = (dspfilter_simd_mask_t)cpu_features_get();
182 unsigned list_size = list ? (unsigned)list->size : 0;
184 for (i = 0; i < list_size; i++)
186 dspfilter_get_implementation_t cb;
187 const struct dspfilter_implementation *impl = NULL;
188 struct retro_dsp_plug *new_plugs = NULL;
190 dylib_load(list->elems[i].data);
195 cb = (dspfilter_get_implementation_t)dylib_proc(lib, "dspfilter_get_implementation");
209 if (impl->api_version != DSPFILTER_API_VERSION)
215 new_plugs = (struct retro_dsp_plug*)
216 realloc(dsp->plugs, sizeof(*dsp->plugs) * (dsp->num_plugs + 1));
225 dsp->plugs = new_plugs;
226 dsp->plugs[dsp->num_plugs].lib = lib;
227 dsp->plugs[dsp->num_plugs].impl = impl;
235 retro_dsp_filter_t *retro_dsp_filter_new(
236 const char *filter_config,
240 config_file_t *conf = NULL;
241 struct string_list *plugs = NULL;
242 retro_dsp_filter_t *dsp = (retro_dsp_filter_t*)calloc(1, sizeof(*dsp));
247 if (!(conf = config_file_new_from_path_to_string(filter_config)))
253 plugs = (struct string_list*)string_data;
255 #if defined(HAVE_DYLIB) || defined(HAVE_FILTERS_BUILTIN)
256 if (!append_plugs(dsp, plugs))
261 string_list_free(plugs);
264 if (!create_filter_graph(dsp, sample_rate))
271 string_list_free(plugs);
272 retro_dsp_filter_free(dsp);
276 void retro_dsp_filter_free(retro_dsp_filter_t *dsp)
282 for (i = 0; i < dsp->num_instances; i++)
284 if (dsp->instances[i].impl_data && dsp->instances[i].impl)
285 dsp->instances[i].impl->free(dsp->instances[i].impl_data);
287 free(dsp->instances);
290 for (i = 0; i < dsp->num_plugs; i++)
292 if (dsp->plugs[i].lib)
293 dylib_close(dsp->plugs[i].lib);
299 config_file_free(dsp->conf);
304 void retro_dsp_filter_process(retro_dsp_filter_t *dsp,
305 struct retro_dsp_data *data)
308 struct dspfilter_output output = {0};
309 struct dspfilter_input input = {0};
311 output.samples = data->input;
312 output.frames = data->input_frames;
314 for (i = 0; i < dsp->num_instances; i++)
316 input.samples = output.samples;
317 input.frames = output.frames;
318 dsp->instances[i].impl->process(
319 dsp->instances[i].impl_data, &output, &input);
322 data->output = output.samples;
323 data->output_frames = output.frames;