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