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