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