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);
+}
+