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