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