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