psxbios: implement some more memcard details
[pcsx_rearmed.git] / deps / libretro-common / audio / dsp_filter.c
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
39 struct retro_dsp_plug
40 {
41 #ifdef HAVE_DYLIB
42    dylib_t lib;
43 #endif
44    const struct dspfilter_implementation *impl;
45 };
46
47 struct retro_dsp_instance
48 {
49    const struct dspfilter_implementation *impl;
50    void *impl_data;
51 };
52
53 struct 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
64 static 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
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,
83    config_userdata_free,
84 };
85
86 static 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)
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);
144
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,
153 };
154
155 static 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)
178 static 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
235 retro_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
269 error:
270    if (plugs)
271       string_list_free(plugs);
272    retro_dsp_filter_free(dsp);
273    return NULL;
274 }
275
276 void 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
304 void 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 }