1 /*
2 * ZamPhono production/reproduction filters
3 * Copyright (C) 2016 Damien Zammit <damien@zamaudio.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16 #include "ZamPhonoPlugin.hpp"
17
18 START_NAMESPACE_DISTRHO
19
20 // -----------------------------------------------------------------------
21
ZamPhonoPlugin()22 ZamPhonoPlugin::ZamPhonoPlugin()
23 : Plugin(paramCount, 1, 0)
24 {
25 // set default values
26 loadProgram(0);
27 }
28
29 // -----------------------------------------------------------------------
30 // Init
31
initParameter(uint32_t index,Parameter & parameter)32 void ZamPhonoPlugin::initParameter(uint32_t index, Parameter& parameter)
33 {
34 switch (index)
35 {
36 case paramToggle:
37 parameter.hints = kParameterIsAutomable | kParameterIsBoolean;
38 parameter.name = "Reproduction/Production";
39 parameter.symbol = "inv";
40 parameter.unit = " ";
41 parameter.ranges.def = 0.0f;
42 parameter.ranges.min = 0.0f;
43 parameter.ranges.max = 1.0f;
44 break;
45 case paramType:
46 parameter.hints = kParameterIsAutomable | kParameterIsInteger;
47 parameter.name = "Phono Filter Type";
48 parameter.symbol = "type";
49 parameter.unit = " ";
50 parameter.ranges.def = 3.0f;
51 parameter.ranges.min = 0.0f;
52 parameter.ranges.max = 4.0f;
53 break;
54 }
55 }
56
initProgramName(uint32_t index,String & programName)57 void ZamPhonoPlugin::initProgramName(uint32_t index, String& programName)
58 {
59 switch(index) {
60 case 0:
61 programName = "RIAA (Playback)";
62 break;
63 }
64 }
65
loadProgram(uint32_t index)66 void ZamPhonoPlugin::loadProgram(uint32_t index)
67 {
68 switch(index) {
69 default:
70 case 0:
71 type = 3.0;
72 inv = 0.0;
73 break;
74 }
75
76 activate();
77 }
78
79 // -----------------------------------------------------------------------
80 // Internal data
81
getParameterValue(uint32_t index) const82 float ZamPhonoPlugin::getParameterValue(uint32_t index) const
83 {
84 switch (index)
85 {
86 case paramToggle:
87 return inv;
88 break;
89 case paramType:
90 return type;
91 break;
92 default:
93 return 0.0f;
94 }
95 }
96
setParameterValue(uint32_t index,float value)97 void ZamPhonoPlugin::setParameterValue(uint32_t index, float value)
98 {
99 switch (index)
100 {
101 case paramToggle:
102 inv = value;
103 break;
104 case paramType:
105 type = value;
106 break;
107 }
108 }
109
110 // -----------------------------------------------------------------------
111 // Process
112
brickwall(float fc,float srate)113 void ZamPhonoPlugin::brickwall(float fc, float srate)
114 {
115 float w0, alpha, cw, sw, q;
116 q = 0.707;
117 w0 = (2. * M_PI * fc / srate);
118 sw = sin(w0);
119 cw = cos(w0);
120 alpha = sw / (2. * q);
121
122 A0 = 1. + alpha;
123 A1 = -2. * cw;
124 A2 = 1. - alpha;
125 B0 = (1. - cw) / 2.;
126 B1 = (1. - cw);
127 B2 = B0;
128 }
129
clearbrickwall(void)130 void ZamPhonoPlugin::clearbrickwall(void)
131 {
132 state[0] = state[1] = state[2] = state[3] = 0.0;
133 }
134
activate()135 void ZamPhonoPlugin::activate()
136 {
137 float srate = getSampleRate();
138
139 typeold = -1.f;
140 invold = -1.f;
141
142 zn1 = zn2 = zd1 = zd2 = 0.0;
143 clearbrickwall();
144 brickwall(std::min(0.45 * srate, 21000.), srate);
145 }
146
run_brickwall(double in)147 double ZamPhonoPlugin::run_brickwall(double in)
148 {
149 double out;
150 in = sanitize_denormal(in);
151
152 out = B0/A0*in + B1/A0*state[0] + B2/A0*state[1]
153 -A1/A0*state[2] - A2/A0*state[3] + 1e-20;
154
155 state[1] = state[0];
156 state[0] = in;
157 state[3] = state[2];
158 state[2] = sanitize_denormal(out);
159 return state[2];
160 }
161
emphasis(float srate)162 void ZamPhonoPlugin::emphasis(float srate)
163 {
164 float t,i,j,k,g,tau1,tau2,tau3,freq;
165
166 switch((int)type) {
167 case 0: //"Columbia"
168 i = 100.f;
169 j = 500.f;
170 k = 1590.f;
171 break;
172 case 1: //"EMI"
173 i = 70.f;
174 j = 500.f;
175 k = 2500.f;
176 break;
177 case 2: //"BSI(78rpm)"
178 i = 50.f;
179 j = 353.f;
180 k = 3180.f;
181 break;
182 default:
183 case 3: //"RIAA"
184 tau1 = 0.003180f;
185 tau2 = 0.000318f;
186 tau3 = 0.000075f;
187 i = 1.f / (2.f * M_PI * tau1);
188 j = 1.f / (2.f * M_PI * tau2);
189 k = 1.f / (2.f * M_PI * tau3);
190 break;
191 case 4: //"CD Emphasis"
192 tau1 = 0.000050f;
193 tau2 = 0.000015f;
194 tau3 = 0.0000001f;// 1.6MHz out of audible range for null impact
195 i = 1.f / (2.f * M_PI * tau1);
196 j = 1.f / (2.f * M_PI * tau2);
197 k = 1.f / (2.f * M_PI * tau3);
198 break;
199 }
200
201 t = 1.f / srate;
202 g = 1.0;
203
204 i *= 2.f * M_PI;
205 j *= 2.f * M_PI;
206 k *= 2.f * M_PI;
207
208 if (inv < 0.5) {
209 //Reproduction
210 g = 1.f / (4.f+2.f*i*t+2.f*k*t+i*k*t*t);
211 b0 = (2.f*t+j*t*t)*g;
212 b1 = (2.f*j*t*t)*g;
213 b2 = (-2.f*t+j*t*t)*g;
214 a1 = (-8.f+2.f*i*k*t*t)*g;
215 a2 = (4.f-2.f*i*t-2.f*k*t+i*k*t*t)*g;
216 } else {
217 //Production
218 g = 1.f / (2.f*t+j*t*t);
219 b0 = (4.f+2.f*i*t+2.f*k*t+i*k*t*t)*g;
220 b1 = (-8.f+2.f*i*k*t*t)*g;
221 b2 = (4.f-2.f*i*t-2.f*k*t+i*k*t*t)*g;
222 a1 = (2.f*j*t*t)*g;
223 a2 = (-2.f*t+j*t*t)*g;
224 }
225
226 freq = 1000.0 * 2.0 * M_PI / srate;
227 std::complex<double> z = 1.0 / exp(std::complex<double>(0.0, freq));
228 g = std::abs((std::complex<double>(b0) + double(b1) * z + double(b2) * z*z) / (std::complex<double>(1.0) + double(a1) * z + double(a2) * z*z));
229 b0 /= g;
230 b1 /= g;
231 b2 /= g;
232 }
233
run_filter(double in)234 double ZamPhonoPlugin::run_filter(double in)
235 {
236 double out;
237
238 in = sanitize_denormal(in);
239 out = in * b0 + zn1 * b1 + zn2 * b2
240 - zd1 * a1 - zd2 * a2;
241 out = sanitize_denormal(out);
242 zn2 = sanitize_denormal(zn1);
243 zd2 = sanitize_denormal(zd1);
244 zn1 = in;
245 zd1 = out;
246
247 return out;
248 }
249
run(const float ** inputs,float ** outputs,uint32_t frames)250 void ZamPhonoPlugin::run(const float** inputs, float** outputs, uint32_t frames)
251 {
252 float srate = getSampleRate();
253 int recalc = 0;
254
255 if (type != typeold) {
256 recalc = 1;
257 }
258
259 if (inv != invold) {
260 recalc = 1;
261 }
262
263 // Settings changed
264 if (recalc) {
265 // Clear filter states
266 zn1 = zn2 = zd1 = zd2 = 0.0;
267 clearbrickwall();
268
269 // Recalculate filter coeffs
270 brickwall(std::min(0.45 * srate, 21000.), srate);
271 emphasis(srate);
272 }
273
274 double tmp;
275 double in;
276
277 for (uint32_t i = 0; i < frames; i++) {
278 in = inputs[0][i];
279 tmp = run_filter(in);
280 tmp = run_brickwall(tmp);
281 outputs[0][i] = in;
282 outputs[0][i] = (float)tmp;
283 }
284
285 typeold = type;
286 invold = inv;
287 }
288
289 // -----------------------------------------------------------------------
290
createPlugin()291 Plugin* createPlugin()
292 {
293 return new ZamPhonoPlugin();
294 }
295
296 // -----------------------------------------------------------------------
297
298 END_NAMESPACE_DISTRHO
299