various fixes to MidiRegionView selection handling, key handling, drawing of ghost...
[ardour2.git] / libs / rubberband / src / StretchCalculator.cpp
blob15417597623ec18cef883cd7655cc98e17707eef
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
3 /*
4 Rubber Band
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"
17 #include <algorithm>
18 #include <math.h>
19 #include <algorithm>
20 #include <iostream>
21 #include <deque>
22 #include <set>
23 #include <cassert>
24 #include <algorithm>
26 #include "sysutils.h"
28 namespace RubberBand
31 StretchCalculator::StretchCalculator(size_t sampleRate,
32 size_t inputIncrement,
33 bool useHardPeaks) :
34 m_sampleRate(sampleRate),
35 m_increment(inputIncrement),
36 m_prevDf(0),
37 m_divergence(0),
38 m_recovery(0),
39 m_prevRatio(1.0),
40 m_transientAmnesty(0),
41 m_useHardPeaks(useHardPeaks)
43 // std::cerr << "StretchCalculator::StretchCalculator: useHardPeaks = " << useHardPeaks << std::endl;
46 StretchCalculator::~StretchCalculator()
50 std::vector<int>
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;
103 if (i == 0) {
104 regionStartChunk = 0;
105 regionStart = 0;
106 } else {
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;
115 } else {
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;
165 return increments;
169 StretchCalculator::calculateSingle(double ratio,
170 float df,
171 size_t increment)
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) {
190 isTransient = true;
193 if (m_debugLevel > 2) {
194 std::cerr << "df = " << df << ", prevDf = " << m_prevDf
195 << ", thresh = " << transientThreshold << std::endl;
198 m_prevDf = df;
200 bool ratioChanged = (ratio != m_prevRatio);
201 m_prevRatio = ratio;
203 if (isTransient && m_transientAmnesty == 0) {
204 if (m_debugLevel > 1) {
205 std::cerr << "StretchCalculator::calculateSingle: transient"
206 << std::endl;
208 m_divergence += increment - (increment * ratio);
210 // as in offline mode, 0.05 sec approx min between transients
211 m_transientAmnesty =
212 lrint(ceil(double(m_sampleRate) / (20 * double(increment))));
214 m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment);
215 return -int(increment);
218 if (ratioChanged) {
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);
247 return incr;
250 void
251 StretchCalculator::reset()
253 m_prevDf = 0;
254 m_divergence = 0;
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) {
296 continue;
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
304 << std::endl;
307 if (!hard) {
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"
313 << std::endl;
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"
325 << std::endl;
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"
340 << std::endl;
344 if (!hard) continue;
346 // (df[i+1] > df[i] && df[i+1] > df[i-1] * 1.8) ||
347 // df[i] > 0.4) {
349 size_t peakLocation = i;
351 if (i + 1 < rawDf.size() &&
352 rawDf[i + 1] > rawDf[i] * 1.4) {
354 ++peakLocation;
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) {
373 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]);
414 } else {
415 medianwin.push_back(0);
417 continue;
420 if (m_debugLevel > 2) {
421 // std::cerr << "have " << mediansize << " in median buffer" << std::endl;
424 sorted.clear();
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] << " ";
442 // }
443 // std::cerr << std::endl;
444 // }
445 // }
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];
458 maxindex = j;
459 } else if (medianwin[j] < medianwin[middle]) {
460 break;
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] << " > "
474 << thresh << " and "
475 << medianwin[middle]
476 << " > " << medianwin[middle-1] << " and "
477 << medianwin[middle]
478 << " > " << medianwin[middle+1]
479 << std::endl;
482 if (peak >= df.size()) {
483 if (m_debugLevel > 2) {
484 std::cerr << "peak is beyond end" << std::endl;
486 } else {
487 softPeakCandidates.insert(peak);
488 lastSoftPeak = 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]);
504 } else {
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);
519 Peak peak;
520 peak.hard = false;
521 peak.chunk = softPeak;
523 bool ignore = false;
525 if (haveHardPeak &&
526 (!haveSoftPeak || hardPeak <= softPeak)) {
528 if (m_debugLevel > 2) {
529 std::cerr << "Hard peak: " << hardPeak << std::endl;
532 peak.hard = true;
533 peak.chunk = hardPeak;
534 hardPeakCandidates.erase(hardPeakCandidates.begin());
536 } else {
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)"
545 << std::endl;
547 ignore = true;
551 if (haveSoftPeak && peak.chunk == softPeak) {
552 softPeakCandidates.erase(softPeakCandidates.begin());
555 if (!ignore) {
556 peaks.push_back(peak);
560 return peaks;
563 std::vector<float>
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);
578 return smoothedDF;
581 std::vector<int>
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) {
604 df[j] = df[i-1];
606 break;
610 float maxDf = 0;
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
642 // wrong.
644 // Note that the ratio is only provided to this function for the
645 // purposes of establishing this bound to the displacement.
647 // so if
648 // maxDisplacement / totalDisplacement > increment * ratio*2 - increment
649 // (for ratio > 1)
650 // or
651 // maxDisplacement / totalDisplacement < increment * ratio/2
652 // (for ratio < 1)
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
661 maxDf = 0;
662 float adj = 0;
664 while (!acceptableSquashRange) {
666 acceptableSquashRange = true;
667 calculateDisplacements(df, maxDf, totalDisplacement, maxDisplacement,
668 adj);
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;
679 adj = 1;
681 continue;
684 int extremeIncrement = m_increment + lrint((toAllot * maxDisplacement) / totalDisplacement);
685 if (ratio < 1.0) {
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;
694 } else {
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.
709 adj += maxDf/10;
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;
723 } else {
724 increments.push_back(m_increment);
725 totalIncrement += m_increment;
727 totalDisplacement -= displacement;
728 continue;
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
744 // input increment
745 std::cerr << "*** WARNING: increment " << increment << " <= 0, rounding to zero" << std::endl;
746 increment = 0;
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;
769 return increments;
772 void
773 StretchCalculator::calculateDisplacements(const std::vector<float> &df,
774 float &maxDf,
775 double &totalDisplacement,
776 double &maxDisplacement,
777 float adj) const
779 totalDisplacement = maxDisplacement = 0;
781 maxDf = 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;