1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
5 An audio time-stretching and pitch-shifting library.
6 Copyright 2007 Chris Cannam.
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version. See the file
12 COPYING included with this distribution for more information.
15 #include "RubberBandVampPlugin.h"
17 #include "StretchCalculator.h"
26 class RubberBandVampPlugin::Impl
39 bool m_phaseIndependent;
42 RubberBand::RubberBandStretcher *m_stretcher;
44 int m_incrementsOutput;
45 int m_aggregateIncrementsOutput;
46 int m_divergenceOutput;
47 int m_phaseResetDfOutput;
48 int m_smoothedPhaseResetDfOutput;
49 int m_phaseResetPointsOutput;
50 int m_timeSyncPointsOutput;
53 size_t m_accumulatedIncrement;
57 FeatureSet processOffline(const float *const *inputBuffers,
58 Vamp::RealTime timestamp);
60 FeatureSet getRemainingFeaturesOffline();
62 FeatureSet processRealTime(const float *const *inputBuffers,
63 Vamp::RealTime timestamp);
65 FeatureSet getRemainingFeaturesRealTime();
67 FeatureSet createFeatures(size_t inputIncrement,
68 std::vector<int> &outputIncrements,
69 std::vector<float> &phaseResetDf,
70 std::vector<int> &exactPoints,
71 std::vector<float> &smoothedDf,
77 RubberBandVampPlugin::RubberBandVampPlugin(float inputSampleRate) :
78 Plugin(inputSampleRate)
82 m_d->m_timeRatio = 1.f;
83 m_d->m_pitchRatio = 1.f;
84 m_d->m_realtime = false;
85 m_d->m_elasticTiming = true;
86 m_d->m_transientMode = 0;
87 m_d->m_phaseIndependent = false;
88 m_d->m_windowLength = 0;
90 m_d->m_sampleRate = lrintf(m_inputSampleRate);
93 RubberBandVampPlugin::~RubberBandVampPlugin()
95 if (m_d->m_outputDump) {
96 for (size_t i = 0; i < m_d->m_stretcher->getChannelCount(); ++i) {
97 delete[] m_d->m_outputDump[i];
99 delete[] m_d->m_outputDump;
101 delete m_d->m_stretcher;
106 RubberBandVampPlugin::getIdentifier() const
112 RubberBandVampPlugin::getName() const
114 return "Rubber Band Timestretch Analysis";
118 RubberBandVampPlugin::getDescription() const
120 return "Carry out analysis phases of time stretcher process";
124 RubberBandVampPlugin::getMaker() const
126 return "Rubber Band"; ///!!!
130 RubberBandVampPlugin::getPluginVersion() const
136 RubberBandVampPlugin::getCopyright() const
141 RubberBandVampPlugin::OutputList
142 RubberBandVampPlugin::getOutputDescriptors() const
147 if (m_d->m_stretcher) {
148 rate = lrintf(m_inputSampleRate / m_d->m_stretcher->getInputIncrement());
152 d.identifier = "increments";
153 d.name = "Output Increments";
154 d.description = "Output time increment for each input step";
156 d.hasFixedBinCount = true;
158 d.hasKnownExtents = false;
159 d.isQuantized = true;
160 d.quantizeStep = 1.0;
161 d.sampleType = OutputDescriptor::VariableSampleRate;
163 m_d->m_incrementsOutput = list.size();
166 d.identifier = "aggregate_increments";
167 d.name = "Accumulated Output Increments";
168 d.description = "Accumulated output time increments";
170 m_d->m_aggregateIncrementsOutput = list.size();
173 d.identifier = "divergence";
174 d.name = "Divergence from Linear";
175 d.description = "Difference between actual output time and the output time for a theoretical linear stretch";
176 d.isQuantized = false;
178 m_d->m_divergenceOutput = list.size();
181 d.identifier = "phaseresetdf";
182 d.name = "Phase Reset Detection Function";
183 d.description = "Curve whose peaks are used to identify transients for phase reset points";
186 m_d->m_phaseResetDfOutput = list.size();
189 d.identifier = "smoothedphaseresetdf";
190 d.name = "Smoothed Phase Reset Detection Function";
191 d.description = "Phase reset curve smoothed for peak picking";
193 m_d->m_smoothedPhaseResetDfOutput = list.size();
196 d.identifier = "phaseresetpoints";
197 d.name = "Phase Reset Points";
198 d.description = "Points estimated as transients at which phase reset occurs";
200 d.hasFixedBinCount = true;
202 d.hasKnownExtents = false;
203 d.isQuantized = false;
205 m_d->m_phaseResetPointsOutput = list.size();
208 d.identifier = "timesyncpoints";
209 d.name = "Time Sync Points";
210 d.description = "Salient points which stretcher aims to place with strictly correct timing";
212 d.hasFixedBinCount = true;
214 d.hasKnownExtents = false;
215 d.isQuantized = false;
217 m_d->m_timeSyncPointsOutput = list.size();
223 RubberBandVampPlugin::ParameterList
224 RubberBandVampPlugin::getParameterDescriptors() const
228 ParameterDescriptor d;
229 d.identifier = "timeratio";
230 d.name = "Time Ratio";
231 d.description = "Ratio to modify overall duration by";
235 d.defaultValue = 100;
236 d.isQuantized = false;
239 d.identifier = "pitchratio";
240 d.name = "Pitch Scale Ratio";
241 d.description = "Frequency ratio to modify pitch by";
245 d.defaultValue = 100;
246 d.isQuantized = false;
249 d.identifier = "mode";
250 d.name = "Processing Mode";
251 d.description = ""; //!!!
256 d.isQuantized = true;
258 d.valueNames.clear();
259 d.valueNames.push_back("Offline");
260 d.valueNames.push_back("Real Time");
263 d.identifier = "stretchtype";
264 d.name = "Stretch Flexibility";
265 d.description = ""; //!!!
270 d.isQuantized = true;
272 d.valueNames.clear();
273 d.valueNames.push_back("Elastic");
274 d.valueNames.push_back("Precise");
277 d.identifier = "transientmode";
278 d.name = "Transient Handling";
279 d.description = ""; //!!!
284 d.isQuantized = true;
286 d.valueNames.clear();
287 d.valueNames.push_back("Mixed");
288 d.valueNames.push_back("Smooth");
289 d.valueNames.push_back("Crisp");
292 d.identifier = "phasemode";
293 d.name = "Phase Handling";
294 d.description = ""; //!!!
299 d.isQuantized = true;
301 d.valueNames.clear();
302 d.valueNames.push_back("Peak Locked");
303 d.valueNames.push_back("Independent");
306 d.identifier = "windowmode";
307 d.name = "Window Length";
308 d.description = ""; //!!!
313 d.isQuantized = true;
315 d.valueNames.clear();
316 d.valueNames.push_back("Standard");
317 d.valueNames.push_back("Short");
318 d.valueNames.push_back("Long");
325 RubberBandVampPlugin::getParameter(std::string id) const
327 if (id == "timeratio") return m_d->m_timeRatio * 100.f;
328 if (id == "pitchratio") return m_d->m_pitchRatio * 100.f;
329 if (id == "mode") return m_d->m_realtime ? 1 : 0;
330 if (id == "stretchtype") return m_d->m_elasticTiming ? 0 : 1;
331 if (id == "transientmode") return m_d->m_transientMode;
332 if (id == "phasemode") return m_d->m_phaseIndependent ? 1 : 0;
333 if (id == "windowmode") return m_d->m_windowLength;
338 RubberBandVampPlugin::setParameter(std::string id, float value)
340 if (id == "timeratio") {
341 m_d->m_timeRatio = value / 100;
342 } else if (id == "pitchratio") {
343 m_d->m_pitchRatio = value / 100;
345 bool set = (value > 0.5);
346 if (id == "mode") m_d->m_realtime = set;
347 else if (id == "stretchtype") m_d->m_elasticTiming = !set;
348 else if (id == "transientmode") m_d->m_transientMode = int(value + 0.5);
349 else if (id == "phasemode") m_d->m_phaseIndependent = set;
350 else if (id == "windowmode") m_d->m_windowLength = int(value + 0.5);
355 RubberBandVampPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
357 if (channels < getMinChannelCount() ||
358 channels > getMaxChannelCount()) return false;
360 m_d->m_stepSize = std::min(stepSize, blockSize);
361 m_d->m_blockSize = stepSize;
363 RubberBand::RubberBandStretcher::Options options = 0;
366 options |= RubberBand::RubberBandStretcher::OptionProcessRealTime;
367 else options |= RubberBand::RubberBandStretcher::OptionProcessOffline;
369 if (m_d->m_elasticTiming)
370 options |= RubberBand::RubberBandStretcher::OptionStretchElastic;
371 else options |= RubberBand::RubberBandStretcher::OptionStretchPrecise;
373 if (m_d->m_transientMode == 0)
374 options |= RubberBand::RubberBandStretcher::OptionTransientsMixed;
375 else if (m_d->m_transientMode == 1)
376 options |= RubberBand::RubberBandStretcher::OptionTransientsSmooth;
377 else options |= RubberBand::RubberBandStretcher::OptionTransientsCrisp;
379 if (m_d->m_phaseIndependent)
380 options |= RubberBand::RubberBandStretcher::OptionPhaseIndependent;
381 else options |= RubberBand::RubberBandStretcher::OptionPhasePeakLocked;
383 if (m_d->m_windowLength == 0)
384 options |= RubberBand::RubberBandStretcher::OptionWindowStandard;
385 else if (m_d->m_windowLength == 1)
386 options |= RubberBand::RubberBandStretcher::OptionWindowShort;
387 else options |= RubberBand::RubberBandStretcher::OptionWindowLong;
389 delete m_d->m_stretcher;
390 m_d->m_stretcher = new RubberBand::RubberBandStretcher
391 (m_d->m_sampleRate, channels, options);
392 m_d->m_stretcher->setDebugLevel(1);
393 m_d->m_stretcher->setTimeRatio(m_d->m_timeRatio);
394 m_d->m_stretcher->setPitchScale(m_d->m_pitchRatio);
397 m_d->m_accumulatedIncrement = 0;
399 m_d->m_outputDump = 0;
405 RubberBandVampPlugin::reset()
407 // delete m_stretcher; //!!! or just if (m_stretcher) m_stretcher->reset();
408 // m_stretcher = new RubberBand::RubberBandStretcher(lrintf(m_inputSampleRate), channels);
409 if (m_d->m_stretcher) m_d->m_stretcher->reset();
412 RubberBandVampPlugin::FeatureSet
413 RubberBandVampPlugin::process(const float *const *inputBuffers,
414 Vamp::RealTime timestamp)
416 if (m_d->m_realtime) {
417 return m_d->processRealTime(inputBuffers, timestamp);
419 return m_d->processOffline(inputBuffers, timestamp);
423 RubberBandVampPlugin::FeatureSet
424 RubberBandVampPlugin::getRemainingFeatures()
426 if (m_d->m_realtime) {
427 return m_d->getRemainingFeaturesRealTime();
429 return m_d->getRemainingFeaturesOffline();
433 RubberBandVampPlugin::FeatureSet
434 RubberBandVampPlugin::Impl::processOffline(const float *const *inputBuffers,
435 Vamp::RealTime timestamp)
438 cerr << "ERROR: RubberBandVampPlugin::processOffline: "
439 << "RubberBandVampPlugin has not been initialised"
444 m_stretcher->study(inputBuffers, m_blockSize, false);
448 RubberBandVampPlugin::FeatureSet
449 RubberBandVampPlugin::Impl::getRemainingFeaturesOffline()
451 m_stretcher->study(0, 0, true);
453 m_stretcher->calculateStretch();
455 int rate = m_sampleRate;
457 RubberBand::StretchCalculator sc(rate,
458 m_stretcher->getInputIncrement(),
461 size_t inputIncrement = m_stretcher->getInputIncrement();
462 std::vector<int> outputIncrements = m_stretcher->getOutputIncrements();
463 std::vector<float> phaseResetDf = m_stretcher->getPhaseResetCurve();
464 std::vector<int> peaks = m_stretcher->getExactTimePoints();
465 std::vector<float> smoothedDf = sc.smoothDF(phaseResetDf);
467 FeatureSet features = createFeatures
468 (inputIncrement, outputIncrements, phaseResetDf, peaks, smoothedDf,
474 RubberBandVampPlugin::FeatureSet
475 RubberBandVampPlugin::Impl::processRealTime(const float *const *inputBuffers,
476 Vamp::RealTime timestamp)
478 // This function is not in any way a real-time function (i.e. it
479 // has no requirement to be RT safe); it simply operates the
480 // stretcher in RT mode.
483 cerr << "ERROR: RubberBandVampPlugin::processRealTime: "
484 << "RubberBandVampPlugin has not been initialised"
489 m_stretcher->process(inputBuffers, m_blockSize, false);
491 size_t inputIncrement = m_stretcher->getInputIncrement();
492 std::vector<int> outputIncrements = m_stretcher->getOutputIncrements();
493 std::vector<float> phaseResetDf = m_stretcher->getPhaseResetCurve();
494 std::vector<float> smoothedDf; // not meaningful in RT mode
495 std::vector<int> dummyPoints;
496 FeatureSet features = createFeatures
497 (inputIncrement, outputIncrements, phaseResetDf, dummyPoints, smoothedDf,
499 m_counter += outputIncrements.size();
502 while ((available = m_stretcher->available()) > 0) {
504 m_outputDump = new float *[m_stretcher->getChannelCount()];
505 for (size_t i = 0; i < m_stretcher->getChannelCount(); ++i) {
506 m_outputDump[i] = new float[m_blockSize];
509 m_stretcher->retrieve(m_outputDump,
510 std::min(int(m_blockSize), available));
516 RubberBandVampPlugin::FeatureSet
517 RubberBandVampPlugin::Impl::getRemainingFeaturesRealTime()
522 RubberBandVampPlugin::FeatureSet
523 RubberBandVampPlugin::Impl::createFeatures(size_t inputIncrement,
524 std::vector<int> &outputIncrements,
525 std::vector<float> &phaseResetDf,
526 std::vector<int> &exactPoints,
527 std::vector<float> &smoothedDf,
531 size_t actual = m_accumulatedIncrement;
533 double overallRatio = m_timeRatio * m_pitchRatio;
539 int rate = m_sampleRate;
543 for (size_t i = 0; i < outputIncrements.size(); ++i) {
545 size_t frame = (baseCount + i) * inputIncrement;
547 int oi = outputIncrements[i];
556 if (epi < exactPoints.size() && int(i) == exactPoints[epi]) {
561 double linear = (frame * overallRatio);
563 Vamp::RealTime t = Vamp::RealTime::frame2RealTime(frame, rate);
566 feature.hasTimestamp = true;
567 feature.timestamp = t;
568 feature.values.push_back(oi);
569 feature.label = Vamp::RealTime::frame2RealTime(oi, rate).toText();
570 features[m_incrementsOutput].push_back(feature);
572 feature.values.clear();
573 feature.values.push_back(actual);
574 feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText();
575 features[m_aggregateIncrementsOutput].push_back(feature);
577 feature.values.clear();
578 feature.values.push_back(actual - linear);
580 sprintf(label, "expected %ld, actual %ld, difference %ld (%s ms)",
581 long(linear), long(actual), long(actual - linear),
582 // frame2RealTime expects an integer frame number,
583 // hence our multiplication factor
584 (Vamp::RealTime::frame2RealTime
585 (lrintf((actual - linear) * 1000), rate) / 1000)
587 feature.label = label;
589 features[m_divergenceOutput].push_back(feature);
594 if (i < phaseResetDf.size()) {
595 feature.values.clear();
596 feature.values.push_back(phaseResetDf[i]);
597 sprintf(buf, "%d", baseCount + i);
599 features[m_phaseResetDfOutput].push_back(feature);
602 if (i < smoothedDf.size()) {
603 feature.values.clear();
604 feature.values.push_back(smoothedDf[i]);
605 features[m_smoothedPhaseResetDfOutput].push_back(feature);
609 feature.values.clear();
610 feature.label = "Phase Reset";
611 features[m_phaseResetPointsOutput].push_back(feature);
615 feature.values.clear();
616 feature.label = "Time Sync";
617 features[m_timeSyncPointsOutput].push_back(feature);
622 Vamp::RealTime t = Vamp::RealTime::frame2RealTime
623 (inputIncrement * (baseCount + outputIncrements.size()), rate);
625 feature.hasTimestamp = true;
626 feature.timestamp = t;
627 feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText();
628 feature.values.clear();
629 feature.values.push_back(actual);
630 features[m_aggregateIncrementsOutput].push_back(feature);
632 float linear = ((baseCount + outputIncrements.size())
633 * inputIncrement * overallRatio);
634 feature.values.clear();
635 feature.values.push_back(actual - linear);
636 feature.label = // see earlier comment
637 (Vamp::RealTime::frame2RealTime //!!! update this as earlier label
638 (lrintf((actual - linear) * 1000), rate) / 1000)
640 features[m_divergenceOutput].push_back(feature);
643 m_accumulatedIncrement = actual;