view LowFreq.cpp @ 2:a84bae4ee627

Fill in most of the implementation
author Chris Cannam
date Fri, 07 Mar 2014 08:23:48 +0000
parents 411c5c28fc43
children 2fc53e089662
line wrap: on
line source

#include "LowFreq.h"

#include "dsp/rateconversion/Resampler.h"
#include "dsp/transforms/FFT.h"

#include <cmath>

using std::cerr;
using std::endl;
using std::vector;

static float minP = 0.01;
static float maxP = 10;
static float defaultP = 0.1;

static int minN = 1;
static int maxN = 16384;
static int defaultN = 64;

LowFreq::LowFreq(float inputSampleRate) :
    Plugin(inputSampleRate),
    m_p(defaultP),
    m_n(defaultN),
    m_blockSize(0),
    m_resampler(0),
    m_fft(0)
{
}

LowFreq::~LowFreq()
{
    delete m_resampler;
    delete m_fft;
}

string
LowFreq::getIdentifier() const
{
    return "lowfreq";
}

string
LowFreq::getName() const
{
    return "Low-frequency Spectrogram";
}

string
LowFreq::getDescription() const
{
    //!!! Return something helpful here!
    return "";
}

string
LowFreq::getMaker() const
{
    return "Queen Mary, University of London";
}

int
LowFreq::getPluginVersion() const
{
    return 1;
}

string
LowFreq::getCopyright() const
{
    return "GPL";
}

LowFreq::InputDomain
LowFreq::getInputDomain() const
{
    return TimeDomain;
}

size_t
LowFreq::getPreferredBlockSize() const
{
    return 1024;
}

size_t 
LowFreq::getPreferredStepSize() const
{
    return 1024;
}

size_t
LowFreq::getMinChannelCount() const
{
    return 1;
}

size_t
LowFreq::getMaxChannelCount() const
{
    return 1;
}

LowFreq::ParameterList
LowFreq::getParameterDescriptors() const
{
    ParameterList list;

    ParameterDescriptor d;
    d.identifier = "p";
    d.name = "Shortest Period";
    d.description = "Period in seconds of the highest-frequency component to include in the spectrogram. That is, 1/f where f is the highest frequency (in Hz) spanned by the spectrogram.";
    d.unit = "s";
    d.minValue = minP;
    d.maxValue = maxP;
    d.defaultValue = defaultP;
    d.isQuantized = false;
    list.push_back(d);

    d.identifier = "n";
    d.name = "Number of bins";
    d.description = "Number of spectrogram bins to calculate.";
    d.unit = "";
    d.minValue = minN;
    d.maxValue = maxN;
    d.defaultValue = defaultN;
    d.isQuantized = true;
    d.quantizeStep = 1;
    list.push_back(d);

    return list;
}

float
LowFreq::getParameter(string identifier) const
{
    if (identifier == "p") {
	return m_p;
    } else if (identifier == "n") {
	return m_n;
    }
    return 0;
}

void
LowFreq::setParameter(string identifier, float value) 
{
    if (identifier == "p") {
	m_p = value;
    } else if (identifier == "n") {
	m_n = int(value + 0.01);
    }
}

LowFreq::ProgramList
LowFreq::getPrograms() const
{
    ProgramList list;
    return list;
}

string
LowFreq::getCurrentProgram() const
{
    return ""; // no programs
}

void
LowFreq::selectProgram(string name)
{
}

LowFreq::OutputList
LowFreq::getOutputDescriptors() const
{
    OutputList list;

    OutputDescriptor d;
    d.identifier = "spectrogram";
    d.name = "Spectrogram";
    d.description = "";
    d.unit = "";
    d.hasFixedBinCount = true;
    d.binCount = m_n/2 + 1;
    d.hasKnownExtents = false;
    d.isQuantized = false;
    d.sampleType = OutputDescriptor::OneSamplePerStep;
    d.hasDuration = false;
    list.push_back(d);

    return list;
}

bool
LowFreq::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
    if (channels < getMinChannelCount() ||
	channels > getMaxChannelCount()) {
	cerr << "LowFreq::initialise: ERROR: channels " << channels
	     << " out of acceptable range " << getMinChannelCount()
	     << " -> " << getMaxChannelCount() << endl;
	return false;
    }

    if (stepSize != blockSize) {
	// We don't actually care what the block size is, but there
	// must be no overlap
	cerr << "LowFreq::initialise: ERROR: step size and block size must be "
	     << "equal (" << stepSize << " != " << blockSize << ")" << endl;
	return false;
    }

    if (m_n < minN || m_n > maxN) {
	cerr << "LowFreq::initialise: ERROR: bin count " << m_n
	     << " out of acceptable range " << minN
	     << " -> " << maxN << endl;
	return false;
    }

    if (m_p < minP || m_n > maxP) {
	cerr << "LowFreq::initialise: ERROR: shortest period " << m_p
	     << " out of acceptable range " << minP
	     << " -> " << maxP << endl;
	return false;
    }

    if (fabs(m_inputSampleRate - round(m_inputSampleRate)) > 1e-6) {
	cerr << "LowFreq::initialise: WARNING: input sample rate " 
	     << m_inputSampleRate << " is non-integral, output frequencies "
	     << "will be skewed by rounding it to nearest integer" << endl;
    }

    m_blockSize = blockSize;

    reset();

    return true;
}

void
LowFreq::reset()
{
    delete m_resampler;
    delete m_fft;

    // We have shortest period p and number of bins n.  The bin
    // frequency for the highest bin of a spectrogram is fs/2 where fs
    // is the sample rate of the signal. So we must downsample to a
    // sample rate of (1/p)*2 and then run an n-point FFT.

    int targetRate = int(ceil(2.0 / m_p));

    cerr << "LowFreq::reset: target rate for p = " << m_p
	 << " is " << targetRate << endl;

    m_resampler = new Resampler(int(round(m_inputSampleRate)), targetRate);
    m_fft = new FFT(m_n);
    m_buffer = std::vector<double>();
}

LowFreq::FeatureSet
LowFreq::process(const float *const *inputBuffers, Vamp::RealTime timestamp)
{
    double *data = new double[m_blockSize];
    for (int i = 0; i < m_blockSize; ++i) {
	data[i] = inputBuffers[0][i];
    }

    vector<double> resampled = m_resampler->process(data, m_blockSize);
    m_buffer.insert(m_buffer.end(), resampled.begin(), resampled.end());

    delete[] data;

    //!!! ah, our processing block size and step size must be equal --
    //!!! any overlap must be handled here by us

    FeatureSet fs;

    while (int(m_buffer.size()) >= m_n) {
	Feature f = processColumn();
	fs[0].push_back(f);
    }

    return fs;
}

LowFreq::FeatureSet
LowFreq::getRemainingFeatures()
{
    FeatureSet fs;

    while (int(m_buffer.size()) >= m_n) {
	Feature f = processColumn();
	fs[0].push_back(f);
    }

    return fs;
}

LowFreq::Feature
LowFreq::processColumn()
{
    Feature f;

    double *realOut = new double[m_n];
    double *imagOut = new double[m_n];

    m_fft->process(false, m_buffer.data(), 0, realOut, imagOut);

    for (int i = 0; i <= m_n/2; ++i) {
	f.values.push_back(realOut[i] * realOut[i] + imagOut[i] * imagOut[i]);
    }

    int step = m_n/2; //!!!

    std::vector<double> advanced(m_buffer.data() + step,
				 m_buffer.data() + m_buffer.size());
    
    m_buffer = advanced;

    return f;
}