Audacity  2.2.2
WaveTrack.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  WaveTrack.cpp
6 
7  Dominic Mazzoni
8 
9 *******************************************************************//****************************************************************//****************************************************************//*******************************************************************/
28 
29 
30 #include "WaveTrack.h"
31 #include <wx/defs.h>
32 #include <wx/intl.h>
33 #include <wx/debug.h>
34 
35 #include <float.h>
36 #include <math.h>
37 #include <algorithm>
38 #include "MemoryX.h"
39 
40 #include "float_cast.h"
41 
42 #include "Envelope.h"
43 #include "Sequence.h"
44 #include "Spectrum.h"
45 
46 #include "Project.h"
47 #include "Internat.h"
48 
49 #include "AudioIO.h"
50 #include "Prefs.h"
51 
52 #include "ondemand/ODManager.h"
53 
54 #include "effects/TimeWarper.h"
55 #include "prefs/SpectrumPrefs.h"
56 #include "prefs/TracksPrefs.h"
57 #include "prefs/WaveformPrefs.h"
58 
59 #include "InconsistencyException.h"
60 
61 #include "Experimental.h"
62 
63 #include "TrackPanel.h" // for TrackInfo
64 // Assumptions in objects separation were wrong. We need to activate
65 // VZooming (that lives in WaveTrackVRulerHandle) from an action on the
66 // TCP collapse/expand. So we need visibility here.
68 
69 using std::max;
70 
72 {
73  return std::unique_ptr<WaveTrack>
74  { static_cast<WaveTrack*>(orig.Duplicate().release()) };
75 }
76 
77 
79 {
80  return std::unique_ptr<WaveTrack>
81  { safenew WaveTrack(mDirManager, format, rate) };
82 }
83 
84 WaveTrack::WaveTrack(const std::shared_ptr<DirManager> &projDirManager, sampleFormat format, double rate) :
85  PlayableTrack(projDirManager)
86 {
87  if (format == (sampleFormat)0)
88  {
89  format = GetActiveProject()->GetDefaultFormat();
90  }
91  if (rate == 0)
92  {
93  rate = GetActiveProject()->GetRate();
94  }
95 
96  // Force creation always:
98 
101  mDisplay = Waveform;
103  }
104 
106 
107  mFormat = format;
108  mRate = (int) rate;
109  mGain = 1.0;
110  mPan = 0.0;
111  mWaveColorIndex = 0;
114  mDisplayMin = -1.0;
115  mDisplayMax = 1.0;
116  mSpectrumMin = mSpectrumMax = -1; // so values will default to settings
117  mLastScaleType = -1;
118  mLastdBRange = -1;
119  mAutoSaveIdent = 0;
120 
122 }
123 
125  PlayableTrack(orig)
126  , mpSpectrumSettings(orig.mpSpectrumSettings
127  ? std::make_unique<SpectrogramSettings>(*orig.mpSpectrumSettings)
128  : nullptr
129  )
130  , mpWaveformSettings(orig.mpWaveformSettings
131  ? std::make_unique<WaveformSettings>(*orig.mpWaveformSettings)
132  : nullptr
133  )
134 {
135  mLastScaleType = -1;
136  mLastdBRange = -1;
137 
139 
140  Init(orig);
141 
142  for (const auto &clip : orig.mClips)
143  mClips.push_back
144  ( std::make_unique<WaveClip>( *clip, mDirManager, true ) );
145 }
146 
147 // Copy the track metadata but not the contents.
148 void WaveTrack::Init(const WaveTrack &orig)
149 {
150  PlayableTrack::Init(orig);
151  mFormat = orig.mFormat;
153  mRate = orig.mRate;
154  mGain = orig.mGain;
155  mPan = orig.mPan;
157  SetName(orig.GetName());
158  mDisplay = orig.mDisplay;
159  mDisplayMin = orig.mDisplayMin;
160  mDisplayMax = orig.mDisplayMax;
161  mSpectrumMin = orig.mSpectrumMin;
162  mSpectrumMax = orig.mSpectrumMax;
163  mDisplayLocationsCache.clear();
164 }
165 
166 void WaveTrack::Reinit(const WaveTrack &orig)
167 {
168  Init(orig);
169 
170  {
171  auto &settings = orig.mpSpectrumSettings;
172  if (settings)
173  mpSpectrumSettings = std::make_unique<SpectrogramSettings>(*settings);
174  else
175  mpSpectrumSettings.reset();
176  }
177 
178  {
179  auto &settings = orig.mpWaveformSettings;
180  if (settings)
181  mpWaveformSettings = std::make_unique<WaveformSettings>(*settings);
182  else
183  mpWaveformSettings.reset();
184  }
185 
186  this->SetOffset(orig.GetOffset());
187 
188 #ifdef EXPERIMENTAL_OUTPUT_DISPLAY
189  // To do: mYv, mHeightV, mPerY, mVirtualStereo
190 #endif
191 }
192 
193 void WaveTrack::Merge(const Track &orig)
194 {
195  if (orig.GetKind() == Wave)
196  {
197  const WaveTrack &wt = static_cast<const WaveTrack&>(orig);
198  mDisplay = wt.mDisplay;
199  mGain = wt.mGain;
200  mPan = wt.mPan;
204  ? std::make_unique<SpectrogramSettings>(*wt.mpSpectrumSettings) : nullptr);
206  (wt.mpWaveformSettings ? std::make_unique<WaveformSettings>(*wt.mpWaveformSettings) : nullptr);
207  }
208  PlayableTrack::Merge(orig);
209 }
210 
212 {
213  //Let the ODManager know this WaveTrack is disappearing.
214  //Deschedules tasks associated with this track.
217 }
218 
219 double WaveTrack::GetOffset() const
220 {
221  return GetStartTime();
222 }
223 
224 void WaveTrack::SetOffset(double o)
225 // NOFAIL-GUARANTEE
226 {
227  double delta = o - GetOffset();
228 
229  for (const auto &clip : mClips)
230  // assume NOFAIL-GUARANTEE
231  clip->SetOffset(clip->GetOffset() + delta);
232 
233  mOffset = o;
234 }
235 
237 {
239  return mChannel;
240  auto pan = GetPan();
241  if( pan < -0.99 )
242  return Track::LeftChannel;
243  if( pan > 0.99 )
244  return Track::RightChannel;
245  return mChannel;
246 }
247 
249 {
251  SetPan( -1.0f );
252  else if( mChannel == Track::RightChannel )
253  SetPan( 1.0f );
254 };
255 
256 
257 // static
260 {
261  // Remap old values.
262  enum OldValues {
263  Waveform,
264  WaveformDB,
265  Spectrogram,
266  SpectrogramLogF,
267  Pitch,
268  };
269 
270  WaveTrackDisplay newValue;
271  switch (oldValue) {
272  default:
273  case Waveform:
274  newValue = WaveTrack::Waveform; break;
275  case WaveformDB:
276  newValue = WaveTrack::obsoleteWaveformDBDisplay; break;
277  case Spectrogram:
278  case SpectrogramLogF:
279  case Pitch:
280  newValue = WaveTrack::Spectrum; break;
281  /*
282  case SpectrogramLogF:
283  newValue = WaveTrack::SpectrumLogDisplay; break;
284  case Pitch:
285  newValue = WaveTrack::PitchDisplay; break;
286  */
287  }
288  return newValue;
289 }
290 
291 // static
294 {
295  switch (display) {
296  // non-obsolete codes
297  case Waveform:
299  case Spectrum:
300  return display;
301 
302  // obsolete codes
303  case obsolete1: // was SpectrumLogDisplay
304  case obsolete2: // was SpectralSelectionDisplay
305  case obsolete3: // was SpectralSelectionLogDisplay
306  case obsolete4: // was PitchDisplay
307  return Spectrum;
308 
309  // codes out of bounds (from future prefs files?)
310  default:
311  return MinDisplay;
312  }
313 }
314 
316 {
318 }
319 
321 {
323 }
324 
325 void WaveTrack::GetDisplayBounds(float *min, float *max) const
326 {
327  *min = mDisplayMin;
328  *max = mDisplayMax;
329 }
330 
331 void WaveTrack::SetDisplayBounds(float min, float max) const
332 {
333  mDisplayMin = min;
334  mDisplayMax = max;
335 }
336 
337 void WaveTrack::GetSpectrumBounds(float *min, float *max) const
338 {
339  const double rate = GetRate();
340 
341  const SpectrogramSettings &settings = GetSpectrogramSettings();
342  const SpectrogramSettings::ScaleType type = settings.scaleType;
343 
344  const float top = (rate / 2.);
345 
346  float bottom;
347  if (type == SpectrogramSettings::stLinear)
348  bottom = 0.0f;
349  else if (type == SpectrogramSettings::stPeriod) {
350  // special case
351  const auto half = settings.GetFFTLength() / 2;
352  // EAC returns no data for below this frequency:
353  const float bin2 = rate / half;
354  bottom = bin2;
355  }
356  else
357  // logarithmic, etc.
358  bottom = 1.0f;
359 
360  {
361  float spectrumMax = mSpectrumMax;
362  if (spectrumMax < 0)
363  spectrumMax = settings.maxFreq;
364  if (spectrumMax < 0)
365  *max = top;
366  else
367  *max = std::max(bottom, std::min(top, spectrumMax));
368  }
369 
370  {
371  float spectrumMin = mSpectrumMin;
372  if (spectrumMin < 0)
373  spectrumMin = settings.minFreq;
374  if (spectrumMin < 0)
375  *min = std::max(bottom, top / 1000.0f);
376  else
377  *min = std::max(bottom, std::min(top, spectrumMin));
378  }
379 }
380 
381 void WaveTrack::SetSpectrumBounds(float min, float max) const
382 {
383  mSpectrumMin = min;
384  mSpectrumMax = max;
385 }
386 
387 int WaveTrack::ZeroLevelYCoordinate(wxRect rect) const
388 {
389  return rect.GetTop() +
390  (int)((mDisplayMax / (mDisplayMax - mDisplayMin)) * rect.height);
391 }
392 
394 {
395  return Track::Holder{ safenew WaveTrack{ *this } };
396 }
397 
398 double WaveTrack::GetRate() const
399 {
400  return mRate;
401 }
402 
403 void WaveTrack::SetRate(double newRate)
404 {
405  wxASSERT( newRate > 0 );
406  newRate = std::max( 1.0, newRate );
407  auto ratio = mRate / newRate;
408  mRate = (int) newRate;
409  for (const auto &clip : mClips) {
410  clip->SetRate((int)newRate);
411  clip->SetOffset( clip->GetOffset() * ratio );
412  }
413 }
414 
415 float WaveTrack::GetGain() const
416 {
417  return mGain;
418 }
419 
420 void WaveTrack::SetGain(float newGain)
421 {
422  mGain = newGain;
423 }
424 
425 float WaveTrack::GetPan() const
426 {
427  return mPan;
428 }
429 
430 void WaveTrack::SetPan(float newPan)
431 {
432  if (newPan > 1.0)
433  mPan = 1.0;
434  else if (newPan < -1.0)
435  mPan = -1.0;
436  else
437  mPan = newPan;
438 }
439 
440 float WaveTrack::GetChannelGain(int channel) const
441 {
442  float left = 1.0;
443  float right = 1.0;
444 
445  if (mPan < 0)
446  right = (mPan + 1.0);
447  else if (mPan > 0)
448  left = 1.0 - mPan;
449 
450  if ((channel%2) == 0)
451  return left*mGain;
452  else
453  return right*mGain;
454 }
455 
456 void WaveTrack::DoSetMinimized(bool isMinimized){
457 
458 #ifdef EXPERIMENTAL_HALF_WAVE
459  bool bHalfWave;
460  gPrefs->Read(wxT("/GUI/CollapseToHalfWave"), &bHalfWave, false);
461  if( bHalfWave )
462  {
463  // Show half wave on collapse, full on restore.
464  std::shared_ptr<TrackVRulerControls> pTvc = GetVRulerControls();
465 
466  // An awkward workaround for a function that lives 'in the wrong place'.
467  // We use magic numbers, 0 and 1, to tell it to zoom reset or zoom half-wave.
468  WaveTrackVRulerControls * pWtvc = reinterpret_cast<WaveTrackVRulerControls*>(pTvc.get());
469  if( pWtvc )
470  pWtvc->DoZoomPreset( isMinimized ? 1:0);
471  }
472 #endif
473 
474  PlayableTrack::DoSetMinimized( isMinimized );
475 }
476 
477 void WaveTrack::SetWaveColorIndex(int colorIndex)
478 // STRONG-GUARANTEE
479 {
480  for (const auto &clip : mClips)
481  clip->SetColourIndex( colorIndex );
482  mWaveColorIndex = colorIndex;
483 }
484 
485 
487 // WEAK-GUARANTEE
488 // might complete on only some tracks
489 {
490  for (const auto &clip : mClips)
491  clip->ConvertToSampleFormat(format);
492  mFormat = format;
493 }
494 
495 bool WaveTrack::IsEmpty(double t0, double t1) const
496 {
497  if (t0 > t1)
498  return true;
499 
500  //wxPrintf("Searching for overlap in %.6f...%.6f\n", t0, t1);
501  for (const auto &clip : mClips)
502  {
503  if (!clip->BeforeClip(t1) && !clip->AfterClip(t0)) {
504  //wxPrintf("Overlapping clip: %.6f...%.6f\n",
505  // clip->GetStartTime(),
506  // clip->GetEndTime());
507  // We found a clip that overlaps this region
508  return false;
509  }
510  }
511  //wxPrintf("No overlap found\n");
512 
513  // Otherwise, no clips overlap this region
514  return true;
515 }
516 
517 Track::Holder WaveTrack::Cut(double t0, double t1)
518 {
519  if (t1 < t0)
521 
522  auto tmp = Copy(t0, t1);
523 
524  Clear(t0, t1);
525 
526  return tmp;
527 }
528 
529 Track::Holder WaveTrack::SplitCut(double t0, double t1)
530 // STRONG-GUARANTEE
531 {
532  if (t1 < t0)
534 
535  // SplitCut is the same as 'Copy', then 'SplitDelete'
536  auto tmp = Copy(t0, t1);
537 
538  SplitDelete(t0, t1);
539 
540  return tmp;
541 }
542 
543 #if 0
544 Track::Holder WaveTrack::CutAndAddCutLine(double t0, double t1)
545 {
546  if (t1 < t0)
548 
549  // Cut is the same as 'Copy', then 'Delete'
550  auto tmp = Copy(t0, t1);
551 
552  ClearAndAddCutLine(t0, t1);
553 
554  return tmp;
555 }
556 #endif
557 
558 
559 
560 //Trim trims within a clip, rather than trimming everything.
561 //If a bound is outside a clip, it trims everything.
562 void WaveTrack::Trim (double t0, double t1)
563 // WEAK-GUARANTEE
564 {
565  bool inside0 = false;
566  bool inside1 = false;
567  //Keeps track of the offset of the first clip greater than
568  // the left selection t0.
569  double firstGreaterOffset = -1;
570 
571  for (const auto &clip : mClips)
572  {
573  //Find the first clip greater than the offset.
574  //If we end up clipping the entire track, this is useful.
575  if(firstGreaterOffset < 0 &&
576  clip->GetStartTime() >= t0)
577  firstGreaterOffset = clip->GetStartTime();
578 
579  if(t1 > clip->GetStartTime() && t1 < clip->GetEndTime())
580  {
581  clip->Clear(t1,clip->GetEndTime());
582  inside1 = true;
583  }
584 
585  if(t0 > clip->GetStartTime() && t0 < clip->GetEndTime())
586  {
587  clip->Clear(clip->GetStartTime(),t0);
588  clip->SetOffset(t0);
589  inside0 = true;
590  }
591  }
592 
593  //if inside0 is false, then the left selector was between
594  //clips, so DELETE everything to its left.
595  if(!inside1 && t1 < GetEndTime())
596  Clear(t1,GetEndTime());
597 
598  if(!inside0 && t0 > GetStartTime())
599  SplitDelete(GetStartTime(), t0);
600 }
601 
602 
603 
604 
605 Track::Holder WaveTrack::Copy(double t0, double t1, bool forClipboard) const
606 {
607  if (t1 < t0)
609 
610  WaveTrack *newTrack;
611  Track::Holder result
612  { newTrack = safenew WaveTrack{ mDirManager } };
613 
614  newTrack->Init(*this);
615 
616  // PRL: Why shouldn't cutlines be copied and pasted too? I don't know, but
617  // that was the old behavior. But this function is also used by the
618  // Duplicate command and I changed its behavior in that case.
619 
620  for (const auto &clip : mClips)
621  {
622  if (t0 <= clip->GetStartTime() && t1 >= clip->GetEndTime())
623  {
624  // Whole clip is in copy region
625  //wxPrintf("copy: clip %i is in copy region\n", (int)clip);
626 
627  newTrack->mClips.push_back
628  (std::make_unique<WaveClip>(*clip, mDirManager, ! forClipboard));
629  WaveClip *const newClip = newTrack->mClips.back().get();
630  newClip->Offset(-t0);
631  }
632  else if (t1 > clip->GetStartTime() && t0 < clip->GetEndTime())
633  {
634  // Clip is affected by command
635  //wxPrintf("copy: clip %i is affected by command\n", (int)clip);
636 
637  const double clip_t0 = std::max(t0, clip->GetStartTime());
638  const double clip_t1 = std::min(t1, clip->GetEndTime());
639 
640  auto newClip = std::make_unique<WaveClip>
641  (*clip, mDirManager, ! forClipboard, clip_t0, clip_t1);
642 
643  //wxPrintf("copy: clip_t0=%f, clip_t1=%f\n", clip_t0, clip_t1);
644 
645  newClip->Offset(-t0);
646  if (newClip->GetOffset() < 0)
647  newClip->SetOffset(0);
648 
649  newTrack->mClips.push_back(std::move(newClip)); // transfer ownership
650  }
651  }
652 
653  // AWD, Oct 2009: If the selection ends in whitespace, create a placeholder
654  // clip representing that whitespace
655  // PRL: Only if we want the track for pasting into other tracks. Not if it
656  // goes directly into a project as in the Duplicate command.
657  if (forClipboard &&
658  newTrack->GetEndTime() + 1.0 / newTrack->GetRate() < t1 - t0)
659  {
660  auto placeholder = std::make_unique<WaveClip>(mDirManager,
661  newTrack->GetSampleFormat(),
662  static_cast<int>(newTrack->GetRate()),
663  0 /*colourindex*/);
664  placeholder->SetIsPlaceholder(true);
665  placeholder->InsertSilence(0, (t1 - t0) - newTrack->GetEndTime());
666  placeholder->Offset(newTrack->GetEndTime());
667  newTrack->mClips.push_back(std::move(placeholder)); // transfer ownership
668  }
669 
670  return result;
671 }
672 
674 {
675  return Copy(t0, t1);
676 }
677 
678 void WaveTrack::Clear(double t0, double t1)
679 // STRONG-GUARANTEE
680 {
681  HandleClear(t0, t1, false, false);
682 }
683 
684 void WaveTrack::ClearAndAddCutLine(double t0, double t1)
685 // STRONG-GUARANTEE
686 {
687  HandleClear(t0, t1, true, false);
688 }
689 
691 {
692  if (mpSpectrumSettings)
693  return *mpSpectrumSettings;
694  else
696 }
697 
699 {
700  if (mpSpectrumSettings)
701  return *mpSpectrumSettings;
702  else
704 }
705 
707 {
708  if (!mpSpectrumSettings)
710  std::make_unique<SpectrogramSettings>(SpectrogramSettings::defaults());
711  return *mpSpectrumSettings;
712 }
713 
714 void WaveTrack::SetSpectrogramSettings(std::unique_ptr<SpectrogramSettings> &&pSettings)
715 {
716  if (mpSpectrumSettings != pSettings) {
717  mpSpectrumSettings = std::move(pSettings);
718  }
719 }
720 
722 {
723  if( bUse ){
724  if( !mpSpectrumSettings )
725  return;
726  // reset it, and next we will be getting the defaults.
727  mpSpectrumSettings.reset();
728  }
729  else {
730  if( mpSpectrumSettings )
731  return;
733  }
734 }
735 
736 
737 
739 {
740  if (mpWaveformSettings)
741  return *mpWaveformSettings;
742  else
744 }
745 
747 {
748  if (mpWaveformSettings)
749  return *mpWaveformSettings;
750  else
752 }
753 
755 {
756  if (!mpWaveformSettings)
757  mpWaveformSettings = std::make_unique<WaveformSettings>(WaveformSettings::defaults());
758  return *mpWaveformSettings;
759 }
760 
761 void WaveTrack::SetWaveformSettings(std::unique_ptr<WaveformSettings> &&pSettings)
762 {
763  if (mpWaveformSettings != pSettings) {
764  mpWaveformSettings = std::move(pSettings);
765  }
766 }
767 
768 //
769 // ClearAndPaste() is a specialized version of HandleClear()
770 // followed by Paste() and is used mostly by effects that
771 // can't replace track data directly using Get()/Set().
772 //
773 // HandleClear() removes any cut/split lines lines with the
774 // cleared range, but, in most cases, effects want to preserve
775 // the existing cut/split lines, so they are saved before the
776 // HandleClear()/Paste() and restored after.
777 //
778 // If the pasted track overlaps two or more clips, then it will
779 // be pasted with visible split lines. Normally, effects do not
780 // want these extra lines, so they may be merged out.
781 //
782 void WaveTrack::ClearAndPaste(double t0, // Start of time to clear
783  double t1, // End of time to clear
784  const Track *src, // What to paste
785  bool preserve, // Whether to reinsert splits/cuts
786  bool merge, // Whether to remove 'extra' splits
787  const TimeWarper *effectWarper // How does time change
788  )
789 // WEAK-GUARANTEE
790 // this WaveTrack remains destructible in case of AudacityException.
791 // But some of its cutline clips may have been destroyed.
792 {
793  double dur = std::min(t1 - t0, src->GetEndTime());
794 
795  // If duration is 0, then it's just a plain paste
796  if (dur == 0.0) {
797  // use WEAK-GUARANTEE
798  Paste(t0, src);
799  return;
800  }
801 
802  std::vector<double> splits;
803  WaveClipHolders cuts;
804 
805  // If provided time warper was NULL, use a default one that does nothing
806  IdentityTimeWarper localWarper;
807  const TimeWarper *warper = (effectWarper ? effectWarper : &localWarper);
808 
809  // Align to a sample
812 
813  // Save the cut/split lines whether preserving or not since merging
814  // needs to know if a clip boundary is being crossed since Paste()
815  // will add split lines around the pasted clip if so.
816  for (const auto &clip : mClips) {
817  double st;
818 
819  // Remember clip boundaries as locations to split
820  st = LongSamplesToTime(TimeToLongSamples(clip->GetStartTime()));
821  if (st >= t0 && st <= t1 && !make_iterator_range(splits).contains(st)) {
822  splits.push_back(st);
823  }
824 
825  st = LongSamplesToTime(TimeToLongSamples(clip->GetEndTime()));
826  if (st >= t0 && st <= t1 && !make_iterator_range(splits).contains(st)) {
827  splits.push_back(st);
828  }
829 
830  // Search for cut lines
831  auto &cutlines = clip->GetCutLines();
832  // May erase from cutlines, so don't use range-for
833  for (auto it = cutlines.begin(); it != cutlines.end(); ) {
834  WaveClip *cut = it->get();
835  double cs = LongSamplesToTime(TimeToLongSamples(clip->GetOffset() +
836  cut->GetOffset()));
837 
838  // Remember cut point
839  if (cs >= t0 && cs <= t1) {
840 
841  // Remember the absolute offset and add to our cuts array.
842  cut->SetOffset(cs);
843  cuts.push_back(std::move(*it)); // transfer ownership!
844  it = cutlines.erase(it);
845  }
846  else
847  ++it;
848  }
849  }
850 
851  const auto tolerance = 2.0 / GetRate();
852 
853  // Now, clear the selection
854  HandleClear(t0, t1, false, false);
855  {
856 
857  // And paste in the NEW data
858  Paste(t0, src);
859  {
860  // First, merge the NEW clip(s) in with the existing clips
861  if (merge && splits.size() > 0)
862  {
863  // Now t1 represents the absolute end of the pasted data.
864  t1 = t0 + src->GetEndTime();
865 
866  // Get a sorted array of the clips
867  auto clips = SortedClipArray();
868 
869  // Scan the sorted clips for the first clip whose start time
870  // exceeds the pasted regions end time.
871  {
872  WaveClip *prev = nullptr;
873  for (const auto clip : clips) {
874  // Merge this clip and the previous clip if the end time
875  // falls within it and this isn't the first clip in the track.
876  if (fabs(t1 - clip->GetStartTime()) < tolerance) {
877  if (prev)
878  MergeClips(GetClipIndex(prev), GetClipIndex(clip));
879  break;
880  }
881  prev = clip;
882  }
883  }
884  }
885 
886  // Refill the array since clips have changed.
887  auto clips = SortedClipArray();
888 
889  {
890  // Scan the sorted clips to look for the start of the pasted
891  // region.
892  WaveClip *prev = nullptr;
893  for (const auto clip : clips) {
894  if (prev) {
895  // It must be that clip is what was pasted and it begins where
896  // prev ends.
897  // use WEAK-GUARANTEE
898  MergeClips(GetClipIndex(prev), GetClipIndex(clip));
899  break;
900  }
901  if (fabs(t0 - clip->GetEndTime()) < tolerance)
902  // Merge this clip and the next clip if the start time
903  // falls within it and this isn't the last clip in the track.
904  prev = clip;
905  else
906  prev = nullptr;
907  }
908  }
909  }
910 
911  // Restore cut/split lines
912  if (preserve) {
913 
914  // Restore the split lines, transforming the position appropriately
915  for (const auto split: splits) {
916  SplitAt(warper->Warp(split));
917  }
918 
919  // Restore the saved cut lines, also transforming if time altered
920  for (const auto &clip : mClips) {
921  double st;
922  double et;
923 
924  st = clip->GetStartTime();
925  et = clip->GetEndTime();
926 
927  // Scan the cuts for any that live within this clip
928  for (auto it = cuts.begin(); it != cuts.end();) {
929  WaveClip *cut = it->get();
930  double cs = cut->GetOffset();
931 
932  // Offset the cut from the start of the clip and add it to
933  // this clips cutlines.
934  if (cs >= st && cs <= et) {
935  cut->SetOffset(warper->Warp(cs) - st);
936  clip->GetCutLines().push_back( std::move(*it) ); // transfer ownership!
937  it = cuts.erase(it);
938  }
939  else
940  ++it;
941  }
942  }
943  }
944  }
945 }
946 
947 void WaveTrack::SplitDelete(double t0, double t1)
948 // STRONG-GUARANTEE
949 {
950  bool addCutLines = false;
951  bool split = true;
952  HandleClear(t0, t1, addCutLines, split);
953 }
954 
955 namespace
956 {
957  WaveClipHolders::const_iterator
958  FindClip(const WaveClipHolders &list, const WaveClip *clip, int *distance = nullptr)
959  {
960  if (distance)
961  *distance = 0;
962  auto it = list.begin();
963  for (const auto end = list.end(); it != end; ++it)
964  {
965  if (it->get() == clip)
966  break;
967  if (distance)
968  ++*distance;
969  }
970  return it;
971  }
972 
973  WaveClipHolders::iterator
974  FindClip(WaveClipHolders &list, const WaveClip *clip, int *distance = nullptr)
975  {
976  if (distance)
977  *distance = 0;
978  auto it = list.begin();
979  for (const auto end = list.end(); it != end; ++it)
980  {
981  if (it->get() == clip)
982  break;
983  if (distance)
984  ++*distance;
985  }
986  return it;
987  }
988 }
989 
990 std::shared_ptr<WaveClip> WaveTrack::RemoveAndReturnClip(WaveClip* clip)
991 {
992  // Be clear about who owns the clip!!
993  auto it = FindClip(mClips, clip);
994  if (it != mClips.end()) {
995  auto result = std::move(*it); // Array stops owning the clip, before we shrink it
996  mClips.erase(it);
997  return result;
998  }
999  else
1000  return {};
1001 }
1002 
1003 void WaveTrack::AddClip(std::shared_ptr<WaveClip> &&clip)
1004 {
1005  // Uncomment the following line after we correct the problem of zero-length clips
1006  //if (CanInsertClip(clip))
1007  mClips.push_back(std::move(clip)); // transfer ownership
1008 }
1009 
1010 void WaveTrack::HandleClear(double t0, double t1,
1011  bool addCutLines, bool split)
1012 // STRONG-GUARANTEE
1013 {
1014  if (t1 < t0)
1016 
1017  bool editClipCanMove = gPrefs->GetEditClipsCanMove();
1018 
1019  WaveClipPointers clipsToDelete;
1020  WaveClipHolders clipsToAdd;
1021 
1022  // We only add cut lines when deleting in the middle of a single clip
1023  // The cut line code is not really prepared to handle other situations
1024  if (addCutLines)
1025  {
1026  for (const auto &clip : mClips)
1027  {
1028  if (!clip->BeforeClip(t1) && !clip->AfterClip(t0) &&
1029  (clip->BeforeClip(t0) || clip->AfterClip(t1)))
1030  {
1031  addCutLines = false;
1032  break;
1033  }
1034  }
1035  }
1036 
1037  for (const auto &clip : mClips)
1038  {
1039  if (clip->BeforeClip(t0) && clip->AfterClip(t1))
1040  {
1041  // Whole clip must be deleted - remember this
1042  clipsToDelete.push_back(clip.get());
1043  }
1044  else if (!clip->BeforeClip(t1) && !clip->AfterClip(t0))
1045  {
1046  // Clip data is affected by command
1047  if (addCutLines)
1048  {
1049  // Don't modify this clip in place, because we want a strong
1050  // guarantee, and might modify another clip
1051  clipsToDelete.push_back( clip.get() );
1052  auto newClip = std::make_unique<WaveClip>( *clip, mDirManager, true );
1053  newClip->ClearAndAddCutLine( t0, t1 );
1054  clipsToAdd.push_back( std::move( newClip ) );
1055  }
1056  else
1057  {
1058  if (split) {
1059  // Three cases:
1060 
1061  if (clip->BeforeClip(t0)) {
1062  // Delete from the left edge
1063 
1064  // Don't modify this clip in place, because we want a strong
1065  // guarantee, and might modify another clip
1066  clipsToDelete.push_back( clip.get() );
1067  auto newClip = std::make_unique<WaveClip>( *clip, mDirManager, true );
1068  newClip->Clear(clip->GetStartTime(), t1);
1069  newClip->Offset(t1-clip->GetStartTime());
1070 
1071  clipsToAdd.push_back( std::move( newClip ) );
1072  }
1073  else if (clip->AfterClip(t1)) {
1074  // Delete to right edge
1075 
1076  // Don't modify this clip in place, because we want a strong
1077  // guarantee, and might modify another clip
1078  clipsToDelete.push_back( clip.get() );
1079  auto newClip = std::make_unique<WaveClip>( *clip, mDirManager, true );
1080  newClip->Clear(t0, clip->GetEndTime());
1081 
1082  clipsToAdd.push_back( std::move( newClip ) );
1083  }
1084  else {
1085  // Delete in the middle of the clip...we actually create two
1086  // NEW clips out of the left and right halves...
1087 
1088  // left
1089  clipsToAdd.push_back
1090  ( std::make_unique<WaveClip>( *clip, mDirManager, true ) );
1091  clipsToAdd.back()->Clear(t0, clip->GetEndTime());
1092 
1093  // right
1094  clipsToAdd.push_back
1095  ( std::make_unique<WaveClip>( *clip, mDirManager, true ) );
1096  WaveClip *const right = clipsToAdd.back().get();
1097  right->Clear(clip->GetStartTime(), t1);
1098  right->Offset(t1 - clip->GetStartTime());
1099 
1100  clipsToDelete.push_back(clip.get());
1101  }
1102  }
1103  else {
1104  // (We are not doing a split cut)
1105 
1106  // Don't modify this clip in place, because we want a strong
1107  // guarantee, and might modify another clip
1108  clipsToDelete.push_back( clip.get() );
1109  auto newClip = std::make_unique<WaveClip>( *clip, mDirManager, true );
1110 
1111  // clip->Clear keeps points < t0 and >= t1 via Envelope::CollapseRegion
1112  newClip->Clear(t0,t1);
1113 
1114  clipsToAdd.push_back( std::move( newClip ) );
1115  }
1116  }
1117  }
1118  }
1119 
1120  // Only now, change the contents of this track
1121  // use NOFAIL-GUARANTEE for the rest
1122 
1123  for (const auto &clip : mClips)
1124  {
1125  if (clip->BeforeClip(t1))
1126  {
1127  // Clip is "behind" the region -- offset it unless we're splitting
1128  // or we're using the "don't move other clips" mode
1129  if (!split && editClipCanMove)
1130  clip->Offset(-(t1-t0));
1131  }
1132  }
1133 
1134  for (const auto &clip: clipsToDelete)
1135  {
1136  auto myIt = FindClip(mClips, clip);
1137  if (myIt != mClips.end())
1138  mClips.erase(myIt); // deletes the clip!
1139  else
1140  wxASSERT(false);
1141  }
1142 
1143  for (auto &clip: clipsToAdd)
1144  mClips.push_back(std::move(clip)); // transfer ownership
1145 }
1146 
1147 void WaveTrack::SyncLockAdjust(double oldT1, double newT1)
1148 {
1149  if (newT1 > oldT1) {
1150  // Insert space within the track
1151 
1152  // JKC: This is a rare case where using >= rather than > on a float matters.
1153  // GetEndTime() looks through the clips and may give us EXACTLY the same
1154  // value as T1, when T1 was set to be at the end of one of those clips.
1155  if (oldT1 >= GetEndTime())
1156  return;
1157 
1158  // If track is empty at oldT1 insert whitespace; otherwise, silence
1159  if (IsEmpty(oldT1, oldT1))
1160  {
1161  // Check if clips can move
1162  bool clipsCanMove = true;
1163  gPrefs->Read(wxT("/GUI/EditClipCanMove"), &clipsCanMove);
1164  if (clipsCanMove) {
1165  auto tmp = Cut (oldT1, GetEndTime() + 1.0/GetRate());
1166 
1167  Paste(newT1, tmp.get());
1168  }
1169  return;
1170  }
1171  else {
1172  // AWD: Could just use InsertSilence() on its own here, but it doesn't
1173  // follow EditClipCanMove rules (Paste() does it right)
1175  if (!p)
1177  TrackFactory *f = p->GetTrackFactory();
1178  if (!f)
1180  auto tmp = f->NewWaveTrack(GetSampleFormat(), GetRate());
1181 
1182  tmp->InsertSilence(0.0, newT1 - oldT1);
1183  tmp->Flush();
1184  Paste(oldT1, tmp.get());
1185  }
1186  }
1187  else if (newT1 < oldT1) {
1188  Clear(newT1, oldT1);
1189  }
1190 }
1191 
1192 void WaveTrack::Paste(double t0, const Track *src)
1193 // WEAK-GUARANTEE
1194 {
1195  bool editClipCanMove = gPrefs->GetEditClipsCanMove();
1196 
1197  if( src == NULL )
1198  // THROW_INCONSISTENCY_EXCEPTION; // ?
1199  return;
1200 
1201  if (src->GetKind() != Track::Wave)
1202  // THROW_INCONSISTENCY_EXCEPTION; // ?
1203  return;
1204 
1205  const WaveTrack* other = static_cast<const WaveTrack*>(src);
1206 
1207  //
1208  // Pasting is a bit complicated, because with the existence of multiclip mode,
1209  // we must guess the behaviour the user wants.
1210  //
1211  // Currently, two modes are implemented:
1212  //
1213  // - If a single clip should be pasted, and it should be pasted inside another
1214  // clip, no NEW clips are generated. The audio is simply inserted.
1215  // This resembles the old (pre-multiclip support) behaviour. However, if
1216  // the clip is pasted outside of any clip, a NEW clip is generated. This is
1217  // the only behaviour which is different to what was done before, but it
1218  // shouldn't confuse users too much.
1219  //
1220  // - If multiple clips should be pasted, or a single clip that does not fill
1221  // the duration of the pasted track, these are always pasted as single
1222  // clips, and the current clip is splitted, when necessary. This may seem
1223  // strange at first, but it probably is better than trying to auto-merge
1224  // anything. The user can still merge the clips by hand (which should be a
1225  // simple command reachable by a hotkey or single mouse click).
1226  //
1227 
1228  if (other->GetNumClips() == 0)
1229  return;
1230 
1231  //wxPrintf("paste: we have at least one clip\n");
1232 
1233  bool singleClipMode = (other->GetNumClips() == 1 &&
1234  other->GetStartTime() == 0.0);
1235 
1236  const double insertDuration = other->GetEndTime();
1237  if( insertDuration != 0 && insertDuration < 1.0/mRate )
1238  // PRL: I added this check to avoid violations of preconditions in other WaveClip and Sequence
1239  // methods, but allow the value 0 so I don't subvert the purpose of commit
1240  // 739422ba70ceb4be0bb1829b6feb0c5401de641e which causes append-recording always to make
1241  // a new clip.
1242  return;
1243 
1244  //wxPrintf("Check if we need to make room for the pasted data\n");
1245 
1246  // Make room for the pasted data
1247  if (editClipCanMove) {
1248  if (!singleClipMode) {
1249  // We need to insert multiple clips, so split the current clip and
1250  // move everything to the right, then try to paste again
1251  if (!IsEmpty(t0, GetEndTime())) {
1252  auto tmp = Cut(t0, GetEndTime()+1.0/mRate);
1253  Paste(t0 + insertDuration, tmp.get());
1254  }
1255  }
1256  else {
1257  // We only need to insert one single clip, so just move all clips
1258  // to the right of the paste point out of the way
1259  for (const auto &clip : mClips)
1260  {
1261  if (clip->GetStartTime() > t0-(1.0/mRate))
1262  clip->Offset(insertDuration);
1263  }
1264  }
1265  }
1266 
1267  if (singleClipMode)
1268  {
1269  // Single clip mode
1270  // wxPrintf("paste: checking for single clip mode!\n");
1271 
1272  WaveClip *insideClip = NULL;
1273 
1274  for (const auto &clip : mClips)
1275  {
1276  if (editClipCanMove)
1277  {
1278  if (clip->WithinClip(t0))
1279  {
1280  //wxPrintf("t0=%.6f: inside clip is %.6f ... %.6f\n",
1281  // t0, clip->GetStartTime(), clip->GetEndTime());
1282  insideClip = clip.get();
1283  break;
1284  }
1285  }
1286  else
1287  {
1288  // If clips are immovable we also allow prepending to clips
1289  if (clip->WithinClip(t0) ||
1290  TimeToLongSamples(t0) == clip->GetStartSample())
1291  {
1292  insideClip = clip.get();
1293  break;
1294  }
1295  }
1296  }
1297 
1298  if (insideClip)
1299  {
1300  // Exhibit traditional behaviour
1301  //wxPrintf("paste: traditional behaviour\n");
1302  if (!editClipCanMove)
1303  {
1304  // We did not move other clips out of the way already, so
1305  // check if we can paste without having to move other clips
1306  for (const auto &clip : mClips)
1307  {
1308  if (clip->GetStartTime() > insideClip->GetStartTime() &&
1309  insideClip->GetEndTime() + insertDuration >
1310  clip->GetStartTime())
1311  // STRONG-GUARANTEE in case of this path
1312  // not that it matters.
1314  _("There is not enough room available to paste the selection")
1315  };
1316  }
1317  }
1318 
1319  insideClip->Paste(t0, other->GetClipByIndex(0));
1320  return;
1321  }
1322 
1323  // Just fall through and exhibit NEW behaviour
1324  }
1325 
1326  // Insert NEW clips
1327  //wxPrintf("paste: multi clip mode!\n");
1328 
1329  if (!editClipCanMove && !IsEmpty(t0, t0+insertDuration-1.0/mRate))
1330  // STRONG-GUARANTEE in case of this path
1331  // not that it matters.
1333  _("There is not enough room available to paste the selection")
1334  };
1335 
1336  for (const auto &clip : other->mClips)
1337  {
1338  // AWD Oct. 2009: Don't actually paste in placeholder clips
1339  if (!clip->GetIsPlaceholder())
1340  {
1341  auto newClip =
1342  std::make_unique<WaveClip>( *clip, mDirManager, true );
1343  newClip->Resample(mRate);
1344  newClip->Offset(t0);
1345  newClip->MarkChanged();
1346  mClips.push_back(std::move(newClip)); // transfer ownership
1347  }
1348  }
1349 }
1350 
1351 void WaveTrack::Silence(double t0, double t1)
1352 {
1353  if (t1 < t0)
1355 
1356  auto start = (sampleCount)floor(t0 * mRate + 0.5);
1357  auto len = (sampleCount)floor(t1 * mRate + 0.5) - start;
1358 
1359  for (const auto &clip : mClips)
1360  {
1361  auto clipStart = clip->GetStartSample();
1362  auto clipEnd = clip->GetEndSample();
1363 
1364  if (clipEnd > start && clipStart < start+len)
1365  {
1366  // Clip sample region and Get/Put sample region overlap
1367  auto samplesToCopy = start+len - clipStart;
1368  if (samplesToCopy > clip->GetNumSamples())
1369  samplesToCopy = clip->GetNumSamples();
1370  auto startDelta = clipStart - start;
1371  decltype(startDelta) inclipDelta = 0;
1372  if (startDelta < 0)
1373  {
1374  inclipDelta = -startDelta; // make positive value
1375  samplesToCopy -= inclipDelta;
1376  startDelta = 0;
1377  }
1378 
1379  clip->GetSequence()->SetSilence(inclipDelta, samplesToCopy);
1380  clip->MarkChanged();
1381  }
1382  }
1383 }
1384 
1385 void WaveTrack::InsertSilence(double t, double len)
1386 // STRONG-GUARANTEE
1387 {
1388  // Nothing to do, if length is zero.
1389  // Fixes Bug 1626
1390  if( len == 0 )
1391  return;
1392  if (len <= 0)
1394 
1395  if (mClips.empty())
1396  {
1397  // Special case if there is no clip yet
1398  auto clip = std::make_unique<WaveClip>(mDirManager, mFormat, mRate, this->GetWaveColorIndex());
1399  clip->InsertSilence(0, len);
1400  // use NOFAIL-GUARANTEE
1401  mClips.push_back( std::move( clip ) );
1402  return;
1403  }
1404  else {
1405  // Assume at most one clip contains t
1406  const auto end = mClips.end();
1407  const auto it = std::find_if( mClips.begin(), end,
1408  [&](const WaveClipHolder &clip) { return clip->WithinClip(t); } );
1409 
1410  // use STRONG-GUARANTEE
1411  if (it != end)
1412  it->get()->InsertSilence(t, len);
1413 
1414  // use NOFAIL-GUARANTEE
1415  for (const auto &clip : mClips)
1416  {
1417  if (clip->BeforeClip(t))
1418  clip->Offset(len);
1419  }
1420  }
1421 }
1422 
1423 //Performs the opposite of Join
1424 //Analyses selected region for possible Joined clips and disjoins them
1425 void WaveTrack::Disjoin(double t0, double t1)
1426 // WEAK-GUARANTEE
1427 {
1429  const size_t maxAtOnce = 1048576;
1430  Floats buffer{ maxAtOnce };
1431  Regions regions;
1432 
1433  wxBusyCursor busy;
1434 
1435  for (const auto &clip : mClips)
1436  {
1437  double startTime = clip->GetStartTime();
1438  double endTime = clip->GetEndTime();
1439 
1440  if( endTime < t0 || startTime > t1 )
1441  continue;
1442 
1443  if( t0 > startTime )
1444  startTime = t0;
1445  if( t1 < endTime )
1446  endTime = t1;
1447 
1448  //simply look for a sequence of zeroes and if the sequence
1449  //is greater than minimum number, split-DELETE the region
1450 
1451  sampleCount seqStart = -1;
1452  sampleCount start, end;
1453  clip->TimeToSamplesClip( startTime, &start );
1454  clip->TimeToSamplesClip( endTime, &end );
1455 
1456  auto len = ( end - start );
1457  for( decltype(len) done = 0; done < len; done += maxAtOnce )
1458  {
1459  auto numSamples = limitSampleBufferSize( maxAtOnce, len - done );
1460 
1461  clip->GetSamples( ( samplePtr )buffer.get(), floatSample, start + done,
1462  numSamples );
1463  for( decltype(numSamples) i = 0; i < numSamples; i++ )
1464  {
1465  auto curSamplePos = start + done + i;
1466 
1467  //start a NEW sequence
1468  if( buffer[ i ] == 0.0 && seqStart == -1 )
1469  seqStart = curSamplePos;
1470  else if( buffer[ i ] != 0.0 || curSamplePos == end - 1 )
1471  {
1472  if( seqStart != -1 )
1473  {
1474  decltype(end) seqEnd;
1475 
1476  //consider the end case, where selection ends in zeroes
1477  if( curSamplePos == end - 1 && buffer[ i ] == 0.0 )
1478  seqEnd = end;
1479  else
1480  seqEnd = curSamplePos;
1481  if( seqEnd - seqStart + 1 > minSamples )
1482  {
1483  regions.push_back(Region(
1484  seqStart.as_double() / GetRate()
1485  + clip->GetStartTime(),
1486  seqEnd.as_double() / GetRate()
1487  + clip->GetStartTime()));
1488  }
1489  seqStart = -1;
1490  }
1491  }
1492  }
1493  }
1494  }
1495 
1496  for( unsigned int i = 0; i < regions.size(); i++ )
1497  {
1498  const Region &region = regions.at(i);
1499  SplitDelete(region.start, region.end );
1500  }
1501 }
1502 
1503 void WaveTrack::Join(double t0, double t1)
1504 // WEAK-GUARANTEE
1505 {
1506  // Merge all WaveClips overlapping selection into one
1507 
1508  WaveClipPointers clipsToDelete;
1509  WaveClip *newClip;
1510 
1511  for (const auto &clip: mClips)
1512  {
1513  if (clip->GetStartTime() < t1-(1.0/mRate) &&
1514  clip->GetEndTime()-(1.0/mRate) > t0) {
1515 
1516  // Put in sorted order
1517  auto it = clipsToDelete.begin(), end = clipsToDelete.end();
1518  for (; it != end; ++it)
1519  if ((*it)->GetStartTime() > clip->GetStartTime())
1520  break;
1521  //wxPrintf("Insert clip %.6f at position %d\n", clip->GetStartTime(), i);
1522  clipsToDelete.insert(it, clip.get());
1523  }
1524  }
1525 
1526  //if there are no clips to DELETE, nothing to do
1527  if( clipsToDelete.size() == 0 )
1528  return;
1529 
1530  newClip = CreateClip();
1531  double t = clipsToDelete[0]->GetOffset();
1532  newClip->SetOffset(t);
1533  for (const auto &clip : clipsToDelete)
1534  {
1535  //wxPrintf("t=%.6f adding clip (offset %.6f, %.6f ... %.6f)\n",
1536  // t, clip->GetOffset(), clip->GetStartTime(), clip->GetEndTime());
1537 
1538  if (clip->GetOffset() - t > (1.0 / mRate)) {
1539  double addedSilence = (clip->GetOffset() - t);
1540  //wxPrintf("Adding %.6f seconds of silence\n");
1541  auto offset = clip->GetOffset();
1542  auto value = clip->GetEnvelope()->GetValue( offset );
1543  newClip->AppendSilence( addedSilence, value );
1544  t += addedSilence;
1545  }
1546 
1547  //wxPrintf("Pasting at %.6f\n", t);
1548  newClip->Paste(t, clip);
1549 
1550  t = newClip->GetEndTime();
1551 
1552  auto it = FindClip(mClips, clip);
1553  mClips.erase(it); // deletes the clip
1554  }
1555 }
1556 
1558  size_t len, unsigned int stride /* = 1 */,
1559  XMLWriter *blockFileLog /* = NULL */)
1560 // PARTIAL-GUARANTEE in case of exceptions:
1561 // Some prefix (maybe none) of the buffer is appended, and no content already
1562 // flushed to disk is lost.
1563 {
1564  RightmostOrNewClip()->Append(buffer, format, len, stride,
1565  blockFileLog);
1566 }
1567 
1568 void WaveTrack::AppendAlias(const wxString &fName, sampleCount start,
1569  size_t len, int channel,bool useOD)
1570 // STRONG-GUARANTEE
1571 {
1572  RightmostOrNewClip()->AppendAlias(fName, start, len, channel, useOD);
1573 }
1574 
1575 void WaveTrack::AppendCoded(const wxString &fName, sampleCount start,
1576  size_t len, int channel, int decodeType)
1577 // STRONG-GUARANTEE
1578 {
1579  RightmostOrNewClip()->AppendCoded(fName, start, len, channel, decodeType);
1580 }
1581 
1583 unsigned int WaveTrack::GetODFlags() const
1584 {
1585  unsigned int ret = 0;
1586  for (const auto &clip : mClips)
1587  {
1588  ret = ret | clip->GetSequence()->GetODFlags();
1589  }
1590  return ret;
1591 }
1592 
1593 
1595 {
1596  for (const auto &clip : mClips)
1597  {
1598  const auto startSample = (sampleCount)floor(0.5 + clip->GetStartTime()*mRate);
1599  const auto endSample = startSample + clip->GetNumSamples();
1600  if (s >= startSample && s < endSample)
1601  return startSample + clip->GetSequence()->GetBlockStart(s - startSample);
1602  }
1603 
1604  return -1;
1605 }
1606 
1608 {
1609  auto bestBlockSize = GetMaxBlockSize();
1610 
1611  for (const auto &clip : mClips)
1612  {
1613  auto startSample = (sampleCount)floor(clip->GetStartTime()*mRate + 0.5);
1614  auto endSample = startSample + clip->GetNumSamples();
1615  if (s >= startSample && s < endSample)
1616  {
1617  bestBlockSize = clip->GetSequence()->GetBestBlockSize(s - startSample);
1618  break;
1619  }
1620  }
1621 
1622  return bestBlockSize;
1623 }
1624 
1626 {
1627  decltype(GetMaxBlockSize()) maxblocksize = 0;
1628  for (const auto &clip : mClips)
1629  {
1630  maxblocksize = std::max(maxblocksize, clip->GetSequence()->GetMaxBlockSize());
1631  }
1632 
1633  if (maxblocksize == 0)
1634  {
1635  // We really need the maximum block size, so create a
1636  // temporary sequence to get it.
1637  maxblocksize = Sequence{ mDirManager, mFormat }.GetMaxBlockSize();
1638  }
1639 
1640  wxASSERT(maxblocksize > 0);
1641 
1642  return maxblocksize;
1643 }
1644 
1646 {
1648 }
1649 
1651 // NOFAIL-GUARANTEE that the rightmost clip will be in a flushed state.
1652 // PARTIAL-GUARANTEE in case of exceptions:
1653 // Some initial portion (maybe none) of the append buffer of the rightmost
1654 // clip gets appended; no previously saved contents are lost.
1655 {
1656  // After appending, presumably. Do this to the clip that gets appended.
1658 }
1659 
1660 bool WaveTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
1661 {
1662  if (!wxStrcmp(tag, wxT("wavetrack"))) {
1663  double dblValue;
1664  long nValue;
1665  while(*attrs) {
1666  const wxChar *attr = *attrs++;
1667  const wxChar *value = *attrs++;
1668 
1669  if (!value)
1670  break;
1671 
1672  const wxString strValue = value;
1673  if (!wxStrcmp(attr, wxT("rate")))
1674  {
1675  // mRate is an int, but "rate" in the project file is a float.
1676  if (!XMLValueChecker::IsGoodString(strValue) ||
1677  !Internat::CompatibleToDouble(strValue, &dblValue) ||
1678  (dblValue < 1.0) || (dblValue > 1000000.0)) // allow a large range to be read
1679  return false;
1680  mRate = lrint(dblValue);
1681  }
1682  else if (!wxStrcmp(attr, wxT("offset")) &&
1683  XMLValueChecker::IsGoodString(strValue) &&
1684  Internat::CompatibleToDouble(strValue, &dblValue))
1685  {
1686  // Offset is only relevant for legacy project files. The value
1687  // is cached until the actual WaveClip containing the legacy
1688  // track is created.
1689  mLegacyProjectFileOffset = dblValue;
1690  }
1691  else if (this->PlayableTrack::HandleXMLAttribute(attr, value))
1692  {}
1693  else if (!wxStrcmp(attr, wxT("height")) &&
1694  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
1695  mHeight = nValue;
1696  else if (!wxStrcmp(attr, wxT("minimized")) &&
1697  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
1698  mMinimized = (nValue != 0);
1699  else if (!wxStrcmp(attr, wxT("isSelected")) &&
1700  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
1701  this->SetSelected(nValue != 0);
1702  else if (!wxStrcmp(attr, wxT("gain")) &&
1703  XMLValueChecker::IsGoodString(strValue) &&
1704  Internat::CompatibleToDouble(strValue, &dblValue))
1705  mGain = dblValue;
1706  else if (!wxStrcmp(attr, wxT("pan")) &&
1707  XMLValueChecker::IsGoodString(strValue) &&
1708  Internat::CompatibleToDouble(strValue, &dblValue) &&
1709  (dblValue >= -1.0) && (dblValue <= 1.0))
1710  mPan = dblValue;
1711  else if (!wxStrcmp(attr, wxT("name")) && XMLValueChecker::IsGoodString(strValue))
1712  mName = strValue;
1713  else if (!wxStrcmp(attr, wxT("channel")))
1714  {
1715  if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&nValue) ||
1717  return false;
1718  mChannel = nValue;
1719  }
1720  else if (!wxStrcmp(attr, wxT("linked")) &&
1721  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
1722  SetLinked(nValue != 0);
1723  else if (!wxStrcmp(attr, wxT("autosaveid")) &&
1724  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
1725  mAutoSaveIdent = (int) nValue;
1726  else if (!wxStrcmp(attr, wxT("colorindex")) &&
1727  XMLValueChecker::IsGoodString(strValue) &&
1728  strValue.ToLong(&nValue))
1729  // Don't use SetWaveColorIndex as it sets the clips too.
1730  mWaveColorIndex = nValue;
1731  } // while
1732  return true;
1733  }
1734 
1735  return false;
1736 }
1737 
1738 void WaveTrack::HandleXMLEndTag(const wxChar * WXUNUSED(tag))
1739 {
1740  // In case we opened a pre-multiclip project, we need to
1741  // simulate closing the waveclip tag.
1742  NewestOrNewClip()->HandleXMLEndTag(wxT("waveclip"));
1743 }
1744 
1746 {
1747  //
1748  // This is legacy code (1.2 and previous) and is not called for NEW projects!
1749  //
1750  if (!wxStrcmp(tag, wxT("sequence")) || !wxStrcmp(tag, wxT("envelope")))
1751  {
1752  // This is a legacy project, so set the cached offset
1754 
1755  // Legacy project file tracks are imported as one single wave clip
1756  if (!wxStrcmp(tag, wxT("sequence")))
1757  return NewestOrNewClip()->GetSequence();
1758  else if (!wxStrcmp(tag, wxT("envelope")))
1759  return NewestOrNewClip()->GetEnvelope();
1760  }
1761 
1762  // JKC... for 1.1.0, one step better than what we had, but still badly broken.
1763  //If we see a waveblock at this level, we'd better generate a sequence.
1764  if( !wxStrcmp( tag, wxT("waveblock" )))
1765  {
1766  // This is a legacy project, so set the cached offset
1768  Sequence *pSeq = NewestOrNewClip()->GetSequence();
1769  return pSeq;
1770  }
1771 
1772  //
1773  // This is for the NEW file format (post-1.2)
1774  //
1775  if (!wxStrcmp(tag, wxT("waveclip")))
1776  return CreateClip();
1777  else
1778  return NULL;
1779 }
1780 
1781 void WaveTrack::WriteXML(XMLWriter &xmlFile) const
1782 // may throw
1783 {
1784  xmlFile.StartTag(wxT("wavetrack"));
1785  if (mAutoSaveIdent)
1786  {
1787  xmlFile.WriteAttr(wxT("autosaveid"), mAutoSaveIdent);
1788  }
1789  xmlFile.WriteAttr(wxT("name"), mName);
1790  xmlFile.WriteAttr(wxT("channel"), mChannel);
1791  xmlFile.WriteAttr(wxT("linked"), mLinked);
1792  this->PlayableTrack::WriteXMLAttributes(xmlFile);
1793  xmlFile.WriteAttr(wxT("height"), this->GetActualHeight());
1794  xmlFile.WriteAttr(wxT("minimized"), this->GetMinimized());
1795  xmlFile.WriteAttr(wxT("isSelected"), this->GetSelected());
1796  xmlFile.WriteAttr(wxT("rate"), mRate);
1797  xmlFile.WriteAttr(wxT("gain"), (double)mGain);
1798  xmlFile.WriteAttr(wxT("pan"), (double)mPan);
1799  xmlFile.WriteAttr(wxT("colorindex"), mWaveColorIndex );
1800 
1801  for (const auto &clip : mClips)
1802  {
1803  clip->WriteXML(xmlFile);
1804  }
1805 
1806  xmlFile.EndTag(wxT("wavetrack"));
1807 }
1808 
1810 {
1811  for (const auto &clip : mClips)
1812  if (clip->GetSequence()->GetErrorOpening())
1813  return true;
1814 
1815  return false;
1816 }
1817 
1818 bool WaveTrack::Lock() const
1819 {
1820  for (const auto &clip : mClips)
1821  clip->Lock();
1822 
1823  return true;
1824 }
1825 
1827 {
1828  for (const auto &clip : mClips)
1829  clip->CloseLock();
1830 
1831  return true;
1832 }
1833 
1834 
1835 bool WaveTrack::Unlock() const
1836 {
1837  for (const auto &clip : mClips)
1838  clip->Unlock();
1839 
1840  return true;
1841 }
1842 
1843 AUDACITY_DLL_API sampleCount WaveTrack::TimeToLongSamples(double t0) const
1844 {
1845  return sampleCount( floor(t0 * mRate + 0.5) );
1846 }
1847 
1849 {
1850  return pos.as_double() / mRate;
1851 }
1852 
1854 {
1855  bool found = false;
1856  double best = 0.0;
1857 
1858  if (mClips.empty())
1859  return 0;
1860 
1861  for (const auto &clip : mClips)
1862  if (!found)
1863  {
1864  found = true;
1865  best = clip->GetStartTime();
1866  }
1867  else if (clip->GetStartTime() < best)
1868  best = clip->GetStartTime();
1869 
1870  return best;
1871 }
1872 
1874 {
1875  bool found = false;
1876  double best = 0.0;
1877 
1878  if (mClips.empty())
1879  return 0;
1880 
1881  for (const auto &clip : mClips)
1882  if (!found)
1883  {
1884  found = true;
1885  best = clip->GetEndTime();
1886  }
1887  else if (clip->GetEndTime() > best)
1888  best = clip->GetEndTime();
1889 
1890  return best;
1891 }
1892 
1893 //
1894 // Getting/setting samples. The sample counts here are
1895 // expressed relative to t=0.0 at the track's sample rate.
1896 //
1897 
1898 std::pair<float, float> WaveTrack::GetMinMax(
1899  double t0, double t1, bool mayThrow) const
1900 {
1901  std::pair<float, float> results {
1902  // we need these at extremes to make sure we find true min and max
1903  FLT_MAX, -FLT_MAX
1904  };
1905  bool clipFound = false;
1906 
1907  if (t0 > t1) {
1908  if (mayThrow)
1910  return results;
1911  }
1912 
1913  if (t0 == t1)
1914  return results;
1915 
1916  for (const auto &clip: mClips)
1917  {
1918  if (t1 >= clip->GetStartTime() && t0 <= clip->GetEndTime())
1919  {
1920  clipFound = true;
1921  auto clipResults = clip->GetMinMax(t0, t1, mayThrow);
1922  if (clipResults.first < results.first)
1923  results.first = clipResults.first;
1924  if (clipResults.second > results.second)
1925  results.second = clipResults.second;
1926  }
1927  }
1928 
1929  if(!clipFound)
1930  {
1931  results = { 0.f, 0.f }; // sensible defaults if no clips found
1932  }
1933 
1934  return results;
1935 }
1936 
1937 float WaveTrack::GetRMS(double t0, double t1, bool mayThrow) const
1938 {
1939  if (t0 > t1) {
1940  if (mayThrow)
1942  return 0.f;
1943  }
1944 
1945  if (t0 == t1)
1946  return 0.f;
1947 
1948  double sumsq = 0.0;
1949  sampleCount length = 0;
1950 
1951  for (const auto &clip: mClips)
1952  {
1953  // If t1 == clip->GetStartTime() or t0 == clip->GetEndTime(), then the clip
1954  // is not inside the selection, so we don't want it.
1955  // if (t1 >= clip->GetStartTime() && t0 <= clip->GetEndTime())
1956  if (t1 >= clip->GetStartTime() && t0 <= clip->GetEndTime())
1957  {
1958  sampleCount clipStart, clipEnd;
1959 
1960  float cliprms = clip->GetRMS(t0, t1, mayThrow);
1961 
1962  clip->TimeToSamplesClip(wxMax(t0, clip->GetStartTime()), &clipStart);
1963  clip->TimeToSamplesClip(wxMin(t1, clip->GetEndTime()), &clipEnd);
1964  sumsq += cliprms * cliprms * (clipEnd - clipStart).as_float();
1965  length += (clipEnd - clipStart);
1966  }
1967  }
1968  return length > 0 ? sqrt(sumsq / length.as_double()) : 0.0;
1969 }
1970 
1972  sampleCount start, size_t len, fillFormat fill,
1973  bool mayThrow, sampleCount * pNumCopied) const
1974 {
1975  // Simple optimization: When this buffer is completely contained within one clip,
1976  // don't clear anything (because we won't have to). Otherwise, just clear
1977  // everything to be on the safe side.
1978  bool doClear = true;
1979  bool result = true;
1980  sampleCount samplesCopied = 0;
1981  for (const auto &clip: mClips)
1982  {
1983  if (start >= clip->GetStartSample() && start+len <= clip->GetEndSample())
1984  {
1985  doClear = false;
1986  break;
1987  }
1988  }
1989  if (doClear)
1990  {
1991  // Usually we fill in empty space with zero
1992  if( fill == fillZero )
1993  ClearSamples(buffer, format, 0, len);
1994  // but we don't have to.
1995  else if( fill==fillTwo )
1996  {
1997  wxASSERT( format==floatSample );
1998  float * pBuffer = (float*)buffer;
1999  for(size_t i=0;i<len;i++)
2000  pBuffer[i]=2.0f;
2001  }
2002  else
2003  {
2004  wxFAIL_MSG(wxT("Invalid fill format"));
2005  }
2006  }
2007 
2008  for (const auto &clip: mClips)
2009  {
2010  auto clipStart = clip->GetStartSample();
2011  auto clipEnd = clip->GetEndSample();
2012 
2013  if (clipEnd > start && clipStart < start+len)
2014  {
2015  // Clip sample region and Get/Put sample region overlap
2016  auto samplesToCopy =
2017  std::min( start+len - clipStart, clip->GetNumSamples() );
2018  auto startDelta = clipStart - start;
2019  decltype(startDelta) inclipDelta = 0;
2020  if (startDelta < 0)
2021  {
2022  inclipDelta = -startDelta; // make positive value
2023  samplesToCopy -= inclipDelta;
2024  // samplesToCopy is now either len or
2025  // (clipEnd - clipStart) - (start - clipStart)
2026  // == clipEnd - start > 0
2027  // samplesToCopy is not more than len
2028  //
2029  startDelta = 0;
2030  // startDelta is zero
2031  }
2032  else {
2033  // startDelta is nonnegative and less than than len
2034  // samplesToCopy is positive and not more than len
2035  }
2036 
2037  if (!clip->GetSamples(
2038  (samplePtr)(((char*)buffer) +
2039  startDelta.as_size_t() *
2040  SAMPLE_SIZE(format)),
2041  format, inclipDelta, samplesToCopy.as_size_t(), mayThrow ))
2042  result = false;
2043  else
2044  samplesCopied += samplesToCopy;
2045  }
2046  }
2047  if( pNumCopied )
2048  *pNumCopied = samplesCopied;
2049  return result;
2050 }
2051 
2053  sampleCount start, size_t len)
2054 // WEAK-GUARANTEE
2055 {
2056  for (const auto &clip: mClips)
2057  {
2058  auto clipStart = clip->GetStartSample();
2059  auto clipEnd = clip->GetEndSample();
2060 
2061  if (clipEnd > start && clipStart < start+len)
2062  {
2063  // Clip sample region and Get/Put sample region overlap
2064  auto samplesToCopy =
2065  std::min( start+len - clipStart, clip->GetNumSamples() );
2066  auto startDelta = clipStart - start;
2067  decltype(startDelta) inclipDelta = 0;
2068  if (startDelta < 0)
2069  {
2070  inclipDelta = -startDelta; // make positive value
2071  samplesToCopy -= inclipDelta;
2072  // samplesToCopy is now either len or
2073  // (clipEnd - clipStart) - (start - clipStart)
2074  // == clipEnd - start > 0
2075  // samplesToCopy is not more than len
2076  //
2077  startDelta = 0;
2078  // startDelta is zero
2079  }
2080  else {
2081  // startDelta is nonnegative and less than than len
2082  // samplesToCopy is positive and not more than len
2083  }
2084 
2085  clip->SetSamples(
2086  (samplePtr)(((char*)buffer) +
2087  startDelta.as_size_t() *
2088  SAMPLE_SIZE(format)),
2089  format, inclipDelta, samplesToCopy.as_size_t() );
2090  clip->MarkChanged();
2091  }
2092  }
2093 }
2094 
2095 void WaveTrack::GetEnvelopeValues(double *buffer, size_t bufferLen,
2096  double t0) const
2097 {
2098  // The output buffer corresponds to an unbroken span of time which the callers expect
2099  // to be fully valid. As clips are processed below, the output buffer is updated with
2100  // envelope values from any portion of a clip, start, end, middle, or none at all.
2101  // Since this does not guarantee that the entire buffer is filled with values we need
2102  // to initialize the entire buffer to a default value.
2103  //
2104  // This does mean that, in the cases where a usable clip is located, the buffer value will
2105  // be set twice. Unfortunately, there is no easy way around this since the clips are not
2106  // stored in increasing time order. If they were, we could just track the time as the
2107  // buffer is filled.
2108  for (decltype(bufferLen) i = 0; i < bufferLen; i++)
2109  {
2110  buffer[i] = 1.0;
2111  }
2112 
2113  double startTime = t0;
2114  auto tstep = 1.0 / mRate;
2115  double endTime = t0 + tstep * bufferLen;
2116  for (const auto &clip: mClips)
2117  {
2118  // IF clip intersects startTime..endTime THEN...
2119  auto dClipStartTime = clip->GetStartTime();
2120  auto dClipEndTime = clip->GetEndTime();
2121  if ((dClipStartTime < endTime) && (dClipEndTime > startTime))
2122  {
2123  auto rbuf = buffer;
2124  auto rlen = bufferLen;
2125  auto rt0 = t0;
2126 
2127  if (rt0 < dClipStartTime)
2128  {
2129  // This is not more than the number of samples in
2130  // (endTime - startTime) which is bufferLen:
2131  auto nDiff = (sampleCount)floor((dClipStartTime - rt0) * mRate + 0.5);
2132  auto snDiff = nDiff.as_size_t();
2133  rbuf += snDiff;
2134  wxASSERT(snDiff <= rlen);
2135  rlen -= snDiff;
2136  rt0 = dClipStartTime;
2137  }
2138 
2139  if (rt0 + rlen*tstep > dClipEndTime)
2140  {
2141  auto nClipLen = clip->GetEndSample() - clip->GetStartSample();
2142 
2143  if (nClipLen <= 0) // Testing for bug 641, this problem is consistently '== 0', but doesn't hurt to check <.
2144  return;
2145 
2146  // This check prevents problem cited in http://bugzilla.audacityteam.org/show_bug.cgi?id=528#c11,
2147  // Gale's cross_fade_out project, which was already corrupted by bug 528.
2148  // This conditional prevents the previous write past the buffer end, in clip->GetEnvelope() call.
2149  // Never increase rlen here.
2150  // PRL bug 827: rewrote it again
2151  rlen = limitSampleBufferSize( rlen, nClipLen );
2152  rlen = std::min(rlen, size_t(floor(0.5 + (dClipEndTime - rt0) / tstep)));
2153  }
2154  // Samples are obtained for the purpose of rendering a wave track,
2155  // so quantize time
2156  clip->GetEnvelope()->GetValues(rbuf, rlen, rt0, tstep);
2157  }
2158  }
2159 }
2160 
2162 {
2163  for (const auto &clip: mClips)
2164  {
2165  wxRect r;
2166  clip->GetDisplayRect(&r);
2167  if (xcoord >= r.x && xcoord < r.x+r.width)
2168  return clip.get();
2169  }
2170 
2171  return NULL;
2172 }
2173 
2175 {
2176  for (const auto &clip: mClips)
2177  {
2178  auto start = clip->GetStartSample();
2179  auto len = clip->GetNumSamples();
2180 
2181  if (sample >= start && sample < start + len)
2182  return clip.get();
2183  }
2184 
2185  return NULL;
2186 }
2187 
2188 // When the time is both the end of a clip and the start of the next clip, the
2189 // latter clip is returned.
2191 {
2192 
2193  const auto clips = SortedClipArray();
2194  auto p = std::find_if(clips.rbegin(), clips.rend(), [&] (WaveClip* const& clip) {
2195  return time >= clip->GetStartTime() && time <= clip->GetEndTime(); });
2196 
2197  // When two clips are immediately next to each other, the GetEndTime() of the first clip
2198  // and the GetStartTime() of the second clip may not be exactly equal due to rounding errors.
2199  // If "time" is the end time of the first of two such clips, and the end time is slightly
2200  // less than the start time of the second clip, then the first rather than the
2201  // second clip is found by the above code. So correct this.
2202  if (p != clips.rend() && p != clips.rbegin() &&
2203  time == (*p)->GetEndTime() &&
2204  (*p)->SharesBoundaryWithNextClip(*(p-1))) {
2205  p--;
2206  }
2207 
2208  return p != clips.rend() ? *p : nullptr;
2209 }
2210 
2212 {
2213  WaveClip* clip = GetClipAtX(xcoord);
2214  if (clip)
2215  return clip->GetEnvelope();
2216  else
2217  return NULL;
2218 }
2219 
2221 {
2222  WaveClip* clip = GetClipAtX(xcoord);
2223  if (clip)
2224  return clip->GetSequence();
2225  else
2226  return NULL;
2227 }
2228 
2230 {
2231  mClips.push_back(std::make_unique<WaveClip>(mDirManager, mFormat, mRate, GetWaveColorIndex()));
2232  return mClips.back().get();
2233 }
2234 
2236 {
2237  if (mClips.empty()) {
2238  WaveClip *clip = CreateClip();
2239  clip->SetOffset(mOffset);
2240  return clip;
2241  }
2242  else
2243  return mClips.back().get();
2244 }
2245 
2247 // NOFAIL-GUARANTEE
2248 {
2249  if (mClips.empty()) {
2250  WaveClip *clip = CreateClip();
2251  clip->SetOffset(mOffset);
2252  return clip;
2253  }
2254  else
2255  {
2256  auto it = mClips.begin();
2257  WaveClip *rightmost = (*it++).get();
2258  double maxOffset = rightmost->GetOffset();
2259  for (auto end = mClips.end(); it != end; ++it)
2260  {
2261  WaveClip *clip = it->get();
2262  double offset = clip->GetOffset();
2263  if (maxOffset < offset)
2264  maxOffset = offset, rightmost = clip;
2265  }
2266  return rightmost;
2267  }
2268 }
2269 
2270 int WaveTrack::GetClipIndex(const WaveClip* clip) const
2271 {
2272  int result;
2273  FindClip(mClips, clip, &result);
2274  return result;
2275 }
2276 
2278 {
2279  if(index < (int)mClips.size())
2280  return mClips[index].get();
2281  else
2282  return nullptr;
2283 }
2284 
2285 const WaveClip* WaveTrack::GetClipByIndex(int index) const
2286 {
2287  return const_cast<WaveTrack&>(*this).GetClipByIndex(index);
2288 }
2289 
2291 {
2292  return mClips.size();
2293 }
2294 
2295 bool WaveTrack::CanOffsetClip(WaveClip* clip, double amount,
2296  double *allowedAmount /* = NULL */)
2297 {
2298  if (allowedAmount)
2299  *allowedAmount = amount;
2300 
2301  for (const auto &c: mClips)
2302  {
2303  if (c.get() != clip && c->GetStartTime() < clip->GetEndTime()+amount &&
2304  c->GetEndTime() > clip->GetStartTime()+amount)
2305  {
2306  if (!allowedAmount)
2307  return false; // clips overlap
2308 
2309  if (amount > 0)
2310  {
2311  if (c->GetStartTime()-clip->GetEndTime() < *allowedAmount)
2312  *allowedAmount = c->GetStartTime()-clip->GetEndTime();
2313  if (*allowedAmount < 0)
2314  *allowedAmount = 0;
2315  } else
2316  {
2317  if (c->GetEndTime()-clip->GetStartTime() > *allowedAmount)
2318  *allowedAmount = c->GetEndTime()-clip->GetStartTime();
2319  if (*allowedAmount > 0)
2320  *allowedAmount = 0;
2321  }
2322  }
2323  }
2324 
2325  if (allowedAmount)
2326  {
2327  if (*allowedAmount == amount)
2328  return true;
2329 
2330  // Check if the NEW calculated amount would not violate
2331  // any other constraint
2332  if (!CanOffsetClip(clip, *allowedAmount, NULL)) {
2333  *allowedAmount = 0; // play safe and don't allow anything
2334  return false;
2335  }
2336  else
2337  return true;
2338  } else
2339  return true;
2340 }
2341 
2342 bool WaveTrack::CanInsertClip(WaveClip* clip, double &slideBy, double &tolerance)
2343 {
2344  for (const auto &c : mClips)
2345  {
2346  double d1 = c->GetStartTime() - (clip->GetEndTime()+slideBy);
2347  double d2 = (clip->GetStartTime()+slideBy) - c->GetEndTime();
2348  if ( (d1<0) && (d2<0) )
2349  {
2350  // clips overlap.
2351  // Try to rescue it.
2352  // The rescue logic is not perfect, and will typically
2353  // move the clip at most once.
2354  // We divide by 1000 rather than set to 0, to allow for
2355  // a second 'micro move' that is really about rounding error.
2356  if( -d1 < tolerance ){
2357  // right edge of clip overlaps slightly.
2358  // slide clip left a small amount.
2359  slideBy +=d1;
2360  tolerance /=1000;
2361  } else if( -d2 < tolerance ){
2362  // left edge of clip overlaps slightly.
2363  // slide clip right a small amount.
2364  slideBy -= d2;
2365  tolerance /=1000;
2366  }
2367  else
2368  return false; // clips overlap No tolerance left.
2369  }
2370  }
2371 
2372  return true;
2373 }
2374 
2375 void WaveTrack::Split( double t0, double t1 )
2376 // WEAK-GUARANTEE
2377 {
2378  SplitAt( t0 );
2379  if( t0 != t1 )
2380  SplitAt( t1 );
2381 }
2382 
2383 void WaveTrack::SplitAt(double t)
2384 // WEAK-GUARANTEE
2385 {
2386  for (const auto &c : mClips)
2387  {
2388  if (c->WithinClip(t))
2389  {
2390  t = LongSamplesToTime(TimeToLongSamples(t)); // put t on a sample
2391  auto newClip = std::make_unique<WaveClip>( *c, mDirManager, true );
2392  c->Clear(t, c->GetEndTime());
2393  newClip->Clear(c->GetStartTime(), t);
2394 
2395  //offset the NEW clip by the splitpoint (noting that it is already offset to c->GetStartTime())
2396  sampleCount here = llrint(floor(((t - c->GetStartTime()) * mRate) + 0.5));
2397  newClip->Offset(here.as_double()/(double)mRate);
2398  // This could invalidate the iterators for the loop! But we return
2399  // at once so it's okay
2400  mClips.push_back(std::move(newClip)); // transfer ownership
2401  return;
2402  }
2403  }
2404 }
2405 
2407 {
2408  auto clips = SortedClipArray();
2409 
2410  mDisplayLocationsCache.clear();
2411 
2412  // Count number of display locations
2413  int num = 0;
2414  {
2415  const WaveClip *prev = nullptr;
2416  for (const auto clip : clips)
2417  {
2418  num += clip->NumCutLines();
2419 
2420  if (prev && fabs(prev->GetEndTime() -
2422  ++num;
2423 
2424  prev = clip;
2425  }
2426  }
2427 
2428  if (num == 0)
2429  return;
2430 
2431  // Alloc necessary number of display locations
2432  mDisplayLocationsCache.reserve(num);
2433 
2434  // Add all display locations to cache
2435  int curpos = 0;
2436 
2437  const WaveClip *previousClip = nullptr;
2438  for (const auto clip: clips)
2439  {
2440  for (const auto &cc : clip->GetCutLines())
2441  {
2442  // Add cut line expander point
2444  clip->GetOffset() + cc->GetOffset(),
2446  });
2447  curpos++;
2448  }
2449 
2450  if (previousClip)
2451  {
2452  if (fabs(previousClip->GetEndTime() - clip->GetStartTime())
2454  {
2455  // Add merge point
2457  previousClip->GetEndTime(),
2459  GetClipIndex(previousClip),
2460  GetClipIndex(clip)
2461  });
2462  curpos++;
2463  }
2464  }
2465 
2466  previousClip = clip;
2467  }
2468 
2469  wxASSERT(curpos == num);
2470 }
2471 
2472 // Expand cut line (that is, re-insert audio, then DELETE audio saved in cut line)
2473 void WaveTrack::ExpandCutLine(double cutLinePosition, double* cutlineStart,
2474  double* cutlineEnd)
2475 // STRONG-GUARANTEE
2476 {
2477  bool editClipCanMove = gPrefs->GetEditClipsCanMove();
2478 
2479  // Find clip which contains this cut line
2480  double start = 0, end = 0;
2481  auto pEnd = mClips.end();
2482  auto pClip = std::find_if( mClips.begin(), pEnd,
2483  [&](const WaveClipHolder &clip) {
2484  return clip->FindCutLine(cutLinePosition, &start, &end); } );
2485  if (pClip != pEnd)
2486  {
2487  auto &clip = *pClip;
2488  if (!editClipCanMove)
2489  {
2490  // We are not allowed to move the other clips, so see if there
2491  // is enough room to expand the cut line
2492  for (const auto &clip2: mClips)
2493  {
2494  if (clip2->GetStartTime() > clip->GetStartTime() &&
2495  clip->GetEndTime() + end - start > clip2->GetStartTime())
2496  // STRONG-GUARANTEE in case of this path
2498  _("There is not enough room available to expand the cut line")
2499  };
2500  }
2501  }
2502 
2503  clip->ExpandCutLine(cutLinePosition);
2504 
2505  // STRONG-GUARANTEE provided that the following gives NOFAIL-GUARANTEE
2506 
2507  if (cutlineStart)
2508  *cutlineStart = start;
2509  if (cutlineEnd)
2510  *cutlineEnd = end;
2511 
2512  // Move clips which are to the right of the cut line
2513  if (editClipCanMove)
2514  {
2515  for (const auto &clip2 : mClips)
2516  {
2517  if (clip2->GetStartTime() > clip->GetStartTime())
2518  clip2->Offset(end - start);
2519  }
2520  }
2521  }
2522 }
2523 
2524 bool WaveTrack::RemoveCutLine(double cutLinePosition)
2525 {
2526  for (const auto &clip : mClips)
2527  if (clip->RemoveCutLine(cutLinePosition))
2528  return true;
2529 
2530  return false;
2531 }
2532 
2533 void WaveTrack::MergeClips(int clipidx1, int clipidx2)
2534 // STRONG-GUARANTEE
2535 {
2536  WaveClip* clip1 = GetClipByIndex(clipidx1);
2537  WaveClip* clip2 = GetClipByIndex(clipidx2);
2538 
2539  if (!clip1 || !clip2) // Could happen if one track of a linked pair had a split and the other didn't.
2540  return; // Don't throw, just do nothing.
2541 
2542  // Append data from second clip to first clip
2543  // use STRONG-GUARANTEE
2544  clip1->Paste(clip1->GetEndTime(), clip2);
2545 
2546  // use NOFAIL-GUARANTEE for the rest
2547  // Delete second clip
2548  auto it = FindClip(mClips, clip2);
2549  mClips.erase(it);
2550 }
2551 
2552 void WaveTrack::Resample(int rate, ProgressDialog *progress)
2553 // WEAK-GUARANTEE
2554 // Partial completion may leave clips at differing sample rates!
2555 {
2556  for (const auto &clip : mClips)
2557  clip->Resample(rate, progress);
2558 
2559  mRate = rate;
2560 }
2561 
2562 namespace {
2563  template < typename Cont1, typename Cont2 >
2564  Cont1 FillSortedClipArray(const Cont2& mClips)
2565  {
2566  Cont1 clips;
2567  for (const auto &clip : mClips)
2568  clips.push_back(clip.get());
2569  std::sort(clips.begin(), clips.end(),
2570  [](const WaveClip *a, const WaveClip *b)
2571  { return a->GetStartTime() < b->GetStartTime(); });
2572  return clips;
2573  }
2574 }
2575 
2577 {
2578  return FillSortedClipArray<WaveClipPointers>(mClips);
2579 }
2580 
2582 {
2583  return FillSortedClipArray<WaveClipConstPointers>(mClips);
2584 }
2585 
2588 {
2589  for (const auto &clip : mClips)
2590  clip->ClearWaveCache();
2591 }
2592 
2595 {
2596  for (const auto &clip : mClips)
2597  clip->AddInvalidRegion(startSample, endSample);
2598 }
2599 
2601 {
2602  return mAutoSaveIdent;
2603 }
2604 
2606 {
2608 }
2609 
2611 {
2612 }
2613 
2614 void WaveTrackCache::SetTrack(const std::shared_ptr<const WaveTrack> &pTrack)
2615 {
2616  if (mPTrack != pTrack) {
2617  if (pTrack) {
2618  mBufferSize = pTrack->GetMaxBlockSize();
2619  if (!mPTrack ||
2620  mPTrack->GetMaxBlockSize() != mBufferSize) {
2621  Free();
2622  mBuffers[0].data = Floats{ mBufferSize };
2623  mBuffers[1].data = Floats{ mBufferSize };
2624  }
2625  }
2626  else
2627  Free();
2628  mPTrack = pTrack;
2629  mNValidBuffers = 0;
2630  }
2631 }
2632 
2634  sampleCount start, size_t len, bool mayThrow)
2635 {
2636  if (format == floatSample && len > 0) {
2637  const auto end = start + len;
2638 
2639  bool fillFirst = (mNValidBuffers < 1);
2640  bool fillSecond = (mNValidBuffers < 2);
2641 
2642  // Discard cached results that we no longer need
2643  if (mNValidBuffers > 0 &&
2644  (end <= mBuffers[0].start ||
2645  start >= mBuffers[mNValidBuffers - 1].end())) {
2646  // Complete miss
2647  fillFirst = true;
2648  fillSecond = true;
2649  }
2650  else if (mNValidBuffers == 2 &&
2651  start >= mBuffers[1].start &&
2652  end > mBuffers[1].end()) {
2653  // Request starts in the second buffer and extends past it.
2654  // Discard the first buffer.
2655  // (But don't deallocate the buffer space.)
2656  mBuffers[0] .swap ( mBuffers[1] );
2657  fillSecond = true;
2658  mNValidBuffers = 1;
2659  }
2660  else if (mNValidBuffers > 0 &&
2661  start < mBuffers[0].start &&
2662  0 <= mPTrack->GetBlockStart(start)) {
2663  // Request is not a total miss but starts before the cache,
2664  // and there is a clip to fetch from.
2665  // Not the access pattern for drawing spectrogram or playback,
2666  // but maybe scrubbing causes this.
2667  // Move the first buffer into second place, and later
2668  // refill the first.
2669  // (This case might be useful when marching backwards through
2670  // the track, as with scrubbing.)
2671  mBuffers[0] .swap ( mBuffers[1] );
2672  fillFirst = true;
2673  fillSecond = false;
2674  // Cache is not in a consistent state yet
2675  mNValidBuffers = 0;
2676  }
2677 
2678  // Refill buffers as needed
2679  if (fillFirst) {
2680  const auto start0 = mPTrack->GetBlockStart(start);
2681  if (start0 >= 0) {
2682  const auto len0 = mPTrack->GetBestBlockSize(start0);
2683  wxASSERT(len0 <= mBufferSize);
2684  if (!mPTrack->Get(
2685  samplePtr(mBuffers[0].data.get()), floatSample, start0, len0,
2686  fillZero, mayThrow))
2687  return 0;
2688  mBuffers[0].start = start0;
2689  mBuffers[0].len = len0;
2690  if (!fillSecond &&
2691  mBuffers[0].end() != mBuffers[1].start)
2692  fillSecond = true;
2693  // Keep the partially updated state consistent:
2694  mNValidBuffers = fillSecond ? 1 : 2;
2695  }
2696  else {
2697  // Request may fall between the clips of a track.
2698  // Invalidate all. WaveTrack::Get() will return zeroes.
2699  mNValidBuffers = 0;
2700  fillSecond = false;
2701  }
2702  }
2703  wxASSERT(!fillSecond || mNValidBuffers > 0);
2704  if (fillSecond) {
2705  mNValidBuffers = 1;
2706  const auto end0 = mBuffers[0].end();
2707  if (end > end0) {
2708  const auto start1 = mPTrack->GetBlockStart(end0);
2709  if (start1 == end0) {
2710  const auto len1 = mPTrack->GetBestBlockSize(start1);
2711  wxASSERT(len1 <= mBufferSize);
2712  if (!mPTrack->Get(samplePtr(mBuffers[1].data.get()), floatSample, start1, len1, fillZero, mayThrow))
2713  return 0;
2714  mBuffers[1].start = start1;
2715  mBuffers[1].len = len1;
2716  mNValidBuffers = 2;
2717  }
2718  }
2719  }
2720  wxASSERT(mNValidBuffers < 2 || mBuffers[0].end() == mBuffers[1].start);
2721 
2722  samplePtr buffer = 0;
2723  auto remaining = len;
2724 
2725  // Possibly get an initial portion that is uncached
2726 
2727  // This may be negative
2728  const auto initLen =
2729  mNValidBuffers < 1 ? sampleCount( len )
2730  : std::min(sampleCount( len ), mBuffers[0].start - start);
2731 
2732  if (initLen > 0) {
2733  // This might be fetching zeroes between clips
2734  mOverlapBuffer.Resize(len, format);
2735  // initLen is not more than len:
2736  auto sinitLen = initLen.as_size_t();
2737  if (!mPTrack->Get(mOverlapBuffer.ptr(), format, start, sinitLen,
2738  fillZero, mayThrow))
2739  return 0;
2740  wxASSERT( sinitLen <= remaining );
2741  remaining -= sinitLen;
2742  start += initLen;
2743  buffer = mOverlapBuffer.ptr() + sinitLen * SAMPLE_SIZE(format);
2744  }
2745 
2746  // Now satisfy the request from the buffers
2747  for (int ii = 0; ii < mNValidBuffers && remaining > 0; ++ii) {
2748  const auto starti = start - mBuffers[ii].start;
2749  // Treatment of initLen above establishes this loop invariant,
2750  // and statements below preserve it:
2751  wxASSERT(starti >= 0);
2752 
2753  // This may be negative
2754  const auto leni =
2755  std::min( sampleCount( remaining ), mBuffers[ii].len - starti );
2756  if (initLen <= 0 && leni == len) {
2757  // All is contiguous already. We can completely avoid copying
2758  // leni is nonnegative, therefore start falls within mBuffers[ii],
2759  // so starti is bounded between 0 and buffer length
2760  return samplePtr(mBuffers[ii].data.get() + starti.as_size_t() );
2761  }
2762  else if (leni > 0) {
2763  // leni is nonnegative, therefore start falls within mBuffers[ii]
2764  // But we can't satisfy all from one buffer, so copy
2765  if (buffer == 0) {
2766  mOverlapBuffer.Resize(len, format);
2767  buffer = mOverlapBuffer.ptr();
2768  }
2769  // leni is positive and not more than remaining
2770  const size_t size = sizeof(float) * leni.as_size_t();
2771  // starti is less than mBuffers[ii].len and nonnegative
2772  memcpy(buffer, mBuffers[ii].data.get() + starti.as_size_t(), size);
2773  wxASSERT( leni <= remaining );
2774  remaining -= leni.as_size_t();
2775  start += leni;
2776  buffer += size;
2777  }
2778  }
2779 
2780  if (remaining > 0) {
2781  // Very big request!
2782  // Fall back to direct fetch
2783  if (buffer == 0) {
2784  mOverlapBuffer.Resize(len, format);
2785  buffer = mOverlapBuffer.ptr();
2786  }
2787  if (!mPTrack->Get(buffer, format, start, remaining, fillZero, mayThrow))
2788  return 0;
2789  }
2790 
2791  return mOverlapBuffer.ptr();
2792  }
2793 
2794  // Cache works only for float format.
2795  mOverlapBuffer.Resize(len, format);
2796  if (mPTrack->Get(mOverlapBuffer.ptr(), format, start, len, fillZero, mayThrow))
2797  return mOverlapBuffer.ptr();
2798  else
2799  return 0;
2800 }
2801 
2803 {
2804  mBuffers[0].Free();
2805  mBuffers[1].Free();
2806  mOverlapBuffer.Free();
2807  mNValidBuffers = 0;
2808 }
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override
Definition: WaveTrack.cpp:1660
static bool IsInstanceCreated()
returns whether or not the singleton instance was created yet
Definition: ODManager.cpp:205
std::unique_ptr< SpectrogramSettings > mpSpectrumSettings
Definition: WaveTrack.h:649
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
bool RemoveCutLine(double cutLinePosition)
Remove cut line, without expanding the audio in it.
Definition: WaveClip.cpp:1844
static ODManager *(* Instance)()
Definition: ODManager.h:49
GrowableSampleBuffer & Resize(size_t count, sampleFormat format)
Definition: SampleFormat.h:101
void Merge(const Track &init) override
Definition: Track.cpp:350
static CommandHandlerObject & ident(AudacityProject &project)
Definition: Menus.cpp:298
void SplitAt(double t)
Definition: WaveTrack.cpp:2383
bool FindCutLine(double cutLinePosition, double *cutLineStart=NULL, double *cutLineEnd=NULL) const
Definition: WaveClip.cpp:1792
int mAutoSaveIdent
Definition: WaveTrack.h:647
void WriteXML(XMLWriter &xmlFile) const override
Definition: WaveTrack.cpp:1781
size_t NumCutLines() const
Definition: WaveClip.h:317
Spectrogram settings, either for one track or as defaults.
void TimeToSamplesClip(double t0, sampleCount *s0) const
Definition: WaveClip.cpp:1367
int ZeroLevelYCoordinate(wxRect rect) const
Definition: WaveTrack.cpp:387
sampleCount GetNumSamples() const
Definition: WaveClip.cpp:448
WaveClip * CreateClip()
Definition: WaveTrack.cpp:2229
void ExpandCutLine(double cutLinePosition, double *cutlineStart=NULL, double *cutlineEnd=NULL)
Definition: WaveTrack.cpp:2473
ProgressDialog Class.
int mLastdBRange
Definition: WaveTrack.h:631
Sequence * GetSequence()
Definition: WaveClip.h:250
void InsertSilence(double t, double len, double *pEnvelopeValue=nullptr)
Definition: WaveClip.cpp:1636
WaveClip * GetClipByIndex(int index)
Definition: WaveTrack.cpp:2277
Track::Holder Duplicate() const override
Definition: WaveTrack.cpp:393
void ClearAndAddCutLine(double t0, double t1)
Definition: WaveTrack.cpp:684
double GetStartTime() const
Definition: WaveClip.cpp:421
fillFormat
Definition: SampleFormat.h:37
float mDisplayMin
Definition: WaveTrack.h:624
void Init(const PlayableTrack &init)
Definition: Track.cpp:343
void SetLinked(bool l)
Definition: Track.cpp:244
const WaveformSettings & GetWaveformSettings() const
Definition: WaveTrack.cpp:738
static WaveTrackDisplay ConvertLegacyDisplayValue(int oldValue)
Definition: WaveTrack.cpp:259
static bool IsValidChannel(const int nValue)
A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interfac...
Definition: Sequence.h:54
WaveformSettings & GetIndependentWaveformSettings()
Definition: WaveTrack.cpp:754
void Offset(double delta)
Definition: WaveClip.h:223
bool GetErrorOpening() override
Definition: WaveTrack.cpp:1809
No change to time at all.
Definition: TimeWarper.h:68
std::unique_ptr< WaveTrack > DuplicateWaveTrack(const WaveTrack &orig)
Definition: WaveTrack.cpp:71
void MarkChanged()
Definition: WaveClip.h:255
Envelope * GetEnvelopeAtX(int xcoord)
Definition: WaveTrack.cpp:2211
Buffer mBuffers[2]
Definition: WaveTrack.h:715
bool HandleXMLAttribute(const wxChar *attr, const wxChar *value)
Definition: Track.cpp:368
float GetPan() const
Definition: WaveTrack.cpp:425
float GetRMS(double t0, double t1, bool mayThrow=true) const
Definition: WaveClip.cpp:1331
void GetSpectrumBounds(float *min, float *max) const
Definition: WaveTrack.cpp:337
void Reinit(const WaveTrack &orig)
Definition: WaveTrack.cpp:166
void SetRate(double newRate)
Definition: WaveTrack.cpp:403
virtual double GetEndTime() const =0
virtual ~WaveTrack()
Definition: WaveTrack.cpp:211
int mHeight
Definition: Track.h:117
void Merge(const Track &orig) override
Definition: WaveTrack.cpp:193
WaveClipPointers SortedClipArray()
Definition: WaveTrack.cpp:2576
static wxString GetDefaultAudioTrackNamePreference()
void SetHeight(int h)
Definition: Track.cpp:189
#define SAMPLE_SIZE(SampleFormat)
Definition: Types.h:198
float mSpectrumMax
Definition: WaveTrack.h:627
unsigned int GetODFlags() const
gets an int with OD flags so that we can determine which ODTasks should be run on this track after sa...
Definition: WaveTrack.cpp:1583
double GetOffset() const override
Definition: WaveTrack.cpp:219
void RemoveWaveTrack(WaveTrack *track)
removes a wavetrack and notifies its associated tasks to stop using its reference.
Definition: ODManager.cpp:372
int GetAutoSaveIdent()
Definition: WaveTrack.cpp:2600
void SetDisplayBounds(float min, float max) const
Definition: WaveTrack.cpp:331
size_t GetFFTLength() const
int mLastScaleType
Definition: WaveTrack.h:630
Draggable curve used in TrackPanel for varying amplification.
Definition: Envelope.h:77
bool BeforeClip(double t) const
Definition: WaveClip.cpp:459
void ClearAndPaste(double t0, double t1, const Track *src, bool preserve=true, bool merge=true, const TimeWarper *effectWarper=NULL)
Definition: WaveTrack.cpp:782
Track::Holder Copy(double t0, double t1, bool forClipboard=true) const override
Definition: WaveTrack.cpp:605
std::unique_ptr< WaveformSettings > mpWaveformSettings
Definition: WaveTrack.h:650
std::pair< float, float > GetMinMax(double t0, double t1, bool mayThrow=true) const
Definition: WaveClip.cpp:1308
const std::shared_ptr< DirManager > mDirManager
Definition: Track.h:871
double as_double() const
Definition: Types.h:88
int mNValidBuffers
Definition: WaveTrack.h:717
void Resample(int rate, ProgressDialog *progress=NULL)
Definition: WaveClip.cpp:1898
void SyncLockAdjust(double oldT1, double newT1) override
Definition: WaveTrack.cpp:1147
int WaveTrackDisplay
Definition: WaveTrack.h:531
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:678
void AppendAlias(const wxString &fName, sampleCount start, size_t len, int channel, bool useOD)
Definition: WaveTrack.cpp:1568
std::vector< WaveClipHolder > WaveClipHolders
Definition: WaveClip.h:122
double GetEndTime() const override
Get the time at which the last clip in the track ends, plus recorded stuff.
Definition: WaveTrack.cpp:1873
double GetOffset() const
Definition: WaveClip.h:222
float GetChannelGain(int channel) const
Definition: WaveTrack.cpp:440
void Split(double t0, double t1)
Definition: WaveTrack.cpp:2375
WaveClip * GetClipAtSample(sampleCount sample)
Definition: WaveTrack.cpp:2174
static WaveformSettings & defaults()
#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
size_t GetBestBlockSize(sampleCount t) const
Definition: WaveTrack.cpp:1607
std::vector< const WaveClip * > WaveClipConstPointers
Definition: WaveClip.h:127
Track::Holder SplitCut(double t0, double t1)
Definition: WaveTrack.cpp:529
WaveClip * NewestOrNewClip()
Get access to the most recently added clip, or create a clip, if there is not already one...
Definition: WaveTrack.cpp:2235
SpectrogramSettings & GetIndependentSpectrogramSettings()
Definition: WaveTrack.cpp:706
void UpdateLocationsCache() const
Definition: WaveTrack.cpp:2406
WaveClip * RightmostOrNewClip()
Get access to the last (rightmost) clip, or create a clip, if there is not already one...
Definition: WaveTrack.cpp:2246
virtual void SetPanFromChannelType() override
Definition: WaveTrack.cpp:248
void SetSpectrogramSettings(std::unique_ptr< SpectrogramSettings > &&pSettings)
Definition: WaveTrack.cpp:714
void swap(Buffer &other)
Definition: WaveTrack.h:705
GrowableSampleBuffer mOverlapBuffer
Definition: WaveTrack.h:716
int GetWaveColorIndex() const
Definition: WaveTrack.h:143
#define safenew
Definition: Audacity.h:230
static bool IsGoodInt(const wxString &strInt)
Check that the supplied string can be converted to a long (32bit) integer.
Envelope * GetEnvelope()
Definition: WaveClip.h:243
static unsigned DefaultWaveTrackHeight()
std::vector< Location > mDisplayLocationsCache
Definition: WaveTrack.h:632
virtual void SetSelected(bool s)
Definition: Track.cpp:99
virtual int GetKind() const
Definition: Track.h:329
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: Types.h:178
Used to create a WaveTrack, or a LabelTrack.. Implementation of the functions of this class are dispe...
Definition: Track.h:862
void InsertSilence(double t, double len) override
Definition: WaveTrack.cpp:1385
void GetDisplayBounds(float *min, float *max) const
Definition: WaveTrack.cpp:325
float GetRMS(double t0, double t1, bool mayThrow=true) const
Definition: WaveTrack.cpp:1937
wxString GetDefaultName() const
Definition: Track.h:272
std::shared_ptr< WaveClip > RemoveAndReturnClip(WaveClip *clip)
Definition: WaveTrack.cpp:990
void Resample(int rate, ProgressDialog *progress=NULL)
Definition: WaveTrack.cpp:2552
Track::Holder Cut(double t0, double t1) override
Definition: WaveTrack.cpp:517
float mSpectrumMin
Definition: WaveTrack.h:626
void SplitDelete(double t0, double t1)
Definition: WaveTrack.cpp:947
Contains declarations for TimeWarper, IdentityTimeWarper, ShiftTimeWarper, LinearTimeWarper, LinearInputRateSlideTimeWarper, LinearOutputRateSlideTimeWarper, LinearInputInverseRateTimeWarper, GeometricInputRateTimeWarper, GeometricOutputRateTimeWarper classes.
void Unlock()
Unlock all blockfiles.
Definition: WaveClip.cpp:1883
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:176
double GetStartTime() const override
Get the time at which the first clip in the track starts.
Definition: WaveTrack.cpp:1853
void Init(const WaveTrack &orig)
Definition: WaveTrack.cpp:148
void WriteXMLAttributes(XMLWriter &xmlFile) const
Definition: Track.cpp:360
void AddInvalidRegion(sampleCount startSample, sampleCount endSample)
Adds an invalid region to the wavecache so it redraws that portion only.
Definition: WaveTrack.cpp:2594
WaveClip * GetClipAtTime(double time)
Definition: WaveTrack.cpp:2190
int format
Definition: ExportPCM.cpp:56
Sequence * GetSequenceAtX(int xcoord)
Definition: WaveTrack.cpp:2220
void SetOffset(double offset)
Definition: WaveClip.cpp:392
static bool IsGoodString(const wxString &str)
std::unique_ptr< WaveTrack > Holder
Definition: WaveTrack.h:90
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:122
bool CloseLock()
Definition: WaveTrack.cpp:1826
sampleCount GetBlockStart(sampleCount position) const
Definition: Sequence.cpp:786
void Set(samplePtr buffer, sampleFormat format, sampleCount start, size_t len)
Definition: WaveTrack.cpp:2052
WaveClip * GetClipAtX(int xcoord)
Definition: WaveTrack.cpp:2161
sampleCount GetEndSample() const
Definition: WaveClip.cpp:443
void SetWaveColorIndex(int colorIndex)
Definition: WaveTrack.cpp:477
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:176
size_t GetIdealBlockSize()
Definition: WaveTrack.cpp:1645
bool mMinimized
Definition: Track.h:124
bool RemoveCutLine(double cutLinePosition)
Definition: WaveTrack.cpp:2524
Structure to hold region of a wavetrack and a comparison function for sortability.
Definition: WaveTrack.h:42
void Join(double t0, double t1)
Definition: WaveTrack.cpp:1503
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:677
sampleFormat
Definition: Types.h:188
#define lrint(dbl)
Definition: float_cast.h:136
#define Region
Definition: VSTControlGTK.h:15
void SetAutoSaveIdent(int id)
Definition: WaveTrack.cpp:2605
char * samplePtr
Definition: Types.h:203
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
bool CanOffsetClip(WaveClip *clip, double amount, double *allowedAmount=NULL)
Definition: WaveTrack.cpp:2295
void WriteXML(XMLWriter &xmlFile) const
Definition: WaveClip.cpp:1563
double start
Definition: WaveTrack.h:47
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:101
virtual void DoSetMinimized(bool isMinimized)
Definition: Track.cpp:239
void SetTrack(const std::shared_ptr< const WaveTrack > &pTrack)
Definition: WaveTrack.cpp:2614
static WaveTrackDisplay ValidateWaveTrackDisplay(WaveTrackDisplay display)
Definition: WaveTrack.cpp:293
size_t mBufferSize
Definition: WaveTrack.h:714
void Paste(double t0, const Track *src) override
Definition: WaveTrack.cpp:1192
This class is an interface which should be implemented by classes which wish to be able to load and s...
Definition: XMLTagHandler.h:70
void ConvertToSampleFormat(sampleFormat format)
Definition: WaveTrack.cpp:486
WaveTrackDisplay mDisplay
Definition: WaveTrack.h:629
sampleCount GetStartSample() const
Definition: WaveClip.cpp:438
int min(int a, int b)
wxString GetName() const
Definition: Track.h:270
void SetGain(float newGain)
Definition: WaveTrack.cpp:420
samplePtr ptr() const
Definition: SampleFormat.h:81
sampleFormat GetDefaultFormat()
Definition: Project.h:197
sampleCount GetBlockStart(sampleCount t) const
Definition: WaveTrack.cpp:1594
void ExpandCutLine(double cutLinePosition)
Definition: WaveClip.cpp:1811
double GetValue(double t, double sampleDur=0) const
Get envelope value at time t.
Definition: Envelope.cpp:1114
#define WAVETRACK_MERGE_POINT_TOLERANCE
Definition: WaveTrack.h:38
void AppendAlias(const wxString &fName, sampleCount start, size_t len, int channel, bool useOD)
Definition: WaveClip.cpp:1450
int GetNumClips() const
Definition: WaveTrack.cpp:2290
void HandleXMLEndTag(const wxChar *tag) override
Definition: WaveTrack.cpp:1738
static WaveTrack::WaveTrackDisplay ViewModeChoice()
void Append(samplePtr buffer, sampleFormat format, size_t len, unsigned int stride=1, XMLWriter *blockFileLog=NULL)
Append the sample data to the WaveTrack. You must call Flush() after the last Append.
Definition: WaveTrack.cpp:1557
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:1625
Track::Holder CopyNonconst(double t0, double t1)
Definition: WaveTrack.cpp:673
void HandleClear(double t0, double t1, bool addCutLines, bool split)
Definition: WaveTrack.cpp:1010
int mRate
Definition: WaveTrack.h:614
std::shared_ptr< const WaveTrack > mPTrack
Definition: WaveTrack.h:713
void SetSilence(sampleCount s0, sampleCount len)
Definition: Sequence.cpp:662
An AudioTrack that can be played and stopped.
Definition: Track.h:375
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
double mOffset
Definition: Track.h:229
float mGain
Definition: WaveTrack.h:615
std::unique_ptr< WaveTrack > NewWaveTrack(sampleFormat format=(sampleFormat) 0, double rate=0)
Definition: WaveTrack.cpp:78
void SetDefaultName(const wxString &n)
Definition: Track.h:273
WaveTrack(const std::shared_ptr< DirManager > &projDirManager, sampleFormat format=(sampleFormat) 0, double rate=0)
Definition: WaveTrack.cpp:84
bool WithinClip(double t) const
Definition: WaveClip.cpp:453
sampleFormat mFormat
Definition: WaveTrack.h:613
void MergeClips(int clipidx1, int clipidx2)
Definition: WaveTrack.cpp:2533
WaveClipHolders mClips
Definition: WaveTrack.h:611
bool Unlock() const
Definition: WaveTrack.cpp:1835
_("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
WaveClipHolders & GetCutLines()
Get access to cut lines list.
Definition: WaveClip.h:314
void UseSpectralPrefs(bool bUse=true)
Definition: WaveTrack.cpp:721
bool Lock() const
Definition: WaveTrack.cpp:1818
unsigned int GetODFlags()
gets an int with OD flags so that we can determine which ODTasks should be run on this track after sa...
Definition: Sequence.cpp:773
void AddClip(std::shared_ptr< WaveClip > &&clip)
Definition: WaveTrack.cpp:1003
void HandleXMLEndTag(const wxChar *tag) override
Definition: WaveClip.cpp:1539
void SetName(const wxString &n)
Definition: Track.h:271
double end
Definition: WaveTrack.h:47
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
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: WaveTrack.cpp:1843
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:308
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
void SetSpectrumBounds(float min, float max) const
Definition: WaveTrack.cpp:381
sampleFormat GetSampleFormat() const
Definition: WaveTrack.h:146
float mPan
Definition: WaveTrack.h:616
const char * constSamplePtr
Definition: Types.h:204
void Trim(double t0, double t1)
Definition: WaveTrack.cpp:562
virtual double Warp(double originalTime) const =0
void AddInvalidRegion(sampleCount startSample, sampleCount endSample)
Adds an invalid region to the wavecache so it redraws that portion only.
Definition: WaveClip.cpp:479
void SetOffset(double o) override
Definition: WaveTrack.cpp:224
std::pair< float, float > GetMinMax(double t0, double t1, bool mayThrow=true) const
Definition: WaveTrack.cpp:1898
double GetEndTime() const
Definition: WaveClip.cpp:427
size_t GetMaxBlockSize() const
Definition: Sequence.cpp:83
Transforms one point in time to another point. For example, a time stretching effect might use one to...
Definition: TimeWarper.h:61
void DoSetMinimized(bool isMinimized) override
Definition: WaveTrack.cpp:456
std::vector< WaveClip * > WaveClipPointers
Definition: WaveClip.h:126
bool GetErrorOpening()
Definition: Sequence.h:145
XMLTagHandler * HandleXMLChild(const wxChar *tag) override
Definition: WaveTrack.cpp:1745
float GetGain() const
Definition: WaveTrack.cpp:415
bool AfterClip(double t) const
Definition: WaveClip.cpp:465
double GetRate() const
Definition: Project.h:199
std::shared_ptr< WaveClip > WaveClipHolder
Definition: WaveClip.h:121
void GetValues(double *buffer, int len, double t0, double tstep) const
Get many envelope points at once.
Definition: Envelope.cpp:1214
std::unique_ptr< Track > Holder
Definition: Track.h:263
void GetEnvelopeValues(double *buffer, size_t bufferLen, double t0) const
Definition: WaveTrack.cpp:2095
double GetRate() const
Definition: WaveTrack.cpp:398
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true, sampleCount *pNumCopied=nullptr) const
Definition: WaveTrack.cpp:1971
void SetLastScaleType() const
Definition: WaveTrack.cpp:315
void SetPan(float newPan) override
Definition: WaveTrack.cpp:430
void ClearWaveCaches()
Invalidates all clips' wavecaches. Careful, This may not be threadsafe.
Definition: WaveTrack.cpp:2587
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:22
double LongSamplesToTime(sampleCount pos) const
Convert correctly between an number of samples and an (absolute) time in seconds. ...
Definition: WaveTrack.cpp:1848
bool CanInsertClip(WaveClip *clip, double &slideBy, double &tolerance)
Definition: WaveTrack.cpp:2342
void Paste(double t0, const WaveClip *other)
Paste data from other clip, resampling it if not equal rate.
Definition: WaveClip.cpp:1579
void AppendSilence(double len, double envelopeValue)
Definition: WaveClip.cpp:1669
void GetDisplayRect(wxRect *r)
Definition: WaveClip.cpp:1388
size_t GetIdealBlockSize() const
Definition: Sequence.cpp:88
float mDisplayMax
Definition: WaveTrack.h:625
bool IsEmpty(double t0, double t1) const
Returns true if there are no WaveClips in the specified region.
Definition: WaveTrack.cpp:495
void AppendCoded(const wxString &fName, sampleCount start, size_t len, int channel, int decodeType)
Definition: WaveTrack.cpp:1575
TrackFactory * GetTrackFactory()
Definition: Project.cpp:1427
int GetClipIndex(const WaveClip *clip) const
Definition: WaveTrack.cpp:2270
bool GetEditClipsCanMove()
Definition: Prefs.cpp:158
std::shared_ptr< TrackVRulerControls > GetVRulerControls() override
void SetSamples(samplePtr buffer, sampleFormat format, sampleCount start, size_t len)
Definition: WaveClip.cpp:405
void SetLastdBRange() const
Definition: WaveTrack.cpp:320
virtual int GetChannel() const override
Definition: WaveTrack.cpp:236
std::shared_ptr< DirManager > mDirManager
Definition: Track.h:231
sampleCount end() const
Definition: WaveTrack.h:703
void CloseLock()
Definition: WaveClip.cpp:1876
void Silence(double t0, double t1) override
Definition: WaveTrack.cpp:1351
double mLegacyProjectFileOffset
Definition: WaveTrack.h:646
static SpectrogramSettings & defaults()
int mWaveColorIndex
Definition: WaveTrack.h:617
void Flush()
Flush must be called after last Append.
Definition: WaveTrack.cpp:1650
void Lock()
Lock all blockfiles.
Definition: WaveClip.cpp:1869
int mChannel
Definition: Track.h:228
size_t GetBestBlockSize(sampleCount start) const
Definition: Sequence.cpp:792
void Disjoin(double t0, double t1)
Definition: WaveTrack.cpp:1425
void SetWaveformSettings(std::unique_ptr< WaveformSettings > &&pSettings)
Definition: WaveTrack.cpp:761
Waveform settings, either for one track or as defaults.
void Clear(double t0, double t1)
Definition: WaveClip.cpp:1676
wxString mName
Definition: Track.h:118