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-2008 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 "StretchCalculator.h"
31 StretchCalculator::StretchCalculator(size_t sampleRate
,
32 size_t inputIncrement
,
34 m_sampleRate(sampleRate
),
35 m_increment(inputIncrement
),
40 m_transientAmnesty(0),
41 m_useHardPeaks(useHardPeaks
)
43 // std::cerr << "StretchCalculator::StretchCalculator: useHardPeaks = " << useHardPeaks << std::endl;
46 StretchCalculator::~StretchCalculator()
51 StretchCalculator::calculate(double ratio
, size_t inputDuration
,
52 const std::vector
<float> &phaseResetDf
,
53 const std::vector
<float> &stretchDf
)
55 assert(phaseResetDf
.size() == stretchDf
.size());
57 m_lastPeaks
= findPeaks(phaseResetDf
);
58 std::vector
<Peak
> &peaks
= m_lastPeaks
;
59 size_t totalCount
= phaseResetDf
.size();
61 std::vector
<int> increments
;
63 size_t outputDuration
= lrint(inputDuration
* ratio
);
65 if (m_debugLevel
> 0) {
66 std::cerr
<< "StretchCalculator::calculate(): inputDuration " << inputDuration
<< ", ratio " << ratio
<< ", outputDuration " << outputDuration
;
69 outputDuration
= lrint((phaseResetDf
.size() * m_increment
) * ratio
);
71 if (m_debugLevel
> 0) {
72 std::cerr
<< " (rounded up to " << outputDuration
<< ")";
73 std::cerr
<< ", df size " << phaseResetDf
.size() << std::endl
;
76 std::vector
<size_t> fixedAudioChunks
;
77 for (size_t i
= 0; i
< peaks
.size(); ++i
) {
78 fixedAudioChunks
.push_back
79 (lrint((double(peaks
[i
].chunk
) * outputDuration
) / totalCount
));
82 if (m_debugLevel
> 1) {
83 std::cerr
<< "have " << peaks
.size() << " fixed positions" << std::endl
;
86 size_t totalInput
= 0, totalOutput
= 0;
88 // For each region between two consecutive time sync points, we
89 // want to take the number of output chunks to be allocated and
90 // the detection function values within the range, and produce a
91 // series of increments that sum to the number of output chunks,
92 // such that each increment is displaced from the input increment
93 // by an amount inversely proportional to the magnitude of the
94 // stretch detection function at that input step.
96 size_t regionTotalChunks
= 0;
98 for (size_t i
= 0; i
<= peaks
.size(); ++i
) {
100 size_t regionStart
, regionStartChunk
, regionEnd
, regionEndChunk
;
101 bool phaseReset
= false;
104 regionStartChunk
= 0;
107 regionStartChunk
= peaks
[i
-1].chunk
;
108 regionStart
= fixedAudioChunks
[i
-1];
109 phaseReset
= peaks
[i
-1].hard
;
112 if (i
== peaks
.size()) {
113 regionEndChunk
= totalCount
;
114 regionEnd
= outputDuration
;
116 regionEndChunk
= peaks
[i
].chunk
;
117 regionEnd
= fixedAudioChunks
[i
];
120 size_t regionDuration
= regionEnd
- regionStart
;
121 regionTotalChunks
+= regionDuration
;
123 std::vector
<float> dfRegion
;
125 for (size_t j
= regionStartChunk
; j
!= regionEndChunk
; ++j
) {
126 dfRegion
.push_back(stretchDf
[j
]);
129 if (m_debugLevel
> 1) {
130 std::cerr
<< "distributeRegion from " << regionStartChunk
<< " to " << regionEndChunk
<< " (chunks " << regionStart
<< " to " << regionEnd
<< ")" << std::endl
;
133 dfRegion
= smoothDF(dfRegion
);
135 std::vector
<int> regionIncrements
= distributeRegion
136 (dfRegion
, regionDuration
, ratio
, phaseReset
);
138 size_t totalForRegion
= 0;
140 for (size_t j
= 0; j
< regionIncrements
.size(); ++j
) {
142 int incr
= regionIncrements
[j
];
144 if (j
== 0 && phaseReset
) increments
.push_back(-incr
);
145 else increments
.push_back(incr
);
147 if (incr
> 0) totalForRegion
+= incr
;
148 else totalForRegion
+= -incr
;
150 totalInput
+= m_increment
;
153 if (totalForRegion
!= regionDuration
) {
154 std::cerr
<< "*** WARNING: distributeRegion returned wrong duration " << totalForRegion
<< ", expected " << regionDuration
<< std::endl
;
157 totalOutput
+= totalForRegion
;
160 if (m_debugLevel
> 0) {
161 std::cerr
<< "total input increment = " << totalInput
<< " (= " << totalInput
/ m_increment
<< " chunks), output = " << totalOutput
<< ", ratio = " << double(totalOutput
)/double(totalInput
) << ", ideal output " << size_t(ceil(totalInput
* ratio
)) << std::endl
;
162 std::cerr
<< "(region total = " << regionTotalChunks
<< ")" << std::endl
;
169 StretchCalculator::calculateSingle(double ratio
,
173 if (increment
== 0) increment
= m_increment
;
175 bool isTransient
= false;
177 // We want to ensure, as close as possible, that the phase reset
178 // points appear at _exactly_ the right audio frame numbers.
180 // In principle, the threshold depends on chunk size: larger chunk
181 // sizes need higher thresholds. Since chunk size depends on
182 // ratio, I suppose we could in theory calculate the threshold
183 // from the ratio directly. For the moment we're happy if it
184 // works well in common situations.
186 float transientThreshold
= 0.35f
;
187 if (ratio
> 1) transientThreshold
= 0.25f
;
189 if (m_useHardPeaks
&& df
> m_prevDf
* 1.1f
&& df
> transientThreshold
) {
193 if (m_debugLevel
> 2) {
194 std::cerr
<< "df = " << df
<< ", prevDf = " << m_prevDf
195 << ", thresh = " << transientThreshold
<< std::endl
;
200 bool ratioChanged
= (ratio
!= m_prevRatio
);
203 if (isTransient
&& m_transientAmnesty
== 0) {
204 if (m_debugLevel
> 1) {
205 std::cerr
<< "StretchCalculator::calculateSingle: transient"
208 m_divergence
+= increment
- (increment
* ratio
);
210 // as in offline mode, 0.05 sec approx min between transients
212 lrint(ceil(double(m_sampleRate
) / (20 * double(increment
))));
214 m_recovery
= m_divergence
/ ((m_sampleRate
/ 10.0) / increment
);
215 return -int(increment
);
219 m_recovery
= m_divergence
/ ((m_sampleRate
/ 10.0) / increment
);
222 if (m_transientAmnesty
> 0) --m_transientAmnesty
;
224 int incr
= lrint(increment
* ratio
- m_recovery
);
225 if (m_debugLevel
> 2 || (m_debugLevel
> 1 && m_divergence
!= 0)) {
226 std::cerr
<< "divergence = " << m_divergence
<< ", recovery = " << m_recovery
<< ", incr = " << incr
<< ", ";
228 if (incr
< lrint((increment
* ratio
) / 2)) {
229 incr
= lrint((increment
* ratio
) / 2);
230 } else if (incr
> lrint(increment
* ratio
* 2)) {
231 incr
= lrint(increment
* ratio
* 2);
234 double divdiff
= (increment
* ratio
) - incr
;
236 if (m_debugLevel
> 2 || (m_debugLevel
> 1 && m_divergence
!= 0)) {
237 std::cerr
<< "divdiff = " << divdiff
<< std::endl
;
240 double prevDivergence
= m_divergence
;
241 m_divergence
-= divdiff
;
242 if ((prevDivergence
< 0 && m_divergence
> 0) ||
243 (prevDivergence
> 0 && m_divergence
< 0)) {
244 m_recovery
= m_divergence
/ ((m_sampleRate
/ 10.0) / increment
);
251 StretchCalculator::reset()
257 std::vector
<StretchCalculator::Peak
>
258 StretchCalculator::findPeaks(const std::vector
<float> &rawDf
)
260 std::vector
<float> df
= smoothDF(rawDf
);
262 // We distinguish between "soft" and "hard" peaks. A soft peak is
263 // simply the result of peak-picking on the smoothed onset
264 // detection function, and it represents any (strong-ish) onset.
265 // We aim to ensure always that soft peaks are placed at the
266 // correct position in time. A hard peak is where there is a very
267 // rapid rise in detection function, and it presumably represents
268 // a more broadband, noisy transient. For these we perform a
269 // phase reset (if in the appropriate mode), and we locate the
270 // reset at the first point where we notice enough of a rapid
271 // rise, rather than necessarily at the peak itself, in order to
272 // preserve the shape of the transient.
274 std::set
<size_t> hardPeakCandidates
;
275 std::set
<size_t> softPeakCandidates
;
277 if (m_useHardPeaks
) {
279 // 0.05 sec approx min between hard peaks
280 size_t hardPeakAmnesty
= lrint(ceil(double(m_sampleRate
) /
281 (20 * double(m_increment
))));
282 size_t prevHardPeak
= 0;
284 if (m_debugLevel
> 1) {
285 std::cerr
<< "hardPeakAmnesty = " << hardPeakAmnesty
<< std::endl
;
288 for (size_t i
= 1; i
+ 1 < df
.size(); ++i
) {
290 if (df
[i
] < 0.1) continue;
291 if (df
[i
] <= df
[i
-1] * 1.1) continue;
292 if (df
[i
] < 0.22) continue;
294 if (!hardPeakCandidates
.empty() &&
295 i
< prevHardPeak
+ hardPeakAmnesty
) {
299 bool hard
= (df
[i
] > 0.4);
301 if (hard
&& (m_debugLevel
> 1)) {
302 std::cerr
<< "hard peak at " << i
<< ": " << df
[i
]
303 << " > absolute " << 0.4
308 hard
= (df
[i
] > df
[i
-1] * 1.4);
310 if (hard
&& (m_debugLevel
> 1)) {
311 std::cerr
<< "hard peak at " << i
<< ": " << df
[i
]
312 << " > prev " << df
[i
-1] << " * 1.4"
317 if (!hard
&& i
> 1) {
318 hard
= (df
[i
] > df
[i
-1] * 1.2 &&
319 df
[i
-1] > df
[i
-2] * 1.2);
321 if (hard
&& (m_debugLevel
> 1)) {
322 std::cerr
<< "hard peak at " << i
<< ": " << df
[i
]
323 << " > prev " << df
[i
-1] << " * 1.2 and "
324 << df
[i
-1] << " > prev " << df
[i
-2] << " * 1.2"
329 if (!hard
&& i
> 2) {
330 // have already established that df[i] > df[i-1] * 1.1
331 hard
= (df
[i
] > 0.3 &&
332 df
[i
-1] > df
[i
-2] * 1.1 &&
333 df
[i
-2] > df
[i
-3] * 1.1);
335 if (hard
&& (m_debugLevel
> 1)) {
336 std::cerr
<< "hard peak at " << i
<< ": " << df
[i
]
337 << " > prev " << df
[i
-1] << " * 1.1 and "
338 << df
[i
-1] << " > prev " << df
[i
-2] << " * 1.1 and "
339 << df
[i
-2] << " > prev " << df
[i
-3] << " * 1.1"
346 // (df[i+1] > df[i] && df[i+1] > df[i-1] * 1.8) ||
349 size_t peakLocation
= i
;
351 if (i
+ 1 < rawDf
.size() &&
352 rawDf
[i
+ 1] > rawDf
[i
] * 1.4) {
356 if (m_debugLevel
> 1) {
357 std::cerr
<< "pushing hard peak forward to " << peakLocation
<< ": " << df
[peakLocation
] << " > " << df
[peakLocation
-1] << " * " << 1.4 << std::endl
;
361 hardPeakCandidates
.insert(peakLocation
);
362 prevHardPeak
= peakLocation
;
366 size_t medianmaxsize
= lrint(ceil(double(m_sampleRate
) /
367 double(m_increment
))); // 1 sec ish
369 if (m_debugLevel
> 1) {
370 std::cerr
<< "mediansize = " << medianmaxsize
<< std::endl
;
372 if (medianmaxsize
< 7) {
374 if (m_debugLevel
> 1) {
375 std::cerr
<< "adjusted mediansize = " << medianmaxsize
<< std::endl
;
379 int minspacing
= lrint(ceil(double(m_sampleRate
) /
380 (20 * double(m_increment
)))); // 0.05 sec ish
382 std::deque
<float> medianwin
;
383 std::vector
<float> sorted
;
384 int softPeakAmnesty
= 0;
386 for (size_t i
= 0; i
< medianmaxsize
/2; ++i
) {
387 medianwin
.push_back(0);
389 for (size_t i
= 0; i
< medianmaxsize
/2 && i
< df
.size(); ++i
) {
390 medianwin
.push_back(df
[i
]);
393 size_t lastSoftPeak
= 0;
395 for (size_t i
= 0; i
< df
.size(); ++i
) {
397 size_t mediansize
= medianmaxsize
;
399 if (medianwin
.size() < mediansize
) {
400 mediansize
= medianwin
.size();
403 size_t middle
= medianmaxsize
/ 2;
404 if (middle
>= mediansize
) middle
= mediansize
-1;
406 size_t nextDf
= i
+ mediansize
- middle
;
408 if (mediansize
< 2) {
409 if (mediansize
> medianmaxsize
) { // absurd, but never mind that
410 medianwin
.pop_front();
412 if (nextDf
< df
.size()) {
413 medianwin
.push_back(df
[nextDf
]);
415 medianwin
.push_back(0);
420 if (m_debugLevel
> 2) {
421 // std::cerr << "have " << mediansize << " in median buffer" << std::endl;
425 for (size_t j
= 0; j
< mediansize
; ++j
) {
426 sorted
.push_back(medianwin
[j
]);
428 std::sort(sorted
.begin(), sorted
.end());
430 size_t n
= 90; // percentile above which we pick peaks
431 size_t index
= (sorted
.size() * n
) / 100;
432 if (index
>= sorted
.size()) index
= sorted
.size()-1;
433 if (index
== sorted
.size()-1 && index
> 0) --index
;
434 float thresh
= sorted
[index
];
436 // if (m_debugLevel > 2) {
437 // std::cerr << "medianwin[" << middle << "] = " << medianwin[middle] << ", thresh = " << thresh << std::endl;
438 // if (medianwin[middle] == 0.f) {
439 // std::cerr << "contents: ";
440 // for (size_t j = 0; j < medianwin.size(); ++j) {
441 // std::cerr << medianwin[j] << " ";
443 // std::cerr << std::endl;
447 if (medianwin
[middle
] > thresh
&&
448 medianwin
[middle
] > medianwin
[middle
-1] &&
449 medianwin
[middle
] > medianwin
[middle
+1] &&
450 softPeakAmnesty
== 0) {
452 size_t maxindex
= middle
;
453 float maxval
= medianwin
[middle
];
455 for (size_t j
= middle
+1; j
< mediansize
; ++j
) {
456 if (medianwin
[j
] > maxval
) {
457 maxval
= medianwin
[j
];
459 } else if (medianwin
[j
] < medianwin
[middle
]) {
464 size_t peak
= i
+ maxindex
- middle
;
466 // std::cerr << "i = " << i << ", maxindex = " << maxindex << ", middle = " << middle << ", so peak at " << peak << std::endl;
468 if (softPeakCandidates
.empty() || lastSoftPeak
!= peak
) {
470 if (m_debugLevel
> 1) {
471 std::cerr
<< "soft peak at " << peak
<< " ("
472 << peak
* m_increment
<< "): "
473 << medianwin
[middle
] << " > "
476 << " > " << medianwin
[middle
-1] << " and "
478 << " > " << medianwin
[middle
+1]
482 if (peak
>= df
.size()) {
483 if (m_debugLevel
> 2) {
484 std::cerr
<< "peak is beyond end" << std::endl
;
487 softPeakCandidates
.insert(peak
);
492 softPeakAmnesty
= minspacing
+ maxindex
- middle
;
493 if (m_debugLevel
> 2) {
494 std::cerr
<< "amnesty = " << softPeakAmnesty
<< std::endl
;
497 } else if (softPeakAmnesty
> 0) --softPeakAmnesty
;
499 if (mediansize
>= medianmaxsize
) {
500 medianwin
.pop_front();
502 if (nextDf
< df
.size()) {
503 medianwin
.push_back(df
[nextDf
]);
505 medianwin
.push_back(0);
509 std::vector
<Peak
> peaks
;
511 while (!hardPeakCandidates
.empty() || !softPeakCandidates
.empty()) {
513 bool haveHardPeak
= !hardPeakCandidates
.empty();
514 bool haveSoftPeak
= !softPeakCandidates
.empty();
516 size_t hardPeak
= (haveHardPeak
? *hardPeakCandidates
.begin() : 0);
517 size_t softPeak
= (haveSoftPeak
? *softPeakCandidates
.begin() : 0);
521 peak
.chunk
= softPeak
;
526 (!haveSoftPeak
|| hardPeak
<= softPeak
)) {
528 if (m_debugLevel
> 2) {
529 std::cerr
<< "Hard peak: " << hardPeak
<< std::endl
;
533 peak
.chunk
= hardPeak
;
534 hardPeakCandidates
.erase(hardPeakCandidates
.begin());
537 if (m_debugLevel
> 2) {
538 std::cerr
<< "Soft peak: " << softPeak
<< std::endl
;
540 if (!peaks
.empty() &&
541 peaks
[peaks
.size()-1].hard
&&
542 peaks
[peaks
.size()-1].chunk
+ 3 >= softPeak
) {
543 if (m_debugLevel
> 2) {
544 std::cerr
<< "(ignoring, as we just had a hard peak)"
551 if (haveSoftPeak
&& peak
.chunk
== softPeak
) {
552 softPeakCandidates
.erase(softPeakCandidates
.begin());
556 peaks
.push_back(peak
);
564 StretchCalculator::smoothDF(const std::vector
<float> &df
)
566 std::vector
<float> smoothedDF
;
568 for (size_t i
= 0; i
< df
.size(); ++i
) {
569 // three-value moving mean window for simple smoothing
570 float total
= 0.f
, count
= 0;
571 if (i
> 0) { total
+= df
[i
-1]; ++count
; }
572 total
+= df
[i
]; ++count
;
573 if (i
+1 < df
.size()) { total
+= df
[i
+1]; ++count
; }
574 float mean
= total
/ count
;
575 smoothedDF
.push_back(mean
);
582 StretchCalculator::distributeRegion(const std::vector
<float> &dfIn
,
583 size_t duration
, float ratio
, bool phaseReset
)
585 std::vector
<float> df(dfIn
);
586 std::vector
<int> increments
;
588 // The peak for the stretch detection function may appear after
589 // the peak that we're using to calculate the start of the region.
590 // We don't want that. If we find a peak in the first half of
591 // the region, we should set all the values up to that point to
592 // the same value as the peak.
594 // (This might not be subtle enough, especially if the region is
595 // long -- we want a bound that corresponds to acoustic perception
596 // of the audible bounce.)
598 for (size_t i
= 1; i
< df
.size()/2; ++i
) {
599 if (df
[i
] < df
[i
-1]) {
600 if (m_debugLevel
> 1) {
601 std::cerr
<< "stretch peak offset: " << i
-1 << " (peak " << df
[i
-1] << ")" << std::endl
;
603 for (size_t j
= 0; j
< i
-1; ++j
) {
612 for (size_t i
= 0; i
< df
.size(); ++i
) {
613 if (i
== 0 || df
[i
] > maxDf
) maxDf
= df
[i
];
616 // We want to try to ensure the last 100ms or so (if possible) are
617 // tending back towards the maximum df, so that the stretchiness
618 // reduces at the end of the stretched region.
620 int reducedRegion
= lrint((0.1 * m_sampleRate
) / m_increment
);
621 if (reducedRegion
> int(df
.size()/5)) reducedRegion
= df
.size()/5;
623 for (int i
= 0; i
< reducedRegion
; ++i
) {
624 size_t index
= df
.size() - reducedRegion
+ i
;
625 df
[index
] = df
[index
] + ((maxDf
- df
[index
]) * i
) / reducedRegion
;
628 long toAllot
= long(duration
) - long(m_increment
* df
.size());
630 if (m_debugLevel
> 1) {
631 std::cerr
<< "region of " << df
.size() << " chunks, output duration " << duration
<< ", toAllot " << toAllot
<< std::endl
;
634 size_t totalIncrement
= 0;
636 // We place limits on the amount of displacement per chunk. if
637 // ratio < 0, no increment should be larger than increment*ratio
638 // or smaller than increment*ratio/2; if ratio > 0, none should be
639 // smaller than increment*ratio or larger than increment*ratio*2.
640 // We need to enforce this in the assignment of displacements to
641 // allotments, not by trying to respond if something turns out
644 // Note that the ratio is only provided to this function for the
645 // purposes of establishing this bound to the displacement.
648 // maxDisplacement / totalDisplacement > increment * ratio*2 - increment
651 // maxDisplacement / totalDisplacement < increment * ratio/2
654 // then we need to adjust and accommodate
656 bool acceptableSquashRange
= false;
658 double totalDisplacement
= 0;
659 double maxDisplacement
= 0; // min displacement will be 0 by definition
664 while (!acceptableSquashRange
) {
666 acceptableSquashRange
= true;
667 calculateDisplacements(df
, maxDf
, totalDisplacement
, maxDisplacement
,
670 if (m_debugLevel
> 1) {
671 std::cerr
<< "totalDisplacement " << totalDisplacement
<< ", max " << maxDisplacement
<< " (maxDf " << maxDf
<< ", df count " << df
.size() << ")" << std::endl
;
674 if (totalDisplacement
== 0) {
675 // Not usually a problem, in fact
676 // std::cerr << "WARNING: totalDisplacement == 0 (duration " << duration << ", " << df.size() << " values in df)" << std::endl;
677 if (!df
.empty() && adj
== 0) {
678 acceptableSquashRange
= false;
684 int extremeIncrement
= m_increment
+ lrint((toAllot
* maxDisplacement
) / totalDisplacement
);
686 if (extremeIncrement
> lrint(ceil(m_increment
* ratio
))) {
687 std::cerr
<< "ERROR: extreme increment " << extremeIncrement
<< " > " << m_increment
* ratio
<< " (this should not happen)" << std::endl
;
688 } else if (extremeIncrement
< (m_increment
* ratio
) / 2) {
689 if (m_debugLevel
> 0) {
690 std::cerr
<< "WARNING: extreme increment " << extremeIncrement
<< " < " << (m_increment
* ratio
) / 2 << std::endl
;
692 acceptableSquashRange
= false;
695 if (extremeIncrement
> m_increment
* ratio
* 2) {
696 if (m_debugLevel
> 0) {
697 std::cerr
<< "WARNING: extreme increment " << extremeIncrement
<< " > " << m_increment
* ratio
* 2 << std::endl
;
699 acceptableSquashRange
= false;
700 } else if (extremeIncrement
< lrint(floor(m_increment
* ratio
))) {
701 std::cerr
<< "ERROR: extreme increment " << extremeIncrement
<< " < " << m_increment
* ratio
<< " (I thought this couldn't happen?)" << std::endl
;
705 if (!acceptableSquashRange
) {
706 // Need to make maxDisplacement smaller as a proportion of
707 // the total displacement, yet ensure that the
708 // displacements still sum to the total.
713 for (size_t i
= 0; i
< df
.size(); ++i
) {
715 double displacement
= maxDf
- df
[i
];
716 if (displacement
< 0) displacement
-= adj
;
717 else displacement
+= adj
;
719 if (i
== 0 && phaseReset
) {
720 if (df
.size() == 1) {
721 increments
.push_back(duration
);
722 totalIncrement
+= duration
;
724 increments
.push_back(m_increment
);
725 totalIncrement
+= m_increment
;
727 totalDisplacement
-= displacement
;
731 double theoreticalAllotment
= 0;
733 if (totalDisplacement
!= 0) {
734 theoreticalAllotment
= (toAllot
* displacement
) / totalDisplacement
;
736 int allotment
= lrint(theoreticalAllotment
);
737 if (i
+ 1 == df
.size()) allotment
= toAllot
;
739 int increment
= m_increment
+ allotment
;
741 if (increment
<= 0) {
742 // this is a serious problem, the allocation is quite
743 // wrong if it allows increment to diverge so far from the
745 std::cerr
<< "*** WARNING: increment " << increment
<< " <= 0, rounding to zero" << std::endl
;
747 allotment
= increment
- m_increment
;
750 increments
.push_back(increment
);
751 totalIncrement
+= increment
;
753 toAllot
-= allotment
;
754 totalDisplacement
-= displacement
;
756 if (m_debugLevel
> 2) {
757 std::cerr
<< "df " << df
[i
] << ", smoothed " << df
[i
] << ", disp " << displacement
<< ", allot " << theoreticalAllotment
<< ", incr " << increment
<< ", remain " << toAllot
<< std::endl
;
761 if (m_debugLevel
> 2) {
762 std::cerr
<< "total increment: " << totalIncrement
<< ", left over: " << toAllot
<< " to allot, displacement " << totalDisplacement
<< std::endl
;
765 if (totalIncrement
!= duration
) {
766 std::cerr
<< "*** WARNING: calculated output duration " << totalIncrement
<< " != expected " << duration
<< std::endl
;
773 StretchCalculator::calculateDisplacements(const std::vector
<float> &df
,
775 double &totalDisplacement
,
776 double &maxDisplacement
,
779 totalDisplacement
= maxDisplacement
= 0;
783 for (size_t i
= 0; i
< df
.size(); ++i
) {
784 if (i
== 0 || df
[i
] > maxDf
) maxDf
= df
[i
];
787 for (size_t i
= 0; i
< df
.size(); ++i
) {
788 double displacement
= maxDf
- df
[i
];
789 if (displacement
< 0) displacement
-= adj
;
790 else displacement
+= adj
;
791 totalDisplacement
+= displacement
;
792 if (i
== 0 || displacement
> maxDisplacement
) {
793 maxDisplacement
= displacement
;