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