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