view host/Processor.cpp @ 0:a6020bf991cd

* Initial import of what may or may not become a simple live visual-response host for causal Vamp plugins
author cannam
date Thu, 19 Oct 2006 16:53:48 +0000
parents
children 4342352b8ef3
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */

#include "Processor.h"

#include "plugin/FeatureExtractionPluginFactory.h"

using std::cout;
using std::cerr;
using std::endl;

void printFeatures(int, int, int, Vamp::Plugin::FeatureSet &);
void transformInput(float *, size_t);
void fft(unsigned int, bool, double *, double *, double *, double *);

Processor::Processor(BufferingAudioCallbackRecordTarget *audioRecordTarget) :
    m_exiting(false),
    m_audioRecordTarget(audioRecordTarget),
    m_nextNumber(1),
    m_pluginBlockSize(0)
{
}

Processor::~Processor()
{
    m_exiting = true;
    wait();
}

void
Processor::run()
{
    while (!m_exiting) {

        size_t bs = m_pluginBlockSize;
        if (bs == 0) {
            msleep(100);
            continue;
        }

        size_t nframes = m_audioRecordTarget->samplesReady();

        cout << "Have " << nframes << endl;

        if (nframes > bs) {

            runPlugins();
            
            nframes = m_audioRecordTarget->samplesReady();

            if (nframes > 0) msleep(10);
            else msleep(50);

        } else {
            msleep(50);
        }
    }
}

void
Processor::runPlugins()
{
    QMutexLocker locker(&m_mutex);

    static size_t frame = 0;

    size_t bs = m_pluginBlockSize;

    size_t sr = m_audioRecordTarget->getSourceSampleRate();

    size_t ch = m_audioRecordTarget->getChannelCount();
    float **buffers = new float *[ch];
    for (size_t c = 0; c < ch; ++c) {
        buffers[c] = new float[bs];
        if (m_audioRecordTarget->getSamples(c, bs, buffers[c]) < bs) {
            cerr << "ERROR: not enough samples from record in" << endl;
        }
    }
    
    // run one plugin for now -- question -- can the plugin modify its input buffers?

    PluginMap::iterator i = m_plugins.begin();

    if (i != m_plugins.end()) {
        
        for (size_t c = 0; c < ch; ++c) {
            if (i->second->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
                transformInput(buffers[c], bs);
            }
        }

        Vamp::Plugin::FeatureSet fs =
            i->second->process(buffers, Vamp::RealTime::frame2RealTime(frame, sr));

        for (Vamp::Plugin::FeatureSet::iterator j = fs.begin();
             j != fs.end(); ++j) {
            printFeatures(frame, sr, j->first, fs);
        }
    }

    frame += bs;

    for (size_t c = 0; c < ch; ++c) {
        delete[] buffers[c];
    }
    delete[] buffers;
}
    
int
Processor::addPlugin(QString pluginId)
{
    QMutexLocker locker(&m_mutex);

    size_t sr = m_audioRecordTarget->getSourceSampleRate();

    if (!sr) {
        cerr << "ERROR: Processor::addPlugin: Source sample rate is not defined" << endl;
        return 0;
    }

    FeatureExtractionPluginFactory *factory = 
        FeatureExtractionPluginFactory::instanceFor(pluginId);

    if (!factory) {
        cerr << "ERROR: Processor::addPlugin: No factory for plugin \"" << pluginId.toStdString() << "\"" << endl;
        return 0;
    }

    Vamp::Plugin *plugin = factory->instantiatePlugin(pluginId, sr);

    if (!plugin) {
        cerr << "ERROR: Processor::addPlugin: Failed to instantiate plugin \"" << pluginId.toStdString() << "\"" << endl;
        return 0;
    }

    size_t bs = plugin->getPreferredBlockSize();

    if (m_pluginBlockSize != 0) bs = m_pluginBlockSize;
    
    if (bs == 0) bs = m_audioRecordTarget->getSourceBlockSize();

    size_t step = bs; //!!! fix -- separate peek and advance in bufferer

    int ch = m_audioRecordTarget->getChannelCount();
    if (ch > plugin->getMaxChannelCount()) ch = plugin->getMaxChannelCount();

    if (!plugin->initialise(ch, bs, step)) {
        cerr << "ERROR: Processor::addPlugin: Initialisation failed with step size " << step << ", block size " << bs << ", channels " << ch << endl;
        delete plugin;
        return 0;
    }

    m_pluginBlockSize = bs;

    int number = m_nextNumber++;

    m_plugins[number] = plugin;
    return number;
}

void
Processor::removePlugin(int number)
{
    QMutexLocker locker(&m_mutex);

    if (m_plugins.find(number) == m_plugins.end()) {
        cerr << "ERROR: Processor::removePlugin: No such plugin number "
                  << number << endl;
        return;
    }
    
    Vamp::Plugin *plugin = m_plugins[number];
    m_plugins.erase(number);
    delete plugin;
}


void
printFeatures(int frame, int sr, int output, Vamp::Plugin::FeatureSet &features)
{
    for (unsigned int i = 0; i < features[output].size(); ++i) {
        Vamp::RealTime rt = Vamp::RealTime::frame2RealTime(frame, sr);
        if (features[output][i].hasTimestamp) {
            rt = features[output][i].timestamp;
        }
        cout << rt.toString() << ":";
        for (unsigned int j = 0; j < features[output][i].values.size(); ++j) {
            cout << " " << features[output][i].values[j];
        }
        cout << endl;
    }
}
        
void
transformInput(float *buffer, size_t size)
{
    double *inbuf = new double[size * 2];
    double *outbuf = new double[size * 2];

    // Copy across with Hanning window
    for (size_t i = 0; i < size; ++i) {
        inbuf[i] = double(buffer[i]) * (0.50 - 0.50 * cos(2 * M_PI * i / size));
        inbuf[i + size] = 0.0;
    }
    
    for (size_t i = 0; i < size/2; ++i) {
        double temp = inbuf[i];
        inbuf[i] = inbuf[i + size/2];
        inbuf[i + size/2] = temp;
    }

    fft(size, false, inbuf, inbuf + size, outbuf, outbuf + size);

    for (size_t i = 0; i < size/2; ++i) {
        buffer[i * 2] = outbuf[i];
        buffer[i * 2 + 1] = outbuf[i + size];
    }
    
    delete inbuf;
    delete outbuf;
}

void
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;
	}
    }
}