Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (echo.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 | #include <libretro_dspfilter.h> | |
27 | ||
28 | struct echo_channel | |
29 | { | |
30 | float *buffer; | |
31 | unsigned ptr; | |
32 | unsigned frames; | |
33 | float feedback; | |
34 | }; | |
35 | ||
36 | struct echo_data | |
37 | { | |
38 | struct echo_channel *channels; | |
39 | unsigned num_channels; | |
40 | float amp; | |
41 | }; | |
42 | ||
43 | static void echo_free(void *data) | |
44 | { | |
45 | unsigned i; | |
46 | struct echo_data *echo = (struct echo_data*)data; | |
47 | ||
48 | for (i = 0; i < echo->num_channels; i++) | |
49 | free(echo->channels[i].buffer); | |
50 | free(echo->channels); | |
51 | free(echo); | |
52 | } | |
53 | ||
54 | static void echo_process(void *data, struct dspfilter_output *output, | |
55 | const struct dspfilter_input *input) | |
56 | { | |
57 | unsigned i, c; | |
58 | float *out = NULL; | |
59 | struct echo_data *echo = (struct echo_data*)data; | |
60 | ||
61 | output->samples = input->samples; | |
62 | output->frames = input->frames; | |
63 | ||
64 | out = output->samples; | |
65 | ||
66 | for (i = 0; i < input->frames; i++, out += 2) | |
67 | { | |
68 | float left, right; | |
69 | float echo_left = 0.0f; | |
70 | float echo_right = 0.0f; | |
71 | ||
72 | for (c = 0; c < echo->num_channels; c++) | |
73 | { | |
74 | echo_left += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0]; | |
75 | echo_right += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1]; | |
76 | } | |
77 | ||
78 | echo_left *= echo->amp; | |
79 | echo_right *= echo->amp; | |
80 | ||
81 | left = out[0] + echo_left; | |
82 | right = out[1] + echo_right; | |
83 | ||
84 | for (c = 0; c < echo->num_channels; c++) | |
85 | { | |
86 | float feedback_left = out[0] + echo->channels[c].feedback * echo_left; | |
87 | float feedback_right = out[1] + echo->channels[c].feedback * echo_right; | |
88 | ||
89 | echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0] = feedback_left; | |
90 | echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1] = feedback_right; | |
91 | ||
92 | echo->channels[c].ptr = (echo->channels[c].ptr + 1) % echo->channels[c].frames; | |
93 | } | |
94 | ||
95 | out[0] = left; | |
96 | out[1] = right; | |
97 | } | |
98 | } | |
99 | ||
100 | static void *echo_init(const struct dspfilter_info *info, | |
101 | const struct dspfilter_config *config, void *userdata) | |
102 | { | |
103 | unsigned i, channels; | |
104 | struct echo_channel *echo_channels = NULL; | |
105 | float *delay = NULL; | |
106 | float *feedback = NULL; | |
107 | unsigned num_delay = 0; | |
108 | unsigned num_feedback = 0; | |
109 | ||
110 | static const float default_delay[] = { 200.0f }; | |
111 | static const float default_feedback[] = { 0.5f }; | |
112 | struct echo_data *echo = (struct echo_data*) | |
113 | calloc(1, sizeof(*echo)); | |
114 | ||
115 | if (!echo) | |
116 | return NULL; | |
117 | ||
118 | config->get_float_array(userdata, "delay", &delay, | |
119 | &num_delay, default_delay, 1); | |
120 | config->get_float_array(userdata, "feedback", &feedback, | |
121 | &num_feedback, default_feedback, 1); | |
122 | config->get_float(userdata, "amp", &echo->amp, 0.2f); | |
123 | ||
124 | channels = num_feedback = num_delay = MIN(num_delay, num_feedback); | |
125 | ||
126 | if (!(echo_channels = (struct echo_channel*)calloc(channels, | |
127 | sizeof(*echo_channels)))) | |
128 | goto error; | |
129 | ||
130 | echo->channels = echo_channels; | |
131 | echo->num_channels = channels; | |
132 | ||
133 | for (i = 0; i < channels; i++) | |
134 | { | |
135 | unsigned frames = (unsigned)(delay[i] * info->input_rate / 1000.0f + 0.5f); | |
136 | if (!frames) | |
137 | goto error; | |
138 | ||
139 | if (!(echo->channels[i].buffer = (float*)calloc(frames, 2 * sizeof(float)))) | |
140 | goto error; | |
141 | ||
142 | echo->channels[i].frames = frames; | |
143 | echo->channels[i].feedback = feedback[i]; | |
144 | } | |
145 | ||
146 | config->free(delay); | |
147 | config->free(feedback); | |
148 | return echo; | |
149 | ||
150 | error: | |
151 | config->free(delay); | |
152 | config->free(feedback); | |
153 | echo_free(echo); | |
154 | return NULL; | |
155 | } | |
156 | ||
157 | static const struct dspfilter_implementation echo_plug = { | |
158 | echo_init, | |
159 | echo_process, | |
160 | echo_free, | |
161 | ||
162 | DSPFILTER_API_VERSION, | |
163 | "Multi-Echo", | |
164 | "echo", | |
165 | }; | |
166 | ||
167 | #ifdef HAVE_FILTERS_BUILTIN | |
168 | #define dspfilter_get_implementation echo_dspfilter_get_implementation | |
169 | #endif | |
170 | ||
171 | const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) | |
172 | { | |
173 | return &echo_plug; | |
174 | } | |
175 | ||
176 | #undef dspfilter_get_implementation |