Mercurial > hg > devuvuzelator
comparison devuvuzelator-vst.cpp @ 0:fe4c331213c5
* First cut at a devuvuzelator
author | Chris Cannam |
---|---|
date | Thu, 10 Jun 2010 21:39:32 +0100 |
parents | |
children | 0d2126c32309 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:fe4c331213c5 |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 #include <alloca.h> | |
4 #include <iostream> | |
5 #include <cmath> | |
6 | |
7 #include <vst2.x/audioeffect.h> | |
8 | |
9 #define FFTSIZE 1024 | |
10 | |
11 class Devuvuzelator : public AudioEffect | |
12 { | |
13 enum { | |
14 LowParam = 0, | |
15 HighParam = 1, | |
16 FundamentalParam = 2, | |
17 BandwidthParam = 3, | |
18 HarmonicsParam = 4, | |
19 ReductionParam = 5, | |
20 NumParams = 6 | |
21 }; | |
22 | |
23 public: | |
24 Devuvuzelator(audioMasterCallback cb); | |
25 ~Devuvuzelator(); | |
26 | |
27 virtual void getEffectName(char *n) { | |
28 vst_strncpy(n, "Devuvuzelator", kVstMaxEffectNameLen); | |
29 } | |
30 virtual void getProductString(char *n) { | |
31 vst_strncpy(n, "Devuvuzelator", kVstMaxProductStrLen); | |
32 } | |
33 virtual void getVendorString(char *n) { | |
34 vst_strncpy(n, "Queen Mary, University of London", kVstMaxVendorStrLen); | |
35 } | |
36 | |
37 virtual void setParameter(VstInt32 index, float value); | |
38 virtual float getParameter(VstInt32 index); | |
39 virtual void getParameterLabel(VstInt32 index, char* label); | |
40 virtual void getParameterDisplay(VstInt32 index, char* text); | |
41 virtual void getParameterName(VstInt32 index, char* text); | |
42 | |
43 virtual void setSampleRate (float sampleRate) { | |
44 m_sampleRate = sampleRate; | |
45 AudioEffect::setSampleRate(sampleRate); | |
46 } | |
47 | |
48 virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames) { | |
49 m_input = inputs[0]; | |
50 m_output = outputs[0]; | |
51 runImpl(sampleFrames); | |
52 } | |
53 | |
54 void reset(); | |
55 void window(float *); | |
56 void runImpl(unsigned long); | |
57 void processFrame(); | |
58 void processSpectralFrame(); | |
59 | |
60 static void fft(unsigned int n, bool inverse, | |
61 double *ri, double *ii, double *ro, double *io); | |
62 | |
63 int m_sampleRate; | |
64 float *m_input; | |
65 float *m_output; | |
66 | |
67 float m_high; | |
68 float m_low; | |
69 float m_fundamental; | |
70 float m_bandwidth; | |
71 float m_harmonics; | |
72 float m_reduction; | |
73 | |
74 const int m_fftsize; | |
75 const int m_increment; | |
76 int m_fill; | |
77 int m_read; | |
78 float *m_buffer; | |
79 float *m_outacc; | |
80 double *m_real; | |
81 double *m_imag; | |
82 double *m_window; | |
83 }; | |
84 | |
85 void | |
86 Devuvuzelator::setParameter(VstInt32 index, float value) | |
87 { | |
88 float *params[NumParams] = { | |
89 m_low, | |
90 m_high, | |
91 m_fundamental, | |
92 m_bandwidth, | |
93 m_harmonics, | |
94 m_reduction, | |
95 }; | |
96 | |
97 *params[index] = value; | |
98 } | |
99 | |
100 float | |
101 Devuvuzelator::getParameter(VstInt32 index) | |
102 { | |
103 float *params[NumParams] = { | |
104 m_low, | |
105 m_high, | |
106 m_fundamental, | |
107 m_bandwidth, | |
108 m_harmonics, | |
109 m_reduction, | |
110 }; | |
111 | |
112 return *params[index]; | |
113 } | |
114 | |
115 // NB! The max name length for VST parameter names, labels | |
116 // (i.e. units) and display values (i.e. string renderings of current | |
117 // value) is a rather amazing 8 bytes | |
118 | |
119 void | |
120 Devuvuzelator::getParameterLabel(VstInt32 index, char *label) | |
121 { | |
122 const char *units[NumParams] = { | |
123 "dB", | |
124 "dB", | |
125 "Hz", | |
126 "Hz", | |
127 "", | |
128 "dB", | |
129 }; | |
130 | |
131 vst_strncpy(label, units[index], kVstMaxParamStrLen); | |
132 } | |
133 | |
134 void | |
135 Devuvuzelator::getParameterDisplay(VstInt32 index, char *label) | |
136 { | |
137 snprintf(label, kVstMaxParamStrLen, "%f", getParameter(index)); | |
138 } | |
139 | |
140 void | |
141 Devuvuzelator::getParameterName(VstInt32 index, char *label) | |
142 { | |
143 const char *names[NumParams] = { | |
144 "Floor", | |
145 "Ceiling", | |
146 "Pitch", | |
147 "B/W", | |
148 "Partials", | |
149 "Reductn", | |
150 }; | |
151 | |
152 vst_strncpy(label, names[index], kVstMaxParamStrLen); | |
153 } | |
154 | |
155 Devuvuzelator::Devuvuzelator(audioMasterCallback cb) : | |
156 AudioEffect(cb, 0, NumParams), | |
157 m_sampleRate(0), | |
158 m_input(0), | |
159 m_output(0), | |
160 m_low(0), | |
161 m_high(0), | |
162 m_fftsize(FFTSIZE), | |
163 m_increment(m_fftsize/4), | |
164 m_fill(0), | |
165 m_read(0) | |
166 { | |
167 m_buffer = new float[m_fftsize]; | |
168 m_outacc = new float[m_fftsize * 2]; | |
169 m_real = new double[m_fftsize]; | |
170 m_imag = new double[m_fftsize]; | |
171 m_window = new double[m_fftsize]; | |
172 | |
173 for (int i = 0; i < m_fftsize; ++i) { | |
174 m_window[i] = 0.5 - 0.5 * cos(2 * M_PI * i / m_fftsize); | |
175 } | |
176 | |
177 m_low = -40; | |
178 m_high = -20; | |
179 m_fundamental = 220; | |
180 m_bandwidth = 60; | |
181 m_harmonics = 3; | |
182 m_reduction = 10; | |
183 | |
184 setUniqueID("qmvz"); | |
185 setNumInputs(1); | |
186 setNumOutputs(1); | |
187 canProcessReplacing(true); | |
188 canDoubleReplacing(false); | |
189 | |
190 reset(); | |
191 } | |
192 | |
193 Devuvuzelator::~Devuvuzelator() | |
194 { | |
195 delete[] m_buffer; | |
196 delete[] m_outacc; | |
197 delete[] m_real; | |
198 delete[] m_imag; | |
199 delete[] m_window; | |
200 } | |
201 | |
202 void | |
203 Devuvuzelator::reset() | |
204 { | |
205 for (int i = 0; i < m_fftsize; ++i) { | |
206 m_buffer[i] = 0.f; | |
207 } | |
208 for (int i = 0; i < m_fftsize*2; ++i) { | |
209 m_outacc[i] = 0.f; | |
210 } | |
211 m_fill = 0; | |
212 m_read = 0; | |
213 } | |
214 | |
215 void | |
216 Devuvuzelator::runImpl(unsigned long sampleCount) | |
217 { | |
218 if (!m_input || !m_output) return; | |
219 | |
220 int ii = 0; | |
221 int oi = 0; | |
222 | |
223 while (ii < sampleCount) { | |
224 | |
225 m_output[oi++] = m_outacc[m_read++] / 1.5f; | |
226 | |
227 if (m_fill == m_fftsize) { | |
228 | |
229 processFrame(); | |
230 | |
231 for (int j = m_increment; j < m_fftsize; ++j) { | |
232 m_buffer[j - m_increment] = m_buffer[j]; | |
233 } | |
234 | |
235 for (int j = m_increment; j < m_fftsize*2; ++j) { | |
236 m_outacc[j - m_increment] = m_outacc[j]; | |
237 } | |
238 | |
239 for (int j = m_fftsize*2 - m_increment; j < m_fftsize*2; ++j) { | |
240 m_outacc[j] = 0.f; | |
241 } | |
242 | |
243 m_fill -= m_increment; | |
244 m_read -= m_increment; | |
245 } | |
246 | |
247 m_buffer[m_fill++] = m_input[ii++]; | |
248 } | |
249 } | |
250 | |
251 void | |
252 Devuvuzelator::processFrame() | |
253 { | |
254 double *frame = (double *)alloca(m_fftsize * sizeof(double)); | |
255 int ix = m_fftsize/2; | |
256 for (int i = 0; i < m_fftsize; ++i) { | |
257 frame[ix++] = m_buffer[i] * m_window[i]; | |
258 if (ix == m_fftsize) ix = 0; | |
259 } | |
260 | |
261 fft(m_fftsize, false, frame, 0, m_real, m_imag); | |
262 | |
263 processSpectralFrame(); | |
264 | |
265 for (int i = 0; i < m_fftsize/2-1; ++i) { | |
266 m_real[m_fftsize-i] = m_real[i+1]; | |
267 m_imag[m_fftsize-i] = -m_imag[i+1]; | |
268 } | |
269 | |
270 double *spare = (double *)alloca(m_fftsize * sizeof(double)); | |
271 fft(m_fftsize, true, m_real, m_imag, frame, spare); | |
272 | |
273 ix = m_fftsize/2; | |
274 for (int i = 0; i < m_fftsize; ++i) { | |
275 m_outacc[m_fftsize + i] += frame[ix++] * m_window[i]; | |
276 if (ix == m_fftsize) ix = 0; | |
277 } | |
278 } | |
279 | |
280 void | |
281 Devuvuzelator::processSpectralFrame() | |
282 { | |
283 const int hs = m_fftsize/2 + 1; | |
284 double *mags = (double *)alloca(hs * sizeof(double)); | |
285 double *ratios = (double *)alloca(hs * sizeof(double)); | |
286 for (int i = 0; i < hs; ++i) { | |
287 ratios[i] = 1.0; | |
288 mags[i] = sqrt(m_real[i] * m_real[i] + m_imag[i] * m_imag[i]); | |
289 } | |
290 | |
291 double low = -35; | |
292 double high = -20; | |
293 | |
294 if (m_low) low = *m_low; | |
295 if (m_high) high = *m_high; | |
296 | |
297 int harmonics = 3; | |
298 if (m_harmonics) harmonics = int(*m_harmonics + 0.5); | |
299 | |
300 double fun = 200; | |
301 if (m_fundamental) fun = *m_fundamental; | |
302 | |
303 double bw = 40; | |
304 if (m_bandwidth) bw = *m_bandwidth; | |
305 | |
306 double lowfun = fun - bw/2; | |
307 double highfun = fun + bw+2; | |
308 | |
309 double reduction = 10; | |
310 if (m_reduction) reduction = *m_reduction; | |
311 | |
312 for (int h = 0; h < harmonics; ++h) { | |
313 | |
314 double lowfreq = lowfun * (h+1); | |
315 double highfreq = highfun * (h+1); | |
316 | |
317 int lowbin = (m_fftsize * lowfreq) / m_sampleRate; | |
318 int highbin = (m_fftsize * highfreq) / m_sampleRate; | |
319 | |
320 for (int i = lowbin; i <= highbin; ++i) { | |
321 ratios[i] = 1.0; | |
322 double db = 10 * log10(mags[i]); | |
323 if (db > low && db < high) { | |
324 double r = reduction; | |
325 ratios[i] = pow(10, -r / 10); | |
326 } | |
327 } | |
328 } | |
329 | |
330 for (int i = 0; i < hs-1; ++i) { | |
331 if (ratios[i] == 1.0 && ratios[i+1] < 1.0) { | |
332 ratios[i] = (ratios[i+1] + 1) / 2; | |
333 } else if (ratios[i] < 1.0 && ratios[i+1] == 1.0) { | |
334 ratios[i+1] = (ratios[i] + 1) / 2; | |
335 ++i; | |
336 } | |
337 } | |
338 | |
339 for (int i = 0; i < hs; ++i) { | |
340 m_real[i] *= ratios[i]; | |
341 m_imag[i] *= ratios[i]; | |
342 } | |
343 } | |
344 | |
345 // FFT implementation by Don Cross, public domain. | |
346 // This version scales the forward transform. | |
347 | |
348 void Devuvuzelator::fft(unsigned int n, bool inverse, | |
349 double *ri, double *ii, double *ro, double *io) | |
350 { | |
351 if (!ri || !ro || !io) return; | |
352 | |
353 unsigned int bits; | |
354 unsigned int i, j, k, m; | |
355 unsigned int blockSize, blockEnd; | |
356 | |
357 double tr, ti; | |
358 | |
359 if (n < 2) return; | |
360 if (n & (n-1)) return; | |
361 | |
362 double angle = 2.0 * M_PI; | |
363 if (inverse) angle = -angle; | |
364 | |
365 for (i = 0; ; ++i) { | |
366 if (n & (1 << i)) { | |
367 bits = i; | |
368 break; | |
369 } | |
370 } | |
371 | |
372 static unsigned int tableSize = 0; | |
373 static int *table = 0; | |
374 | |
375 if (tableSize != n) { | |
376 | |
377 delete[] table; | |
378 | |
379 table = new int[n]; | |
380 | |
381 for (i = 0; i < n; ++i) { | |
382 | |
383 m = i; | |
384 | |
385 for (j = k = 0; j < bits; ++j) { | |
386 k = (k << 1) | (m & 1); | |
387 m >>= 1; | |
388 } | |
389 | |
390 table[i] = k; | |
391 } | |
392 | |
393 tableSize = n; | |
394 } | |
395 | |
396 if (ii) { | |
397 for (i = 0; i < n; ++i) { | |
398 ro[table[i]] = ri[i]; | |
399 io[table[i]] = ii[i]; | |
400 } | |
401 } else { | |
402 for (i = 0; i < n; ++i) { | |
403 ro[table[i]] = ri[i]; | |
404 io[table[i]] = 0.0; | |
405 } | |
406 } | |
407 | |
408 blockEnd = 1; | |
409 | |
410 for (blockSize = 2; blockSize <= n; blockSize <<= 1) { | |
411 | |
412 double delta = angle / (double)blockSize; | |
413 double sm2 = -sin(-2 * delta); | |
414 double sm1 = -sin(-delta); | |
415 double cm2 = cos(-2 * delta); | |
416 double cm1 = cos(-delta); | |
417 double w = 2 * cm1; | |
418 double ar[3], ai[3]; | |
419 | |
420 for (i = 0; i < n; i += blockSize) { | |
421 | |
422 ar[2] = cm2; | |
423 ar[1] = cm1; | |
424 | |
425 ai[2] = sm2; | |
426 ai[1] = sm1; | |
427 | |
428 for (j = i, m = 0; m < blockEnd; j++, m++) { | |
429 | |
430 ar[0] = w * ar[1] - ar[2]; | |
431 ar[2] = ar[1]; | |
432 ar[1] = ar[0]; | |
433 | |
434 ai[0] = w * ai[1] - ai[2]; | |
435 ai[2] = ai[1]; | |
436 ai[1] = ai[0]; | |
437 | |
438 k = j + blockEnd; | |
439 tr = ar[0] * ro[k] - ai[0] * io[k]; | |
440 ti = ar[0] * io[k] + ai[0] * ro[k]; | |
441 | |
442 ro[k] = ro[j] - tr; | |
443 io[k] = io[j] - ti; | |
444 | |
445 ro[j] += tr; | |
446 io[j] += ti; | |
447 } | |
448 } | |
449 | |
450 blockEnd = blockSize; | |
451 } | |
452 | |
453 if (!inverse) { | |
454 | |
455 double denom = (double)n; | |
456 | |
457 for (i = 0; i < n; i++) { | |
458 ro[i] /= denom; | |
459 io[i] /= denom; | |
460 } | |
461 } | |
462 } | |
463 | |
464 AudioEffect *createEffectInstance(audioMasterCallback audioMaster) | |
465 { | |
466 return new Devuvuzelator(audioMaster); | |
467 } | |
468 |