diff src/rubberband-1.8.1/vamp/RubberBandVampPlugin.cpp @ 10:37bf6b4a2645

Add FFTW3
author Chris Cannam
date Wed, 20 Mar 2013 15:35:50 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rubberband-1.8.1/vamp/RubberBandVampPlugin.cpp	Wed Mar 20 15:35:50 2013 +0000
@@ -0,0 +1,654 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Rubber Band Library
+    An audio time-stretching and pitch-shifting library.
+    Copyright 2007-2012 Particular Programs Ltd.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+
+    Alternatively, if you have a valid commercial licence for the
+    Rubber Band Library obtained by agreement with the copyright
+    holders, you may redistribute and/or modify it under the terms
+    described in that licence.
+
+    If you wish to distribute code using the Rubber Band Library
+    under terms other than those of the GNU General Public License,
+    you must obtain a valid commercial licence before doing so.
+*/
+
+#include "RubberBandVampPlugin.h"
+
+#include "StretchCalculator.h"
+#include "system/sysutils.h"
+
+#include <cmath>
+#include <cstdio>
+
+using std::string;
+using std::vector;
+using std::cerr;
+using std::endl;
+
+class RubberBandVampPlugin::Impl
+{
+public:
+    size_t m_stepSize;
+    size_t m_blockSize;
+    size_t m_sampleRate;
+
+    float m_timeRatio;
+    float m_pitchRatio;
+
+    bool m_realtime;
+    bool m_elasticTiming;
+    int m_transientMode;
+    bool m_phaseIndependent;
+    int m_windowLength;
+
+    RubberBand::RubberBandStretcher *m_stretcher;
+
+    int m_incrementsOutput;
+    int m_aggregateIncrementsOutput;
+    int m_divergenceOutput;
+    int m_phaseResetDfOutput;
+    int m_smoothedPhaseResetDfOutput;
+    int m_phaseResetPointsOutput;
+    int m_timeSyncPointsOutput;
+
+    size_t m_counter;
+    size_t m_accumulatedIncrement;
+
+    float **m_outputDump;
+
+    FeatureSet processOffline(const float *const *inputBuffers,
+                              Vamp::RealTime timestamp);
+
+    FeatureSet getRemainingFeaturesOffline();
+
+    FeatureSet processRealTime(const float *const *inputBuffers,
+                               Vamp::RealTime timestamp);
+
+    FeatureSet getRemainingFeaturesRealTime();
+
+    FeatureSet createFeatures(size_t inputIncrement,
+                              std::vector<int> &outputIncrements,
+                              std::vector<float> &phaseResetDf,
+                              std::vector<int> &exactPoints,
+                              std::vector<float> &smoothedDf,
+                              size_t baseCount,
+                              bool includeFinal);
+};
+
+
+RubberBandVampPlugin::RubberBandVampPlugin(float inputSampleRate) :
+    Plugin(inputSampleRate)
+{
+    m_d = new Impl();
+    m_d->m_stepSize = 0;
+    m_d->m_timeRatio = 1.f;
+    m_d->m_pitchRatio = 1.f;
+    m_d->m_realtime = false;
+    m_d->m_elasticTiming = true;
+    m_d->m_transientMode = 0;
+    m_d->m_phaseIndependent = false;
+    m_d->m_windowLength = 0;
+    m_d->m_stretcher = 0;
+    m_d->m_sampleRate = lrintf(m_inputSampleRate);
+}
+
+RubberBandVampPlugin::~RubberBandVampPlugin()
+{
+    if (m_d->m_outputDump) {
+        for (size_t i = 0; i < m_d->m_stretcher->getChannelCount(); ++i) {
+            delete[] m_d->m_outputDump[i];
+        }
+        delete[] m_d->m_outputDump;
+    }
+    delete m_d->m_stretcher;
+    delete m_d;
+}
+
+string
+RubberBandVampPlugin::getIdentifier() const
+{
+    return "rubberband";
+}
+
+string
+RubberBandVampPlugin::getName() const
+{
+    return "Rubber Band Timestretch Analysis";
+}
+
+string
+RubberBandVampPlugin::getDescription() const
+{
+    return "Carry out analysis phases of time stretcher process";
+}
+
+string
+RubberBandVampPlugin::getMaker() const
+{
+    return "Breakfast Quay";
+}
+
+int
+RubberBandVampPlugin::getPluginVersion() const
+{
+    return 1;
+}
+
+string
+RubberBandVampPlugin::getCopyright() const
+{
+    return "";//!!!
+}
+
+RubberBandVampPlugin::OutputList
+RubberBandVampPlugin::getOutputDescriptors() const
+{
+    OutputList list;
+
+    size_t rate = 0;
+    if (m_d->m_stretcher) {
+        rate = lrintf(m_inputSampleRate / m_d->m_stretcher->getInputIncrement());
+    }
+
+    OutputDescriptor d;
+    d.identifier = "increments";
+    d.name = "Output Increments";
+    d.description = "Output time increment for each input step";
+    d.unit = "samples";
+    d.hasFixedBinCount = true;
+    d.binCount = 1;
+    d.hasKnownExtents = false;
+    d.isQuantized = true;
+    d.quantizeStep = 1.0;
+    d.sampleType = OutputDescriptor::VariableSampleRate;
+    d.sampleRate = float(rate);
+    m_d->m_incrementsOutput = list.size();
+    list.push_back(d);
+
+    d.identifier = "aggregate_increments";
+    d.name = "Accumulated Output Increments";
+    d.description = "Accumulated output time increments";
+    d.sampleRate = 0;
+    m_d->m_aggregateIncrementsOutput = list.size();
+    list.push_back(d);
+
+    d.identifier = "divergence";
+    d.name = "Divergence from Linear";
+    d.description = "Difference between actual output time and the output time for a theoretical linear stretch";
+    d.isQuantized = false;
+    d.sampleRate = 0;
+    m_d->m_divergenceOutput = list.size();
+    list.push_back(d);
+
+    d.identifier = "phaseresetdf";
+    d.name = "Phase Reset Detection Function";
+    d.description = "Curve whose peaks are used to identify transients for phase reset points";
+    d.unit = "";
+    d.sampleRate = float(rate);
+    m_d->m_phaseResetDfOutput = list.size();
+    list.push_back(d);
+
+    d.identifier = "smoothedphaseresetdf";
+    d.name = "Smoothed Phase Reset Detection Function";
+    d.description = "Phase reset curve smoothed for peak picking";
+    d.unit = "";
+    m_d->m_smoothedPhaseResetDfOutput = list.size();
+    list.push_back(d);
+
+    d.identifier = "phaseresetpoints";
+    d.name = "Phase Reset Points";
+    d.description = "Points estimated as transients at which phase reset occurs";
+    d.unit = "";
+    d.hasFixedBinCount = true;
+    d.binCount = 0;
+    d.hasKnownExtents = false;
+    d.isQuantized = false;
+    d.sampleRate = 0;
+    m_d->m_phaseResetPointsOutput = list.size();
+    list.push_back(d);
+
+    d.identifier = "timesyncpoints";
+    d.name = "Time Sync Points";
+    d.description = "Salient points which stretcher aims to place with strictly correct timing";
+    d.unit = "";
+    d.hasFixedBinCount = true;
+    d.binCount = 0;
+    d.hasKnownExtents = false;
+    d.isQuantized = false;
+    d.sampleRate = 0;
+    m_d->m_timeSyncPointsOutput = list.size();
+    list.push_back(d);
+
+    return list;
+}
+
+RubberBandVampPlugin::ParameterList
+RubberBandVampPlugin::getParameterDescriptors() const
+{
+    ParameterList list;
+
+    ParameterDescriptor d;
+    d.identifier = "timeratio";
+    d.name = "Time Ratio";
+    d.description = "Ratio to modify overall duration by";
+    d.unit = "%";
+    d.minValue = 1;
+    d.maxValue = 500;
+    d.defaultValue = 100;
+    d.isQuantized = false;
+    list.push_back(d);
+
+    d.identifier = "pitchratio";
+    d.name = "Pitch Scale Ratio";
+    d.description = "Frequency ratio to modify pitch by";
+    d.unit = "%";
+    d.minValue = 1;
+    d.maxValue = 500;
+    d.defaultValue = 100;
+    d.isQuantized = false;
+    list.push_back(d);
+
+    d.identifier = "mode";
+    d.name = "Processing Mode";
+    d.description = ""; //!!!
+    d.unit = "";
+    d.minValue = 0;
+    d.maxValue = 1;
+    d.defaultValue = 0;
+    d.isQuantized = true;
+    d.quantizeStep = 1;
+    d.valueNames.clear();
+    d.valueNames.push_back("Offline");
+    d.valueNames.push_back("Real Time");
+    list.push_back(d);
+
+    d.identifier = "stretchtype";
+    d.name = "Stretch Flexibility";
+    d.description = ""; //!!!
+    d.unit = "";
+    d.minValue = 0;
+    d.maxValue = 1;
+    d.defaultValue = 0;
+    d.isQuantized = true;
+    d.quantizeStep = 1;
+    d.valueNames.clear();
+    d.valueNames.push_back("Elastic");
+    d.valueNames.push_back("Precise");
+    list.push_back(d);
+
+    d.identifier = "transientmode";
+    d.name = "Transient Handling";
+    d.description = ""; //!!!
+    d.unit = "";
+    d.minValue = 0;
+    d.maxValue = 2;
+    d.defaultValue = 0;
+    d.isQuantized = true;
+    d.quantizeStep = 1;
+    d.valueNames.clear();
+    d.valueNames.push_back("Mixed");
+    d.valueNames.push_back("Smooth");
+    d.valueNames.push_back("Crisp");
+    list.push_back(d);
+
+    d.identifier = "phasemode";
+    d.name = "Phase Handling";
+    d.description = ""; //!!!
+    d.unit = "";
+    d.minValue = 0;
+    d.maxValue = 1;
+    d.defaultValue = 0;
+    d.isQuantized = true;
+    d.quantizeStep = 1;
+    d.valueNames.clear();
+    d.valueNames.push_back("Laminar");
+    d.valueNames.push_back("Independent");
+    list.push_back(d);
+
+    d.identifier = "windowmode";
+    d.name = "Window Length";
+    d.description = ""; //!!!
+    d.unit = "";
+    d.minValue = 0;
+    d.maxValue = 2;
+    d.defaultValue = 0;
+    d.isQuantized = true;
+    d.quantizeStep = 1;
+    d.valueNames.clear();
+    d.valueNames.push_back("Standard");
+    d.valueNames.push_back("Short");
+    d.valueNames.push_back("Long");
+    list.push_back(d);
+
+    return list;
+}
+
+float
+RubberBandVampPlugin::getParameter(std::string id) const
+{
+    if (id == "timeratio") return m_d->m_timeRatio * 100.f;
+    if (id == "pitchratio") return m_d->m_pitchRatio * 100.f;
+    if (id == "mode") return m_d->m_realtime ? 1.f : 0.f;
+    if (id == "stretchtype") return m_d->m_elasticTiming ? 0.f : 1.f;
+    if (id == "transientmode") return float(m_d->m_transientMode);
+    if (id == "phasemode") return m_d->m_phaseIndependent ? 1.f : 0.f;
+    if (id == "windowmode") return float(m_d->m_windowLength);
+    return 0.f;
+}
+
+void
+RubberBandVampPlugin::setParameter(std::string id, float value)
+{
+    if (id == "timeratio") {
+        m_d->m_timeRatio = value / 100;
+    } else if (id == "pitchratio") {
+        m_d->m_pitchRatio = value / 100;
+    } else {
+        bool set = (value > 0.5);
+        if (id == "mode") m_d->m_realtime = set;
+        else if (id == "stretchtype") m_d->m_elasticTiming = !set;
+        else if (id == "transientmode") m_d->m_transientMode = int(value + 0.5);
+        else if (id == "phasemode") m_d->m_phaseIndependent = set;
+        else if (id == "windowmode") m_d->m_windowLength = int(value + 0.5);
+    }
+}
+
+bool
+RubberBandVampPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+    if (channels < getMinChannelCount() ||
+	channels > getMaxChannelCount()) return false;
+
+    m_d->m_stepSize = std::min(stepSize, blockSize);
+    m_d->m_blockSize = stepSize;
+
+    RubberBand::RubberBandStretcher::Options options = 0;
+
+    if (m_d->m_realtime)
+         options |= RubberBand::RubberBandStretcher::OptionProcessRealTime;
+    else options |= RubberBand::RubberBandStretcher::OptionProcessOffline;
+
+    if (m_d->m_elasticTiming)
+         options |= RubberBand::RubberBandStretcher::OptionStretchElastic;
+    else options |= RubberBand::RubberBandStretcher::OptionStretchPrecise;
+ 
+    if (m_d->m_transientMode == 0) 
+         options |= RubberBand::RubberBandStretcher::OptionTransientsMixed;
+    else if (m_d->m_transientMode == 1) 
+         options |= RubberBand::RubberBandStretcher::OptionTransientsSmooth;
+    else options |= RubberBand::RubberBandStretcher::OptionTransientsCrisp;
+
+    if (m_d->m_phaseIndependent) 
+         options |= RubberBand::RubberBandStretcher::OptionPhaseIndependent;
+    else options |= RubberBand::RubberBandStretcher::OptionPhaseLaminar;
+
+    if (m_d->m_windowLength == 0)
+         options |= RubberBand::RubberBandStretcher::OptionWindowStandard;
+    else if (m_d->m_windowLength == 1)
+         options |= RubberBand::RubberBandStretcher::OptionWindowShort;
+    else options |= RubberBand::RubberBandStretcher::OptionWindowLong;
+
+    delete m_d->m_stretcher;
+    m_d->m_stretcher = new RubberBand::RubberBandStretcher
+        (m_d->m_sampleRate, channels, options);
+    m_d->m_stretcher->setDebugLevel(1);
+    m_d->m_stretcher->setTimeRatio(m_d->m_timeRatio);
+    m_d->m_stretcher->setPitchScale(m_d->m_pitchRatio);
+
+    m_d->m_counter = 0;
+    m_d->m_accumulatedIncrement = 0;
+
+    m_d->m_outputDump = 0;
+
+    return true;
+}
+
+void
+RubberBandVampPlugin::reset()
+{
+    if (m_d->m_stretcher) m_d->m_stretcher->reset();
+}
+
+RubberBandVampPlugin::FeatureSet
+RubberBandVampPlugin::process(const float *const *inputBuffers,
+                              Vamp::RealTime timestamp)
+{
+    if (m_d->m_realtime) {
+        return m_d->processRealTime(inputBuffers, timestamp);
+    } else {
+        return m_d->processOffline(inputBuffers, timestamp);
+    }        
+}
+
+RubberBandVampPlugin::FeatureSet
+RubberBandVampPlugin::getRemainingFeatures()
+{
+    if (m_d->m_realtime) {
+        return m_d->getRemainingFeaturesRealTime();
+    } else {
+        return m_d->getRemainingFeaturesOffline();
+    }
+}
+
+RubberBandVampPlugin::FeatureSet
+RubberBandVampPlugin::Impl::processOffline(const float *const *inputBuffers,
+                                           Vamp::RealTime timestamp)
+{
+    if (!m_stretcher) {
+	cerr << "ERROR: RubberBandVampPlugin::processOffline: "
+	     << "RubberBandVampPlugin has not been initialised"
+	     << endl;
+	return FeatureSet();
+    }
+
+    m_stretcher->study(inputBuffers, m_blockSize, false);
+    return FeatureSet();
+}
+
+RubberBandVampPlugin::FeatureSet
+RubberBandVampPlugin::Impl::getRemainingFeaturesOffline()
+{
+    m_stretcher->study(0, 0, true);
+
+    m_stretcher->calculateStretch();
+
+    int rate = m_sampleRate;
+
+    RubberBand::StretchCalculator sc(rate, m_stretcher->getInputIncrement(), true);
+
+    size_t inputIncrement = m_stretcher->getInputIncrement();
+    std::vector<int> outputIncrements = m_stretcher->getOutputIncrements();
+    std::vector<float> phaseResetDf = m_stretcher->getPhaseResetCurve();
+    std::vector<int> peaks = m_stretcher->getExactTimePoints();
+    std::vector<float> smoothedDf = sc.smoothDF(phaseResetDf);
+
+    FeatureSet features = createFeatures
+        (inputIncrement, outputIncrements, phaseResetDf, peaks, smoothedDf,
+         0, true);
+
+    return features;
+}
+
+RubberBandVampPlugin::FeatureSet
+RubberBandVampPlugin::Impl::processRealTime(const float *const *inputBuffers,
+                                            Vamp::RealTime timestamp)
+{
+    // This function is not in any way a real-time function (i.e. it
+    // has no requirement to be RT safe); it simply operates the
+    // stretcher in RT mode.
+
+    if (!m_stretcher) {
+	cerr << "ERROR: RubberBandVampPlugin::processRealTime: "
+	     << "RubberBandVampPlugin has not been initialised"
+	     << endl;
+	return FeatureSet();
+    }
+
+    m_stretcher->process(inputBuffers, m_blockSize, false);
+    
+    size_t inputIncrement = m_stretcher->getInputIncrement();
+    std::vector<int> outputIncrements = m_stretcher->getOutputIncrements();
+    std::vector<float> phaseResetDf = m_stretcher->getPhaseResetCurve();
+    std::vector<float> smoothedDf; // not meaningful in RT mode
+    std::vector<int> dummyPoints;
+    FeatureSet features = createFeatures
+        (inputIncrement, outputIncrements, phaseResetDf, dummyPoints, smoothedDf, 
+         m_counter, false);
+    m_counter += outputIncrements.size();
+
+    int available = 0;
+    while ((available = m_stretcher->available()) > 0) {
+        if (!m_outputDump) {
+            m_outputDump = new float *[m_stretcher->getChannelCount()];
+            for (size_t i = 0; i < m_stretcher->getChannelCount(); ++i) {
+                m_outputDump[i] = new float[m_blockSize];
+            }
+        }
+        m_stretcher->retrieve(m_outputDump,
+                              std::min(int(m_blockSize), available));
+    }
+
+    return features;
+}
+
+RubberBandVampPlugin::FeatureSet
+RubberBandVampPlugin::Impl::getRemainingFeaturesRealTime()
+{
+    return FeatureSet();
+}
+
+RubberBandVampPlugin::FeatureSet
+RubberBandVampPlugin::Impl::createFeatures(size_t inputIncrement,
+                                           std::vector<int> &outputIncrements,
+                                           std::vector<float> &phaseResetDf,
+                                           std::vector<int> &exactPoints,
+                                           std::vector<float> &smoothedDf,
+                                           size_t baseCount,
+                                           bool includeFinal)
+{
+    size_t actual = m_accumulatedIncrement;
+
+    double overallRatio = m_timeRatio * m_pitchRatio;
+
+    char label[200];
+
+    FeatureSet features;
+
+    int rate = m_sampleRate;
+
+    size_t epi = 0;
+
+    for (size_t i = 0; i < outputIncrements.size(); ++i) {
+
+        size_t frame = (baseCount + i) * inputIncrement;
+
+        int oi = outputIncrements[i];
+        bool hard = false;
+        bool soft = false;
+
+        if (oi < 0) {
+            oi = -oi;
+            hard = true;
+        }
+
+        if (epi < exactPoints.size() && int(i) == exactPoints[epi]) {
+            soft = true;
+            ++epi;
+        }
+
+        double linear = (frame * overallRatio);
+
+        Vamp::RealTime t = Vamp::RealTime::frame2RealTime(frame, rate);
+
+        Feature feature;
+        feature.hasTimestamp = true;
+        feature.timestamp = t;
+        feature.values.push_back(float(oi));
+        feature.label = Vamp::RealTime::frame2RealTime(oi, rate).toText();
+        features[m_incrementsOutput].push_back(feature);
+
+        feature.values.clear();
+        feature.values.push_back(float(actual));
+        feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText();
+        features[m_aggregateIncrementsOutput].push_back(feature);
+
+        feature.values.clear();
+        feature.values.push_back(actual - linear);
+
+        sprintf(label, "expected %ld, actual %ld, difference %ld (%s ms)",
+                long(linear), long(actual), long(actual - linear),
+                // frame2RealTime expects an integer frame number,
+                // hence our multiplication factor
+                (Vamp::RealTime::frame2RealTime
+                 (lrintf((actual - linear) * 1000), rate) / 1000)
+                .toText().c_str());
+        feature.label = label;
+
+        features[m_divergenceOutput].push_back(feature);
+        actual += oi;
+        
+        char buf[30];
+
+        if (i < phaseResetDf.size()) {
+            feature.values.clear();
+            feature.values.push_back(phaseResetDf[i]);
+            sprintf(buf, "%d", int(baseCount + i));
+            feature.label = buf;
+            features[m_phaseResetDfOutput].push_back(feature);
+        }
+
+        if (i < smoothedDf.size()) {
+            feature.values.clear();
+            feature.values.push_back(smoothedDf[i]);
+            features[m_smoothedPhaseResetDfOutput].push_back(feature);
+        }
+
+        if (hard) {
+            feature.values.clear();
+            feature.label = "Phase Reset";
+            features[m_phaseResetPointsOutput].push_back(feature);
+        }
+
+        if (hard || soft) {
+            feature.values.clear();
+            feature.label = "Time Sync";
+            features[m_timeSyncPointsOutput].push_back(feature);
+        }            
+    }
+
+    if (includeFinal) {
+        Vamp::RealTime t = Vamp::RealTime::frame2RealTime
+            (inputIncrement * (baseCount + outputIncrements.size()), rate);
+        Feature feature;
+        feature.hasTimestamp = true;
+        feature.timestamp = t;
+        feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText();
+        feature.values.clear();
+        feature.values.push_back(float(actual));
+        features[m_aggregateIncrementsOutput].push_back(feature);
+
+        float linear = ((baseCount + outputIncrements.size())
+                        * inputIncrement * overallRatio);
+        feature.values.clear();
+        feature.values.push_back(actual - linear);
+        feature.label =  // see earlier comment
+            (Vamp::RealTime::frame2RealTime //!!! update this as earlier label
+             (lrintf((actual - linear) * 1000), rate) / 1000)
+            .toText();
+        features[m_divergenceOutput].push_back(feature);
+    }
+
+    m_accumulatedIncrement = actual;
+
+    return features;
+}
+