comparison 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
comparison
equal deleted inserted replaced
9:c0fb53affa76 10:37bf6b4a2645
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Rubber Band Library
5 An audio time-stretching and pitch-shifting library.
6 Copyright 2007-2012 Particular Programs Ltd.
7
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.
13
14 Alternatively, if you have a valid commercial licence for the
15 Rubber Band Library obtained by agreement with the copyright
16 holders, you may redistribute and/or modify it under the terms
17 described in that licence.
18
19 If you wish to distribute code using the Rubber Band Library
20 under terms other than those of the GNU General Public License,
21 you must obtain a valid commercial licence before doing so.
22 */
23
24 #include "RubberBandVampPlugin.h"
25
26 #include "StretchCalculator.h"
27 #include "system/sysutils.h"
28
29 #include <cmath>
30 #include <cstdio>
31
32 using std::string;
33 using std::vector;
34 using std::cerr;
35 using std::endl;
36
37 class RubberBandVampPlugin::Impl
38 {
39 public:
40 size_t m_stepSize;
41 size_t m_blockSize;
42 size_t m_sampleRate;
43
44 float m_timeRatio;
45 float m_pitchRatio;
46
47 bool m_realtime;
48 bool m_elasticTiming;
49 int m_transientMode;
50 bool m_phaseIndependent;
51 int m_windowLength;
52
53 RubberBand::RubberBandStretcher *m_stretcher;
54
55 int m_incrementsOutput;
56 int m_aggregateIncrementsOutput;
57 int m_divergenceOutput;
58 int m_phaseResetDfOutput;
59 int m_smoothedPhaseResetDfOutput;
60 int m_phaseResetPointsOutput;
61 int m_timeSyncPointsOutput;
62
63 size_t m_counter;
64 size_t m_accumulatedIncrement;
65
66 float **m_outputDump;
67
68 FeatureSet processOffline(const float *const *inputBuffers,
69 Vamp::RealTime timestamp);
70
71 FeatureSet getRemainingFeaturesOffline();
72
73 FeatureSet processRealTime(const float *const *inputBuffers,
74 Vamp::RealTime timestamp);
75
76 FeatureSet getRemainingFeaturesRealTime();
77
78 FeatureSet createFeatures(size_t inputIncrement,
79 std::vector<int> &outputIncrements,
80 std::vector<float> &phaseResetDf,
81 std::vector<int> &exactPoints,
82 std::vector<float> &smoothedDf,
83 size_t baseCount,
84 bool includeFinal);
85 };
86
87
88 RubberBandVampPlugin::RubberBandVampPlugin(float inputSampleRate) :
89 Plugin(inputSampleRate)
90 {
91 m_d = new Impl();
92 m_d->m_stepSize = 0;
93 m_d->m_timeRatio = 1.f;
94 m_d->m_pitchRatio = 1.f;
95 m_d->m_realtime = false;
96 m_d->m_elasticTiming = true;
97 m_d->m_transientMode = 0;
98 m_d->m_phaseIndependent = false;
99 m_d->m_windowLength = 0;
100 m_d->m_stretcher = 0;
101 m_d->m_sampleRate = lrintf(m_inputSampleRate);
102 }
103
104 RubberBandVampPlugin::~RubberBandVampPlugin()
105 {
106 if (m_d->m_outputDump) {
107 for (size_t i = 0; i < m_d->m_stretcher->getChannelCount(); ++i) {
108 delete[] m_d->m_outputDump[i];
109 }
110 delete[] m_d->m_outputDump;
111 }
112 delete m_d->m_stretcher;
113 delete m_d;
114 }
115
116 string
117 RubberBandVampPlugin::getIdentifier() const
118 {
119 return "rubberband";
120 }
121
122 string
123 RubberBandVampPlugin::getName() const
124 {
125 return "Rubber Band Timestretch Analysis";
126 }
127
128 string
129 RubberBandVampPlugin::getDescription() const
130 {
131 return "Carry out analysis phases of time stretcher process";
132 }
133
134 string
135 RubberBandVampPlugin::getMaker() const
136 {
137 return "Breakfast Quay";
138 }
139
140 int
141 RubberBandVampPlugin::getPluginVersion() const
142 {
143 return 1;
144 }
145
146 string
147 RubberBandVampPlugin::getCopyright() const
148 {
149 return "";//!!!
150 }
151
152 RubberBandVampPlugin::OutputList
153 RubberBandVampPlugin::getOutputDescriptors() const
154 {
155 OutputList list;
156
157 size_t rate = 0;
158 if (m_d->m_stretcher) {
159 rate = lrintf(m_inputSampleRate / m_d->m_stretcher->getInputIncrement());
160 }
161
162 OutputDescriptor d;
163 d.identifier = "increments";
164 d.name = "Output Increments";
165 d.description = "Output time increment for each input step";
166 d.unit = "samples";
167 d.hasFixedBinCount = true;
168 d.binCount = 1;
169 d.hasKnownExtents = false;
170 d.isQuantized = true;
171 d.quantizeStep = 1.0;
172 d.sampleType = OutputDescriptor::VariableSampleRate;
173 d.sampleRate = float(rate);
174 m_d->m_incrementsOutput = list.size();
175 list.push_back(d);
176
177 d.identifier = "aggregate_increments";
178 d.name = "Accumulated Output Increments";
179 d.description = "Accumulated output time increments";
180 d.sampleRate = 0;
181 m_d->m_aggregateIncrementsOutput = list.size();
182 list.push_back(d);
183
184 d.identifier = "divergence";
185 d.name = "Divergence from Linear";
186 d.description = "Difference between actual output time and the output time for a theoretical linear stretch";
187 d.isQuantized = false;
188 d.sampleRate = 0;
189 m_d->m_divergenceOutput = list.size();
190 list.push_back(d);
191
192 d.identifier = "phaseresetdf";
193 d.name = "Phase Reset Detection Function";
194 d.description = "Curve whose peaks are used to identify transients for phase reset points";
195 d.unit = "";
196 d.sampleRate = float(rate);
197 m_d->m_phaseResetDfOutput = list.size();
198 list.push_back(d);
199
200 d.identifier = "smoothedphaseresetdf";
201 d.name = "Smoothed Phase Reset Detection Function";
202 d.description = "Phase reset curve smoothed for peak picking";
203 d.unit = "";
204 m_d->m_smoothedPhaseResetDfOutput = list.size();
205 list.push_back(d);
206
207 d.identifier = "phaseresetpoints";
208 d.name = "Phase Reset Points";
209 d.description = "Points estimated as transients at which phase reset occurs";
210 d.unit = "";
211 d.hasFixedBinCount = true;
212 d.binCount = 0;
213 d.hasKnownExtents = false;
214 d.isQuantized = false;
215 d.sampleRate = 0;
216 m_d->m_phaseResetPointsOutput = list.size();
217 list.push_back(d);
218
219 d.identifier = "timesyncpoints";
220 d.name = "Time Sync Points";
221 d.description = "Salient points which stretcher aims to place with strictly correct timing";
222 d.unit = "";
223 d.hasFixedBinCount = true;
224 d.binCount = 0;
225 d.hasKnownExtents = false;
226 d.isQuantized = false;
227 d.sampleRate = 0;
228 m_d->m_timeSyncPointsOutput = list.size();
229 list.push_back(d);
230
231 return list;
232 }
233
234 RubberBandVampPlugin::ParameterList
235 RubberBandVampPlugin::getParameterDescriptors() const
236 {
237 ParameterList list;
238
239 ParameterDescriptor d;
240 d.identifier = "timeratio";
241 d.name = "Time Ratio";
242 d.description = "Ratio to modify overall duration by";
243 d.unit = "%";
244 d.minValue = 1;
245 d.maxValue = 500;
246 d.defaultValue = 100;
247 d.isQuantized = false;
248 list.push_back(d);
249
250 d.identifier = "pitchratio";
251 d.name = "Pitch Scale Ratio";
252 d.description = "Frequency ratio to modify pitch by";
253 d.unit = "%";
254 d.minValue = 1;
255 d.maxValue = 500;
256 d.defaultValue = 100;
257 d.isQuantized = false;
258 list.push_back(d);
259
260 d.identifier = "mode";
261 d.name = "Processing Mode";
262 d.description = ""; //!!!
263 d.unit = "";
264 d.minValue = 0;
265 d.maxValue = 1;
266 d.defaultValue = 0;
267 d.isQuantized = true;
268 d.quantizeStep = 1;
269 d.valueNames.clear();
270 d.valueNames.push_back("Offline");
271 d.valueNames.push_back("Real Time");
272 list.push_back(d);
273
274 d.identifier = "stretchtype";
275 d.name = "Stretch Flexibility";
276 d.description = ""; //!!!
277 d.unit = "";
278 d.minValue = 0;
279 d.maxValue = 1;
280 d.defaultValue = 0;
281 d.isQuantized = true;
282 d.quantizeStep = 1;
283 d.valueNames.clear();
284 d.valueNames.push_back("Elastic");
285 d.valueNames.push_back("Precise");
286 list.push_back(d);
287
288 d.identifier = "transientmode";
289 d.name = "Transient Handling";
290 d.description = ""; //!!!
291 d.unit = "";
292 d.minValue = 0;
293 d.maxValue = 2;
294 d.defaultValue = 0;
295 d.isQuantized = true;
296 d.quantizeStep = 1;
297 d.valueNames.clear();
298 d.valueNames.push_back("Mixed");
299 d.valueNames.push_back("Smooth");
300 d.valueNames.push_back("Crisp");
301 list.push_back(d);
302
303 d.identifier = "phasemode";
304 d.name = "Phase Handling";
305 d.description = ""; //!!!
306 d.unit = "";
307 d.minValue = 0;
308 d.maxValue = 1;
309 d.defaultValue = 0;
310 d.isQuantized = true;
311 d.quantizeStep = 1;
312 d.valueNames.clear();
313 d.valueNames.push_back("Laminar");
314 d.valueNames.push_back("Independent");
315 list.push_back(d);
316
317 d.identifier = "windowmode";
318 d.name = "Window Length";
319 d.description = ""; //!!!
320 d.unit = "";
321 d.minValue = 0;
322 d.maxValue = 2;
323 d.defaultValue = 0;
324 d.isQuantized = true;
325 d.quantizeStep = 1;
326 d.valueNames.clear();
327 d.valueNames.push_back("Standard");
328 d.valueNames.push_back("Short");
329 d.valueNames.push_back("Long");
330 list.push_back(d);
331
332 return list;
333 }
334
335 float
336 RubberBandVampPlugin::getParameter(std::string id) const
337 {
338 if (id == "timeratio") return m_d->m_timeRatio * 100.f;
339 if (id == "pitchratio") return m_d->m_pitchRatio * 100.f;
340 if (id == "mode") return m_d->m_realtime ? 1.f : 0.f;
341 if (id == "stretchtype") return m_d->m_elasticTiming ? 0.f : 1.f;
342 if (id == "transientmode") return float(m_d->m_transientMode);
343 if (id == "phasemode") return m_d->m_phaseIndependent ? 1.f : 0.f;
344 if (id == "windowmode") return float(m_d->m_windowLength);
345 return 0.f;
346 }
347
348 void
349 RubberBandVampPlugin::setParameter(std::string id, float value)
350 {
351 if (id == "timeratio") {
352 m_d->m_timeRatio = value / 100;
353 } else if (id == "pitchratio") {
354 m_d->m_pitchRatio = value / 100;
355 } else {
356 bool set = (value > 0.5);
357 if (id == "mode") m_d->m_realtime = set;
358 else if (id == "stretchtype") m_d->m_elasticTiming = !set;
359 else if (id == "transientmode") m_d->m_transientMode = int(value + 0.5);
360 else if (id == "phasemode") m_d->m_phaseIndependent = set;
361 else if (id == "windowmode") m_d->m_windowLength = int(value + 0.5);
362 }
363 }
364
365 bool
366 RubberBandVampPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
367 {
368 if (channels < getMinChannelCount() ||
369 channels > getMaxChannelCount()) return false;
370
371 m_d->m_stepSize = std::min(stepSize, blockSize);
372 m_d->m_blockSize = stepSize;
373
374 RubberBand::RubberBandStretcher::Options options = 0;
375
376 if (m_d->m_realtime)
377 options |= RubberBand::RubberBandStretcher::OptionProcessRealTime;
378 else options |= RubberBand::RubberBandStretcher::OptionProcessOffline;
379
380 if (m_d->m_elasticTiming)
381 options |= RubberBand::RubberBandStretcher::OptionStretchElastic;
382 else options |= RubberBand::RubberBandStretcher::OptionStretchPrecise;
383
384 if (m_d->m_transientMode == 0)
385 options |= RubberBand::RubberBandStretcher::OptionTransientsMixed;
386 else if (m_d->m_transientMode == 1)
387 options |= RubberBand::RubberBandStretcher::OptionTransientsSmooth;
388 else options |= RubberBand::RubberBandStretcher::OptionTransientsCrisp;
389
390 if (m_d->m_phaseIndependent)
391 options |= RubberBand::RubberBandStretcher::OptionPhaseIndependent;
392 else options |= RubberBand::RubberBandStretcher::OptionPhaseLaminar;
393
394 if (m_d->m_windowLength == 0)
395 options |= RubberBand::RubberBandStretcher::OptionWindowStandard;
396 else if (m_d->m_windowLength == 1)
397 options |= RubberBand::RubberBandStretcher::OptionWindowShort;
398 else options |= RubberBand::RubberBandStretcher::OptionWindowLong;
399
400 delete m_d->m_stretcher;
401 m_d->m_stretcher = new RubberBand::RubberBandStretcher
402 (m_d->m_sampleRate, channels, options);
403 m_d->m_stretcher->setDebugLevel(1);
404 m_d->m_stretcher->setTimeRatio(m_d->m_timeRatio);
405 m_d->m_stretcher->setPitchScale(m_d->m_pitchRatio);
406
407 m_d->m_counter = 0;
408 m_d->m_accumulatedIncrement = 0;
409
410 m_d->m_outputDump = 0;
411
412 return true;
413 }
414
415 void
416 RubberBandVampPlugin::reset()
417 {
418 if (m_d->m_stretcher) m_d->m_stretcher->reset();
419 }
420
421 RubberBandVampPlugin::FeatureSet
422 RubberBandVampPlugin::process(const float *const *inputBuffers,
423 Vamp::RealTime timestamp)
424 {
425 if (m_d->m_realtime) {
426 return m_d->processRealTime(inputBuffers, timestamp);
427 } else {
428 return m_d->processOffline(inputBuffers, timestamp);
429 }
430 }
431
432 RubberBandVampPlugin::FeatureSet
433 RubberBandVampPlugin::getRemainingFeatures()
434 {
435 if (m_d->m_realtime) {
436 return m_d->getRemainingFeaturesRealTime();
437 } else {
438 return m_d->getRemainingFeaturesOffline();
439 }
440 }
441
442 RubberBandVampPlugin::FeatureSet
443 RubberBandVampPlugin::Impl::processOffline(const float *const *inputBuffers,
444 Vamp::RealTime timestamp)
445 {
446 if (!m_stretcher) {
447 cerr << "ERROR: RubberBandVampPlugin::processOffline: "
448 << "RubberBandVampPlugin has not been initialised"
449 << endl;
450 return FeatureSet();
451 }
452
453 m_stretcher->study(inputBuffers, m_blockSize, false);
454 return FeatureSet();
455 }
456
457 RubberBandVampPlugin::FeatureSet
458 RubberBandVampPlugin::Impl::getRemainingFeaturesOffline()
459 {
460 m_stretcher->study(0, 0, true);
461
462 m_stretcher->calculateStretch();
463
464 int rate = m_sampleRate;
465
466 RubberBand::StretchCalculator sc(rate, m_stretcher->getInputIncrement(), true);
467
468 size_t inputIncrement = m_stretcher->getInputIncrement();
469 std::vector<int> outputIncrements = m_stretcher->getOutputIncrements();
470 std::vector<float> phaseResetDf = m_stretcher->getPhaseResetCurve();
471 std::vector<int> peaks = m_stretcher->getExactTimePoints();
472 std::vector<float> smoothedDf = sc.smoothDF(phaseResetDf);
473
474 FeatureSet features = createFeatures
475 (inputIncrement, outputIncrements, phaseResetDf, peaks, smoothedDf,
476 0, true);
477
478 return features;
479 }
480
481 RubberBandVampPlugin::FeatureSet
482 RubberBandVampPlugin::Impl::processRealTime(const float *const *inputBuffers,
483 Vamp::RealTime timestamp)
484 {
485 // This function is not in any way a real-time function (i.e. it
486 // has no requirement to be RT safe); it simply operates the
487 // stretcher in RT mode.
488
489 if (!m_stretcher) {
490 cerr << "ERROR: RubberBandVampPlugin::processRealTime: "
491 << "RubberBandVampPlugin has not been initialised"
492 << endl;
493 return FeatureSet();
494 }
495
496 m_stretcher->process(inputBuffers, m_blockSize, false);
497
498 size_t inputIncrement = m_stretcher->getInputIncrement();
499 std::vector<int> outputIncrements = m_stretcher->getOutputIncrements();
500 std::vector<float> phaseResetDf = m_stretcher->getPhaseResetCurve();
501 std::vector<float> smoothedDf; // not meaningful in RT mode
502 std::vector<int> dummyPoints;
503 FeatureSet features = createFeatures
504 (inputIncrement, outputIncrements, phaseResetDf, dummyPoints, smoothedDf,
505 m_counter, false);
506 m_counter += outputIncrements.size();
507
508 int available = 0;
509 while ((available = m_stretcher->available()) > 0) {
510 if (!m_outputDump) {
511 m_outputDump = new float *[m_stretcher->getChannelCount()];
512 for (size_t i = 0; i < m_stretcher->getChannelCount(); ++i) {
513 m_outputDump[i] = new float[m_blockSize];
514 }
515 }
516 m_stretcher->retrieve(m_outputDump,
517 std::min(int(m_blockSize), available));
518 }
519
520 return features;
521 }
522
523 RubberBandVampPlugin::FeatureSet
524 RubberBandVampPlugin::Impl::getRemainingFeaturesRealTime()
525 {
526 return FeatureSet();
527 }
528
529 RubberBandVampPlugin::FeatureSet
530 RubberBandVampPlugin::Impl::createFeatures(size_t inputIncrement,
531 std::vector<int> &outputIncrements,
532 std::vector<float> &phaseResetDf,
533 std::vector<int> &exactPoints,
534 std::vector<float> &smoothedDf,
535 size_t baseCount,
536 bool includeFinal)
537 {
538 size_t actual = m_accumulatedIncrement;
539
540 double overallRatio = m_timeRatio * m_pitchRatio;
541
542 char label[200];
543
544 FeatureSet features;
545
546 int rate = m_sampleRate;
547
548 size_t epi = 0;
549
550 for (size_t i = 0; i < outputIncrements.size(); ++i) {
551
552 size_t frame = (baseCount + i) * inputIncrement;
553
554 int oi = outputIncrements[i];
555 bool hard = false;
556 bool soft = false;
557
558 if (oi < 0) {
559 oi = -oi;
560 hard = true;
561 }
562
563 if (epi < exactPoints.size() && int(i) == exactPoints[epi]) {
564 soft = true;
565 ++epi;
566 }
567
568 double linear = (frame * overallRatio);
569
570 Vamp::RealTime t = Vamp::RealTime::frame2RealTime(frame, rate);
571
572 Feature feature;
573 feature.hasTimestamp = true;
574 feature.timestamp = t;
575 feature.values.push_back(float(oi));
576 feature.label = Vamp::RealTime::frame2RealTime(oi, rate).toText();
577 features[m_incrementsOutput].push_back(feature);
578
579 feature.values.clear();
580 feature.values.push_back(float(actual));
581 feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText();
582 features[m_aggregateIncrementsOutput].push_back(feature);
583
584 feature.values.clear();
585 feature.values.push_back(actual - linear);
586
587 sprintf(label, "expected %ld, actual %ld, difference %ld (%s ms)",
588 long(linear), long(actual), long(actual - linear),
589 // frame2RealTime expects an integer frame number,
590 // hence our multiplication factor
591 (Vamp::RealTime::frame2RealTime
592 (lrintf((actual - linear) * 1000), rate) / 1000)
593 .toText().c_str());
594 feature.label = label;
595
596 features[m_divergenceOutput].push_back(feature);
597 actual += oi;
598
599 char buf[30];
600
601 if (i < phaseResetDf.size()) {
602 feature.values.clear();
603 feature.values.push_back(phaseResetDf[i]);
604 sprintf(buf, "%d", int(baseCount + i));
605 feature.label = buf;
606 features[m_phaseResetDfOutput].push_back(feature);
607 }
608
609 if (i < smoothedDf.size()) {
610 feature.values.clear();
611 feature.values.push_back(smoothedDf[i]);
612 features[m_smoothedPhaseResetDfOutput].push_back(feature);
613 }
614
615 if (hard) {
616 feature.values.clear();
617 feature.label = "Phase Reset";
618 features[m_phaseResetPointsOutput].push_back(feature);
619 }
620
621 if (hard || soft) {
622 feature.values.clear();
623 feature.label = "Time Sync";
624 features[m_timeSyncPointsOutput].push_back(feature);
625 }
626 }
627
628 if (includeFinal) {
629 Vamp::RealTime t = Vamp::RealTime::frame2RealTime
630 (inputIncrement * (baseCount + outputIncrements.size()), rate);
631 Feature feature;
632 feature.hasTimestamp = true;
633 feature.timestamp = t;
634 feature.label = Vamp::RealTime::frame2RealTime(actual, rate).toText();
635 feature.values.clear();
636 feature.values.push_back(float(actual));
637 features[m_aggregateIncrementsOutput].push_back(feature);
638
639 float linear = ((baseCount + outputIncrements.size())
640 * inputIncrement * overallRatio);
641 feature.values.clear();
642 feature.values.push_back(actual - linear);
643 feature.label = // see earlier comment
644 (Vamp::RealTime::frame2RealTime //!!! update this as earlier label
645 (lrintf((actual - linear) * 1000), rate) / 1000)
646 .toText();
647 features[m_divergenceOutput].push_back(feature);
648 }
649
650 m_accumulatedIncrement = actual;
651
652 return features;
653 }
654