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