Audacity  2.3.1
Track.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Track.cpp
6 
7  Dominic Mazzoni
8 
9 *******************************************************************//*******************************************************************/
23 
24 #include <algorithm>
25 #include <numeric>
26 #include "Track.h"
27 
28 #include <float.h>
29 #include <wx/file.h>
30 #include <wx/textfile.h>
31 #include <wx/log.h>
32 
33 #include "TimeTrack.h"
34 #include "WaveTrack.h"
35 #include "NoteTrack.h"
36 #include "LabelTrack.h"
37 #include "Project.h"
38 #include "DirManager.h"
39 
40 #include "Experimental.h"
41 #include "InconsistencyException.h"
42 
43 #include "TrackPanel.h" // for TrackInfo
44 
45 #ifdef _MSC_VER
46 //Disable truncation warnings
47 #pragma warning( disable : 4786 )
48 #endif
49 
50 Track::Track(const std::shared_ptr<DirManager> &projDirManager)
51 : vrulerSize(36,0),
52  mDirManager(projDirManager)
53 {
54  mSelected = false;
55  mLinked = false;
56 
57  mY = 0;
59  mIndex = 0;
60 
61  mMinimized = false;
62 
63  mOffset = 0.0;
64 
66 }
67 
68 Track::Track(const Track &orig)
69 : vrulerSize( orig.vrulerSize )
70 {
71  mY = 0;
72  mIndex = 0;
73  Init(orig);
74  mOffset = orig.mOffset;
75 }
76 
77 // Copy all the track properties except the actual contents
78 void Track::Init(const Track &orig)
79 {
80  mId = orig.mId;
81 
83  mName = orig.mName;
84 
85  mDirManager = orig.mDirManager;
86 
87  mSelected = orig.mSelected;
88  mLinked = orig.mLinked;
89  mHeight = orig.mHeight;
90  mMinimized = orig.mMinimized;
91  mChannel = orig.mChannel;
92 }
93 
94 void Track::SetName( const wxString &n )
95 {
96  if ( mName != n ) {
97  mName = n;
98  Notify();
99  }
100 }
101 
102 void Track::SetSelected(bool s)
103 {
104  if (mSelected != s) {
105  mSelected = s;
106  auto pList = mList.lock();
107  if (pList)
108  pList->SelectionEvent( Pointer( this ) );
109  }
110 }
111 
112 void Track::Merge(const Track &orig)
113 {
114  mSelected = orig.mSelected;
115 }
116 
118 {
119 }
120 
121 
123 {
124  wxASSERT(mList.lock() == NULL || this == mNode.first->get());
125  return mNode;
126 }
127 
128 void Track::SetOwner
129 (const std::weak_ptr<TrackList> &list, TrackNodePointer node)
130 {
131  // BUG: When using this function to clear an owner, we may need to clear
132  // focussed track too. Otherwise focus could remain on an invisible (or deleted) track.
133  mList = list;
134  mNode = node;
135 }
136 
138 {
139  auto height = TrackInfo::MinimumTrackHeight();
140  auto channels = TrackList::Channels(this);
141  auto nChannels = channels.size();
142  auto begin = channels.begin();
143  auto index = std::distance(begin, std::find(begin, channels.end(), this));
144  return (height * (index + 1) / nChannels) - (height * index / nChannels);
145 }
146 
147 int Track::GetIndex() const
148 {
149  return mIndex;
150 }
151 
152 void Track::SetIndex(int index)
153 {
154  mIndex = index;
155 }
156 
157 int Track::GetY() const
158 {
159  return mY;
160 }
161 
162 void Track::SetY(int y)
163 {
164  auto pList = mList.lock();
165  if (pList && !pList->mPendingUpdates.empty()) {
166  auto orig = pList->FindById( GetId() );
167  if (orig && orig != this) {
168  // delegate, and rely on the update to copy back
169  orig->SetY(y);
170  pList->UpdatePendingTracks();
171  return;
172  }
173  }
174 
175  DoSetY(y);
176 }
177 
178 void Track::DoSetY(int y)
179 {
180  mY = y;
181 }
182 
183 int Track::GetHeight() const
184 {
185  if (mMinimized) {
186  return GetMinimizedHeight();
187  }
188 
189  return mHeight;
190 }
191 
192 void Track::SetHeight(int h)
193 {
194  auto pList = mList.lock();
195  if (pList && !pList->mPendingUpdates.empty()) {
196  auto orig = pList->FindById( GetId() );
197  if (orig && orig != this) {
198  // delegate, and rely on RecalcPositions to copy back
199  orig->SetHeight(h);
200  return;
201  }
202  }
203 
204  DoSetHeight(h);
205 
206  if (pList) {
207  pList->RecalcPositions(mNode);
208  pList->ResizingEvent(mNode);
209  }
210 }
211 
213 {
214  mHeight = h;
215 }
216 
218 {
219  return mMinimized;
220 }
221 
222 void Track::SetMinimized(bool isMinimized)
223 {
224  auto pList = mList.lock();
225  if (pList && !pList->mPendingUpdates.empty()) {
226  auto orig = pList->FindById( GetId() );
227  if (orig && orig != this) {
228  // delegate, and rely on RecalcPositions to copy back
229  orig->SetMinimized(isMinimized);
230  return;
231  }
232  }
233 
234  DoSetMinimized(isMinimized);
235 
236  if (pList) {
237  pList->RecalcPositions(mNode);
238  pList->ResizingEvent(mNode);
239  }
240 }
241 
242 void Track::DoSetMinimized(bool isMinimized)
243 {
244  mMinimized = isMinimized;
245 }
246 
247 void Track::SetLinked(bool l)
248 {
249  auto pList = mList.lock();
250  if (pList && !pList->mPendingUpdates.empty()) {
251  auto orig = pList->FindById( GetId() );
252  if (orig && orig != this) {
253  // delegate, and rely on RecalcPositions to copy back
254  orig->SetLinked(l);
255  return;
256  }
257  }
258 
259  DoSetLinked(l);
260 
261  if (pList) {
262  pList->RecalcPositions(mNode);
263  pList->ResizingEvent(mNode);
264  }
265 }
266 
267 void Track::DoSetLinked(bool l)
268 {
269  mLinked = l;
270 }
271 
273 {
274  auto pList = mList.lock();
275  if (!pList)
276  return nullptr;
277 
278  if (!pList->isNull(mNode)) {
279  if (mLinked) {
280  auto next = pList->getNext( mNode );
281  if ( !pList->isNull( next ) )
282  return next.first->get();
283  }
284 
285  if (mNode.first != mNode.second->begin()) {
286  auto prev = pList->getPrev( mNode );
287  if ( !pList->isNull( prev ) ) {
288  auto track = prev.first->get();
289  if (track && track->GetLinked())
290  return track;
291  }
292  }
293  }
294 
295  return nullptr;
296 }
297 
298 namespace {
299  inline bool IsSyncLockableNonLabelTrack( const Track *pTrack )
300  {
301  return nullptr != track_cast< const AudioTrack * >( pTrack );
302  }
303 
304  bool IsGoodNextSyncLockTrack(const Track *t, bool inLabelSection)
305  {
306  if (!t)
307  return false;
308  const bool isLabel = ( nullptr != track_cast<const LabelTrack*>(t) );
309  if (inLabelSection)
310  return isLabel;
311  else if (isLabel)
312  return true;
313  else
314  return IsSyncLockableNonLabelTrack( t );
315  }
316 }
317 
319 {
320 #ifdef EXPERIMENTAL_SYNC_LOCK
322  if (!p || !p->IsSyncLocked())
323  return false;
324 
325  auto pList = mList.lock();
326  if (!pList)
327  return false;
328 
329  auto shTrack = this->SubstituteOriginalTrack();
330  if (!shTrack)
331  return false;
332 
333  const auto pTrack = shTrack.get();
334  auto trackRange = TrackList::SyncLockGroup( pTrack );
335 
336  if (trackRange.size() <= 1) {
337  // Not in a sync-locked group.
338  // Return true iff selected and of a sync-lockable type.
339  return (IsSyncLockableNonLabelTrack( pTrack ) ||
340  track_cast<const LabelTrack*>( pTrack )) && GetSelected();
341  }
342 
343  // Return true iff any track in the group is selected.
344  return *(trackRange + &Track::IsSelected).begin();
345 #endif
346 
347  return false;
348 }
349 
350 void Track::Notify( int code )
351 {
352  auto pList = mList.lock();
353  if (pList)
354  pList->DataEvent( Pointer(this), code );
355 }
356 
357 void Track::SyncLockAdjust(double oldT1, double newT1)
358 {
359  if (newT1 > oldT1) {
360  // Insert space within the track
361 
362  if (oldT1 > GetEndTime())
363  return;
364 
365  auto tmp = Cut(oldT1, GetEndTime());
366 
367  Paste(newT1, tmp.get());
368  }
369  else if (newT1 < oldT1) {
370  // Remove from the track
371  Clear(newT1, oldT1);
372  }
373 }
374 
375 std::shared_ptr<Track> Track::FindTrack()
376 {
377  return Pointer( this );
378 }
379 
381 {
382  mMute = orig.mMute;
383  mSolo = orig.mSolo;
384  AudioTrack::Init( orig );
385 }
386 
387 void PlayableTrack::Merge( const Track &orig )
388 {
389  auto pOrig = dynamic_cast<const PlayableTrack *>(&orig);
390  wxASSERT( pOrig );
391  mMute = pOrig->mMute;
392  mSolo = pOrig->mSolo;
393  AudioTrack::Merge( *pOrig );
394 }
395 
397 {
398  if ( mMute != m ) {
399  mMute = m;
400  Notify();
401  }
402 }
403 
405 {
406  if ( mSolo != s ) {
407  mSolo = s;
408  Notify();
409  }
410 }
411 
412 // Serialize, not with tags of its own, but as attributes within a tag.
414 {
415  xmlFile.WriteAttr(wxT("mute"), mMute);
416  xmlFile.WriteAttr(wxT("solo"), mSolo);
418 }
419 
420 // Return true iff the attribute is recognized.
421 bool PlayableTrack::HandleXMLAttribute(const wxChar *attr, const wxChar *value)
422 {
423  const wxString strValue{ value };
424  long nValue;
425  if (!wxStrcmp(attr, wxT("mute")) &&
426  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue)) {
427  mMute = (nValue != 0);
428  return true;
429  }
430  else if (!wxStrcmp(attr, wxT("solo")) &&
431  XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue)) {
432  mSolo = (nValue != 0);
433  return true;
434  }
435 
436  return AudioTrack::HandleXMLAttribute(attr, value);
437 }
438 
439 bool Track::Any() const
440  { return true; }
441 
442 bool Track::IsSelected() const
443  { return GetSelected(); }
444 
446  { return GetSelected() || IsSyncLockSelected(); }
447 
448 bool Track::IsLeader() const
449  { return !GetLink() || GetLinked(); }
450 
452  { return IsSelected() && IsLeader(); }
453 
455 (const Track *n, Track *dest)
456 {
457  if (dest) {
458  dest->SetChannel(n->GetChannel());
459  dest->SetLinked(n->GetLinked());
460  dest->SetName(n->GetName());
461  }
462 }
463 
465 {
466  // Sanity checks for linked tracks; unsetting the linked property
467  // doesn't fix the problem, but it likely leaves us with orphaned
468  // blockfiles instead of much worse problems.
469  bool err = false;
470  if (GetLinked())
471  {
472  Track *l = GetLink();
473  if (l)
474  {
475  // A linked track's partner should never itself be linked
476  if (l->GetLinked())
477  {
478  wxLogWarning(
479  wxT("Left track %s had linked right track %s with extra right track link.\n Removing extra link from right track."),
480  GetName(), l->GetName());
481  err = true;
482  l->SetLinked(false);
483  }
484 
485  // Channels should be left and right
486  if ( !( (GetChannel() == Track::LeftChannel &&
487  l->GetChannel() == Track::RightChannel) ||
489  l->GetChannel() == Track::LeftChannel) ) )
490  {
491  wxLogWarning(
492  wxT("Track %s and %s had left/right track links out of order. Setting tracks to not be linked."),
493  GetName(), l->GetName());
494  err = true;
495  SetLinked(false);
496  }
497  }
498  else
499  {
500  wxLogWarning(
501  wxT("Track %s had link to NULL track. Setting it to not be linked."),
502  GetName());
503  err = true;
504  SetLinked(false);
505  }
506  }
507 
508  return ! err;
509 }
510 
511 std::pair<Track *, Track *> TrackList::FindSyncLockGroup(Track *pMember) const
512 {
513  if (!pMember)
514  return { nullptr, nullptr };
515 
516  // A non-trivial sync-locked group is a maximal sub-sequence of the tracks
517  // consisting of any positive number of audio tracks followed by zero or
518  // more label tracks.
519 
520  // Step back through any label tracks.
521  auto member = pMember;
522  while (member && ( nullptr != track_cast<const LabelTrack*>(member) )) {
523  member = GetPrev(member);
524  }
525 
526  // Step back through the wave and note tracks before the label tracks.
527  Track *first = nullptr;
528  while (member && IsSyncLockableNonLabelTrack(member)) {
529  first = member;
530  member = GetPrev(member);
531  }
532 
533  if (!first)
534  // Can't meet the criteria described above. In that case,
535  // consider the track to be the sole member of a group.
536  return { pMember, pMember };
537 
538  Track *last = first;
539  bool inLabels = false;
540 
541  while (const auto next = GetNext(last)) {
542  if ( ! IsGoodNextSyncLockTrack(next, inLabels) )
543  break;
544  last = next;
545  inLabels = (nullptr != track_cast<const LabelTrack*>(last) );
546  }
547 
548  return { first, last };
549 }
550 
551 
552 // TrackList
553 //
554 // The TrackList sends events whenever certain updates occur to the list it
555 // is managing. Any other classes that may be interested in get these updates
556 // should use TrackList::Connect() or TrackList::Bind().
557 //
558 wxDEFINE_EVENT(EVT_TRACKLIST_TRACK_DATA_CHANGE, TrackListEvent);
559 wxDEFINE_EVENT(EVT_TRACKLIST_SELECTION_CHANGE, TrackListEvent);
560 wxDEFINE_EVENT(EVT_TRACKLIST_PERMUTED, TrackListEvent);
561 wxDEFINE_EVENT(EVT_TRACKLIST_RESIZING, TrackListEvent);
562 wxDEFINE_EVENT(EVT_TRACKLIST_ADDITION, TrackListEvent);
563 wxDEFINE_EVENT(EVT_TRACKLIST_DELETION, TrackListEvent);
564 
565 // same value as in the default constructed TrackId:
566 long TrackList::sCounter = -1;
567 
569 : wxEvtHandler()
570 {
571 }
572 
573 // Factory function
574 std::shared_ptr<TrackList> TrackList::Create()
575 {
576  std::shared_ptr<TrackList> result{ safenew TrackList{} };
577  result->mSelf = result;
578  return result;
579 }
580 
582 {
583  if (this != &that) {
584  this->Clear();
585  Swap(that);
586  }
587  return *this;
588 }
589 
591 {
592  auto SwapLOTs = [](
593  ListOfTracks &a, const std::weak_ptr< TrackList > &aSelf,
594  ListOfTracks &b, const std::weak_ptr< TrackList > &bSelf )
595  {
596  a.swap(b);
597  for (auto it = a.begin(), last = a.end(); it != last; ++it)
598  (*it)->SetOwner(aSelf, {it, &a});
599  for (auto it = b.begin(), last = b.end(); it != last; ++it)
600  (*it)->SetOwner(bSelf, {it, &b});
601  };
602 
603  SwapLOTs( *this, mSelf, that, that.mSelf );
604  SwapLOTs( this->mPendingUpdates, mSelf, that.mPendingUpdates, that.mSelf );
605  mUpdaters.swap(that.mUpdaters);
606 }
607 
609 {
610  Clear(false);
611 }
612 
614 {
615  if ( isNull( node ) )
616  return;
617 
618  Track *t;
619  int i = 0;
620  int y = 0;
621 
622  auto prev = getPrev( node );
623  if ( !isNull( prev ) ) {
624  t = prev.first->get();
625  i = t->GetIndex() + 1;
626  y = t->GetY() + t->GetHeight();
627  }
628 
629  const auto theEnd = end();
630  for (auto n = Find( node.first->get() ); n != theEnd; ++n) {
631  t = *n;
632  t->SetIndex(i++);
633  t->DoSetY(y);
634  y += t->GetHeight();
635  }
636 
638 }
639 
640 void TrackList::SelectionEvent( const std::shared_ptr<Track> &pTrack )
641 {
642  // wxWidgets will own the event object
643  QueueEvent(
644  safenew TrackListEvent{ EVT_TRACKLIST_SELECTION_CHANGE, pTrack } );
645 }
646 
647 void TrackList::DataEvent( const std::shared_ptr<Track> &pTrack, int code )
648 {
649  // wxWidgets will own the event object
650  QueueEvent(
651  safenew TrackListEvent{ EVT_TRACKLIST_TRACK_DATA_CHANGE, pTrack, code } );
652 }
653 
655 {
656  // wxWidgets will own the event object
657  QueueEvent( safenew TrackListEvent{ EVT_TRACKLIST_PERMUTED } );
658 }
659 
661 {
662  // wxWidgets will own the event object
663  QueueEvent( safenew TrackListEvent{ EVT_TRACKLIST_DELETION } );
664 }
665 
667 {
668  // wxWidgets will own the event object
669  QueueEvent( safenew TrackListEvent{ EVT_TRACKLIST_ADDITION, *node.first } );
670 }
671 
673 {
674  // wxWidgets will own the event object
675  QueueEvent( safenew TrackListEvent{ EVT_TRACKLIST_RESIZING, *node.first } );
676 }
677 
679  -> TrackIterRange< Track >
680 {
681  auto it = const_cast<TrackList*>(this)->getEnd();
682  return {
683  { it, it, it, &Track::Any },
684  { it, it, it, &Track::Any }
685  };
686 }
687 
690 {
691  auto pList = pTrack->GetOwner();
692  auto tracks =
693  pList->FindSyncLockGroup( const_cast<Track*>( pTrack ) );
694  return pList->Any().StartingWith(tracks.first).EndingAfter(tracks.second);
695 }
696 
699 {
700  auto iter = Find(pTrack);
701  while( *iter && ! ( *iter )->IsLeader() )
702  --iter;
703  return iter.Filter( &Track::IsLeader );
704 }
705 
706 void TrackList::Permute(const std::vector<TrackNodePointer> &permutation)
707 {
708  for (const auto iter : permutation) {
709  ListOfTracks::value_type track = std::move(*iter.first);
710  erase(iter.first);
711  Track *pTrack = track.get();
712  pTrack->SetOwner(mSelf,
713  { insert(ListOfTracks::end(), std::move(track)), this });
714  }
715  auto n = getBegin();
716  RecalcPositions(n);
718 }
719 
721 {
722  // Linear search. Tracks in a project are usually very few.
723  // Search only the non-pending tracks.
724  auto it = std::find_if( ListOfTracks::begin(), ListOfTracks::end(),
725  [=](const ListOfTracks::value_type &ptr){ return ptr->GetId() == id; } );
726  if (it == ListOfTracks::end())
727  return {};
728  return it->get();
729 }
730 
731 template<typename TrackKind>
732 Track *TrackList::Add(std::unique_ptr<TrackKind> &&t)
733 {
734  Track *pTrack;
735  push_back(ListOfTracks::value_type(pTrack = t.release()));
736 
737  auto n = getPrev( getEnd() );
738 
739  pTrack->SetOwner(mSelf, n);
740  pTrack->SetId( TrackId{ ++sCounter } );
741  RecalcPositions(n);
742  AdditionEvent(n);
743  return back().get();
744 }
745 
746 // Make instantiations for the linker to find
747 template Track *TrackList::Add<TimeTrack>(std::unique_ptr<TimeTrack> &&);
748 #if defined(USE_MIDI)
749 template Track *TrackList::Add<NoteTrack>(std::unique_ptr<NoteTrack> &&);
750 #endif
751 template Track *TrackList::Add<WaveTrack>(std::unique_ptr<WaveTrack> &&);
752 template Track *TrackList::Add<LabelTrack>(std::unique_ptr<LabelTrack> &&);
753 template Track *TrackList::Add<Track>(std::unique_ptr<Track> &&);
754 
755 template<typename TrackKind>
756 Track *TrackList::AddToHead(std::unique_ptr<TrackKind> &&t)
757 {
758  Track *pTrack;
759  push_front(ListOfTracks::value_type(pTrack = t.release()));
760  auto n = getBegin();
761  pTrack->SetOwner(mSelf, n);
762  pTrack->SetId( TrackId{ ++sCounter } );
763  RecalcPositions(n);
764  AdditionEvent(n);
765  return front().get();
766 }
767 
768 // Make instantiations for the linker to find
769 template Track *TrackList::AddToHead<TimeTrack>(std::unique_ptr<TimeTrack> &&);
770 
771 template<typename TrackKind>
772 Track *TrackList::Add(std::shared_ptr<TrackKind> &&t)
773 {
774  push_back(t);
775 
776  auto n = getPrev( getEnd() );
777 
778  t->SetOwner(mSelf, n);
779  t->SetId( TrackId{ ++sCounter } );
780  RecalcPositions(n);
781  AdditionEvent(n);
782  return back().get();
783 }
784 
785 // Make instantiations for the linker to find
786 template Track *TrackList::Add<Track>(std::shared_ptr<Track> &&);
787 template Track *TrackList::Add<WaveTrack>(std::shared_ptr<WaveTrack> &&);
788 
790  Track &track, size_t groupSize, bool resetChannels )
791 {
792  // If group size is more than two, for now only the first two channels
793  // are grouped as stereo, and any others remain mono
794  auto list = track.mList.lock();
795  if ( groupSize > 0 && list.get() == this ) {
796  auto iter = track.mNode.first;
797  auto after = iter;
798  auto end = this->ListOfTracks::end();
799  auto count = groupSize;
800  for ( ; after != end && count; ++after, --count )
801  ;
802  if ( count == 0 ) {
803  auto unlink = [&] ( Track &tr ) {
804  if ( tr.GetLinked() ) {
805  if ( resetChannels ) {
806  auto link = tr.GetLink();
807  if ( link )
808  link->SetChannel( Track::MonoChannel );
809  }
810  tr.SetLinked( false );
811  }
812  if ( resetChannels )
813  tr.SetChannel( Track::MonoChannel );
814  };
815 
816  // Disassociate previous tracks -- at most one
817  auto pLeader = this->FindLeader( &track );
818  if ( *pLeader && *pLeader != &track )
819  unlink( **pLeader );
820 
821  // First disassociate given and later tracks, then reassociate them
822  for ( auto iter2 = iter; iter2 != after; ++iter2 )
823  unlink( **iter2 );
824 
825  if ( groupSize > 1 ) {
826  const auto channel = *iter++;
827  channel->SetLinked( true );
828  channel->SetChannel( Track::LeftChannel );
829  (*iter++)->SetChannel( Track::RightChannel );
830  while (iter != after)
831  (*iter++)->SetChannel( Track::MonoChannel );
832  }
833  return;
834  }
835  }
836  // *this does not contain the track or sufficient following channels
837  // or group size is zero
839 }
840 
841 auto TrackList::Replace(Track * t, ListOfTracks::value_type &&with) ->
842  ListOfTracks::value_type
843 {
844  ListOfTracks::value_type holder;
845  if (t && with) {
846  auto node = t->GetNode();
847  t->SetOwner({}, {});
848 
849  holder = std::move(*node.first);
850 
851  Track *pTrack = with.get();
852  *node.first = std::move(with);
853  pTrack->SetOwner(mSelf, node);
854  pTrack->SetId( t->GetId() );
855  RecalcPositions(node);
856 
857  DeletionEvent();
858  AdditionEvent(node);
859  }
860  return holder;
861 }
862 
864 {
865  auto result = getEnd();
866  if (t) {
867  auto node = t->GetNode();
868  t->SetOwner({}, {});
869 
870  if ( !isNull( node ) ) {
871  ListOfTracks::value_type holder = std::move( *node.first );
872 
873  result = getNext( node );
874  erase(node.first);
875  if ( !isNull( result ) )
876  RecalcPositions(result);
877 
878  DeletionEvent();
879  }
880  }
881  return result;
882 }
883 
884 void TrackList::Clear(bool sendEvent)
885 {
886  // Null out the back-pointers in tracks, in case there are outstanding
887  // shared_ptrs to those tracks.
888  for ( auto pTrack: *this )
889  pTrack->SetOwner( {}, {} );
890  for ( auto pTrack: mPendingUpdates )
891  pTrack->SetOwner( {}, {} );
892 
893  ListOfTracks tempList;
894  tempList.swap( *this );
895 
896  ListOfTracks updating;
897  updating.swap( mPendingUpdates );
898 
899  mUpdaters.clear();
900 
901  if (sendEvent)
902  DeletionEvent();
903 }
904 
906 Track *TrackList::GetNext(Track * t, bool linked) const
907 {
908  if (t) {
909  auto node = t->GetNode();
910  if ( !isNull( node ) ) {
911  if ( linked && t->GetLinked() )
912  node = getNext( node );
913 
914  if ( !isNull( node ) )
915  node = getNext( node );
916 
917  if ( !isNull( node ) )
918  return node.first->get();
919  }
920  }
921 
922  return nullptr;
923 }
924 
925 Track *TrackList::GetPrev(Track * t, bool linked) const
926 {
927  if (t) {
928  TrackNodePointer prev;
929  auto node = t->GetNode();
930  if ( !isNull( node ) ) {
931  // linked is true and input track second in team?
932  if (linked) {
933  prev = getPrev( node );
934  if( !isNull( prev ) &&
935  !t->GetLinked() && t->GetLink() )
936  // Make it the first
937  node = prev;
938  }
939 
940  prev = getPrev( node );
941  if ( !isNull( prev ) ) {
942  // Back up once
943  node = prev;
944 
945  // Back up twice sometimes when linked is true
946  if (linked) {
947  prev = getPrev( node );
948  if( !isNull( prev ) &&
949  !(*node.first)->GetLinked() && (*node.first)->GetLink() )
950  node = prev;
951  }
952 
953  return node.first->get();
954  }
955  }
956  }
957 
958  return nullptr;
959 }
960 
963 int TrackList::GetGroupHeight(const Track * t) const
964 {
965  return Channels(t).sum( &Track::GetHeight );
966 }
967 
969 {
970  return GetPrev(t, true) != NULL;
971 }
972 
974 {
975  return GetNext(t, true) != NULL;
976 }
977 
978 // This is used when you want to swap the channel group starting
979 // at s1 with that starting at s2.
980 // The complication is that the tracks are stored in a single
981 // linked list.
983 {
984  // if a null pointer is passed in, we want to know about it
985  wxASSERT(!isNull(s1));
986  wxASSERT(!isNull(s2));
987 
988  // Deal with first track in each team
989  s1 = ( * FindLeader( s1.first->get() ) )->GetNode();
990  s2 = ( * FindLeader( s2.first->get() ) )->GetNode();
991 
992  // Safety check...
993  if (s1 == s2)
994  return;
995 
996  // Be sure s1 is the earlier iterator
997  if ((*s1.first)->GetIndex() >= (*s2.first)->GetIndex())
998  std::swap(s1, s2);
999 
1000  // For saving the removed tracks
1001  using Saved = std::vector< ListOfTracks::value_type >;
1002  Saved saved1, saved2;
1003 
1004  auto doSave = [&] ( Saved &saved, TrackNodePointer &s ) {
1005  size_t nn = Channels( s.first->get() ).size();
1006  saved.resize( nn );
1007  // Save them in backwards order
1008  while( nn-- )
1009  saved[nn] = std::move( *s.first ), s.first = erase(s.first);
1010  };
1011 
1012  doSave( saved1, s1 );
1013  // The two ranges are assumed to be disjoint but might abut
1014  const bool same = (s1 == s2);
1015  doSave( saved2, s2 );
1016  if (same)
1017  // Careful, we invalidated s1 in the second doSave!
1018  s1 = s2;
1019 
1020  // Reinsert them
1021  auto doInsert = [&] ( Saved &saved, TrackNodePointer &s ) {
1022  Track *pTrack;
1023  for (auto & pointer : saved)
1024  pTrack = pointer.get(),
1025  // Insert before s, and reassign s to point at the new node before
1026  // old s; which is why we saved pointers in backwards order
1027  pTrack->SetOwner(mSelf,
1028  s = { insert(s.first, std::move(pointer)), this } );
1029  };
1030  // This does not invalidate s2 even when it equals s1:
1031  doInsert( saved2, s1 );
1032  // Even if s2 was same as s1, this correctly inserts the saved1 range
1033  // after the saved2 range, when done after:
1034  doInsert( saved1, s2 );
1035 
1036  // Now correct the Index in the tracks, and other things
1037  RecalcPositions(s1);
1038  PermutationEvent();
1039 }
1040 
1042 {
1043  if (t) {
1044  Track *p = GetPrev(t, true);
1045  if (p) {
1046  SwapNodes(p->GetNode(), t->GetNode());
1047  return true;
1048  }
1049  }
1050 
1051  return false;
1052 }
1053 
1055 {
1056  if (t) {
1057  Track *n = GetNext(t, true);
1058  if (n) {
1059  SwapNodes(t->GetNode(), n->GetNode());
1060  return true;
1061  }
1062  }
1063 
1064  return false;
1065 }
1066 
1067 bool TrackList::Contains(const Track * t) const
1068 {
1069  return make_iterator_range( *this ).contains( t );
1070 }
1071 
1072 bool TrackList::empty() const
1073 {
1074  return begin() == end();
1075 }
1076 
1077 size_t TrackList::size() const
1078 {
1079  int cnt = 0;
1080 
1081  if (!empty())
1082  cnt = getPrev( getEnd() ).first->get()->GetIndex() + 1;
1083 
1084  return cnt;
1085 }
1086 
1088 {
1089  return *Any<TimeTrack>().begin();
1090 }
1091 
1093 {
1094  return const_cast<TrackList*>(this)->GetTimeTrack();
1095 }
1096 
1097 unsigned TrackList::GetNumExportChannels(bool selectionOnly) const
1098 {
1099  /* counters for tracks panned different places */
1100  int numLeft = 0;
1101  int numRight = 0;
1102  //int numMono = 0;
1103  /* track iteration kit */
1104 
1105  // Want only unmuted wave tracks.
1106  for (auto tr :
1107  Any< const WaveTrack >()
1108  + (selectionOnly ? &Track::IsSelected : &Track::Any)
1110  ) {
1111  // Found a left channel
1112  if (tr->GetChannel() == Track::LeftChannel) {
1113  numLeft++;
1114  }
1115 
1116  // Found a right channel
1117  else if (tr->GetChannel() == Track::RightChannel) {
1118  numRight++;
1119  }
1120 
1121  // Found a mono channel, but it may be panned
1122  else if (tr->GetChannel() == Track::MonoChannel) {
1123  float pan = tr->GetPan();
1124 
1125  // Figure out what kind of channel it should be
1126  if (pan == -1.0) { // panned hard left
1127  numLeft++;
1128  }
1129  else if (pan == 1.0) { // panned hard right
1130  numRight++;
1131  }
1132  else if (pan == 0) { // panned dead center
1133  // numMono++;
1134  }
1135  else { // panned somewhere else
1136  numLeft++;
1137  numRight++;
1138  }
1139  }
1140  }
1141 
1142  // if there is stereo content, report 2, else report 1
1143  if (numRight > 0 || numLeft > 0) {
1144  return 2;
1145  }
1146 
1147  return 1;
1148 }
1149 
1150 namespace {
1151  template<typename Array, typename TrackRange>
1152  Array GetTypedTracks(const TrackRange &trackRange,
1153  bool selectionOnly, bool includeMuted)
1154  {
1155  Array array;
1156 
1157  using Type = typename
1158  std::remove_reference< decltype( *array[0] ) >::type;
1159  auto subRange =
1160  trackRange.template Filter<Type>();
1161  if ( selectionOnly )
1162  subRange = subRange + &Track::IsSelected;
1163  if ( ! includeMuted )
1164  subRange = subRange - &Type::GetMute;
1165  std::transform(
1166  subRange.begin(), subRange.end(), std::back_inserter( array ),
1167  []( Type *t ){ return Track::Pointer<Type>( t ); }
1168  );
1169 
1170  return array;
1171  }
1172 }
1173 
1174 WaveTrackArray TrackList::GetWaveTrackArray(bool selectionOnly, bool includeMuted)
1175 {
1176  return GetTypedTracks<WaveTrackArray>(Any(), selectionOnly, includeMuted);
1177 }
1178 
1179 WaveTrackConstArray TrackList::GetWaveTrackConstArray(bool selectionOnly, bool includeMuted) const
1180 {
1181  return GetTypedTracks<WaveTrackConstArray>(Any(), selectionOnly, includeMuted);
1182 }
1183 
1184 #if defined(USE_MIDI)
1186 {
1187  return GetTypedTracks<NoteTrackConstArray>(Any(), selectionOnly, true);
1188 }
1189 #endif
1190 
1192 {
1193  int height = 0;
1194 
1195  if (!empty()) {
1196  auto track = getPrev( getEnd() ).first->get();
1197  height = track->GetY() + track->GetHeight();
1198  }
1199 
1200  return height;
1201 }
1202 
1203 namespace {
1204  // Abstract the common pattern of the following three member functions
1205  inline double Accumulate
1206  (const TrackList &list,
1207  double (Track::*memfn)() const,
1208  double ident,
1209  const double &(*combine)(const double&, const double&))
1210  {
1211  // Default the answer to zero for empty list
1212  if (list.empty()) {
1213  return 0.0;
1214  }
1215 
1216  // Otherwise accumulate minimum or maximum of track values
1217  return list.Any().accumulate(ident, combine, memfn);
1218  }
1219 }
1220 
1222 {
1223  return Accumulate(*this, &Track::GetOffset, DBL_MAX, std::min);
1224 }
1225 
1227 {
1228  return Accumulate(*this, &Track::GetStartTime, DBL_MAX, std::min);
1229 }
1230 
1232 {
1233  return Accumulate(*this, &Track::GetEndTime, -DBL_MAX, std::max);
1234 }
1235 
1236 std::shared_ptr<Track>
1238 {
1239  std::shared_ptr<Track> pTrack;
1240  if (src)
1241  // convert from unique_ptr to shared_ptr
1242  pTrack.reset( src->Duplicate().release() );
1243 
1244  if (pTrack) {
1245  mUpdaters.push_back( updater );
1246  mPendingUpdates.push_back( pTrack );
1247  auto n = mPendingUpdates.end();
1248  --n;
1249  pTrack->SetOwner(mSelf, {n, &mPendingUpdates});
1250  }
1251 
1252  return pTrack;
1253 }
1254 
1255 void TrackList::RegisterPendingNewTrack( const std::shared_ptr<Track> &pTrack )
1256 {
1257  auto copy = pTrack;
1258  Add<Track>( std::move( copy ) );
1259  pTrack->SetId( TrackId{} );
1260 }
1261 
1263 {
1264  auto pUpdater = mUpdaters.begin();
1265  for (const auto &pendingTrack : mPendingUpdates) {
1266  // Copy just a part of the track state, according to the update
1267  // function
1268  const auto &updater = *pUpdater;
1269  auto src = FindById( pendingTrack->GetId() );
1270  if (pendingTrack && src) {
1271  if (updater)
1272  updater( *pendingTrack, *src );
1273  pendingTrack->DoSetY(src->GetY());
1274  pendingTrack->DoSetHeight(src->GetHeight());
1275  pendingTrack->DoSetMinimized(src->GetMinimized());
1276  pendingTrack->DoSetLinked(src->GetLinked());
1277  }
1278  ++pUpdater;
1279  }
1280 }
1281 
1283 // NOFAIL-GUARANTEE
1284 {
1285  for (const auto &pTrack: mPendingUpdates)
1286  pTrack->SetOwner( {}, {} );
1287  mPendingUpdates.clear();
1288  mUpdaters.clear();
1289 
1290  if (pAdded)
1291  pAdded->clear();
1292 
1293  for (auto it = ListOfTracks::begin(), stop = ListOfTracks::end();
1294  it != stop;) {
1295  if (it->get()->GetId() == TrackId{}) {
1296  if (pAdded)
1297  pAdded->push_back( *it );
1298  (*it)->SetOwner( {}, {} );
1299  it = erase( it );
1300  }
1301  else
1302  ++it;
1303  }
1304 
1305  if (!empty())
1307 }
1308 
1310 {
1311  bool result = false;
1312 
1313  ListOfTracks additions;
1314  ListOfTracks updates;
1315  {
1316  // Always clear, even if one of the update functions throws
1317  auto cleanup = finally( [&] { ClearPendingTracks( &additions ); } );
1319  updates.swap( mPendingUpdates );
1320  }
1321 
1322  // Remaining steps must be NOFAIL-GUARANTEE so that this function
1323  // gives STRONG-GUARANTEE
1324 
1325  std::vector< std::shared_ptr<Track> > reinstated;
1326 
1327  for (auto &pendingTrack : updates) {
1328  if (pendingTrack) {
1329  auto src = FindById( pendingTrack->GetId() );
1330  if (src)
1331  this->Replace(src, std::move(pendingTrack)), result = true;
1332  else
1333  // Perhaps a track marked for pending changes got deleted by
1334  // some other action. Recreate it so we don't lose the
1335  // accumulated changes.
1336  reinstated.push_back(pendingTrack);
1337  }
1338  }
1339 
1340  // If there are tracks to reinstate, append them to the list.
1341  for (auto &pendingTrack : reinstated)
1342  if (pendingTrack)
1343  this->Add(std::move(pendingTrack)), result = true;
1344 
1345  // Put the pending added tracks back into the list, preserving their
1346  // positions.
1347  bool inserted = false;
1348  ListOfTracks::iterator first;
1349  for (auto &pendingTrack : additions) {
1350  if (pendingTrack) {
1351  auto iter = ListOfTracks::begin();
1352  std::advance( iter, pendingTrack->GetIndex() );
1353  iter = ListOfTracks::insert( iter, pendingTrack );
1354  pendingTrack->SetOwner( mSelf, {iter, this} );
1355  pendingTrack->SetId( TrackId{ ++sCounter } );
1356  if (!inserted) {
1357  first = iter;
1358  inserted = true;
1359  }
1360  }
1361  }
1362  if (inserted) {
1363  RecalcPositions({first, this});
1364  result = true;
1365  }
1366 
1367  return result;
1368 }
1369 
1370 std::shared_ptr<const Track> Track::SubstitutePendingChangedTrack() const
1371 {
1372  // Linear search. Tracks in a project are usually very few.
1373  auto pList = mList.lock();
1374  if (pList) {
1375  const auto id = GetId();
1376  const auto end = pList->mPendingUpdates.end();
1377  auto it = std::find_if(
1378  pList->mPendingUpdates.begin(), end,
1379  [=](const ListOfTracks::value_type &ptr){ return ptr->GetId() == id; } );
1380  if (it != end)
1381  return *it;
1382  }
1383  return Pointer( this );
1384 }
1385 
1386 std::shared_ptr<const Track> Track::SubstituteOriginalTrack() const
1387 {
1388  auto pList = mList.lock();
1389  if (pList) {
1390  const auto id = GetId();
1391  const auto pred = [=]( const ListOfTracks::value_type &ptr ) {
1392  return ptr->GetId() == id; };
1393  const auto end = pList->mPendingUpdates.end();
1394  const auto it = std::find_if( pList->mPendingUpdates.begin(), end, pred );
1395  if (it != end) {
1396  const auto &list2 = (const ListOfTracks &) *pList;
1397  const auto end2 = list2.end();
1398  const auto it2 = std::find_if( list2.begin(), end2, pred );
1399  if ( it2 != end2 )
1400  return *it2;
1401  }
1402  }
1403  return Pointer( this );
1404 }
1405 
1407 {
1408  if ( !mPendingUpdates.empty() )
1409  return true;
1410  if (end() != std::find_if(begin(), end(), [](const Track *t){
1411  return t->GetId() == TrackId{};
1412  }))
1413  return true;
1414  return false;
1415 }
1416 
1417 #include "AudioIO.h"
1418 TransportTracks GetAllPlaybackTracks(TrackList &trackList, bool selectedOnly, bool useMidi)
1419 {
1420  TransportTracks result;
1421  result.playbackTracks = trackList.GetWaveTrackArray(selectedOnly);
1422 #ifdef EXPERIMENTAL_MIDI_OUT
1423  if (useMidi)
1424  result.midiTracks = trackList.GetNoteTrackConstArray(selectedOnly);
1425 #else
1426  WXUNUSED(useMidi);
1427 #endif
1428  return result;
1429 }
void SetY(int y)
Definition: Track.cpp:162
virtual void Paste(double WXUNUSED(t), const Track *WXUNUSED(src))=0
void SetId(TrackId id)
Definition: Track.h:227
bool mSolo
Definition: Track.h:792
void Merge(const Track &init) override
Definition: Track.cpp:387
virtual double GetOffset() const =0
bool CanMoveDown(Track *t) const
Definition: Track.cpp:973
void DataEvent(const std::shared_ptr< Track > &pTrack, int code)
Definition: Track.cpp:647
int GetHeight() const
Definition: Track.cpp:1191
bool isNull(TrackNodePointer p) const
Definition: Track.h:1516
double GetStartTime() const
Definition: Track.cpp:1226
virtual void WriteAttr(const wxString &name, const wxString &value)
Definition: XMLWriter.cpp:131
int mIndex
Definition: Track.h:203
void SetIndex(int index)
Definition: Track.cpp:152
bool GetSelected() const
Definition: Track.h:381
TrackNodePointer mNode
Definition: Track.h:202
void Init(const PlayableTrack &init)
Definition: Track.cpp:380
void SetLinked(bool l)
Definition: Track.cpp:247
TrackNodePointer GetNode() const
Definition: Track.cpp:122
void GroupChannels(Track &track, size_t groupSize, bool resetChannels=true)
Define a group of channels starting at the given track.
Definition: Track.cpp:789
unsigned MinimumTrackHeight()
Definition: TrackPanel.cpp:917
bool HandleXMLAttribute(const wxChar *attr, const wxChar *value)
Definition: Track.cpp:421
bool IsSyncLockSelected() const
Definition: Track.cpp:318
TrackId GetId() const
Definition: Track.h:225
virtual double GetEndTime() const =0
Track * Add(std::unique_ptr< TrackKind > &&t)
Add a Track, giving it a fresh id.
Definition: Track.cpp:732
void Notify(int code=-1)
Definition: Track.cpp:350
int mHeight
Definition: Track.h:205
ChannelType mChannel
Definition: Track.h:353
void DeletionEvent()
Definition: Track.cpp:660
double GetEndTime() const
Definition: Track.cpp:1231
WaveTrackArray playbackTracks
Definition: AudioIO.h:159
void SetHeight(int h)
Definition: Track.cpp:192
bool mSelected
Definition: Track.h:210
void RegisterPendingNewTrack(const std::shared_ptr< Track > &pTrack)
Definition: Track.cpp:1255
iterator begin()
Definition: Track.h:1200
void ClearPendingTracks(ListOfTracks *pAdded=nullptr)
Definition: Track.cpp:1282
std::pair< ListOfTracks::iterator, ListOfTracks * > TrackNodePointer
Definition: Track.h:67
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1246
std::shared_ptr< const Track > SubstitutePendingChangedTrack() const
Definition: Track.cpp:1370
void SwapNodes(TrackNodePointer s1, TrackNodePointer s2)
Definition: Track.cpp:982
bool GetLinked() const
Definition: Track.h:336
int GetIndex() const
Definition: Track.cpp:147
virtual Holder Cut(double WXUNUSED(t0), double WXUNUSED(t1))=0
TrackIter< Track > FindLeader(Track *pTrack)
Definition: Track.cpp:697
auto Find(Track *pTrack) -> TrackIter< TrackType >
Definition: Track.h:1209
static long sCounter
Definition: Track.h:1563
bool GetMinimized() const
Definition: Track.cpp:217
virtual int GetMinimizedHeight() const
Definition: Track.cpp:137
bool MoveUp(Track *t)
Definition: Track.cpp:1041
virtual ChannelType GetChannel() const
Definition: Track.h:387
void DoSetLinked(bool l)
Definition: Track.cpp:267
#define THROW_INCONSISTENCY_EXCEPTION
virtual double GetStartTime() const =0
void SetMinimized(bool isMinimized)
Definition: Track.cpp:222
TrackNodePointer getPrev(TrackNodePointer p) const
Definition: Track.h:1537
bool ApplyPendingTracks()
Definition: Track.cpp:1309
TrackIterRange< Track > EmptyRange() const
Definition: Track.cpp:678
wxString mDefaultName
Definition: Track.h:207
TimeTrack * GetTimeTrack()
Definition: Track.cpp:1087
TrackList & operator=(const TrackList &)=delete
#define safenew
Definition: Audacity.h:230
static bool IsGoodInt(const wxString &strInt)
Check that the supplied string can be converted to a long (32bit) integer.
void ResizingEvent(TrackNodePointer node)
Definition: Track.cpp:672
virtual void SetSelected(bool s)
Definition: Track.cpp:102
static std::shared_ptr< TrackList > Create()
Definition: Track.cpp:574
void SetMute(bool m)
Definition: Track.cpp:396
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:113
std::shared_ptr< Track > RegisterPendingChangedTrack(Updater updater, Track *src)
Definition: Track.cpp:1237
TrackList()
Definition: Track.cpp:568
void Swap(TrackList &that)
Definition: Track.cpp:590
TrackList is a flat linked list of tracks supporting Add, Remove, Clear, and Contains, plus serialization of the list of tracks.
Definition: Track.h:1121
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:174
A kind of Track used to 'warp time'.
Definition: TimeTrack.h:29
size_t size() const
Definition: Track.cpp:1077
std::function< void(Track &dest, const Track &src) > Updater
Definition: Track.h:1566
bool CanMoveUp(Track *t) const
Definition: Track.cpp:968
void WriteXMLAttributes(XMLWriter &xmlFile) const
Definition: Track.cpp:413
WaveTrackConstArray GetWaveTrackConstArray(bool selectionOnly, bool includeMuted=true) const
Definition: Track.cpp:1179
void DoSetY(int y)
Definition: Track.cpp:178
bool HandleXMLAttribute(const wxChar *, const wxChar *)
Definition: Track.h:765
TransportTracks GetAllPlaybackTracks(TrackList &trackList, bool selectedOnly, bool useMidi)
Definition: Track.cpp:1418
ListOfTracks mPendingUpdates
Definition: Track.h:1612
Track * GetNext(Track *t, bool linked=false) const
Return a track in the list that comes after Track t.
Definition: Track.cpp:906
void AdditionEvent(TrackNodePointer node)
Definition: Track.cpp:666
bool Any() const
Definition: Track.cpp:439
Track * AddToHead(std::unique_ptr< TrackKind > &&t)
Add a Track, giving it a fresh id.
Definition: Track.cpp:756
std::vector< std::shared_ptr< WaveTrack > > WaveTrackArray
Definition: AudioIO.h:67
void RecalcPositions(TrackNodePointer node)
Definition: Track.cpp:613
std::vector< std::shared_ptr< const WaveTrack > > WaveTrackConstArray
Definition: AudioIO.h:68
bool mMinimized
Definition: Track.h:214
bool Contains(const Track *t) const
Mainly a test function. Uses a linear search, so could be slow.
Definition: Track.cpp:1067
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:608
A Track that can load/save audio data to/from XML.
Definition: Track.h:754
Track * FindById(TrackId id)
Definition: Track.cpp:720
virtual ~TrackList()
Definition: Track.cpp:608
ListOfTracks::value_type Replace(Track *t, ListOfTracks::value_type &&with)
Definition: Track.cpp:841
std::weak_ptr< TrackList > mList
Definition: Track.h:201
WaveTrackArray GetWaveTrackArray(bool selectionOnly, bool includeMuted=true)
Definition: Track.cpp:1174
bool HasPendingTracks() const
Definition: Track.cpp:1406
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:191
virtual void DoSetMinimized(bool isMinimized)
Definition: Track.cpp:242
virtual void Clear(double WXUNUSED(t0), double WXUNUSED(t1))=0
NoteTrackConstArray midiTracks
Definition: AudioIO.h:162
unsigned GetNumExportChannels(bool selectionOnly) const
Find out how many channels this track list mixes to.
Definition: Track.cpp:1097
static void FinishCopy(const Track *n, Track *dest)
Definition: Track.cpp:455
int min(int a, int b)
wxString GetName() const
Definition: Track.h:376
iterator end()
Definition: Track.h:1201
void SetChannel(ChannelType c)
Definition: Track.h:341
void Clear(bool sendEvent=true)
Make the list empty.
Definition: Track.cpp:884
TrackNodePointer getEnd() const
Definition: Track.h:1519
std::shared_ptr< const Track > SubstituteOriginalTrack() const
Definition: Track.cpp:1386
int GetGroupHeight(const Track *t) const
Definition: Track.cpp:963
An AudioTrack that can be played and stopped.
Definition: Track.h:769
bool MoveDown(Track *t)
Definition: Track.cpp:1054
double mOffset
Definition: Track.h:354
bool mLinked
Definition: Track.h:213
bool IsLeader() const
Definition: Track.cpp:448
TrackNodePointer getBegin() const
Definition: Track.h:1522
double GetMinOffset() const
Definition: Track.cpp:1221
virtual ~Track()
Definition: Track.cpp:117
NoteTrackConstArray GetNoteTrackConstArray(bool selectionOnly) const
Definition: Track.cpp:1185
TrackNodePointer Remove(Track *t)
Definition: Track.cpp:863
Track(const std::shared_ptr< DirManager > &projDirManager)
Definition: Track.cpp:50
std::shared_ptr< Track > FindTrack() override
Definition: Track.cpp:375
void SelectionEvent(const std::shared_ptr< Track > &pTrack)
Definition: Track.cpp:640
Track * GetPrev(Track *t, bool linked=false) const
Definition: Track.cpp:925
void SetName(const wxString &n)
Definition: Track.cpp:94
void Permute(const std::vector< TrackNodePointer > &permutation)
For use in sorting: assume each iterator points into this list, no duplications.
Definition: Track.cpp:706
Track * GetLink() const
Definition: Track.cpp:272
bool IsSyncLocked()
Definition: Project.cpp:5311
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:311
bool IsSelected() const
Definition: Track.cpp:442
void Init(const Track &orig)
Definition: Track.cpp:78
int GetY() const
Definition: Track.cpp:157
static TrackIterRange< Track > SyncLockGroup(Track *pTrack)
Definition: Track.cpp:688
bool LinkConsistencyCheck()
Definition: Track.cpp:464
bool IsSelectedLeader() const
Definition: Track.cpp:451
bool IsSelectedOrSyncLockSelected() const
Definition: Track.cpp:445
void WriteXMLAttributes(XMLWriter &WXUNUSED(xmlFile)) const
Definition: Track.h:762
void SetOwner(const std::weak_ptr< TrackList > &list, TrackNodePointer node)
Definition: Track.cpp:129
bool GetMute() const
Definition: Track.h:776
Definition: Track.h:170
std::vector< Updater > mUpdaters
Definition: Track.h:1614
TrackNodePointer getNext(TrackNodePointer p) const
Definition: Track.h:1527
TrackIterRange EndingAfter(const Track *pTrack) const
Definition: Track.h:1074
wxDEFINE_EVENT(EVT_TRACKLIST_TRACK_DATA_CHANGE, TrackListEvent)
virtual void SyncLockAdjust(double oldT1, double newT1)
Definition: Track.cpp:357
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:22
void PermutationEvent()
Definition: Track.cpp:654
static std::shared_ptr< Subclass > Pointer(Track *t)
Definition: Track.h:233
std::list< std::shared_ptr< Track > > ListOfTracks
Definition: Track.h:64
virtual void Merge(const Track &orig)
Definition: Track.cpp:112
std::enable_if< std::is_pointer< T >::value, T >::type track_cast(Track *track)
Definition: Track.h:801
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1356
virtual void DoSetHeight(int h)
Definition: Track.cpp:212
friend std::enable_if< std::is_pointer< T >::value, T >::type track_cast(Track *track)
Definition: Track.h:801
TrackIterRange StartingWith(const Track *pTrack) const
Definition: Track.h:1060
void SetSolo(bool s)
Definition: Track.cpp:404
std::vector< std::shared_ptr< const NoteTrack > > NoteTrackConstArray
Definition: AudioIO.h:39
int mY
Definition: Track.h:204
std::pair< Track *, Track * > FindSyncLockGroup(Track *pMember) const
Definition: Track.cpp:511
std::shared_ptr< DirManager > mDirManager
Definition: Track.h:356
virtual Holder Duplicate() const =0
int GetHeight() const
Definition: Track.cpp:183
std::weak_ptr< TrackList > mSelf
Definition: Track.h:1558
void UpdatePendingTracks()
Definition: Track.cpp:1262
bool empty() const
Definition: Track.cpp:1072
bool mMute
Definition: Track.h:791
TrackId mId
Definition: Track.h:198
wxString mName
Definition: Track.h:206