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