annotate devuvuzelator-ladspa.cpp @ 1:0d2126c32309

* split out core code, fix some things
author Chris Cannam
date Fri, 11 Jun 2010 10:31:29 +0100
parents
children 8b79175c9f02
rev   line source
Chris@1 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1 2
Chris@1 3 #include <ladspa.h>
Chris@1 4 #include <alloca.h>
Chris@1 5 #include <iostream>
Chris@1 6 #include <cmath>
Chris@1 7
Chris@1 8 #define FFTSIZE 1024
Chris@1 9
Chris@1 10 class Devuvuzelator
Chris@1 11 {
Chris@1 12 public:
Chris@1 13 static const LADSPA_Descriptor *getDescriptor(unsigned long index);
Chris@1 14
Chris@1 15 private:
Chris@1 16 Devuvuzelator(int sampleRate);
Chris@1 17 ~Devuvuzelator();
Chris@1 18
Chris@1 19 enum {
Chris@1 20 InputPort = 0,
Chris@1 21 OutputPort = 1,
Chris@1 22 LowPort = 2,
Chris@1 23 HighPort = 3,
Chris@1 24 FundamentalPort = 4,
Chris@1 25 BandwidthPort = 5,
Chris@1 26 HarmonicsPort = 6,
Chris@1 27 ReductionPort = 7,
Chris@1 28 PortCount = 8,
Chris@1 29 };
Chris@1 30
Chris@1 31 static const char *const portNames[PortCount];
Chris@1 32 static const LADSPA_PortDescriptor ports[PortCount];
Chris@1 33 static const LADSPA_PortRangeHint hints[PortCount];
Chris@1 34 static const LADSPA_Properties properties;
Chris@1 35 static const LADSPA_Descriptor ladspaDescriptor;
Chris@1 36
Chris@1 37 static LADSPA_Handle instantiate(const LADSPA_Descriptor *, unsigned long);
Chris@1 38 static void connectPort(LADSPA_Handle, unsigned long, LADSPA_Data *);
Chris@1 39 static void activate(LADSPA_Handle);
Chris@1 40 static void run(LADSPA_Handle, unsigned long);
Chris@1 41 static void deactivate(LADSPA_Handle);
Chris@1 42 static void cleanup(LADSPA_Handle);
Chris@1 43
Chris@1 44 void reset();
Chris@1 45 void window(float *);
Chris@1 46 void runImpl(unsigned long);
Chris@1 47 void processFrame();
Chris@1 48 void processSpectralFrame();
Chris@1 49
Chris@1 50 static void fft(unsigned int n, bool inverse,
Chris@1 51 double *ri, double *ii, double *ro, double *io);
Chris@1 52
Chris@1 53 int m_sampleRate;
Chris@1 54 float *m_input;
Chris@1 55 float *m_output;
Chris@1 56 float *m_low;
Chris@1 57 float *m_high;
Chris@1 58 float *m_fundamental;
Chris@1 59 float *m_bandwidth;
Chris@1 60 float *m_harmonics;
Chris@1 61 float *m_reduction;
Chris@1 62
Chris@1 63 const int m_fftsize;
Chris@1 64 const int m_increment;
Chris@1 65 int m_fill;
Chris@1 66 int m_read;
Chris@1 67 float *m_buffer;
Chris@1 68 float *m_outacc;
Chris@1 69 double *m_real;
Chris@1 70 double *m_imag;
Chris@1 71 double *m_window;
Chris@1 72 };
Chris@1 73
Chris@1 74 const char *const
Chris@1 75 Devuvuzelator::portNames[PortCount] =
Chris@1 76 {
Chris@1 77 "Input",
Chris@1 78 "Output",
Chris@1 79 "Low threshold (dB)",
Chris@1 80 "High threshold (dB)",
Chris@1 81 "Fundamental frequency (Hz)",
Chris@1 82 "Bandwidth of fundamental (Hz)",
Chris@1 83 "Number of partials",
Chris@1 84 "Reduction (dB)",
Chris@1 85 };
Chris@1 86
Chris@1 87 const LADSPA_PortDescriptor
Chris@1 88 Devuvuzelator::ports[PortCount] =
Chris@1 89 {
Chris@1 90 LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
Chris@1 91 LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
Chris@1 92 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
Chris@1 93 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
Chris@1 94 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
Chris@1 95 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
Chris@1 96 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
Chris@1 97 LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
Chris@1 98 };
Chris@1 99
Chris@1 100 const LADSPA_PortRangeHint
Chris@1 101 Devuvuzelator::hints[PortCount] =
Chris@1 102 {
Chris@1 103 { 0, 0, 0 },
Chris@1 104 { 0, 0, 0 },
Chris@1 105 { LADSPA_HINT_DEFAULT_MIDDLE |
Chris@1 106 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, -80, 0 },
Chris@1 107 { LADSPA_HINT_DEFAULT_HIGH |
Chris@1 108 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, -80, 0 },
Chris@1 109 { LADSPA_HINT_DEFAULT_LOW |
Chris@1 110 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 110, 550 },
Chris@1 111 { LADSPA_HINT_DEFAULT_MIDDLE |
Chris@1 112 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 20, 100 },
Chris@1 113 { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_INTEGER |
Chris@1 114 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 6 },
Chris@1 115 { LADSPA_HINT_DEFAULT_MIDDLE |
Chris@1 116 LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 20 },
Chris@1 117 };
Chris@1 118
Chris@1 119 const LADSPA_Properties
Chris@1 120 Devuvuzelator::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
Chris@1 121
Chris@1 122 const LADSPA_Descriptor
Chris@1 123 Devuvuzelator::ladspaDescriptor =
Chris@1 124 {
Chris@1 125 0, // "Unique" ID
Chris@1 126 "devuvuzelator", // Label
Chris@1 127 properties,
Chris@1 128 "Devuvuzelator", // Name
Chris@1 129 "Queen Mary, University of London", // Maker
Chris@1 130 "All Rights Reserved", // Copyright
Chris@1 131 PortCount,
Chris@1 132 ports,
Chris@1 133 portNames,
Chris@1 134 hints,
Chris@1 135 0, // Implementation data
Chris@1 136 instantiate,
Chris@1 137 connectPort,
Chris@1 138 activate,
Chris@1 139 run,
Chris@1 140 0, // Run adding
Chris@1 141 0, // Set run adding gain
Chris@1 142 deactivate,
Chris@1 143 cleanup
Chris@1 144 };
Chris@1 145
Chris@1 146 const LADSPA_Descriptor *
Chris@1 147 Devuvuzelator::getDescriptor(unsigned long index)
Chris@1 148 {
Chris@1 149 if (index == 0) return &ladspaDescriptor;
Chris@1 150 return 0;
Chris@1 151 }
Chris@1 152
Chris@1 153 Devuvuzelator::Devuvuzelator(int sampleRate) :
Chris@1 154 m_sampleRate(sampleRate),
Chris@1 155 m_input(0),
Chris@1 156 m_output(0),
Chris@1 157 m_low(0),
Chris@1 158 m_high(0),
Chris@1 159 m_fftsize(FFTSIZE),
Chris@1 160 m_increment(m_fftsize/4),
Chris@1 161 m_fill(0),
Chris@1 162 m_read(0)
Chris@1 163 {
Chris@1 164 m_buffer = new float[m_fftsize];
Chris@1 165 m_outacc = new float[m_fftsize * 2];
Chris@1 166 m_real = new double[m_fftsize];
Chris@1 167 m_imag = new double[m_fftsize];
Chris@1 168 m_window = new double[m_fftsize];
Chris@1 169
Chris@1 170 for (int i = 0; i < m_fftsize; ++i) {
Chris@1 171 m_window[i] = 0.5 - 0.5 * cos(2 * M_PI * i / m_fftsize);
Chris@1 172 }
Chris@1 173
Chris@1 174 reset();
Chris@1 175 }
Chris@1 176
Chris@1 177 Devuvuzelator::~Devuvuzelator()
Chris@1 178 {
Chris@1 179 delete[] m_buffer;
Chris@1 180 delete[] m_outacc;
Chris@1 181 delete[] m_real;
Chris@1 182 delete[] m_imag;
Chris@1 183 delete[] m_window;
Chris@1 184 }
Chris@1 185
Chris@1 186 LADSPA_Handle
Chris@1 187 Devuvuzelator::instantiate(const LADSPA_Descriptor *, unsigned long rate)
Chris@1 188 {
Chris@1 189 Devuvuzelator *devuvu = new Devuvuzelator(rate);
Chris@1 190 return devuvu;
Chris@1 191 }
Chris@1 192
Chris@1 193 void
Chris@1 194 Devuvuzelator::connectPort(LADSPA_Handle handle,
Chris@1 195 unsigned long port, LADSPA_Data *location)
Chris@1 196 {
Chris@1 197 Devuvuzelator *devuvu = (Devuvuzelator *)handle;
Chris@1 198
Chris@1 199 float **ports[PortCount] = {
Chris@1 200 &devuvu->m_input,
Chris@1 201 &devuvu->m_output,
Chris@1 202 &devuvu->m_low,
Chris@1 203 &devuvu->m_high,
Chris@1 204 &devuvu->m_fundamental,
Chris@1 205 &devuvu->m_bandwidth,
Chris@1 206 &devuvu->m_harmonics,
Chris@1 207 &devuvu->m_reduction,
Chris@1 208 };
Chris@1 209
Chris@1 210 *ports[port] = (float *)location;
Chris@1 211 }
Chris@1 212
Chris@1 213 void
Chris@1 214 Devuvuzelator::activate(LADSPA_Handle handle)
Chris@1 215 {
Chris@1 216 Devuvuzelator *devuvu = (Devuvuzelator *)handle;
Chris@1 217 devuvu->reset();
Chris@1 218 }
Chris@1 219
Chris@1 220 void
Chris@1 221 Devuvuzelator::run(LADSPA_Handle handle, unsigned long samples)
Chris@1 222 {
Chris@1 223 Devuvuzelator *devuvu = (Devuvuzelator *)handle;
Chris@1 224 devuvu->runImpl(samples);
Chris@1 225 }
Chris@1 226
Chris@1 227 void
Chris@1 228 Devuvuzelator::deactivate(LADSPA_Handle handle)
Chris@1 229 {
Chris@1 230 activate(handle); // both functions just reset the plugin
Chris@1 231 }
Chris@1 232
Chris@1 233 void
Chris@1 234 Devuvuzelator::cleanup(LADSPA_Handle handle)
Chris@1 235 {
Chris@1 236 delete (Devuvuzelator *)handle;
Chris@1 237 }
Chris@1 238
Chris@1 239 void
Chris@1 240 Devuvuzelator::reset()
Chris@1 241 {
Chris@1 242 for (int i = 0; i < m_fftsize; ++i) {
Chris@1 243 m_buffer[i] = 0.f;
Chris@1 244 }
Chris@1 245 for (int i = 0; i < m_fftsize*2; ++i) {
Chris@1 246 m_outacc[i] = 0.f;
Chris@1 247 }
Chris@1 248 m_fill = 0;
Chris@1 249 m_read = 0;
Chris@1 250 }
Chris@1 251
Chris@1 252 void
Chris@1 253 Devuvuzelator::runImpl(unsigned long sampleCount)
Chris@1 254 {
Chris@1 255 if (!m_input || !m_output) return;
Chris@1 256
Chris@1 257 int ii = 0;
Chris@1 258 int oi = 0;
Chris@1 259 const int sc = sampleCount;
Chris@1 260
Chris@1 261 while (ii < sc) {
Chris@1 262
Chris@1 263 m_output[oi++] = m_outacc[m_read++] / 1.5f;
Chris@1 264
Chris@1 265 if (m_fill == m_fftsize) {
Chris@1 266
Chris@1 267 processFrame();
Chris@1 268
Chris@1 269 for (int j = m_increment; j < m_fftsize; ++j) {
Chris@1 270 m_buffer[j - m_increment] = m_buffer[j];
Chris@1 271 }
Chris@1 272
Chris@1 273 for (int j = m_increment; j < m_fftsize*2; ++j) {
Chris@1 274 m_outacc[j - m_increment] = m_outacc[j];
Chris@1 275 }
Chris@1 276
Chris@1 277 for (int j = m_fftsize*2 - m_increment; j < m_fftsize*2; ++j) {
Chris@1 278 m_outacc[j] = 0.f;
Chris@1 279 }
Chris@1 280
Chris@1 281 m_fill -= m_increment;
Chris@1 282 m_read -= m_increment;
Chris@1 283 }
Chris@1 284
Chris@1 285 m_buffer[m_fill++] = m_input[ii++];
Chris@1 286 }
Chris@1 287 }
Chris@1 288
Chris@1 289 void
Chris@1 290 Devuvuzelator::processFrame()
Chris@1 291 {
Chris@1 292 double *frame = (double *)alloca(m_fftsize * sizeof(double));
Chris@1 293 int ix = m_fftsize/2;
Chris@1 294 for (int i = 0; i < m_fftsize; ++i) {
Chris@1 295 frame[ix++] = m_buffer[i] * m_window[i];
Chris@1 296 if (ix == m_fftsize) ix = 0;
Chris@1 297 }
Chris@1 298
Chris@1 299 fft(m_fftsize, false, frame, 0, m_real, m_imag);
Chris@1 300
Chris@1 301 processSpectralFrame();
Chris@1 302
Chris@1 303 for (int i = 0; i < m_fftsize/2-1; ++i) {
Chris@1 304 m_real[m_fftsize-i] = m_real[i+1];
Chris@1 305 m_imag[m_fftsize-i] = -m_imag[i+1];
Chris@1 306 }
Chris@1 307
Chris@1 308 double *spare = (double *)alloca(m_fftsize * sizeof(double));
Chris@1 309 fft(m_fftsize, true, m_real, m_imag, frame, spare);
Chris@1 310
Chris@1 311 ix = m_fftsize/2;
Chris@1 312 for (int i = 0; i < m_fftsize; ++i) {
Chris@1 313 m_outacc[m_fftsize + i] += frame[ix++] * m_window[i];
Chris@1 314 if (ix == m_fftsize) ix = 0;
Chris@1 315 }
Chris@1 316 }
Chris@1 317
Chris@1 318 // FFT implementation by Don Cross, public domain.
Chris@1 319 // This version scales the forward transform.
Chris@1 320
Chris@1 321 void Devuvuzelator::fft(unsigned int n, bool inverse,
Chris@1 322 double *ri, double *ii, double *ro, double *io)
Chris@1 323 {
Chris@1 324 if (!ri || !ro || !io) return;
Chris@1 325
Chris@1 326 unsigned int bits;
Chris@1 327 unsigned int i, j, k, m;
Chris@1 328 unsigned int blockSize, blockEnd;
Chris@1 329
Chris@1 330 double tr, ti;
Chris@1 331
Chris@1 332 if (n < 2) return;
Chris@1 333 if (n & (n-1)) return;
Chris@1 334
Chris@1 335 double angle = 2.0 * M_PI;
Chris@1 336 if (inverse) angle = -angle;
Chris@1 337
Chris@1 338 for (i = 0; ; ++i) {
Chris@1 339 if (n & (1 << i)) {
Chris@1 340 bits = i;
Chris@1 341 break;
Chris@1 342 }
Chris@1 343 }
Chris@1 344
Chris@1 345 static unsigned int tableSize = 0;
Chris@1 346 static int *table = 0;
Chris@1 347
Chris@1 348 if (tableSize != n) {
Chris@1 349
Chris@1 350 delete[] table;
Chris@1 351
Chris@1 352 table = new int[n];
Chris@1 353
Chris@1 354 for (i = 0; i < n; ++i) {
Chris@1 355
Chris@1 356 m = i;
Chris@1 357
Chris@1 358 for (j = k = 0; j < bits; ++j) {
Chris@1 359 k = (k << 1) | (m & 1);
Chris@1 360 m >>= 1;
Chris@1 361 }
Chris@1 362
Chris@1 363 table[i] = k;
Chris@1 364 }
Chris@1 365
Chris@1 366 tableSize = n;
Chris@1 367 }
Chris@1 368
Chris@1 369 if (ii) {
Chris@1 370 for (i = 0; i < n; ++i) {
Chris@1 371 ro[table[i]] = ri[i];
Chris@1 372 io[table[i]] = ii[i];
Chris@1 373 }
Chris@1 374 } else {
Chris@1 375 for (i = 0; i < n; ++i) {
Chris@1 376 ro[table[i]] = ri[i];
Chris@1 377 io[table[i]] = 0.0;
Chris@1 378 }
Chris@1 379 }
Chris@1 380
Chris@1 381 blockEnd = 1;
Chris@1 382
Chris@1 383 for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
Chris@1 384
Chris@1 385 double delta = angle / (double)blockSize;
Chris@1 386 double sm2 = -sin(-2 * delta);
Chris@1 387 double sm1 = -sin(-delta);
Chris@1 388 double cm2 = cos(-2 * delta);
Chris@1 389 double cm1 = cos(-delta);
Chris@1 390 double w = 2 * cm1;
Chris@1 391 double ar[3], ai[3];
Chris@1 392
Chris@1 393 for (i = 0; i < n; i += blockSize) {
Chris@1 394
Chris@1 395 ar[2] = cm2;
Chris@1 396 ar[1] = cm1;
Chris@1 397
Chris@1 398 ai[2] = sm2;
Chris@1 399 ai[1] = sm1;
Chris@1 400
Chris@1 401 for (j = i, m = 0; m < blockEnd; j++, m++) {
Chris@1 402
Chris@1 403 ar[0] = w * ar[1] - ar[2];
Chris@1 404 ar[2] = ar[1];
Chris@1 405 ar[1] = ar[0];
Chris@1 406
Chris@1 407 ai[0] = w * ai[1] - ai[2];
Chris@1 408 ai[2] = ai[1];
Chris@1 409 ai[1] = ai[0];
Chris@1 410
Chris@1 411 k = j + blockEnd;
Chris@1 412 tr = ar[0] * ro[k] - ai[0] * io[k];
Chris@1 413 ti = ar[0] * io[k] + ai[0] * ro[k];
Chris@1 414
Chris@1 415 ro[k] = ro[j] - tr;
Chris@1 416 io[k] = io[j] - ti;
Chris@1 417
Chris@1 418 ro[j] += tr;
Chris@1 419 io[j] += ti;
Chris@1 420 }
Chris@1 421 }
Chris@1 422
Chris@1 423 blockEnd = blockSize;
Chris@1 424 }
Chris@1 425
Chris@1 426 if (!inverse) {
Chris@1 427
Chris@1 428 double denom = (double)n;
Chris@1 429
Chris@1 430 for (i = 0; i < n; i++) {
Chris@1 431 ro[i] /= denom;
Chris@1 432 io[i] /= denom;
Chris@1 433 }
Chris@1 434 }
Chris@1 435 }
Chris@1 436
Chris@1 437 const LADSPA_Descriptor *
Chris@1 438 ladspa_descriptor(unsigned long ix)
Chris@1 439 {
Chris@1 440 return Devuvuzelator::getDescriptor(ix);
Chris@1 441 }
Chris@1 442
Chris@1 443 #include "devuvuzelator.cpp"
Chris@1 444