Mercurial > hg > devuvuzelator
view devuvuzelator-ladspa.cpp @ 3:8b79175c9f02
* fix off-by-one error
author | Chris Cannam |
---|---|
date | Fri, 11 Jun 2010 11:45:41 +0100 |
parents | 0d2126c32309 |
children | d90abfa9585a |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ #include <ladspa.h> #include <alloca.h> #include <iostream> #include <cmath> #define FFTSIZE 1024 class Devuvuzelator { public: static const LADSPA_Descriptor *getDescriptor(unsigned long index); private: Devuvuzelator(int sampleRate); ~Devuvuzelator(); enum { InputPort = 0, OutputPort = 1, LowPort = 2, HighPort = 3, FundamentalPort = 4, BandwidthPort = 5, HarmonicsPort = 6, ReductionPort = 7, PortCount = 8, }; static const char *const portNames[PortCount]; static const LADSPA_PortDescriptor ports[PortCount]; static const LADSPA_PortRangeHint hints[PortCount]; static const LADSPA_Properties properties; static const LADSPA_Descriptor ladspaDescriptor; static LADSPA_Handle instantiate(const LADSPA_Descriptor *, unsigned long); static void connectPort(LADSPA_Handle, unsigned long, LADSPA_Data *); static void activate(LADSPA_Handle); static void run(LADSPA_Handle, unsigned long); static void deactivate(LADSPA_Handle); static void cleanup(LADSPA_Handle); void reset(); void window(float *); void runImpl(unsigned long); void processFrame(); void processSpectralFrame(); static void fft(unsigned int n, bool inverse, double *ri, double *ii, double *ro, double *io); int m_sampleRate; float *m_input; float *m_output; float *m_low; float *m_high; float *m_fundamental; float *m_bandwidth; float *m_harmonics; float *m_reduction; const int m_fftsize; const int m_increment; int m_fill; int m_read; float *m_buffer; float *m_outacc; double *m_real; double *m_imag; double *m_window; }; const char *const Devuvuzelator::portNames[PortCount] = { "Input", "Output", "Low threshold (dB)", "High threshold (dB)", "Fundamental frequency (Hz)", "Bandwidth of fundamental (Hz)", "Number of partials", "Reduction (dB)", }; const LADSPA_PortDescriptor Devuvuzelator::ports[PortCount] = { LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, }; const LADSPA_PortRangeHint Devuvuzelator::hints[PortCount] = { { 0, 0, 0 }, { 0, 0, 0 }, { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, -80, 0 }, { LADSPA_HINT_DEFAULT_HIGH | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, -80, 0 }, { LADSPA_HINT_DEFAULT_LOW | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 110, 550 }, { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 20, 100 }, { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_INTEGER | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 6 }, { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 20 }, }; const LADSPA_Properties Devuvuzelator::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE; const LADSPA_Descriptor Devuvuzelator::ladspaDescriptor = { 0, // "Unique" ID "devuvuzelator", // Label properties, "Devuvuzelator", // Name "Queen Mary, University of London", // Maker "All Rights Reserved", // Copyright PortCount, ports, portNames, hints, 0, // Implementation data instantiate, connectPort, activate, run, 0, // Run adding 0, // Set run adding gain deactivate, cleanup }; const LADSPA_Descriptor * Devuvuzelator::getDescriptor(unsigned long index) { if (index == 0) return &ladspaDescriptor; return 0; } Devuvuzelator::Devuvuzelator(int sampleRate) : m_sampleRate(sampleRate), m_input(0), m_output(0), m_low(0), m_high(0), m_fftsize(FFTSIZE), m_increment(m_fftsize/4), m_fill(0), m_read(0) { m_buffer = new float[m_fftsize]; m_outacc = new float[m_fftsize * 2]; m_real = new double[m_fftsize]; m_imag = new double[m_fftsize]; m_window = new double[m_fftsize]; for (int i = 0; i < m_fftsize; ++i) { m_window[i] = 0.5 - 0.5 * cos(2 * M_PI * i / m_fftsize); } reset(); } Devuvuzelator::~Devuvuzelator() { delete[] m_buffer; delete[] m_outacc; delete[] m_real; delete[] m_imag; delete[] m_window; } LADSPA_Handle Devuvuzelator::instantiate(const LADSPA_Descriptor *, unsigned long rate) { Devuvuzelator *devuvu = new Devuvuzelator(rate); return devuvu; } void Devuvuzelator::connectPort(LADSPA_Handle handle, unsigned long port, LADSPA_Data *location) { Devuvuzelator *devuvu = (Devuvuzelator *)handle; float **ports[PortCount] = { &devuvu->m_input, &devuvu->m_output, &devuvu->m_low, &devuvu->m_high, &devuvu->m_fundamental, &devuvu->m_bandwidth, &devuvu->m_harmonics, &devuvu->m_reduction, }; *ports[port] = (float *)location; } void Devuvuzelator::activate(LADSPA_Handle handle) { Devuvuzelator *devuvu = (Devuvuzelator *)handle; devuvu->reset(); } void Devuvuzelator::run(LADSPA_Handle handle, unsigned long samples) { Devuvuzelator *devuvu = (Devuvuzelator *)handle; devuvu->runImpl(samples); } void Devuvuzelator::deactivate(LADSPA_Handle handle) { activate(handle); // both functions just reset the plugin } void Devuvuzelator::cleanup(LADSPA_Handle handle) { delete (Devuvuzelator *)handle; } void Devuvuzelator::reset() { for (int i = 0; i < m_fftsize; ++i) { m_buffer[i] = 0.f; } for (int i = 0; i < m_fftsize*2; ++i) { m_outacc[i] = 0.f; } m_fill = 0; m_read = 0; } void Devuvuzelator::runImpl(unsigned long sampleCount) { if (!m_input || !m_output) return; int ii = 0; int oi = 0; const int sc = sampleCount; while (ii < sc) { m_output[oi++] = m_outacc[m_read++] / 1.5f; if (m_fill == m_fftsize) { processFrame(); for (int j = m_increment; j < m_fftsize; ++j) { m_buffer[j - m_increment] = m_buffer[j]; } for (int j = m_increment; j < m_fftsize*2; ++j) { m_outacc[j - m_increment] = m_outacc[j]; } for (int j = m_fftsize*2 - m_increment; j < m_fftsize*2; ++j) { m_outacc[j] = 0.f; } m_fill -= m_increment; m_read -= m_increment; } m_buffer[m_fill++] = m_input[ii++]; } } void Devuvuzelator::processFrame() { double *frame = (double *)alloca(m_fftsize * sizeof(double)); int ix = m_fftsize/2; for (int i = 0; i < m_fftsize; ++i) { frame[ix++] = m_buffer[i] * m_window[i]; if (ix == m_fftsize) ix = 0; } fft(m_fftsize, false, frame, 0, m_real, m_imag); processSpectralFrame(); for (int i = 0; i < m_fftsize/2-1; ++i) { m_real[m_fftsize-i-1] = m_real[i+1]; m_imag[m_fftsize-i-1] = -m_imag[i+1]; } double *spare = (double *)alloca(m_fftsize * sizeof(double)); fft(m_fftsize, true, m_real, m_imag, frame, spare); ix = m_fftsize/2; for (int i = 0; i < m_fftsize; ++i) { m_outacc[m_fftsize + i] += frame[ix++] * m_window[i]; if (ix == m_fftsize) ix = 0; } } // FFT implementation by Don Cross, public domain. // This version scales the forward transform. void Devuvuzelator::fft(unsigned int n, bool inverse, double *ri, double *ii, double *ro, double *io) { if (!ri || !ro || !io) return; unsigned int bits; unsigned int i, j, k, m; unsigned int blockSize, blockEnd; double tr, ti; if (n < 2) return; if (n & (n-1)) return; double angle = 2.0 * M_PI; if (inverse) angle = -angle; for (i = 0; ; ++i) { if (n & (1 << i)) { bits = i; break; } } static unsigned int tableSize = 0; static int *table = 0; if (tableSize != n) { delete[] table; table = new int[n]; for (i = 0; i < n; ++i) { m = i; for (j = k = 0; j < bits; ++j) { k = (k << 1) | (m & 1); m >>= 1; } table[i] = k; } tableSize = n; } if (ii) { for (i = 0; i < n; ++i) { ro[table[i]] = ri[i]; io[table[i]] = ii[i]; } } else { for (i = 0; i < n; ++i) { ro[table[i]] = ri[i]; io[table[i]] = 0.0; } } blockEnd = 1; for (blockSize = 2; blockSize <= n; blockSize <<= 1) { double delta = angle / (double)blockSize; double sm2 = -sin(-2 * delta); double sm1 = -sin(-delta); double cm2 = cos(-2 * delta); double cm1 = cos(-delta); double w = 2 * cm1; double ar[3], ai[3]; for (i = 0; i < n; i += blockSize) { ar[2] = cm2; ar[1] = cm1; ai[2] = sm2; ai[1] = sm1; for (j = i, m = 0; m < blockEnd; j++, m++) { ar[0] = w * ar[1] - ar[2]; ar[2] = ar[1]; ar[1] = ar[0]; ai[0] = w * ai[1] - ai[2]; ai[2] = ai[1]; ai[1] = ai[0]; k = j + blockEnd; tr = ar[0] * ro[k] - ai[0] * io[k]; ti = ar[0] * io[k] + ai[0] * ro[k]; ro[k] = ro[j] - tr; io[k] = io[j] - ti; ro[j] += tr; io[j] += ti; } } blockEnd = blockSize; } if (!inverse) { double denom = (double)n; for (i = 0; i < n; i++) { ro[i] /= denom; io[i] /= denom; } } } const LADSPA_Descriptor * ladspa_descriptor(unsigned long ix) { return Devuvuzelator::getDescriptor(ix); } #include "devuvuzelator.cpp"