Chris@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: #include Chris@0: #include Chris@0: #include Chris@0: Chris@0: #include Chris@0: Chris@0: #define FFTSIZE 1024 Chris@0: Chris@0: class Devuvuzelator : public AudioEffect Chris@0: { Chris@0: enum { Chris@0: LowParam = 0, Chris@0: HighParam = 1, Chris@0: FundamentalParam = 2, Chris@0: BandwidthParam = 3, Chris@0: HarmonicsParam = 4, Chris@0: ReductionParam = 5, Chris@0: NumParams = 6 Chris@0: }; Chris@0: Chris@0: public: Chris@0: Devuvuzelator(audioMasterCallback cb); Chris@0: ~Devuvuzelator(); Chris@0: Chris@0: virtual void getEffectName(char *n) { Chris@0: vst_strncpy(n, "Devuvuzelator", kVstMaxEffectNameLen); Chris@0: } Chris@0: virtual void getProductString(char *n) { Chris@0: vst_strncpy(n, "Devuvuzelator", kVstMaxProductStrLen); Chris@0: } Chris@0: virtual void getVendorString(char *n) { Chris@0: vst_strncpy(n, "Queen Mary, University of London", kVstMaxVendorStrLen); Chris@0: } Chris@0: Chris@0: virtual void setParameter(VstInt32 index, float value); Chris@0: virtual float getParameter(VstInt32 index); Chris@0: virtual void getParameterLabel(VstInt32 index, char* label); Chris@0: virtual void getParameterDisplay(VstInt32 index, char* text); Chris@0: virtual void getParameterName(VstInt32 index, char* text); Chris@0: Chris@0: virtual void setSampleRate (float sampleRate) { Chris@0: m_sampleRate = sampleRate; Chris@0: AudioEffect::setSampleRate(sampleRate); Chris@0: } Chris@0: Chris@0: virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames) { Chris@0: m_input = inputs[0]; Chris@0: m_output = outputs[0]; Chris@0: runImpl(sampleFrames); Chris@0: } Chris@0: Chris@0: void reset(); Chris@0: void window(float *); Chris@0: void runImpl(unsigned long); Chris@0: void processFrame(); Chris@0: void processSpectralFrame(); Chris@0: Chris@0: static void fft(unsigned int n, bool inverse, Chris@0: double *ri, double *ii, double *ro, double *io); Chris@0: Chris@0: int m_sampleRate; Chris@0: float *m_input; Chris@0: float *m_output; Chris@0: Chris@1: float m_low; Chris@0: float m_high; Chris@0: float m_fundamental; Chris@0: float m_bandwidth; Chris@0: float m_harmonics; Chris@0: float m_reduction; Chris@0: Chris@0: const int m_fftsize; Chris@0: const int m_increment; Chris@0: int m_fill; Chris@0: int m_read; Chris@0: float *m_buffer; Chris@0: float *m_outacc; Chris@0: double *m_real; Chris@0: double *m_imag; Chris@0: double *m_window; Chris@0: }; Chris@0: Chris@1: // VST params 0->1 Chris@1: Chris@0: void Chris@0: Devuvuzelator::setParameter(VstInt32 index, float value) Chris@0: { Chris@1: switch (index) { Chris@1: case 0: m_low = -80 + 80 * value; break; Chris@1: case 1: m_high = -80 + 80 * value; break; Chris@1: case 2: m_fundamental = 110 + 440 * value; break; Chris@1: case 3: m_bandwidth = 20 + 80 * value; break; Chris@1: case 4: m_harmonics = int(value * 6 + 0.5); break; Chris@1: case 5: m_reduction = 20 * value; break; Chris@1: } Chris@0: } Chris@0: Chris@0: float Chris@0: Devuvuzelator::getParameter(VstInt32 index) Chris@0: { Chris@1: switch (index) { Chris@1: case 0: return (m_low + 80) / 80; Chris@1: case 1: return (m_high + 80) / 80; Chris@1: case 2: return (m_fundamental - 110) / 440; Chris@1: case 3: return (m_bandwidth - 20) / 80; Chris@1: case 4: return (m_harmonics / 6.0); Chris@1: case 5: return m_reduction / 20; Chris@1: } Chris@0: } Chris@0: Chris@0: // NB! The max name length for VST parameter names, labels Chris@0: // (i.e. units) and display values (i.e. string renderings of current Chris@0: // value) is a rather amazing 8 bytes Chris@0: Chris@0: void Chris@0: Devuvuzelator::getParameterLabel(VstInt32 index, char *label) Chris@0: { Chris@0: const char *units[NumParams] = { Chris@0: "dB", Chris@0: "dB", Chris@0: "Hz", Chris@0: "Hz", Chris@0: "", Chris@0: "dB", Chris@0: }; Chris@0: Chris@0: vst_strncpy(label, units[index], kVstMaxParamStrLen); Chris@0: } Chris@0: Chris@0: void Chris@0: Devuvuzelator::getParameterDisplay(VstInt32 index, char *label) Chris@0: { Chris@1: float *params[NumParams] = { Chris@1: m_low, Chris@1: m_high, Chris@1: m_fundamental, Chris@1: m_bandwidth, Chris@1: m_harmonics, Chris@1: m_reduction, Chris@1: }; Chris@1: Chris@1: snprintf(label, kVstMaxParamStrLen, "%f", *params[index]); Chris@0: } Chris@0: Chris@0: void Chris@0: Devuvuzelator::getParameterName(VstInt32 index, char *label) Chris@0: { Chris@0: const char *names[NumParams] = { Chris@0: "Floor", Chris@0: "Ceiling", Chris@0: "Pitch", Chris@0: "B/W", Chris@0: "Partials", Chris@0: "Reductn", Chris@0: }; Chris@0: Chris@0: vst_strncpy(label, names[index], kVstMaxParamStrLen); Chris@0: } Chris@0: Chris@0: Devuvuzelator::Devuvuzelator(audioMasterCallback cb) : Chris@0: AudioEffect(cb, 0, NumParams), Chris@0: m_sampleRate(0), Chris@0: m_input(0), Chris@0: m_output(0), Chris@0: m_low(0), Chris@0: m_high(0), Chris@0: m_fftsize(FFTSIZE), Chris@0: m_increment(m_fftsize/4), Chris@0: m_fill(0), Chris@0: m_read(0) Chris@0: { Chris@0: m_buffer = new float[m_fftsize]; Chris@0: m_outacc = new float[m_fftsize * 2]; Chris@0: m_real = new double[m_fftsize]; Chris@0: m_imag = new double[m_fftsize]; Chris@0: m_window = new double[m_fftsize]; Chris@0: Chris@0: for (int i = 0; i < m_fftsize; ++i) { Chris@0: m_window[i] = 0.5 - 0.5 * cos(2 * M_PI * i / m_fftsize); Chris@0: } Chris@0: Chris@0: m_low = -40; Chris@0: m_high = -20; Chris@0: m_fundamental = 220; Chris@0: m_bandwidth = 60; Chris@0: m_harmonics = 3; Chris@0: m_reduction = 10; Chris@0: Chris@0: setUniqueID("qmvz"); Chris@0: setNumInputs(1); Chris@0: setNumOutputs(1); Chris@0: canProcessReplacing(true); Chris@0: canDoubleReplacing(false); Chris@0: Chris@0: reset(); Chris@0: } Chris@0: Chris@0: Devuvuzelator::~Devuvuzelator() Chris@0: { Chris@0: delete[] m_buffer; Chris@0: delete[] m_outacc; Chris@0: delete[] m_real; Chris@0: delete[] m_imag; Chris@0: delete[] m_window; Chris@0: } Chris@0: Chris@0: void Chris@0: Devuvuzelator::reset() Chris@0: { Chris@0: for (int i = 0; i < m_fftsize; ++i) { Chris@0: m_buffer[i] = 0.f; Chris@0: } Chris@0: for (int i = 0; i < m_fftsize*2; ++i) { Chris@0: m_outacc[i] = 0.f; Chris@0: } Chris@0: m_fill = 0; Chris@0: m_read = 0; Chris@0: } Chris@0: Chris@0: void Chris@0: Devuvuzelator::runImpl(unsigned long sampleCount) Chris@0: { Chris@0: if (!m_input || !m_output) return; Chris@0: Chris@0: int ii = 0; Chris@0: int oi = 0; Chris@0: Chris@0: while (ii < sampleCount) { Chris@0: Chris@0: m_output[oi++] = m_outacc[m_read++] / 1.5f; Chris@0: Chris@0: if (m_fill == m_fftsize) { Chris@0: Chris@0: processFrame(); Chris@0: Chris@0: for (int j = m_increment; j < m_fftsize; ++j) { Chris@0: m_buffer[j - m_increment] = m_buffer[j]; Chris@0: } Chris@0: Chris@0: for (int j = m_increment; j < m_fftsize*2; ++j) { Chris@0: m_outacc[j - m_increment] = m_outacc[j]; Chris@0: } Chris@0: Chris@0: for (int j = m_fftsize*2 - m_increment; j < m_fftsize*2; ++j) { Chris@0: m_outacc[j] = 0.f; Chris@0: } Chris@0: Chris@0: m_fill -= m_increment; Chris@0: m_read -= m_increment; Chris@0: } Chris@0: Chris@0: m_buffer[m_fill++] = m_input[ii++]; Chris@0: } Chris@0: } Chris@0: Chris@0: void Chris@0: Devuvuzelator::processFrame() Chris@0: { Chris@0: double *frame = (double *)alloca(m_fftsize * sizeof(double)); Chris@0: int ix = m_fftsize/2; Chris@0: for (int i = 0; i < m_fftsize; ++i) { Chris@0: frame[ix++] = m_buffer[i] * m_window[i]; Chris@0: if (ix == m_fftsize) ix = 0; Chris@0: } Chris@0: Chris@0: fft(m_fftsize, false, frame, 0, m_real, m_imag); Chris@0: Chris@0: processSpectralFrame(); Chris@0: Chris@0: for (int i = 0; i < m_fftsize/2-1; ++i) { Chris@0: m_real[m_fftsize-i] = m_real[i+1]; Chris@0: m_imag[m_fftsize-i] = -m_imag[i+1]; Chris@0: } Chris@0: Chris@0: double *spare = (double *)alloca(m_fftsize * sizeof(double)); Chris@0: fft(m_fftsize, true, m_real, m_imag, frame, spare); Chris@0: Chris@0: ix = m_fftsize/2; Chris@0: for (int i = 0; i < m_fftsize; ++i) { Chris@0: m_outacc[m_fftsize + i] += frame[ix++] * m_window[i]; Chris@0: if (ix == m_fftsize) ix = 0; Chris@0: } Chris@0: } Chris@0: Chris@0: // FFT implementation by Don Cross, public domain. Chris@0: // This version scales the forward transform. Chris@0: Chris@0: void Devuvuzelator::fft(unsigned int n, bool inverse, Chris@0: double *ri, double *ii, double *ro, double *io) Chris@0: { Chris@0: if (!ri || !ro || !io) return; Chris@0: Chris@0: unsigned int bits; Chris@0: unsigned int i, j, k, m; Chris@0: unsigned int blockSize, blockEnd; Chris@0: Chris@0: double tr, ti; Chris@0: Chris@0: if (n < 2) return; Chris@0: if (n & (n-1)) return; Chris@0: Chris@0: double angle = 2.0 * M_PI; Chris@0: if (inverse) angle = -angle; Chris@0: Chris@0: for (i = 0; ; ++i) { Chris@0: if (n & (1 << i)) { Chris@0: bits = i; Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: static unsigned int tableSize = 0; Chris@0: static int *table = 0; Chris@0: Chris@0: if (tableSize != n) { Chris@0: Chris@0: delete[] table; Chris@0: Chris@0: table = new int[n]; Chris@0: Chris@0: for (i = 0; i < n; ++i) { Chris@0: Chris@0: m = i; Chris@0: Chris@0: for (j = k = 0; j < bits; ++j) { Chris@0: k = (k << 1) | (m & 1); Chris@0: m >>= 1; Chris@0: } Chris@0: Chris@0: table[i] = k; Chris@0: } Chris@0: Chris@0: tableSize = n; Chris@0: } Chris@0: Chris@0: if (ii) { Chris@0: for (i = 0; i < n; ++i) { Chris@0: ro[table[i]] = ri[i]; Chris@0: io[table[i]] = ii[i]; Chris@0: } Chris@0: } else { Chris@0: for (i = 0; i < n; ++i) { Chris@0: ro[table[i]] = ri[i]; Chris@0: io[table[i]] = 0.0; Chris@0: } Chris@0: } Chris@0: Chris@0: blockEnd = 1; Chris@0: Chris@0: for (blockSize = 2; blockSize <= n; blockSize <<= 1) { Chris@0: Chris@0: double delta = angle / (double)blockSize; Chris@0: double sm2 = -sin(-2 * delta); Chris@0: double sm1 = -sin(-delta); Chris@0: double cm2 = cos(-2 * delta); Chris@0: double cm1 = cos(-delta); Chris@0: double w = 2 * cm1; Chris@0: double ar[3], ai[3]; Chris@0: Chris@0: for (i = 0; i < n; i += blockSize) { Chris@0: Chris@0: ar[2] = cm2; Chris@0: ar[1] = cm1; Chris@0: Chris@0: ai[2] = sm2; Chris@0: ai[1] = sm1; Chris@0: Chris@0: for (j = i, m = 0; m < blockEnd; j++, m++) { Chris@0: Chris@0: ar[0] = w * ar[1] - ar[2]; Chris@0: ar[2] = ar[1]; Chris@0: ar[1] = ar[0]; Chris@0: Chris@0: ai[0] = w * ai[1] - ai[2]; Chris@0: ai[2] = ai[1]; Chris@0: ai[1] = ai[0]; Chris@0: Chris@0: k = j + blockEnd; Chris@0: tr = ar[0] * ro[k] - ai[0] * io[k]; Chris@0: ti = ar[0] * io[k] + ai[0] * ro[k]; Chris@0: Chris@0: ro[k] = ro[j] - tr; Chris@0: io[k] = io[j] - ti; Chris@0: Chris@0: ro[j] += tr; Chris@0: io[j] += ti; Chris@0: } Chris@0: } Chris@0: Chris@0: blockEnd = blockSize; Chris@0: } Chris@0: Chris@0: if (!inverse) { Chris@0: Chris@0: double denom = (double)n; Chris@0: Chris@0: for (i = 0; i < n; i++) { Chris@0: ro[i] /= denom; Chris@0: io[i] /= denom; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: AudioEffect *createEffectInstance(audioMasterCallback audioMaster) Chris@0: { Chris@0: return new Devuvuzelator(audioMaster); Chris@0: } Chris@0: Chris@1: #include "devuvuzelator.cpp" Chris@1: