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