Mercurial > hg > devuvuzelator
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devuvuzelator-vst.cpp Thu Jun 10 21:39:32 2010 +0100 @@ -0,0 +1,468 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#include <alloca.h> +#include <iostream> +#include <cmath> + +#include <vst2.x/audioeffect.h> + +#define FFTSIZE 1024 + +class Devuvuzelator : public AudioEffect +{ + enum { + LowParam = 0, + HighParam = 1, + FundamentalParam = 2, + BandwidthParam = 3, + HarmonicsParam = 4, + ReductionParam = 5, + NumParams = 6 + }; + +public: + Devuvuzelator(audioMasterCallback cb); + ~Devuvuzelator(); + + virtual void getEffectName(char *n) { + vst_strncpy(n, "Devuvuzelator", kVstMaxEffectNameLen); + } + virtual void getProductString(char *n) { + vst_strncpy(n, "Devuvuzelator", kVstMaxProductStrLen); + } + virtual void getVendorString(char *n) { + vst_strncpy(n, "Queen Mary, University of London", kVstMaxVendorStrLen); + } + + virtual void setParameter(VstInt32 index, float value); + virtual float getParameter(VstInt32 index); + virtual void getParameterLabel(VstInt32 index, char* label); + virtual void getParameterDisplay(VstInt32 index, char* text); + virtual void getParameterName(VstInt32 index, char* text); + + virtual void setSampleRate (float sampleRate) { + m_sampleRate = sampleRate; + AudioEffect::setSampleRate(sampleRate); + } + + virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames) { + m_input = inputs[0]; + m_output = outputs[0]; + runImpl(sampleFrames); + } + + 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_high; + float m_low; + 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; +}; + +void +Devuvuzelator::setParameter(VstInt32 index, float value) +{ + float *params[NumParams] = { + m_low, + m_high, + m_fundamental, + m_bandwidth, + m_harmonics, + m_reduction, + }; + + *params[index] = value; +} + +float +Devuvuzelator::getParameter(VstInt32 index) +{ + float *params[NumParams] = { + m_low, + m_high, + m_fundamental, + m_bandwidth, + m_harmonics, + m_reduction, + }; + + return *params[index]; +} + +// NB! The max name length for VST parameter names, labels +// (i.e. units) and display values (i.e. string renderings of current +// value) is a rather amazing 8 bytes + +void +Devuvuzelator::getParameterLabel(VstInt32 index, char *label) +{ + const char *units[NumParams] = { + "dB", + "dB", + "Hz", + "Hz", + "", + "dB", + }; + + vst_strncpy(label, units[index], kVstMaxParamStrLen); +} + +void +Devuvuzelator::getParameterDisplay(VstInt32 index, char *label) +{ + snprintf(label, kVstMaxParamStrLen, "%f", getParameter(index)); +} + +void +Devuvuzelator::getParameterName(VstInt32 index, char *label) +{ + const char *names[NumParams] = { + "Floor", + "Ceiling", + "Pitch", + "B/W", + "Partials", + "Reductn", + }; + + vst_strncpy(label, names[index], kVstMaxParamStrLen); +} + +Devuvuzelator::Devuvuzelator(audioMasterCallback cb) : + AudioEffect(cb, 0, NumParams), + m_sampleRate(0), + 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); + } + + m_low = -40; + m_high = -20; + m_fundamental = 220; + m_bandwidth = 60; + m_harmonics = 3; + m_reduction = 10; + + setUniqueID("qmvz"); + setNumInputs(1); + setNumOutputs(1); + canProcessReplacing(true); + canDoubleReplacing(false); + + reset(); +} + +Devuvuzelator::~Devuvuzelator() +{ + delete[] m_buffer; + delete[] m_outacc; + delete[] m_real; + delete[] m_imag; + delete[] m_window; +} + +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; + + while (ii < sampleCount) { + + 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] = m_real[i+1]; + m_imag[m_fftsize-i] = -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; + } +} + +void +Devuvuzelator::processSpectralFrame() +{ + const int hs = m_fftsize/2 + 1; + double *mags = (double *)alloca(hs * sizeof(double)); + double *ratios = (double *)alloca(hs * sizeof(double)); + for (int i = 0; i < hs; ++i) { + ratios[i] = 1.0; + mags[i] = sqrt(m_real[i] * m_real[i] + m_imag[i] * m_imag[i]); + } + + double low = -35; + double high = -20; + + if (m_low) low = *m_low; + if (m_high) high = *m_high; + + int harmonics = 3; + if (m_harmonics) harmonics = int(*m_harmonics + 0.5); + + double fun = 200; + if (m_fundamental) fun = *m_fundamental; + + double bw = 40; + if (m_bandwidth) bw = *m_bandwidth; + + double lowfun = fun - bw/2; + double highfun = fun + bw+2; + + double reduction = 10; + if (m_reduction) reduction = *m_reduction; + + for (int h = 0; h < harmonics; ++h) { + + double lowfreq = lowfun * (h+1); + double highfreq = highfun * (h+1); + + int lowbin = (m_fftsize * lowfreq) / m_sampleRate; + int highbin = (m_fftsize * highfreq) / m_sampleRate; + + for (int i = lowbin; i <= highbin; ++i) { + ratios[i] = 1.0; + double db = 10 * log10(mags[i]); + if (db > low && db < high) { + double r = reduction; + ratios[i] = pow(10, -r / 10); + } + } + } + + for (int i = 0; i < hs-1; ++i) { + if (ratios[i] == 1.0 && ratios[i+1] < 1.0) { + ratios[i] = (ratios[i+1] + 1) / 2; + } else if (ratios[i] < 1.0 && ratios[i+1] == 1.0) { + ratios[i+1] = (ratios[i] + 1) / 2; + ++i; + } + } + + for (int i = 0; i < hs; ++i) { + m_real[i] *= ratios[i]; + m_imag[i] *= ratios[i]; + } +} + +// 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; + } + } +} + +AudioEffect *createEffectInstance(audioMasterCallback audioMaster) +{ + return new Devuvuzelator(audioMaster); +} +