git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / audio / dsp_filter.c
CommitLineData
3719602c
PC
1/* Copyright (C) 2010-2020 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (dsp_filter.c).
5 * ---------------------------------------------------------------------------------------
6 *
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:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
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.
21 */
22
23#include <stdlib.h>
24
25#include <retro_miscellaneous.h>
26
27#include <compat/posix_string.h>
28#include <dynamic/dylib.h>
29
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>
36
37#include <audio/dsp_filter.h>
38
39struct retro_dsp_plug
40{
41#ifdef HAVE_DYLIB
42 dylib_t lib;
43#endif
44 const struct dspfilter_implementation *impl;
45};
46
47struct retro_dsp_instance
48{
49 const struct dspfilter_implementation *impl;
50 void *impl_data;
51};
52
53struct retro_dsp_filter
54{
55 config_file_t *conf;
56
57 struct retro_dsp_plug *plugs;
58 unsigned num_plugs;
59
60 struct retro_dsp_instance *instances;
61 unsigned num_instances;
62};
63
64static const struct dspfilter_implementation *find_implementation(
65 retro_dsp_filter_t *dsp, const char *ident)
66{
67 unsigned i;
68 for (i = 0; i < dsp->num_plugs; i++)
69 {
70 if (string_is_equal(dsp->plugs[i].impl->short_ident, ident))
71 return dsp->plugs[i].impl;
72 }
73
74 return NULL;
75}
76
77static 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,
83 config_userdata_free,
84};
85
86static bool create_filter_graph(retro_dsp_filter_t *dsp, float sample_rate)
87{
88 unsigned i;
89 struct retro_dsp_instance *instances = NULL;
90 unsigned filters = 0;
91
92 if (!config_get_uint(dsp->conf, "filters", &filters))
93 return false;
94
95 instances = (struct retro_dsp_instance*)calloc(filters, sizeof(*instances));
96 if (!instances)
97 return false;
98
99 dsp->instances = instances;
100 dsp->num_instances = filters;
101
102 for (i = 0; i < filters; i++)
103 {
104 struct config_file_userdata userdata;
105 struct dspfilter_info info;
106 char key[64];
107 char name[64];
108
109 key[0] = name[0] = '\0';
110
111 info.input_rate = sample_rate;
112
113 snprintf(key, sizeof(key), "filter%u", i);
114
115 if (!config_get_array(dsp->conf, key, name, sizeof(name)))
116 return false;
117
118 dsp->instances[i].impl = find_implementation(dsp, name);
119 if (!dsp->instances[i].impl)
120 return false;
121
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;
126
127 dsp->instances[i].impl_data = dsp->instances[i].impl->init(&info,
128 &dspfilter_config, &userdata);
129 if (!dsp->instances[i].impl_data)
130 return false;
131 }
132
133 return true;
134}
135
136#if defined(HAVE_FILTERS_BUILTIN)
137extern const struct dspfilter_implementation *panning_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
138extern const struct dspfilter_implementation *iir_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
139extern const struct dspfilter_implementation *echo_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
140extern const struct dspfilter_implementation *phaser_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
141extern const struct dspfilter_implementation *wahwah_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
142extern const struct dspfilter_implementation *eq_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
143extern const struct dspfilter_implementation *chorus_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
144
145static 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,
153};
154
155static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
156{
157 unsigned i;
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));
161
162 if (!plugs)
163 return false;
164
165 dsp->plugs = plugs;
166 dsp->num_plugs = ARRAY_SIZE(dsp_plugs_builtin);
167
168 for (i = 0; i < ARRAY_SIZE(dsp_plugs_builtin); i++)
169 {
170 dsp->plugs[i].impl = dsp_plugs_builtin[i](mask);
171 if (!dsp->plugs[i].impl)
172 return false;
173 }
174
175 return true;
176}
177#elif defined(HAVE_DYLIB)
178static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
179{
180 unsigned i;
181 dspfilter_simd_mask_t mask = (dspfilter_simd_mask_t)cpu_features_get();
182 unsigned list_size = list ? (unsigned)list->size : 0;
183
184 for (i = 0; i < list_size; i++)
185 {
186 dspfilter_get_implementation_t cb;
187 const struct dspfilter_implementation *impl = NULL;
188 struct retro_dsp_plug *new_plugs = NULL;
189 dylib_t lib =
190 dylib_load(list->elems[i].data);
191
192 if (!lib)
193 continue;
194
195 cb = (dspfilter_get_implementation_t)dylib_proc(lib, "dspfilter_get_implementation");
196 if (!cb)
197 {
198 dylib_close(lib);
199 continue;
200 }
201
202 impl = cb(mask);
203 if (!impl)
204 {
205 dylib_close(lib);
206 continue;
207 }
208
209 if (impl->api_version != DSPFILTER_API_VERSION)
210 {
211 dylib_close(lib);
212 continue;
213 }
214
215 new_plugs = (struct retro_dsp_plug*)
216 realloc(dsp->plugs, sizeof(*dsp->plugs) * (dsp->num_plugs + 1));
217 if (!new_plugs)
218 {
219 dylib_close(lib);
220 return false;
221 }
222
223 /* Found plug. */
224
225 dsp->plugs = new_plugs;
226 dsp->plugs[dsp->num_plugs].lib = lib;
227 dsp->plugs[dsp->num_plugs].impl = impl;
228 dsp->num_plugs++;
229 }
230
231 return true;
232}
233#endif
234
235retro_dsp_filter_t *retro_dsp_filter_new(
236 const char *filter_config,
237 void *string_data,
238 float sample_rate)
239{
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));
243
244 if (!dsp)
245 return NULL;
246
247 if (!(conf = config_file_new_from_path_to_string(filter_config)))
248 goto error;
249
250 dsp->conf = conf;
251
252 if (string_data)
253 plugs = (struct string_list*)string_data;
254
255#if defined(HAVE_DYLIB) || defined(HAVE_FILTERS_BUILTIN)
256 if (!append_plugs(dsp, plugs))
257 goto error;
258#endif
259
260 if (plugs)
261 string_list_free(plugs);
262 plugs = NULL;
263
264 if (!create_filter_graph(dsp, sample_rate))
265 goto error;
266
267 return dsp;
268
269error:
270 if (plugs)
271 string_list_free(plugs);
272 retro_dsp_filter_free(dsp);
273 return NULL;
274}
275
276void retro_dsp_filter_free(retro_dsp_filter_t *dsp)
277{
278 unsigned i;
279 if (!dsp)
280 return;
281
282 for (i = 0; i < dsp->num_instances; i++)
283 {
284 if (dsp->instances[i].impl_data && dsp->instances[i].impl)
285 dsp->instances[i].impl->free(dsp->instances[i].impl_data);
286 }
287 free(dsp->instances);
288
289#ifdef HAVE_DYLIB
290 for (i = 0; i < dsp->num_plugs; i++)
291 {
292 if (dsp->plugs[i].lib)
293 dylib_close(dsp->plugs[i].lib);
294 }
295 free(dsp->plugs);
296#endif
297
298 if (dsp->conf)
299 config_file_free(dsp->conf);
300
301 free(dsp);
302}
303
304void retro_dsp_filter_process(retro_dsp_filter_t *dsp,
305 struct retro_dsp_data *data)
306{
307 unsigned i;
308 struct dspfilter_output output = {0};
309 struct dspfilter_input input = {0};
310
311 output.samples = data->input;
312 output.frames = data->input_frames;
313
314 for (i = 0; i < dsp->num_instances; i++)
315 {
316 input.samples = output.samples;
317 input.frames = output.frames;
318 dsp->instances[i].impl->process(
319 dsp->instances[i].impl_data, &output, &input);
320 }
321
322 data->output = output.samples;
323 data->output_frames = output.frames;
324}