view devuvuzelator-vst.cpp @ 8:e15ebd222c63

* (Messy) fixes to VST (e.g. for in-place buffers)
author Chris Cannam
date Fri, 11 Jun 2010 20:20:20 +0100
parents 5adad2ca3188
children a1539d4e3b08
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

#define _USE_MATH_DEFINES

#include <iostream>
#include <cmath>
#include <cstdio>

#include "public.sdk/source/vst2.x/audioeffect.h"

#define snprintf _snprintf
#define alloca _alloca

#define FFTSIZE 1024
#define WINSIZE 1024

#include "median.h"

class Devuvuzelator : public AudioEffect
{
    enum {
        FundamentalParam = 0,
        BandwidthParam = 1,
        HarmonicsParam = 2,
        ReductionParam = 3,
        NumParams      = 4
    };

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

    float m_sampleRate;
    float *m_input;
    float *m_output;

    float m_fundamental;
    float m_bandwidth;
    int m_harmonics;
    float m_reduction;

    const int m_fftsize;
    const int m_winsize;
    const int m_increment;
    int m_fill;
    int m_read;
    float *m_buffer;
    float *m_outacc;
	double *m_frame;
	double *m_spare;
    double *m_real;
    double *m_imag;
    double *m_window;
    MedianFilter<double> **m_medians;
};

// VST params 0->1

void
Devuvuzelator::setParameter(VstInt32 index, float value)
{
    switch (index) {
    case 0: m_fundamental = 50 + 720 * value; break;
    case 1: m_bandwidth = 20 + 80 * value; break;
    case 2: m_harmonics = int(value * 6 + 0.5); break;
    case 3: m_reduction = 100 * value; break;
    }
}

float
Devuvuzelator::getParameter(VstInt32 index)
{
    switch (index) {
    case 0: return (m_fundamental - 50) / 720;
    case 1: return (m_bandwidth - 20) / 80;
    case 2: return (m_harmonics / 6.f);
    case 3: return m_reduction / 100;
    }
    return 0;
}

// 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] = {
        "Hz",
        "Hz",
        "",
        "%",
    };
    
    vst_strncpy(label, units[index], kVstMaxParamStrLen);
}

void
Devuvuzelator::getParameterDisplay(VstInt32 index, char *label)
{
    switch (index) {
    case 0: snprintf(label, kVstMaxParamStrLen, "%f", m_fundamental); break;
    case 1: snprintf(label, kVstMaxParamStrLen, "%f", m_bandwidth); break;
    case 2: snprintf(label, kVstMaxParamStrLen, "%d", m_harmonics); break;
    case 3: snprintf(label, kVstMaxParamStrLen, "%f", m_reduction); break;
    }
}

void
Devuvuzelator::getParameterName(VstInt32 index, char *label)
{
    const char *names[NumParams] = {
        "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_fftsize(FFTSIZE),
    m_winsize(WINSIZE),
    m_increment(m_winsize/2),
    m_fill(0),
    m_read(0)
{
    m_buffer = new float[m_winsize];
    m_outacc = new float[m_winsize * 2];
    m_window = new double[m_winsize];
	m_frame = new double[m_fftsize];
	m_spare = new double[m_fftsize];
    m_real = new double[m_fftsize];
    m_imag = new double[m_fftsize];
    m_medians = new MedianFilter<double> *[m_fftsize/2+1];

    for (int i = 0; i < m_winsize; ++i) {
        m_window[i] = 0.5 - 0.5 * cos(2 * M_PI * i / m_winsize);
    }
    for (int i = 0; i < m_fftsize/2+1; ++i) {
        m_medians[i] = 0;
    }

    m_fundamental = 230;
    m_bandwidth = 60;
    m_harmonics = 3;
    m_reduction = 30;
    
    setUniqueID('qmvz');
    setNumInputs(1);
    setNumOutputs(1);
    canProcessReplacing(true);
    canDoubleReplacing(false);

    reset();
}

Devuvuzelator::~Devuvuzelator()
{
    delete[] m_buffer;
    delete[] m_outacc;
	delete[] m_frame;
	delete[] m_spare;
    delete[] m_real;
    delete[] m_imag;
    delete[] m_window;
    for (int i = 0; i < m_fftsize/2+1; ++i) {
        delete m_medians[i];
    }
    delete[] m_medians;
}

void
Devuvuzelator::reset()
{
    for (int i = 0; i < m_winsize; ++i) {
        m_buffer[i] = 0.f;
    }
    for (int i = 0; i < m_winsize*2; ++i) {
        m_outacc[i] = 0.f;
    }
    m_fill = 0;
    m_read = 0;
    for (int i = 0; i < m_fftsize/2+1; ++i) {
        if (m_medians[i]) m_medians[i]->reset();
    }
}

void
Devuvuzelator::runImpl(unsigned long sampleCount)
{
    if (!m_input || !m_output) return;

    const int sc = sampleCount;
/*
	static FILE *blah = 0;
	if (!blah) {
		blah = fopen("d:\\devuvu-counts.txt", "w");
	}

	if (m_input == m_output) fprintf(blah, "in-place\n");
*/
	float *output = m_output;
	if (m_input == m_output) {
		output = (float *)alloca(sampleCount * sizeof(float));
	}
/*	
	float inmean = 0;
	for (int i = 0; i < sc; ++i) {
		inmean += m_input[i] * m_input[i];
		fprintf(blah, "i:%d:%f ", i, m_input[i]);
	}
	inmean/=sc;
	inmean = sqrt(inmean);

	fprintf(blah, "%d\n", (int)sampleCount);
	fflush(blah);
*/
	int oi = 0;
	for (int ii = 0; ii < sc; ++ii) {

		output[oi++] = m_outacc[m_read++];
//	m_output[oi++] = inmean * float(ii%100)/100;
//	m_read++;
        if (m_fill == m_winsize) {

            processFrame();
/*
	for (int i = 0; i < m_winsize; ++i) {
		float v = m_buffer[i];
		fprintf(blah, "%f ", v);
		m_outacc[m_winsize + i] = m_buffer[i];//m_input[i];//m_buffer[i] ;//* m_window[i];
	}
	fprintf(blah, "\n");

//			for (int j = 0; j < m_winsize; ++j) {
//				m_outacc[m_winsize+j] = inmean * float(j%100)/100;
//			}
*/

            for (int j = m_increment; j < m_winsize; ++j) {
                m_buffer[j - m_increment] = m_buffer[j];
            }

            for (int j = m_increment; j < m_winsize*2; ++j) {
                m_outacc[j - m_increment] = m_outacc[j];
            }

            for (int j = m_winsize*2 - m_increment; j < m_winsize*2; ++j) {
                m_outacc[j] = 0.f;
            }

            m_fill = m_fill - m_increment;
            m_read = m_read - m_increment;
        }
/*
		fprintf(blah, "%d:%f ", ii, m_input[ii]);
*/
		m_buffer[m_fill] = m_input[ii];
		++m_fill;
    }

	static int block = 0;
	for (int i = 0; i < sc; ++i) {
//		m_output[i] = float(block % 100) / 100;
//		m_output[i] = inmean * float(i % 100) / 100;
	}
	++block;

	if (m_input == m_output) {
		for (int i = 0; i < sc; ++i) m_output[i] = output[i];
	}
}

void
Devuvuzelator::processFrame()
{
/*	for (int i = 0; i < m_winsize; ++i) {
		m_outacc[m_winsize + i] += m_buffer[i] ;//* m_window[i];
	}
	return;
*/

    for (int i = 0; i < m_fftsize; ++i) {
        m_frame[i] = 0.0;
    }

    int ix = m_fftsize - m_winsize/2;
    while (ix < 0) ix += m_fftsize;
    for (int i = 0; i < m_winsize; ++i) {
        m_frame[ix++] += m_buffer[i] * m_window[i];
        if (ix == m_fftsize) ix = 0;
    }

    fft(m_fftsize, false, m_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];
    }

    fft(m_fftsize, true, m_real, m_imag, m_frame, m_spare);

    ix = m_fftsize - m_winsize/2;
    while (ix < 0) ix += m_fftsize;
    for (int i = 0; i < m_winsize; ++i) {
        m_outacc[m_winsize + i] += m_frame[ix++];
        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;
	}
    }
}

AudioEffect *createEffectInstance(audioMasterCallback audioMaster)
{
    return new Devuvuzelator(audioMaster);
}

#include "devuvuzelator.cpp"