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 (phaser.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 <math.h> | |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
26 | ||
27 | #include <retro_miscellaneous.h> | |
28 | #include <libretro_dspfilter.h> | |
29 | ||
30 | #define PHASER_LFO_SHAPE 4.0 | |
31 | #define PHASER_LFO_SKIP_SAMPLES 20 | |
32 | ||
33 | struct phaser_data | |
34 | { | |
35 | float freq; | |
36 | float startphase; | |
37 | float fb; | |
38 | float depth; | |
39 | float drywet; | |
40 | float old[2][24]; | |
41 | float gain; | |
42 | float fbout[2]; | |
43 | float lfoskip; | |
44 | float phase; | |
45 | ||
46 | int stages; | |
47 | unsigned long skipcount; | |
48 | }; | |
49 | ||
50 | static void phaser_free(void *data) | |
51 | { | |
52 | free(data); | |
53 | } | |
54 | ||
55 | static void phaser_process(void *data, struct dspfilter_output *output, | |
56 | const struct dspfilter_input *input) | |
57 | { | |
58 | unsigned i, c; | |
59 | int s; | |
60 | float m[2], tmp[2]; | |
61 | struct phaser_data *ph = (struct phaser_data*)data; | |
62 | float *out = output->samples; | |
63 | ||
64 | output->samples = input->samples; | |
65 | output->frames = input->frames; | |
66 | ||
67 | for (i = 0; i < input->frames; i++, out += 2) | |
68 | { | |
69 | float in[2] = { out[0], out[1] }; | |
70 | ||
71 | for (c = 0; c < 2; c++) | |
72 | m[c] = in[c] + ph->fbout[c] * ph->fb * 0.01f; | |
73 | ||
74 | if ((ph->skipcount++ % PHASER_LFO_SKIP_SAMPLES) == 0) | |
75 | { | |
76 | ph->gain = 0.5 * (1.0 + cos(ph->skipcount * ph->lfoskip + ph->phase)); | |
77 | ph->gain = (exp(ph->gain * PHASER_LFO_SHAPE) - 1.0) / (exp(PHASER_LFO_SHAPE) - 1); | |
78 | ph->gain = 1.0 - ph->gain * ph->depth; | |
79 | } | |
80 | ||
81 | for (s = 0; s < ph->stages; s++) | |
82 | { | |
83 | for (c = 0; c < 2; c++) | |
84 | { | |
85 | tmp[c] = ph->old[c][s]; | |
86 | ph->old[c][s] = ph->gain * tmp[c] + m[c]; | |
87 | m[c] = tmp[c] - ph->gain * ph->old[c][s]; | |
88 | } | |
89 | } | |
90 | ||
91 | for (c = 0; c < 2; c++) | |
92 | { | |
93 | ph->fbout[c] = m[c]; | |
94 | out[c] = m[c] * ph->drywet + in[c] * (1.0f - ph->drywet); | |
95 | } | |
96 | } | |
97 | } | |
98 | ||
99 | static void *phaser_init(const struct dspfilter_info *info, | |
100 | const struct dspfilter_config *config, void *userdata) | |
101 | { | |
102 | float lfo_freq, lfo_start_phase; | |
103 | struct phaser_data *ph = (struct phaser_data*)calloc(1, sizeof(*ph)); | |
104 | if (!ph) | |
105 | return NULL; | |
106 | ||
107 | config->get_float(userdata, "lfo_freq", &lfo_freq, 0.4f); | |
108 | config->get_float(userdata, "lfo_start_phase", &lfo_start_phase, 0.0f); | |
109 | config->get_float(userdata, "feedback", &ph->fb, 0.0f); | |
110 | config->get_float(userdata, "depth", &ph->depth, 0.4f); | |
111 | config->get_float(userdata, "dry_wet", &ph->drywet, 0.5f); | |
112 | config->get_int(userdata, "stages", &ph->stages, 2); | |
113 | ||
114 | if (ph->stages < 1) | |
115 | ph->stages = 1; | |
116 | else if (ph->stages > 24) | |
117 | ph->stages = 24; | |
118 | ||
119 | ph->lfoskip = lfo_freq * 2.0 * M_PI / info->input_rate; | |
120 | ph->phase = lfo_start_phase * M_PI / 180.0; | |
121 | ||
122 | return ph; | |
123 | } | |
124 | ||
125 | static const struct dspfilter_implementation phaser_plug = { | |
126 | phaser_init, | |
127 | phaser_process, | |
128 | phaser_free, | |
129 | ||
130 | DSPFILTER_API_VERSION, | |
131 | "Phaser", | |
132 | "phaser", | |
133 | }; | |
134 | ||
135 | #ifdef HAVE_FILTERS_BUILTIN | |
136 | #define dspfilter_get_implementation phaser_dspfilter_get_implementation | |
137 | #endif | |
138 | ||
139 | const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask) | |
140 | { | |
141 | return &phaser_plug; | |
142 | } | |
143 | ||
144 | #undef dspfilter_get_implementation |