Audacity  3.0.3
WaveClip.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  WaveClip.cpp
6 
7  ?? Dominic Mazzoni
8  ?? Markus Meyer
9 
10 *******************************************************************//****************************************************************//*******************************************************************/
21 
22 #include "WaveClip.h"
23 
24 
25 
26 #include <math.h>
27 #include <vector>
28 #include <wx/log.h>
29 
30 #include "Sequence.h"
31 #include "Spectrum.h"
32 #include "Prefs.h"
33 #include "Envelope.h"
34 #include "Resample.h"
35 #include "WaveTrack.h"
36 #include "Profiler.h"
37 #include "InconsistencyException.h"
38 #include "UserException.h"
39 
41 #include "widgets/ProgressDialog.h"
42 
43 #ifdef _OPENMP
44 #include <omp.h>
45 #endif
46 
47 class WaveCache {
48 public:
50  : dirty(-1)
51  , start(-1)
52  , pps(0)
53  , rate(-1)
54  , where(0)
55  , min(0)
56  , max(0)
57  , rms(0)
58  , bl(0)
59  {
60  }
61 
62  WaveCache(size_t len_, double pixelsPerSecond, double rate_, double t0, int dirty_)
63  : dirty(dirty_)
64  , len(len_)
65  , start(t0)
66  , pps(pixelsPerSecond)
67  , rate(rate_)
68  , where(1 + len)
69  , min(len)
70  , max(len)
71  , rms(len)
72  , bl(len)
73  {
74  }
75 
77  {
78  }
79 
80  int dirty;
81  const size_t len { 0 }; // counts pixels, not samples
82  const double start;
83  const double pps;
84  const int rate;
85  std::vector<sampleCount> where;
86  std::vector<float> min;
87  std::vector<float> max;
88  std::vector<float> rms;
89  std::vector<int> bl;
90 };
91 
93  (float * __restrict buffer, const FFTParam *hFFT,
94  const float * __restrict window, size_t len, float * __restrict out)
95 {
96  size_t i;
97  if(len > hFFT->Points * 2)
98  len = hFFT->Points * 2;
99  for(i = 0; i < len; i++)
100  buffer[i] *= window[i];
101  for( ; i < (hFFT->Points * 2); i++)
102  buffer[i] = 0; // zero pad as needed
103  RealFFTf(buffer, hFFT);
104  // Handle the (real-only) DC
105  float power = buffer[0] * buffer[0];
106  if(power <= 0)
107  out[0] = -160.0;
108  else
109  out[0] = 10.0 * log10f(power);
110  for(i = 1; i < hFFT->Points; i++) {
111  const int index = hFFT->BitReversed[i];
112  const float re = buffer[index], im = buffer[index + 1];
113  power = re * re + im * im;
114  if(power <= 0)
115  out[i] = -160.0;
116  else
117  out[i] = 10.0*log10f(power);
118  }
119 }
120 
122  sampleFormat format, int rate, int colourIndex)
123 {
124  mRate = rate;
125  mColourIndex = colourIndex;
126  mSequence = std::make_unique<Sequence>(factory, format);
127 
128  mEnvelope = std::make_unique<Envelope>(true, 1e-7, 2.0, 1.0);
129 
130  mWaveCache = std::make_unique<WaveCache>();
131  mSpecCache = std::make_unique<SpecCache>();
132  mSpecPxCache = std::make_unique<SpecPxCache>(1);
133 }
134 
137  bool copyCutlines)
138 {
139  // essentially a copy constructor - but you must pass in the
140  // current sample block factory, because we might be copying
141  // from one project to another
142 
143  mOffset = orig.mOffset;
144  mRate = orig.mRate;
145  mColourIndex = orig.mColourIndex;
146  mSequence = std::make_unique<Sequence>(*orig.mSequence, factory);
147 
148  mEnvelope = std::make_unique<Envelope>(*orig.mEnvelope);
149 
150  mWaveCache = std::make_unique<WaveCache>();
151  mSpecCache = std::make_unique<SpecCache>();
152  mSpecPxCache = std::make_unique<SpecPxCache>(1);
153 
154  mName = orig.mName;
155 
156  if ( copyCutlines )
157  for (const auto &clip: orig.mCutLines)
158  mCutLines.push_back
159  ( std::make_unique<WaveClip>( *clip, factory, true ) );
160 
162 }
163 
166  bool copyCutlines,
167  double t0, double t1)
168 {
169  // Copy only a range of the other WaveClip
170 
171  mOffset = orig.mOffset;
172  mRate = orig.mRate;
173  mColourIndex = orig.mColourIndex;
174 
175  mWaveCache = std::make_unique<WaveCache>();
176  mSpecCache = std::make_unique<SpecCache>();
177  mSpecPxCache = std::make_unique<SpecPxCache>(1);
178 
180 
181  sampleCount s0, s1;
182 
183  orig.TimeToSamplesClip(t0, &s0);
184  orig.TimeToSamplesClip(t1, &s1);
185 
186  mSequence = orig.mSequence->Copy(factory, s0, s1);
187 
188  mEnvelope = std::make_unique<Envelope>(
189  *orig.mEnvelope,
190  mOffset + s0.as_double()/mRate,
191  mOffset + s1.as_double()/mRate
192  );
193 
194  if ( copyCutlines )
195  // Copy cutline clips that fall in the range
196  for (const auto &ppClip : orig.mCutLines)
197  {
198  const WaveClip* clip = ppClip.get();
199  double cutlinePosition = orig.mOffset + clip->GetOffset();
200  if (cutlinePosition >= t0 && cutlinePosition <= t1)
201  {
202  auto newCutLine =
203  std::make_unique< WaveClip >( *clip, factory, true );
204  newCutLine->SetOffset( cutlinePosition - t0 );
205  mCutLines.push_back(std::move(newCutLine));
206  }
207  }
208 }
209 
210 
212 {
213 }
214 
216 void WaveClip::SetOffset(double offset)
217 {
218  mOffset = offset;
219  mEnvelope->SetOffset(mOffset);
220 }
221 
223  sampleCount start, size_t len, bool mayThrow) const
224 {
225  return mSequence->Get(buffer, format, start, len, mayThrow);
226 }
227 
230  sampleCount start, size_t len)
231 {
232  // use Strong-guarantee
233  mSequence->SetSamples(buffer, format, start, len);
234 
235  // use No-fail-guarantee
236  MarkChanged();
237 }
238 
240 {
241  return &mSequence->GetBlockArray();
242 }
243 
245 {
246  return &mSequence->GetBlockArray();
247 }
248 
250 {
251  // JS: mOffset is the minimum value and it is returned; no clipping to 0
252  return mOffset;
253 }
254 
255 double WaveClip::GetEndTime() const
256 {
257  auto numSamples = mSequence->GetNumSamples();
258 
259  double maxLen = mOffset + (numSamples+mAppendBufferLen).as_double()/mRate;
260  // JS: calculated value is not the length;
261  // it is a maximum value and can be negative; no clipping to 0
262 
263  return maxLen;
264 }
265 
267 {
268  return sampleCount( floor(mOffset * mRate + 0.5) );
269 }
270 
272 {
273  return GetStartSample() + mSequence->GetNumSamples();
274 }
275 
277 {
278  return mSequence->GetNumSamples();
279 }
280 
281 // Bug 2288 allowed overlapping clips.
282 // This was a classic fencepost error.
283 // We are within the clip if start < t <= end.
284 // Note that BeforeClip and AfterClip must be consistent
285 // with this definition.
286 bool WaveClip::WithinClip(double t) const
287 {
288  auto ts = (sampleCount)floor(t * mRate + 0.5);
289  return ts > GetStartSample() && ts < GetEndSample() + mAppendBufferLen;
290 }
291 
292 bool WaveClip::BeforeClip(double t) const
293 {
294  auto ts = (sampleCount)floor(t * mRate + 0.5);
295  return ts <= GetStartSample();
296 }
297 
298 bool WaveClip::AfterClip(double t) const
299 {
300  auto ts = (sampleCount)floor(t * mRate + 0.5);
301  return ts >= GetEndSample() + mAppendBufferLen;
302 }
303 
304 // A sample at time t could be in the clip, but
305 // a clip start at time t still be from a clip
306 // not overlapping this one, with this test.
307 bool WaveClip::IsClipStartAfterClip(double t) const
308 {
309  auto ts = (sampleCount)floor(t * mRate + 0.5);
310  return ts >= GetEndSample() + mAppendBufferLen;
311 }
312 
313 
316 {
317  mWaveCache = std::make_unique<WaveCache>();
318 }
319 
320 namespace {
321 
322 inline
323 void findCorrection(const std::vector<sampleCount> &oldWhere, size_t oldLen,
324  size_t newLen,
325  double t0, double rate, double samplesPerPixel,
326  int &oldX0, double &correction)
327 {
328  // Mitigate the accumulation of location errors
329  // in copies of copies of ... of caches.
330  // Look at the loop that populates "where" below to understand this.
331 
332  // Find the sample position that is the origin in the old cache.
333  const double oldWhere0 = oldWhere[1].as_double() - samplesPerPixel;
334  const double oldWhereLast = oldWhere0 + oldLen * samplesPerPixel;
335  // Find the length in samples of the old cache.
336  const double denom = oldWhereLast - oldWhere0;
337 
338  // What sample would go in where[0] with no correction?
339  const double guessWhere0 = t0 * rate;
340 
341  if ( // Skip if old and NEW are disjoint:
342  oldWhereLast <= guessWhere0 ||
343  guessWhere0 + newLen * samplesPerPixel <= oldWhere0 ||
344  // Skip unless denom rounds off to at least 1.
345  denom < 0.5)
346  {
347  // The computation of oldX0 in the other branch
348  // may underflow and the assertion would be violated.
349  oldX0 = oldLen;
350  correction = 0.0;
351  }
352  else
353  {
354  // What integer position in the old cache array does that map to?
355  // (even if it is out of bounds)
356  oldX0 = floor(0.5 + oldLen * (guessWhere0 - oldWhere0) / denom);
357  // What sample count would the old cache have put there?
358  const double where0 = oldWhere0 + double(oldX0) * samplesPerPixel;
359  // What correction is needed to align the NEW cache with the old?
360  const double correction0 = where0 - guessWhere0;
361  correction = std::max(-samplesPerPixel, std::min(samplesPerPixel, correction0));
362  wxASSERT(correction == correction0);
363  }
364 }
365 
366 inline void
367 fillWhere(std::vector<sampleCount> &where, size_t len, double bias, double correction,
368  double t0, double rate, double samplesPerPixel)
369 {
370  // Be careful to make the first value non-negative
371  const double w0 = 0.5 + correction + bias + t0 * rate;
372  where[0] = sampleCount( std::max(0.0, floor(w0)) );
373  for (decltype(len) x = 1; x < len + 1; x++)
374  where[x] = sampleCount( floor(w0 + double(x) * samplesPerPixel) );
375 }
376 
377 }
378 
379 //
380 // Getting high-level data from the track for screen display and
381 // clipping calculations
382 //
383 
384 bool WaveClip::GetWaveDisplay(WaveDisplay &display, double t0,
385  double pixelsPerSecond) const
386 {
387  const bool allocated = (display.where != 0);
388 
389  const size_t numPixels = (int)display.width;
390 
391  size_t p0 = 0; // least column requiring computation
392  size_t p1 = numPixels; // greatest column requiring computation, plus one
393 
394  float *min;
395  float *max;
396  float *rms;
397  int *bl;
398  std::vector<sampleCount> *pWhere;
399 
400  if (allocated) {
401  // assume ownWhere is filled.
402  min = &display.min[0];
403  max = &display.max[0];
404  rms = &display.rms[0];
405  bl = &display.bl[0];
406  pWhere = &display.ownWhere;
407  }
408  else {
409  const double tstep = 1.0 / pixelsPerSecond;
410  const double samplesPerPixel = mRate * tstep;
411 
412  // Make a tolerant comparison of the pps values in this wise:
413  // accumulated difference of times over the number of pixels is less than
414  // a sample period.
415  const bool ppsMatch = mWaveCache &&
416  (fabs(tstep - 1.0 / mWaveCache->pps) * numPixels < (1.0 / mRate));
417 
418  const bool match =
419  mWaveCache &&
420  ppsMatch &&
421  mWaveCache->len > 0 &&
422  mWaveCache->dirty == mDirty;
423 
424  if (match &&
425  mWaveCache->start == t0 &&
426  mWaveCache->len >= numPixels) {
427 
428  // Satisfy the request completely from the cache
429  display.min = &mWaveCache->min[0];
430  display.max = &mWaveCache->max[0];
431  display.rms = &mWaveCache->rms[0];
432  display.bl = &mWaveCache->bl[0];
433  display.where = &mWaveCache->where[0];
434  return true;
435  }
436 
437  std::unique_ptr<WaveCache> oldCache(std::move(mWaveCache));
438 
439  int oldX0 = 0;
440  double correction = 0.0;
441  size_t copyBegin = 0, copyEnd = 0;
442  if (match) {
443  findCorrection(oldCache->where, oldCache->len, numPixels,
444  t0, mRate, samplesPerPixel,
445  oldX0, correction);
446  // Remember our first pixel maps to oldX0 in the old cache,
447  // possibly out of bounds.
448  // For what range of pixels can data be copied?
449  copyBegin = std::min<size_t>(numPixels, std::max(0, -oldX0));
450  copyEnd = std::min<size_t>(numPixels, std::max(0,
451  (int)oldCache->len - oldX0
452  ));
453  }
454  if (!(copyEnd > copyBegin))
455  oldCache.reset(0);
456 
457  mWaveCache = std::make_unique<WaveCache>(numPixels, pixelsPerSecond, mRate, t0, mDirty);
458  min = &mWaveCache->min[0];
459  max = &mWaveCache->max[0];
460  rms = &mWaveCache->rms[0];
461  bl = &mWaveCache->bl[0];
462  pWhere = &mWaveCache->where;
463 
464  fillWhere(*pWhere, numPixels, 0.0, correction,
465  t0, mRate, samplesPerPixel);
466 
467  // The range of pixels we must fetch from the Sequence:
468  p0 = (copyBegin > 0) ? 0 : copyEnd;
469  p1 = (copyEnd >= numPixels) ? copyBegin : numPixels;
470 
471  // Optimization: if the old cache is good and overlaps
472  // with the current one, re-use as much of the cache as
473  // possible
474 
475  if (oldCache) {
476 
477  // Copy what we can from the old cache.
478  const int length = copyEnd - copyBegin;
479  const size_t sizeFloats = length * sizeof(float);
480  const int srcIdx = (int)copyBegin + oldX0;
481  memcpy(&min[copyBegin], &oldCache->min[srcIdx], sizeFloats);
482  memcpy(&max[copyBegin], &oldCache->max[srcIdx], sizeFloats);
483  memcpy(&rms[copyBegin], &oldCache->rms[srcIdx], sizeFloats);
484  memcpy(&bl[copyBegin], &oldCache->bl[srcIdx], length * sizeof(int));
485  }
486  }
487 
488  if (p1 > p0) {
489  // Cache was not used or did not satisfy the whole request
490  std::vector<sampleCount> &where = *pWhere;
491 
492  /* handle values in the append buffer */
493 
494  auto numSamples = mSequence->GetNumSamples();
495  auto a = p0;
496 
497  // Not all of the required columns might be in the sequence.
498  // Some might be in the append buffer.
499  for (; a < p1; ++a) {
500  if (where[a + 1] > numSamples)
501  break;
502  }
503 
504  // Handle the columns that land in the append buffer.
505  //compute the values that are outside the overlap from scratch.
506  if (a < p1) {
507  sampleFormat seqFormat = mSequence->GetSampleFormat();
508  bool didUpdate = false;
509  for(auto i = a; i < p1; i++) {
510  auto left = std::max(sampleCount{ 0 },
511  where[i] - numSamples);
512  auto right = std::min(sampleCount{ mAppendBufferLen },
513  where[i + 1] - numSamples);
514 
515  //wxCriticalSectionLocker locker(mAppendCriticalSection);
516 
517  if (right > left) {
518  Floats b;
519  float *pb{};
520  // left is nonnegative and at most mAppendBufferLen:
521  auto sLeft = left.as_size_t();
522  // The difference is at most mAppendBufferLen:
523  size_t len = ( right - left ).as_size_t();
524 
525  if (seqFormat == floatSample)
526  pb = &((float *)mAppendBuffer.ptr())[sLeft];
527  else {
528  b.reinit(len);
529  pb = b.get();
531  mAppendBuffer.ptr() + sLeft * SAMPLE_SIZE(seqFormat),
532  seqFormat, pb, len);
533  }
534 
535  float theMax, theMin, sumsq;
536  {
537  const float val = pb[0];
538  theMax = theMin = val;
539  sumsq = val * val;
540  }
541  for(decltype(len) j = 1; j < len; j++) {
542  const float val = pb[j];
543  theMax = std::max(theMax, val);
544  theMin = std::min(theMin, val);
545  sumsq += val * val;
546  }
547 
548  min[i] = theMin;
549  max[i] = theMax;
550  rms[i] = (float)sqrt(sumsq / len);
551  bl[i] = 1; //for now just fake it.
552 
553  didUpdate=true;
554  }
555  }
556 
557  // Shrink the right end of the range to fetch from Sequence
558  if(didUpdate)
559  p1 = a;
560  }
561 
562  // Done with append buffer, now fetch the rest of the cache miss
563  // from the sequence
564  if (p1 > p0) {
565  if (!mSequence->GetWaveDisplay(&min[p0],
566  &max[p0],
567  &rms[p0],
568  &bl[p0],
569  p1-p0,
570  &where[p0]))
571  {
572  return false;
573  }
574  }
575  }
576 
577  if (!allocated) {
578  // Now report the results
579  display.min = min;
580  display.max = max;
581  display.rms = rms;
582  display.bl = bl;
583  display.where = &(*pWhere)[0];
584  }
585 
586  return true;
587 }
588 
589 namespace {
590 
592  (size_t fftLen, double rate, int frequencyGain, std::vector<float> &gainFactors)
593 {
594  if (frequencyGain > 0) {
595  // Compute a frequency-dependent gain factor
596  // scaled such that 1000 Hz gets a gain of 0dB
597 
598  // This is the reciprocal of the bin number of 1000 Hz:
599  const double factor = ((double)rate / (double)fftLen) / 1000.0;
600 
601  auto half = fftLen / 2;
602  gainFactors.reserve(half);
603  // Don't take logarithm of zero! Let bin 0 replicate the gain factor for bin 1.
604  gainFactors.push_back(frequencyGain*log10(factor));
605  for (decltype(half) x = 1; x < half; x++) {
606  gainFactors.push_back(frequencyGain*log10(factor * x));
607  }
608  }
609 }
610 
611 }
612 
614  (int dirty_, double pixelsPerSecond,
615  const SpectrogramSettings &settings, double rate) const
616 {
617  // Make a tolerant comparison of the pps values in this wise:
618  // accumulated difference of times over the number of pixels is less than
619  // a sample period.
620  const double tstep = 1.0 / pixelsPerSecond;
621  const bool ppsMatch =
622  (fabs(tstep - 1.0 / pps) * len < (1.0 / rate));
623 
624  return
625  ppsMatch &&
626  dirty == dirty_ &&
627  windowType == settings.windowType &&
628  windowSize == settings.WindowSize() &&
629  zeroPaddingFactor == settings.ZeroPaddingFactor() &&
630  frequencyGain == settings.frequencyGain &&
631  algorithm == settings.algorithm;
632 }
633 
636  WaveTrackCache &waveTrackCache,
637  const int xx, const sampleCount numSamples,
638  double offset, double rate, double pixelsPerSecond,
639  int lowerBoundX, int upperBoundX,
640  const std::vector<float> &gainFactors,
641  float* __restrict scratch, float* __restrict out) const
642 {
643  bool result = false;
644  const bool reassignment =
646  const size_t windowSizeSetting = settings.WindowSize();
647 
648  sampleCount from;
649 
650  // xx may be for a column that is out of the visible bounds, but only
651  // when we are calculating reassignment contributions that may cross into
652  // the visible area.
653 
654  if (xx < 0)
655  from = sampleCount(
656  where[0].as_double() + xx * (rate / pixelsPerSecond)
657  );
658  else if (xx > (int)len)
659  from = sampleCount(
660  where[len].as_double() + (xx - len) * (rate / pixelsPerSecond)
661  );
662  else
663  from = where[xx];
664 
665  const bool autocorrelation =
667  const size_t zeroPaddingFactorSetting = settings.ZeroPaddingFactor();
668  const size_t padding = (windowSizeSetting * (zeroPaddingFactorSetting - 1)) / 2;
669  const size_t fftLen = windowSizeSetting * zeroPaddingFactorSetting;
670  auto nBins = settings.NBins();
671 
672  if (from < 0 || from >= numSamples) {
673  if (xx >= 0 && xx < (int)len) {
674  // Pixel column is out of bounds of the clip! Should not happen.
675  float *const results = &out[nBins * xx];
676  std::fill(results, results + nBins, 0.0f);
677  }
678  }
679  else {
680 
681 
682  // We can avoid copying memory when ComputeSpectrum is used below
683  bool copy = !autocorrelation || (padding > 0) || reassignment;
684  float *useBuffer = 0;
685  float *adj = scratch + padding;
686 
687  {
688  auto myLen = windowSizeSetting;
689  // Take a window of the track centered at this sample.
690  from -= windowSizeSetting >> 1;
691  if (from < 0) {
692  // Near the start of the clip, pad left with zeroes as needed.
693  // from is at least -windowSize / 2
694  for (auto ii = from; ii < 0; ++ii)
695  *adj++ = 0;
696  myLen += from.as_long_long(); // add a negative
697  from = 0;
698  copy = true;
699  }
700 
701  if (from + myLen >= numSamples) {
702  // Near the end of the clip, pad right with zeroes as needed.
703  // newlen is bounded by myLen:
704  auto newlen = ( numSamples - from ).as_size_t();
705  for (decltype(myLen) ii = newlen; ii < myLen; ++ii)
706  adj[ii] = 0;
707  myLen = newlen;
708  copy = true;
709  }
710 
711  if (myLen > 0) {
712  useBuffer = (float*)(waveTrackCache.GetFloats(
713  sampleCount(
714  floor(0.5 + from.as_double() + offset * rate)
715  ),
716  myLen,
717  // Don't throw in this drawing operation
718  false)
719  );
720 
721  if (copy) {
722  if (useBuffer)
723  memcpy(adj, useBuffer, myLen * sizeof(float));
724  else
725  memset(adj, 0, myLen * sizeof(float));
726  }
727  }
728  }
729 
730  if (copy || !useBuffer)
731  useBuffer = scratch;
732 
733  if (autocorrelation) {
734  // not reassignment, xx is surely within bounds.
735  wxASSERT(xx >= 0);
736  float *const results = &out[nBins * xx];
737  // This function does not mutate useBuffer
738  ComputeSpectrum(useBuffer, windowSizeSetting, windowSizeSetting,
739  rate, results,
740  autocorrelation, settings.windowType);
741  }
742  else if (reassignment) {
743  static const double epsilon = 1e-16;
744  const auto hFFT = settings.hFFT.get();
745 
746  float *const scratch2 = scratch + fftLen;
747  std::copy(scratch, scratch2, scratch2);
748 
749  float *const scratch3 = scratch + 2 * fftLen;
750  std::copy(scratch, scratch2, scratch3);
751 
752  {
753  const float *const window = settings.window.get();
754  for (size_t ii = 0; ii < fftLen; ++ii)
755  scratch[ii] *= window[ii];
756  RealFFTf(scratch, hFFT);
757  }
758 
759  {
760  const float *const dWindow = settings.dWindow.get();
761  for (size_t ii = 0; ii < fftLen; ++ii)
762  scratch2[ii] *= dWindow[ii];
763  RealFFTf(scratch2, hFFT);
764  }
765 
766  {
767  const float *const tWindow = settings.tWindow.get();
768  for (size_t ii = 0; ii < fftLen; ++ii)
769  scratch3[ii] *= tWindow[ii];
770  RealFFTf(scratch3, hFFT);
771  }
772 
773  for (size_t ii = 0; ii < hFFT->Points; ++ii) {
774  const int index = hFFT->BitReversed[ii];
775  const float
776  denomRe = scratch[index],
777  denomIm = ii == 0 ? 0 : scratch[index + 1];
778  const double power = denomRe * denomRe + denomIm * denomIm;
779  if (power < epsilon)
780  // Avoid dividing by near-zero below
781  continue;
782 
783  double freqCorrection;
784  {
785  const double multiplier = -(fftLen / (2.0f * M_PI));
786  const float
787  numRe = scratch2[index],
788  numIm = ii == 0 ? 0 : scratch2[index + 1];
789  // Find complex quotient --
790  // Which means, multiply numerator by conjugate of denominator,
791  // then divide by norm squared of denominator --
792  // Then just take its imaginary part.
793  const double
794  quotIm = (-numRe * denomIm + numIm * denomRe) / power;
795  // With appropriate multiplier, that becomes the correction of
796  // the frequency bin.
797  freqCorrection = multiplier * quotIm;
798  }
799 
800  const int bin = (int)((int)ii + freqCorrection + 0.5f);
801  // Must check if correction takes bin out of bounds, above or below!
802  // bin is signed!
803  if (bin >= 0 && bin < (int)hFFT->Points) {
804  double timeCorrection;
805  {
806  const float
807  numRe = scratch3[index],
808  numIm = ii == 0 ? 0 : scratch3[index + 1];
809  // Find another complex quotient --
810  // Then just take its real part.
811  // The result has sample interval as unit.
812  timeCorrection =
813  (numRe * denomRe + numIm * denomIm) / power;
814  }
815 
816  int correctedX = (floor(0.5 + xx + timeCorrection * pixelsPerSecond / rate));
817  if (correctedX >= lowerBoundX && correctedX < upperBoundX)
818  {
819  result = true;
820 
821  // This is non-negative, because bin and correctedX are
822  auto ind = (int)nBins * correctedX + bin;
823 #ifdef _OPENMP
824  // This assignment can race if index reaches into another thread's bins.
825  // The probability of a race very low, so this carries little overhead,
826  // about 5% slower vs allowing it to race.
827  #pragma omp atomic update
828 #endif
829  out[ind] += power;
830  }
831  }
832  }
833  }
834  else {
835  // not reassignment, xx is surely within bounds.
836  wxASSERT(xx >= 0);
837  float *const results = &out[nBins * xx];
838 
839  // Do the FFT. Note that useBuffer is multiplied by the window,
840  // and the window is initialized with leading and trailing zeroes
841  // when there is padding. Therefore we did not need to reinitialize
842  // the part of useBuffer in the padding zones.
843 
844  // This function mutates useBuffer
846  (useBuffer, settings.hFFT.get(), settings.window.get(), fftLen, results);
847  if (!gainFactors.empty()) {
848  // Apply a frequency-dependent gain factor
849  for (size_t ii = 0; ii < nBins; ++ii)
850  results[ii] += gainFactors[ii];
851  }
852  }
853  }
854 
855  return result;
856 }
857 
859  double pixelsPerSecond, double start_)
860 {
861  settings.CacheWindows();
862 
863  // len columns, and so many rows, column-major.
864  // Don't take column literally -- this isn't pixel data yet, it's the
865  // raw data to be mapped onto the display.
866  freq.resize(len_ * settings.NBins());
867 
868  // Sample counts corresponding to the columns, and to one past the end.
869  where.resize(len_ + 1);
870 
871  len = len_;
872  algorithm = settings.algorithm;
873  pps = pixelsPerSecond;
874  start = start_;
875  windowType = settings.windowType;
876  windowSize = settings.WindowSize();
877  zeroPaddingFactor = settings.ZeroPaddingFactor();
878  frequencyGain = settings.frequencyGain;
879 }
880 
882  (const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache,
883  int copyBegin, int copyEnd, size_t numPixels,
884  sampleCount numSamples,
885  double offset, double rate, double pixelsPerSecond)
886 {
887  const int &frequencyGainSetting = settings.frequencyGain;
888  const size_t windowSizeSetting = settings.WindowSize();
889  const bool autocorrelation =
891  const bool reassignment =
893 #ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
894  const size_t zeroPaddingFactorSetting = settings.ZeroPaddingFactor();
895 #else
896  const size_t zeroPaddingFactorSetting = 1;
897 #endif
898 
899  // FFT length may be longer than the window of samples that affect results
900  // because of zero padding done for increased frequency resolution
901  const size_t fftLen = windowSizeSetting * zeroPaddingFactorSetting;
902  const auto nBins = settings.NBins();
903 
904  const size_t bufferSize = fftLen;
905  const size_t scratchSize = reassignment ? 3 * bufferSize : bufferSize;
906  std::vector<float> scratch(scratchSize);
907 
908  std::vector<float> gainFactors;
909  if (!autocorrelation)
910  ComputeSpectrogramGainFactors(fftLen, rate, frequencyGainSetting, gainFactors);
911 
912  // Loop over the ranges before and after the copied portion and compute anew.
913  // One of the ranges may be empty.
914  for (int jj = 0; jj < 2; ++jj) {
915  const int lowerBoundX = jj == 0 ? 0 : copyEnd;
916  const int upperBoundX = jj == 0 ? copyBegin : numPixels;
917 
918 #ifdef _OPENMP
919  // Storage for mutable per-thread data.
920  // private clause ensures one copy per thread
921  struct ThreadLocalStorage {
922  ThreadLocalStorage() { }
923  ~ThreadLocalStorage() { }
924 
925  void init(WaveTrackCache &waveTrackCache, size_t scratchSize) {
926  if (!cache) {
927  cache = std::make_unique<WaveTrackCache>(waveTrackCache.GetTrack());
928  scratch.resize(scratchSize);
929  }
930  }
931  std::unique_ptr<WaveTrackCache> cache;
932  std::vector<float> scratch;
933  } tls;
934 
935  #pragma omp parallel for private(tls)
936 #endif
937  for (auto xx = lowerBoundX; xx < upperBoundX; ++xx)
938  {
939 #ifdef _OPENMP
940  tls.init(waveTrackCache, scratchSize);
941  WaveTrackCache& cache = *tls.cache;
942  float* buffer = &tls.scratch[0];
943 #else
944  WaveTrackCache& cache = waveTrackCache;
945  float* buffer = &scratch[0];
946 #endif
948  settings, cache, xx, numSamples,
949  offset, rate, pixelsPerSecond,
950  lowerBoundX, upperBoundX,
951  gainFactors, buffer, &freq[0]);
952  }
953 
954  if (reassignment) {
955  // Need to look beyond the edges of the range to accumulate more
956  // time reassignments.
957  // I'm not sure what's a good stopping criterion?
958  auto xx = lowerBoundX;
959  const double pixelsPerSample = pixelsPerSecond / rate;
960  const int limit = std::min((int)(0.5 + fftLen * pixelsPerSample), 100);
961  for (int ii = 0; ii < limit; ++ii)
962  {
963  const bool result =
965  settings, waveTrackCache, --xx, numSamples,
966  offset, rate, pixelsPerSecond,
967  lowerBoundX, upperBoundX,
968  gainFactors, &scratch[0], &freq[0]);
969  if (!result)
970  break;
971  }
972 
973  xx = upperBoundX;
974  for (int ii = 0; ii < limit; ++ii)
975  {
976  const bool result =
978  settings, waveTrackCache, xx++, numSamples,
979  offset, rate, pixelsPerSecond,
980  lowerBoundX, upperBoundX,
981  gainFactors, &scratch[0], &freq[0]);
982  if (!result)
983  break;
984  }
985 
986  // Now Convert to dB terms. Do this only after accumulating
987  // power values, which may cross columns with the time correction.
988 #ifdef _OPENMP
989  #pragma omp parallel for
990 #endif
991  for (xx = lowerBoundX; xx < upperBoundX; ++xx) {
992  float *const results = &freq[nBins * xx];
993  for (size_t ii = 0; ii < nBins; ++ii) {
994  float &power = results[ii];
995  if (power <= 0)
996  power = -160.0;
997  else
998  power = 10.0*log10f(power);
999  }
1000  if (!gainFactors.empty()) {
1001  // Apply a frequency-dependent gain factor
1002  for (size_t ii = 0; ii < nBins; ++ii)
1003  results[ii] += gainFactors[ii];
1004  }
1005  }
1006  }
1007  }
1008 }
1009 
1011  const float *& spectrogram,
1012  const sampleCount *& where,
1013  size_t numPixels,
1014  double t0, double pixelsPerSecond) const
1015 {
1016  const WaveTrack *const track = waveTrackCache.GetTrack().get();
1018 
1019  bool match =
1020  mSpecCache &&
1021  mSpecCache->len > 0 &&
1022  mSpecCache->Matches
1023  (mDirty, pixelsPerSecond, settings, mRate);
1024 
1025  if (match &&
1026  mSpecCache->start == t0 &&
1027  mSpecCache->len >= numPixels) {
1028  spectrogram = &mSpecCache->freq[0];
1029  where = &mSpecCache->where[0];
1030 
1031  return false; //hit cache completely
1032  }
1033 
1034  // Caching is not implemented for reassignment, unless for
1035  // a complete hit, because of the complications of time reassignment
1037  match = false;
1038 
1039  // Free the cache when it won't cause a major stutter.
1040  // If the window size changed, we know there is nothing to be copied
1041  // If we zoomed out, or resized, we can give up memory. But not too much -
1042  // up to 2x extra is needed at the end of the clip to prevent stutter.
1043  if (mSpecCache->freq.capacity() > 2.1 * mSpecCache->freq.size() ||
1044  mSpecCache->windowSize*mSpecCache->zeroPaddingFactor <
1045  settings.WindowSize()*settings.ZeroPaddingFactor())
1046  {
1047  match = false;
1048  mSpecCache = std::make_unique<SpecCache>();
1049  }
1050 
1051  const double tstep = 1.0 / pixelsPerSecond;
1052  const double samplesPerPixel = mRate * tstep;
1053 
1054  int oldX0 = 0;
1055  double correction = 0.0;
1056 
1057  int copyBegin = 0, copyEnd = 0;
1058  if (match) {
1059  findCorrection(mSpecCache->where, mSpecCache->len, numPixels,
1060  t0, mRate, samplesPerPixel,
1061  oldX0, correction);
1062  // Remember our first pixel maps to oldX0 in the old cache,
1063  // possibly out of bounds.
1064  // For what range of pixels can data be copied?
1065  copyBegin = std::min((int)numPixels, std::max(0, -oldX0));
1066  copyEnd = std::min((int)numPixels, std::max(0,
1067  (int)mSpecCache->len - oldX0
1068  ));
1069  }
1070 
1071  // Resize the cache, keep the contents unchanged.
1072  mSpecCache->Grow(numPixels, settings, pixelsPerSecond, t0);
1073  auto nBins = settings.NBins();
1074 
1075  // Optimization: if the old cache is good and overlaps
1076  // with the current one, re-use as much of the cache as
1077  // possible
1078  if (copyEnd > copyBegin)
1079  {
1080  // memmove is required since dst/src overlap
1081  memmove(&mSpecCache->freq[nBins * copyBegin],
1082  &mSpecCache->freq[nBins * (copyBegin + oldX0)],
1083  nBins * (copyEnd - copyBegin) * sizeof(float));
1084  }
1085 
1086  // Reassignment accumulates, so it needs a zeroed buffer
1088  {
1089  // The cache could theoretically copy from the middle, resulting
1090  // in two regions to update. This won't happen in zoom, since
1091  // old cache doesn't match. It won't happen in resize, since the
1092  // spectrum view is pinned to left side of window.
1093  wxASSERT(
1094  (copyBegin >= 0 && copyEnd == (int)numPixels) || // copied the end
1095  (copyBegin == 0 && copyEnd <= (int)numPixels) // copied the beginning
1096  );
1097 
1098  int zeroBegin = copyBegin > 0 ? 0 : copyEnd-copyBegin;
1099  int zeroEnd = copyBegin > 0 ? copyBegin : numPixels;
1100 
1101  memset(&mSpecCache->freq[nBins*zeroBegin], 0, nBins*(zeroEnd-zeroBegin)*sizeof(float));
1102  }
1103 
1104  // purposely offset the display 1/2 sample to the left (as compared
1105  // to waveform display) to properly center response of the FFT
1106  fillWhere(mSpecCache->where, numPixels, 0.5, correction,
1107  t0, mRate, samplesPerPixel);
1108 
1109  mSpecCache->Populate
1110  (settings, waveTrackCache, copyBegin, copyEnd, numPixels,
1111  mSequence->GetNumSamples(),
1112  mOffset, mRate, pixelsPerSecond);
1113 
1114  mSpecCache->dirty = mDirty;
1115  spectrogram = &mSpecCache->freq[0];
1116  where = &mSpecCache->where[0];
1117 
1118  return true;
1119 }
1120 
1121 std::pair<float, float> WaveClip::GetMinMax(
1122  double t0, double t1, bool mayThrow) const
1123 {
1124  if (t0 > t1) {
1125  if (mayThrow)
1127  return {
1128  0.f, // harmless, but unused since Sequence::GetMinMax does not use these values
1129  0.f // harmless, but unused since Sequence::GetMinMax does not use these values
1130  };
1131  }
1132 
1133  if (t0 == t1)
1134  return{ 0.f, 0.f };
1135 
1136  sampleCount s0, s1;
1137 
1138  TimeToSamplesClip(t0, &s0);
1139  TimeToSamplesClip(t1, &s1);
1140 
1141  return mSequence->GetMinMax(s0, s1-s0, mayThrow);
1142 }
1143 
1144 float WaveClip::GetRMS(double t0, double t1, bool mayThrow) const
1145 {
1146  if (t0 > t1) {
1147  if (mayThrow)
1149  return 0.f;
1150  }
1151 
1152  if (t0 == t1)
1153  return 0.f;
1154 
1155  sampleCount s0, s1;
1156 
1157  TimeToSamplesClip(t0, &s0);
1158  TimeToSamplesClip(t1, &s1);
1159 
1160  return mSequence->GetRMS(s0, s1-s0, mayThrow);
1161 }
1162 
1164  const std::function<void(size_t)> & progressReport)
1165 {
1166  // Note: it is not necessary to do this recursively to cutlines.
1167  // They get converted as needed when they are expanded.
1168 
1169  auto bChanged = mSequence->ConvertToSampleFormat(format, progressReport);
1170  if (bChanged)
1171  MarkChanged();
1172 }
1173 
1176 {
1177  auto len = (mSequence->GetNumSamples().as_double()) / mRate;
1178  if (len != mEnvelope->GetTrackLen())
1179  mEnvelope->SetTrackLen(len, 1.0 / GetRate());
1180 }
1181 
1182 void WaveClip::TimeToSamplesClip(double t0, sampleCount *s0) const
1183 {
1184  if (t0 < mOffset)
1185  *s0 = 0;
1186  else if (t0 > mOffset + mSequence->GetNumSamples().as_double()/mRate)
1187  *s0 = mSequence->GetNumSamples();
1188  else
1189  *s0 = sampleCount( floor(((t0 - mOffset) * mRate) + 0.5) );
1190 }
1191 
1193 std::shared_ptr<SampleBlock> WaveClip::AppendNewBlock(
1194  samplePtr buffer, sampleFormat format, size_t len)
1195 {
1196  return mSequence->AppendNewBlock( buffer, format, len );
1197 }
1198 
1200 void WaveClip::AppendSharedBlock(const std::shared_ptr<SampleBlock> &pBlock)
1201 {
1202  mSequence->AppendSharedBlock( pBlock );
1203 }
1204 
1209  size_t len, unsigned int stride)
1210 {
1211  //wxLogDebug(wxT("Append: len=%lli"), (long long) len);
1212  bool result = false;
1213 
1214  auto maxBlockSize = mSequence->GetMaxBlockSize();
1215  auto blockSize = mSequence->GetIdealAppendLen();
1216  sampleFormat seqFormat = mSequence->GetSampleFormat();
1217 
1218  if (!mAppendBuffer.ptr())
1219  mAppendBuffer.Allocate(maxBlockSize, seqFormat);
1220 
1221  auto cleanup = finally( [&] {
1222  // use No-fail-guarantee
1224  MarkChanged();
1225  } );
1226 
1227  for(;;) {
1228  if (mAppendBufferLen >= blockSize) {
1229  // flush some previously appended contents
1230  // use Strong-guarantee
1231  mSequence->Append(mAppendBuffer.ptr(), seqFormat, blockSize);
1232  result = true;
1233 
1234  // use No-fail-guarantee for rest of this "if"
1235  memmove(mAppendBuffer.ptr(),
1236  mAppendBuffer.ptr() + blockSize * SAMPLE_SIZE(seqFormat),
1237  (mAppendBufferLen - blockSize) * SAMPLE_SIZE(seqFormat));
1238  mAppendBufferLen -= blockSize;
1239  blockSize = mSequence->GetIdealAppendLen();
1240  }
1241 
1242  if (len == 0)
1243  break;
1244 
1245  // use No-fail-guarantee for rest of this "for"
1246  wxASSERT(mAppendBufferLen <= maxBlockSize);
1247  auto toCopy = std::min(len, maxBlockSize - mAppendBufferLen);
1248 
1249  CopySamples(buffer, format,
1250  mAppendBuffer.ptr() + mAppendBufferLen * SAMPLE_SIZE(seqFormat),
1251  seqFormat,
1252  toCopy,
1254  stride);
1255 
1256  mAppendBufferLen += toCopy;
1257  buffer += toCopy * SAMPLE_SIZE(format) * stride;
1258  len -= toCopy;
1259  }
1260 
1261  return result;
1262 }
1263 
1270 {
1271  //wxLogDebug(wxT("WaveClip::Flush"));
1272  //wxLogDebug(wxT(" mAppendBufferLen=%lli"), (long long) mAppendBufferLen);
1273  //wxLogDebug(wxT(" previous sample count %lli"), (long long) mSequence->GetNumSamples());
1274 
1275  if (mAppendBufferLen > 0) {
1276 
1277  auto cleanup = finally( [&] {
1278  // Blow away the append buffer even in case of failure. May lose some
1279  // data but don't leave the track in an un-flushed state.
1280 
1281  // Use No-fail-guarantee of these steps.
1282  mAppendBufferLen = 0;
1284  MarkChanged();
1285  } );
1286 
1287  mSequence->Append(mAppendBuffer.ptr(), mSequence->GetSampleFormat(),
1289  }
1290 
1291  //wxLogDebug(wxT("now sample count %lli"), (long long) mSequence->GetNumSamples());
1292 }
1293 
1294 bool WaveClip::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
1295 {
1296  if (!wxStrcmp(tag, wxT("waveclip")))
1297  {
1298  double dblValue;
1299  long longValue;
1300  while (*attrs)
1301  {
1302  const wxChar *attr = *attrs++;
1303  const wxChar *value = *attrs++;
1304 
1305  if (!value)
1306  break;
1307 
1308  const wxString strValue = value;
1309  if (!wxStrcmp(attr, wxT("offset")))
1310  {
1311  if (!XMLValueChecker::IsGoodString(strValue) ||
1312  !Internat::CompatibleToDouble(strValue, &dblValue))
1313  return false;
1314  SetOffset(dblValue);
1315  }
1316  else if (!wxStrcmp(attr, wxT("name")))
1317  {
1318  if(XMLValueChecker::IsGoodLongString(strValue))
1319  SetName(strValue);
1320  }
1321  else if (!wxStrcmp(attr, wxT("colorindex")))
1322  {
1323  if (!XMLValueChecker::IsGoodString(strValue) ||
1324  !strValue.ToLong( &longValue))
1325  return false;
1326  SetColourIndex(longValue);
1327  }
1328  }
1329  return true;
1330  }
1331 
1332  return false;
1333 }
1334 
1335 void WaveClip::HandleXMLEndTag(const wxChar *tag)
1336 {
1337  if (!wxStrcmp(tag, wxT("waveclip")))
1339 }
1340 
1342 {
1343  if (!wxStrcmp(tag, wxT("sequence")))
1344  return mSequence.get();
1345  else if (!wxStrcmp(tag, wxT("envelope")))
1346  return mEnvelope.get();
1347  else if (!wxStrcmp(tag, wxT("waveclip")))
1348  {
1349  // Nested wave clips are cut lines
1350  mCutLines.push_back(
1351  std::make_unique<WaveClip>(mSequence->GetFactory(),
1352  mSequence->GetSampleFormat(), mRate, 0 /*colourindex*/));
1353  return mCutLines.back().get();
1354  }
1355  else
1356  return NULL;
1357 }
1358 
1359 void WaveClip::WriteXML(XMLWriter &xmlFile) const
1360 // may throw
1361 {
1362  xmlFile.StartTag(wxT("waveclip"));
1363  xmlFile.WriteAttr(wxT("offset"), mOffset, 8);
1364  xmlFile.WriteAttr(wxT("name"), mName);
1365  xmlFile.WriteAttr(wxT("colorindex"), mColourIndex );
1366 
1367  mSequence->WriteXML(xmlFile);
1368  mEnvelope->WriteXML(xmlFile);
1369 
1370  for (const auto &clip: mCutLines)
1371  clip->WriteXML(xmlFile);
1372 
1373  xmlFile.EndTag(wxT("waveclip"));
1374 }
1375 
1377 void WaveClip::Paste(double t0, const WaveClip* other)
1378 {
1379  const bool clipNeedsResampling = other->mRate != mRate;
1380  const bool clipNeedsNewFormat =
1381  other->mSequence->GetSampleFormat() != mSequence->GetSampleFormat();
1382  std::unique_ptr<WaveClip> newClip;
1383  const WaveClip* pastedClip;
1384 
1385  if (clipNeedsResampling || clipNeedsNewFormat)
1386  {
1387  newClip =
1388  std::make_unique<WaveClip>(*other, mSequence->GetFactory(), true);
1389  if (clipNeedsResampling)
1390  // The other clip's rate is different from ours, so resample
1391  newClip->Resample(mRate);
1392  if (clipNeedsNewFormat)
1393  // Force sample formats to match.
1394  newClip->ConvertToSampleFormat(mSequence->GetSampleFormat());
1395  pastedClip = newClip.get();
1396  }
1397  else
1398  {
1399  // No resampling or format change needed, just use original clip without making a copy
1400  pastedClip = other;
1401  }
1402 
1403  // Paste cut lines contained in pasted clip
1404  WaveClipHolders newCutlines;
1405  for (const auto &cutline: pastedClip->mCutLines)
1406  {
1407  newCutlines.push_back(
1408  std::make_unique<WaveClip>
1409  ( *cutline, mSequence->GetFactory(),
1410  // Recursively copy cutlines of cutlines. They don't need
1411  // their offsets adjusted.
1412  true));
1413  newCutlines.back()->Offset(t0 - mOffset);
1414  }
1415 
1416  sampleCount s0;
1417  TimeToSamplesClip(t0, &s0);
1418 
1419  // Assume Strong-guarantee from Sequence::Paste
1420  mSequence->Paste(s0, pastedClip->mSequence.get());
1421 
1422  // Assume No-fail-guarantee in the remaining
1423  MarkChanged();
1424  auto sampleTime = 1.0 / GetRate();
1425  mEnvelope->PasteEnvelope
1426  (s0.as_double()/mRate + mOffset, pastedClip->mEnvelope.get(), sampleTime);
1427  OffsetCutLines(t0, pastedClip->GetEndTime() - pastedClip->GetStartTime());
1428 
1429  for (auto &holder : newCutlines)
1430  mCutLines.push_back(std::move(holder));
1431 }
1432 
1434 void WaveClip::InsertSilence( double t, double len, double *pEnvelopeValue )
1435 {
1436  sampleCount s0;
1437  TimeToSamplesClip(t, &s0);
1438  auto slen = (sampleCount)floor(len * mRate + 0.5);
1439 
1440  // use Strong-guarantee
1441  GetSequence()->InsertSilence(s0, slen);
1442 
1443  // use No-fail-guarantee
1444  OffsetCutLines(t, len);
1445 
1446  const auto sampleTime = 1.0 / GetRate();
1447  auto pEnvelope = GetEnvelope();
1448  if ( pEnvelopeValue ) {
1449 
1450  // Preserve limit value at the end
1451  auto oldLen = pEnvelope->GetTrackLen();
1452  auto newLen = oldLen + len;
1453  pEnvelope->Cap( sampleTime );
1454 
1455  // Ramp across the silence to the given value
1456  pEnvelope->SetTrackLen( newLen, sampleTime );
1457  pEnvelope->InsertOrReplace
1458  ( pEnvelope->GetOffset() + newLen, *pEnvelopeValue );
1459  }
1460  else
1461  pEnvelope->InsertSpace( t, len );
1462 
1463  MarkChanged();
1464 }
1465 
1467 void WaveClip::AppendSilence( double len, double envelopeValue )
1468 {
1469  auto t = GetEndTime();
1470  InsertSilence( t, len, &envelopeValue );
1471 }
1472 
1474 void WaveClip::Clear(double t0, double t1)
1475 {
1476  sampleCount s0, s1;
1477 
1478  TimeToSamplesClip(t0, &s0);
1479  TimeToSamplesClip(t1, &s1);
1480 
1481  // use Strong-guarantee
1482  GetSequence()->Delete(s0, s1-s0);
1483 
1484  // use No-fail-guarantee in the remaining
1485 
1486  // msmeyer
1487  //
1488  // Delete all cutlines that are within the given area, if any.
1489  //
1490  // Note that when cutlines are active, two functions are used:
1491  // Clear() and ClearAndAddCutLine(). ClearAndAddCutLine() is called
1492  // whenever the user directly calls a command that removes some audio, e.g.
1493  // "Cut" or "Clear" from the menu. This command takes care about recursive
1494  // preserving of cutlines within clips. Clear() is called when internal
1495  // operations want to remove audio. In the latter case, it is the right
1496  // thing to just remove all cutlines within the area.
1497  //
1498  double clip_t0 = t0;
1499  double clip_t1 = t1;
1500  if (clip_t0 < GetStartTime())
1501  clip_t0 = GetStartTime();
1502  if (clip_t1 > GetEndTime())
1503  clip_t1 = GetEndTime();
1504 
1505  // May DELETE as we iterate, so don't use range-for
1506  for (auto it = mCutLines.begin(); it != mCutLines.end();)
1507  {
1508  WaveClip* clip = it->get();
1509  double cutlinePosition = mOffset + clip->GetOffset();
1510  if (cutlinePosition >= t0 && cutlinePosition <= t1)
1511  {
1512  // This cutline is within the area, DELETE it
1513  it = mCutLines.erase(it);
1514  }
1515  else
1516  {
1517  if (cutlinePosition >= t1)
1518  {
1519  clip->Offset(clip_t0 - clip_t1);
1520  }
1521  ++it;
1522  }
1523  }
1524 
1525  // Collapse envelope
1526  auto sampleTime = 1.0 / GetRate();
1527  GetEnvelope()->CollapseRegion( t0, t1, sampleTime );
1528  if (t0 < GetStartTime())
1529  Offset(-(GetStartTime() - t0));
1530 
1531  MarkChanged();
1532 }
1533 
1537 void WaveClip::ClearAndAddCutLine(double t0, double t1)
1538 {
1539  if (t0 > GetEndTime() || t1 < GetStartTime())
1540  return; // time out of bounds
1541 
1542  const double clip_t0 = std::max( t0, GetStartTime() );
1543  const double clip_t1 = std::min( t1, GetEndTime() );
1544 
1545  auto newClip = std::make_unique< WaveClip >
1546  (*this, mSequence->GetFactory(), true, clip_t0, clip_t1);
1547 
1548  newClip->SetOffset( clip_t0 - mOffset );
1549 
1550  // Remove cutlines from this clip that were in the selection, shift
1551  // left those that were after the selection
1552  // May DELETE as we iterate, so don't use range-for
1553  for (auto it = mCutLines.begin(); it != mCutLines.end();)
1554  {
1555  WaveClip* clip = it->get();
1556  double cutlinePosition = mOffset + clip->GetOffset();
1557  if (cutlinePosition >= t0 && cutlinePosition <= t1)
1558  it = mCutLines.erase(it);
1559  else
1560  {
1561  if (cutlinePosition >= t1)
1562  {
1563  clip->Offset(clip_t0 - clip_t1);
1564  }
1565  ++it;
1566  }
1567  }
1568 
1569  // Clear actual audio data
1570  sampleCount s0, s1;
1571 
1572  TimeToSamplesClip(t0, &s0);
1573  TimeToSamplesClip(t1, &s1);
1574 
1575  // use Weak-guarantee
1576  GetSequence()->Delete(s0, s1-s0);
1577 
1578  // Collapse envelope
1579  auto sampleTime = 1.0 / GetRate();
1580  GetEnvelope()->CollapseRegion( t0, t1, sampleTime );
1581  if (t0 < GetStartTime())
1582  Offset(-(GetStartTime() - t0));
1583 
1584  MarkChanged();
1585 
1586  mCutLines.push_back(std::move(newClip));
1587 }
1588 
1589 bool WaveClip::FindCutLine(double cutLinePosition,
1590  double* cutlineStart /* = NULL */,
1591  double* cutlineEnd /* = NULL */) const
1592 {
1593  for (const auto &cutline: mCutLines)
1594  {
1595  if (fabs(mOffset + cutline->GetOffset() - cutLinePosition) < 0.0001)
1596  {
1597  if (cutlineStart)
1598  *cutlineStart = mOffset+cutline->GetStartTime();
1599  if (cutlineEnd)
1600  *cutlineEnd = mOffset+cutline->GetEndTime();
1601  return true;
1602  }
1603  }
1604 
1605  return false;
1606 }
1607 
1609 void WaveClip::ExpandCutLine(double cutLinePosition)
1610 {
1611  auto end = mCutLines.end();
1612  auto it = std::find_if( mCutLines.begin(), end,
1613  [&](const WaveClipHolder &cutline) {
1614  return fabs(mOffset + cutline->GetOffset() - cutLinePosition) < 0.0001;
1615  } );
1616 
1617  if ( it != end ) {
1618  auto cutline = it->get();
1619  // assume Strong-guarantee from Paste
1620 
1621  // Envelope::Paste takes offset into account, WaveClip::Paste doesn't!
1622  // Do this to get the right result:
1623  cutline->mEnvelope->SetOffset(0);
1624 
1625  Paste(mOffset+cutline->GetOffset(), cutline);
1626  // Now erase the cutline,
1627  // but be careful to find it again, because Paste above may
1628  // have modified the array of cutlines (if our cutline contained
1629  // another cutline!), invalidating the iterator we had.
1630  end = mCutLines.end();
1631  it = std::find_if(mCutLines.begin(), end,
1632  [=](const WaveClipHolder &p) { return p.get() == cutline; });
1633  if (it != end)
1634  mCutLines.erase(it); // deletes cutline!
1635  else {
1636  wxASSERT(false);
1637  }
1638  }
1639 }
1640 
1641 bool WaveClip::RemoveCutLine(double cutLinePosition)
1642 {
1643  for (auto it = mCutLines.begin(); it != mCutLines.end(); ++it)
1644  {
1645  const auto &cutline = *it;
1646  if (fabs(mOffset + cutline->GetOffset() - cutLinePosition) < 0.0001)
1647  {
1648  mCutLines.erase(it); // deletes cutline!
1649  return true;
1650  }
1651  }
1652 
1653  return false;
1654 }
1655 
1657 void WaveClip::OffsetCutLines(double t0, double len)
1658 {
1659  for (const auto &cutLine : mCutLines)
1660  {
1661  if (mOffset + cutLine->GetOffset() >= t0)
1662  cutLine->Offset(len);
1663  }
1664 }
1665 
1667 {
1668  GetSequence()->CloseLock();
1669  for (const auto &cutline: mCutLines)
1670  cutline->CloseLock();
1671 }
1672 
1673 void WaveClip::SetRate(int rate)
1674 {
1675  mRate = rate;
1676  auto newLength = mSequence->GetNumSamples().as_double() / mRate;
1677  mEnvelope->RescaleTimes( newLength );
1678  MarkChanged();
1679 }
1680 
1682 void WaveClip::Resample(int rate, ProgressDialog *progress)
1683 {
1684  // Note: it is not necessary to do this recursively to cutlines.
1685  // They get resampled as needed when they are expanded.
1686 
1687  if (rate == mRate)
1688  return; // Nothing to do
1689 
1690  double factor = (double)rate / (double)mRate;
1691  ::Resample resample(true, factor, factor); // constant rate resampling
1692 
1693  const size_t bufsize = 65536;
1694  Floats inBuffer{ bufsize };
1695  Floats outBuffer{ bufsize };
1696  sampleCount pos = 0;
1697  bool error = false;
1698  int outGenerated = 0;
1699  auto numSamples = mSequence->GetNumSamples();
1700 
1701  auto newSequence =
1702  std::make_unique<Sequence>(mSequence->GetFactory(), mSequence->GetSampleFormat());
1703 
1709  while (pos < numSamples || outGenerated > 0)
1710  {
1711  const auto inLen = limitSampleBufferSize( bufsize, numSamples - pos );
1712 
1713  bool isLast = ((pos + inLen) == numSamples);
1714 
1715  if (!mSequence->Get((samplePtr)inBuffer.get(), floatSample, pos, inLen, true))
1716  {
1717  error = true;
1718  break;
1719  }
1720 
1721  const auto results = resample.Process(factor, inBuffer.get(), inLen, isLast,
1722  outBuffer.get(), bufsize);
1723  outGenerated = results.second;
1724 
1725  pos += results.first;
1726 
1727  if (outGenerated < 0)
1728  {
1729  error = true;
1730  break;
1731  }
1732 
1733  newSequence->Append((samplePtr)outBuffer.get(), floatSample,
1734  outGenerated);
1735 
1736  if (progress)
1737  {
1738  auto updateResult = progress->Update(
1739  pos.as_long_long(),
1740  numSamples.as_long_long()
1741  );
1742  error = (updateResult != ProgressResult::Success);
1743  if (error)
1744  throw UserException{};
1745  }
1746  }
1747 
1748  if (error)
1751  XO("Resampling failed."),
1752  XO("Warning"),
1753  "Error:_Resampling"
1754  };
1755  else
1756  {
1757  // Use No-fail-guarantee in these steps
1758 
1759  // Invalidate wave display cache
1760  mWaveCache = std::make_unique<WaveCache>();
1761  // Invalidate the spectrum display cache
1762  mSpecCache = std::make_unique<SpecCache>();
1763 
1764  mSequence = std::move(newSequence);
1765  mRate = rate;
1766  }
1767 }
1768 
1769 // Used by commands which interact with clips using the keyboard.
1770 // When two clips are immediately next to each other, the GetEndTime()
1771 // of the first clip and the GetStartTime() of the second clip may not
1772 // be exactly equal due to rounding errors.
1774 {
1775  double endThis = GetRate() * GetOffset() + GetNumSamples().as_double();
1776  double startNext = next->GetRate() * next->GetOffset();
1777 
1778  // given that a double has about 15 significant digits, using a criterion
1779  // of half a sample should be safe in all normal usage.
1780  return fabs(startNext - endThis) < 0.5;
1781 }
1782 
1783 void WaveClip::SetName(const wxString& name)
1784 {
1785  mName = name;
1786 }
1787 
1788 const wxString& WaveClip::GetName() const
1789 {
1790  return mName;
1791 }
WaveTrackCache
A short-lived object, during whose lifetime, the contents of the WaveTrack are assumed not to change.
Definition: WaveTrack.h:633
XMLWriter
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:23
WaveClip::Flush
void Flush()
Flush must be called after last Append.
Definition: WaveClip.cpp:1269
WaveCache::~WaveCache
~WaveCache()
Definition: WaveClip.cpp:76
WaveTrack.h
SampleBuffer::Allocate
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:84
SpectrogramSettings
Spectrogram settings, either for one track or as defaults.
Definition: SpectrogramSettings.h:27
Sequence::CloseLock
bool CloseLock()
Definition: Sequence.cpp:86
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:69
WaveCache::rms
std::vector< float > rms
Definition: WaveClip.cpp:88
SpectrogramSettings.h
WaveClip::mEnvelope
std::unique_ptr< Envelope > mEnvelope
Definition: WaveClip.h:371
WaveClip::GetMinMax
std::pair< float, float > GetMinMax(double t0, double t1, bool mayThrow=true) const
Definition: WaveClip.cpp:1121
WaveCache::len
const size_t len
Definition: WaveClip.cpp:81
WaveClip::GetSequenceBlockArray
BlockArray * GetSequenceBlockArray()
Definition: WaveClip.cpp:239
BasicUI::ProgressResult::Success
@ Success
WaveClip::WaveClip
WaveClip(const WaveClip &) PROHIBITED
Sequence::Delete
void Delete(sampleCount start, sampleCount len)
Definition: Sequence.cpp:1633
WaveClip::BeforeClip
bool BeforeClip(double t) const
Definition: WaveClip.cpp:292
SpecCache::windowType
int windowType
Definition: WaveClip.h:85
WaveCache::dirty
int dirty
Definition: WaveClip.cpp:80
WaveClip::UpdateEnvelopeTrackLen
void UpdateEnvelopeTrackLen()
Definition: WaveClip.cpp:1175
WaveClip::ClearWaveCache
void ClearWaveCache()
Delete the wave cache - force redraw. Thread-safe.
Definition: WaveClip.cpp:315
UserException
Can be thrown when user cancels operations, as with a progress dialog. Delayed handler does nothing.
Definition: UserException.h:17
ComputeSpectrum
bool ComputeSpectrum(const float *data, size_t width, size_t windowSize, double WXUNUSED(rate), float *output, bool autocorrelation, int windowFunc)
Definition: Spectrum.cpp:22
WaveClip::GetEnvelope
Envelope * GetEnvelope()
Definition: WaveClip.h:243
WaveDisplay::ownWhere
std::vector< sampleCount > ownWhere
Definition: WaveClip.h:135
WaveClip::GetNumSamples
sampleCount GetNumSamples() const
Definition: WaveClip.cpp:276
WaveClip::GetOffset
double GetOffset() const
Definition: WaveClip.h:220
WaveClip::GetWaveDisplay
bool GetWaveDisplay(WaveDisplay &display, double t0, double pixelsPerSecond) const
Definition: WaveClip.cpp:384
WaveClip::~WaveClip
virtual ~WaveClip()
Definition: WaveClip.cpp:211
InconsistencyException.h
MessageBoxException for violation of preconditions or assertions.
WaveClip::Paste
void Paste(double t0, const WaveClip *other)
Paste data from other clip, resampling it if not equal rate.
Definition: WaveClip.cpp:1377
SpecCache::zeroPaddingFactor
unsigned zeroPaddingFactor
Definition: WaveClip.h:87
WaveClip::HandleXMLChild
XMLTagHandler * HandleXMLChild(const wxChar *tag) override
Definition: WaveClip.cpp:1341
WaveClip::GetRate
int GetRate() const
Definition: WaveClip.h:208
RealFFTf
void RealFFTf(fft_type *buffer, const FFTParam *h)
Definition: RealFFTf.cpp:162
ArrayOf::reinit
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:57
WaveClip::SetOffset
void SetOffset(double offset)
Definition: WaveClip.cpp:216
WaveClip::FindCutLine
bool FindCutLine(double cutLinePosition, double *cutLineStart=NULL, double *cutLineEnd=NULL) const
Definition: WaveClip.cpp:1589
anonymous_namespace{WaveClip.cpp}::findCorrection
void findCorrection(const std::vector< sampleCount > &oldWhere, size_t oldLen, size_t newLen, double t0, double rate, double samplesPerPixel, int &oldX0, double &correction)
Definition: WaveClip.cpp:323
WaveClip::GetEndTime
double GetEndTime() const
Definition: WaveClip.cpp:255
ComputeSpectrumUsingRealFFTf
static void ComputeSpectrumUsingRealFFTf(float *__restrict buffer, const FFTParam *hFFT, const float *__restrict window, size_t len, float *__restrict out)
Definition: WaveClip.cpp:93
SAMPLE_SIZE
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
XO
#define XO(s)
Definition: Internat.h:31
SamplesToFloats
void SamplesToFloats(constSamplePtr src, sampleFormat srcFormat, float *dst, size_t len, size_t srcStride, size_t dstStride)
Copy samples from any format into the widest format, which is 32 bit float, with no dithering.
Definition: SampleFormat.cpp:102
ProgressDialog.h
WaveCache
Cache used with WaveClip to cache wave information (for drawing).
Definition: WaveClip.cpp:47
WaveClip::mWaveCache
std::unique_ptr< WaveCache > mWaveCache
Definition: WaveClip.h:373
WaveTrack::GetSpectrogramSettings
const SpectrogramSettings & GetSpectrogramSettings() const
Definition: WaveTrack.cpp:762
WaveClipHolders
std::vector< WaveClipHolder > WaveClipHolders
Definition: WaveClip.h:122
WaveTrackCache::GetFloats
const float * GetFloats(sampleCount start, size_t len, bool mayThrow)
Retrieve samples as floats from the track or from the memory cache.
Definition: WaveTrack.cpp:2646
WaveCache::bl
std::vector< int > bl
Definition: WaveClip.cpp:89
XMLValueChecker::IsGoodString
static bool IsGoodString(const wxString &str)
Definition: XMLTagHandler.cpp:38
WaveClip::AppendSharedBlock
void AppendSharedBlock(const std::shared_ptr< SampleBlock > &pBlock)
For use in importing pre-version-3 projects to preserve sharing of blocks.
Definition: WaveClip.cpp:1200
SpecCache::dirty
int dirty
Definition: WaveClip.h:92
WaveClip::GetStartSample
sampleCount GetStartSample() const
Definition: WaveClip.cpp:266
WaveClip::ExpandCutLine
void ExpandCutLine(double cutLinePosition)
Definition: WaveClip.cpp:1609
WaveClip::GetStartTime
double GetStartTime() const
Definition: WaveClip.cpp:249
SpecCache::Grow
void Grow(size_t len_, const SpectrogramSettings &settings, double pixelsPerSecond, double start_)
Definition: WaveClip.cpp:858
WaveClip::GetSequence
Sequence * GetSequence()
Definition: WaveClip.h:251
floatSample
@ floatSample
Definition: SampleFormat.h:34
WaveClip::mColourIndex
int mColourIndex
Definition: WaveClip.h:368
WaveClip::WriteXML
void WriteXML(XMLWriter &xmlFile) const
Definition: WaveClip.cpp:1359
WaveClip::GetSamples
bool GetSamples(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow=true) const
Definition: WaveClip.cpp:222
WaveClip
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:173
anonymous_namespace{WaveClip.cpp}::ComputeSpectrogramGainFactors
void ComputeSpectrogramGainFactors(size_t fftLen, double rate, int frequencyGain, std::vector< float > &gainFactors)
Definition: WaveClip.cpp:592
WaveClip::SharesBoundaryWithNextClip
bool SharesBoundaryWithNextClip(const WaveClip *next) const
Definition: WaveClip.cpp:1773
ProgressDialog
ProgressDialog Class.
Definition: ProgressDialog.h:51
WaveClip::GetName
const wxString & GetName() const
Definition: WaveClip.cpp:1788
factory
static RegisteredToolbarFactory factory
Definition: ControlToolBar.cpp:806
sampleCount::as_double
double as_double() const
Definition: SampleCount.h:45
WaveDisplay::max
float * max
Definition: WaveClip.h:132
Profiler.h
CopySamples
void CopySamples(constSamplePtr src, sampleFormat srcFormat, samplePtr dst, sampleFormat dstFormat, size_t len, DitherType ditherType, unsigned int srcStride, unsigned int dstStride)
Copy samples from any format to any other format; apply dithering only if narrowing the format.
Definition: SampleFormat.cpp:111
WaveClip::TimeToSamplesClip
void TimeToSamplesClip(double t0, sampleCount *s0) const
Definition: WaveClip.cpp:1182
sampleCount::as_long_long
long long as_long_long() const
Definition: SampleCount.h:47
constSamplePtr
const char * constSamplePtr
Definition: SampleFormat.h:50
WaveClip::Offset
void Offset(double delta)
Definition: WaveClip.h:222
WaveCache::min
std::vector< float > min
Definition: WaveClip.cpp:86
name
const TranslatableString name
Definition: Distortion.cpp:98
WaveClip::mName
wxString mName
Definition: WaveClip.h:386
WaveDisplay::width
int width
Definition: WaveClip.h:130
SpecCache::Matches
bool Matches(int dirty_, double pixelsPerSecond, const SpectrogramSettings &settings, double rate) const
Definition: WaveClip.cpp:614
format
int format
Definition: ExportPCM.cpp:56
ProgressDialog::Update
ProgressResult Update(int value, const TranslatableString &message={})
Definition: ProgressDialog.cpp:1327
WaveClip::SetName
void SetName(const wxString &name)
Definition: WaveClip.cpp:1783
WaveClip::mRate
int mRate
Definition: WaveClip.h:366
THROW_INCONSISTENCY_EXCEPTION
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
Definition: InconsistencyException.h:79
WaveClip::SetColourIndex
void SetColourIndex(int index)
Definition: WaveClip.h:217
WaveCache::WaveCache
WaveCache()
Definition: WaveClip.cpp:49
Resample
Interface to libsoxr.
Definition: Resample.h:27
WaveClip::CloseLock
void CloseLock()
Definition: WaveClip.cpp:1666
WaveClip::ClearAndAddCutLine
void ClearAndAddCutLine(double t0, double t1)
Clear, and add cut line that starts at t0 and contains everything until t1.
Definition: WaveClip.cpp:1537
WaveClipHolder
std::shared_ptr< WaveClip > WaveClipHolder
Definition: WaveClip.h:121
FFTParam::Points
size_t Points
Definition: RealFFTf.h:10
WaveClip::WithinClip
bool WithinClip(double t) const
Definition: WaveClip.cpp:286
UserException.h
An AudacityException with no visible message.
XMLTagHandler
This class is an interface which should be implemented by classes which wish to be able to load and s...
Definition: XMLTagHandler.h:62
WaveClip::Clear
void Clear(double t0, double t1)
Definition: WaveClip.cpp:1474
WaveClip::SetRate
void SetRate(int rate)
Definition: WaveClip.cpp:1673
WaveClip::AppendSilence
void AppendSilence(double len, double envelopeValue)
Definition: WaveClip.cpp:1467
WaveClip::Append
bool Append(constSamplePtr buffer, sampleFormat format, size_t len, unsigned int stride)
Definition: WaveClip.cpp:1208
Envelope.h
WaveCache::max
std::vector< float > max
Definition: WaveClip.cpp:87
WaveClip::mDirty
int mDirty
Definition: WaveClip.h:367
sampleFormat
sampleFormat
Definition: SampleFormat.h:29
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
WaveClip::GetIsPlaceholder
bool GetIsPlaceholder() const
Definition: WaveClip.h:351
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
Sequence::InsertSilence
void InsertSilence(sampleCount s0, sampleCount len)
Definition: Sequence.cpp:682
WaveClip::GetSpectrogram
bool GetSpectrogram(WaveTrackCache &cache, const float *&spectrogram, const sampleCount *&where, size_t numPixels, double t0, double pixelsPerSecond) const
Definition: WaveClip.cpp:1010
WaveTrackCache::GetTrack
const std::shared_ptr< const WaveTrack > & GetTrack() const
Definition: WaveTrack.h:651
WaveClip::OffsetCutLines
void OffsetCutLines(double t0, double len)
Offset cutlines right to time 't0' by time amount 'len'.
Definition: WaveClip.cpp:1657
FFTParam
Definition: RealFFTf.h:7
BlockArray
Definition: Sequence.h:49
WaveClip::mAppendBuffer
SampleBuffer mAppendBuffer
Definition: WaveClip.h:375
XMLValueChecker::IsGoodLongString
static bool IsGoodLongString(const wxString &str)
Definition: XMLTagHandler.cpp:51
WaveClip::mIsPlaceholder
bool mIsPlaceholder
Definition: WaveClip.h:383
WaveClip::ConvertToSampleFormat
void ConvertToSampleFormat(sampleFormat format, const std::function< void(size_t)> &progressReport={})
Definition: WaveClip.cpp:1163
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
WaveCache::pps
const double pps
Definition: WaveClip.cpp:83
SpectrogramSettings::algPitchEAC
@ algPitchEAC
Definition: SpectrogramSettings.h:159
WaveDisplay::rms
float * rms
Definition: WaveClip.h:132
WaveClip::IsClipStartAfterClip
bool IsClipStartAfterClip(double t) const
Definition: WaveClip.cpp:307
SpecCache::algorithm
int algorithm
Definition: WaveClip.h:82
SpectrogramSettings::algReassignment
@ algReassignment
Definition: SpectrogramSettings.h:158
WaveClip::RemoveCutLine
bool RemoveCutLine(double cutLinePosition)
Remove cut line, without expanding the audio in it.
Definition: WaveClip.cpp:1641
WaveClip::AfterClip
bool AfterClip(double t) const
Definition: WaveClip.cpp:298
WaveCache::rate
const int rate
Definition: WaveClip.cpp:84
SpecCache::len
size_t len
Definition: WaveClip.h:81
Sequence.h
WaveClip::HandleXMLTag
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override
Definition: WaveClip.cpp:1294
SpecCache::Populate
void Populate(const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache, int copyBegin, int copyEnd, size_t numPixels, sampleCount numSamples, double offset, double rate, double pixelsPerSecond)
Definition: WaveClip.cpp:882
WaveCache::WaveCache
WaveCache(size_t len_, double pixelsPerSecond, double rate_, double t0, int dirty_)
Definition: WaveClip.cpp:62
ExceptionType::Internal
@ Internal
Indicates internal failure from Audacity.
WaveClip::GetEndSample
sampleCount GetEndSample() const
Definition: WaveClip.cpp:271
WaveClip::mSpecPxCache
std::unique_ptr< SpecPxCache > mSpecPxCache
Definition: WaveClip.h:362
FFTParam::BitReversed
ArrayOf< int > BitReversed
Definition: RealFFTf.h:8
SpecCache::freq
std::vector< float > freq
Definition: WaveClip.h:89
M_PI
#define M_PI
Definition: Distortion.cpp:29
Resample::Process
std::pair< size_t, size_t > Process(double factor, float *inBuffer, size_t inBufferLen, bool lastFlag, float *outBuffer, size_t outBufferLen)
Main processing function. Resamples from the input buffer to the output buffer.
Definition: Resample.cpp:88
gHighQualityDither
DitherType gHighQualityDither
Definition: SampleFormat.cpp:50
WaveClip::mCutLines
WaveClipHolders mCutLines
Definition: WaveClip.h:380
SpecCache::where
std::vector< sampleCount > where
Definition: WaveClip.h:90
WaveDisplay::min
float * min
Definition: WaveClip.h:132
Prefs.h
WaveClip::mSpecCache
std::unique_ptr< SpecCache > mSpecCache
Definition: WaveClip.h:374
WaveClip::InsertSilence
void InsertSilence(double t, double len, double *pEnvelopeValue=nullptr)
Definition: WaveClip.cpp:1434
WaveClip::mOffset
double mOffset
Definition: WaveClip.h:365
SpecCache::windowSize
size_t windowSize
Definition: WaveClip.h:86
WaveClip::mSequence
std::unique_ptr< Sequence > mSequence
Definition: WaveClip.h:370
SampleBlockFactoryPtr
std::shared_ptr< SampleBlockFactory > SampleBlockFactoryPtr
Definition: SampleBlock.h:25
WaveCache::where
std::vector< sampleCount > where
Definition: WaveClip.cpp:85
WaveClip::GetRMS
float GetRMS(double t0, double t1, bool mayThrow=true) const
Definition: WaveClip.cpp:1144
WaveClip::mAppendBufferLen
size_t mAppendBufferLen
Definition: WaveClip.h:376
settings
static Settings & settings()
Definition: TrackInfo.cpp:86
SpecCache::CalculateOneSpectrum
bool CalculateOneSpectrum(const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache, const int xx, sampleCount numSamples, double offset, double rate, double pixelsPerSecond, int lowerBoundX, int upperBoundX, const std::vector< float > &gainFactors, float *__restrict scratch, float *__restrict out) const
Definition: WaveClip.cpp:635
WaveClip::MarkChanged
void MarkChanged()
Definition: WaveClip.h:258
Spectrum.h
WaveClip::Resample
void Resample(int rate, ProgressDialog *progress=NULL)
Definition: WaveClip.cpp:1682
limitSampleBufferSize
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:23
SimpleMessageBoxException
A MessageBoxException that shows a given, unvarying string.
Definition: AudacityException.h:95
anonymous_namespace{WaveClip.cpp}::fillWhere
void fillWhere(std::vector< sampleCount > &where, size_t len, double bias, double correction, double t0, double rate, double samplesPerPixel)
Definition: WaveClip.cpp:367
WaveClip::AppendNewBlock
std::shared_ptr< SampleBlock > AppendNewBlock(samplePtr buffer, sampleFormat format, size_t len)
For use in importing pre-version-3 projects to preserve sharing of blocks.
Definition: WaveClip.cpp:1193
SpecCache::start
double start
Definition: WaveClip.h:84
ArrayOf< float >
SpecCache::pps
double pps
Definition: WaveClip.h:83
WaveDisplay
Definition: WaveClip.h:128
WaveDisplay::where
sampleCount * where
Definition: WaveClip.h:131
Resample.h
WaveDisplay::bl
int * bl
Definition: WaveClip.h:133
WaveCache::start
const double start
Definition: WaveClip.cpp:82
Internat::CompatibleToDouble
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:134
WaveClip.h
WaveClip::SetSamples
void SetSamples(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len)
Definition: WaveClip.cpp:229
SpecCache::frequencyGain
int frequencyGain
Definition: WaveClip.h:88
SampleBuffer::ptr
samplePtr ptr() const
Definition: SampleFormat.h:98
WaveClip::HandleXMLEndTag
void HandleXMLEndTag(const wxChar *tag) override
Definition: WaveClip.cpp:1335
Envelope::CollapseRegion
void CollapseRegion(double t0, double t1, double sampleDur)
Definition: Envelope.cpp:376