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