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