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