Audacity 3.2.0
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 "Track.h"
25
26#include <algorithm>
27#include <numeric>
28
29#include <float.h>
30#include <wx/file.h>
31#include <wx/textfile.h>
32#include <wx/log.h>
33
34#include "BasicUI.h"
35#include "Project.h"
36#include "UndoManager.h"
37
39
40#ifdef _MSC_VER
41//Disable truncation warnings
42#pragma warning( disable : 4786 )
43#endif
44
46: vrulerSize(36,0)
47{
48 mSelected = false;
49
50 mIndex = 0;
51
52 mOffset = 0.0;
53
55}
56
58: vrulerSize( orig.vrulerSize )
59{
60 mIndex = 0;
61 mOffset = orig.mOffset;
62}
63
64// Copy all the track properties except the actual contents
65void Track::Init(const Track &orig)
66{
67 mId = orig.mId;
68
69 mName = orig.mName;
70
71 mSelected = orig.mSelected;
72
73 // Deep copy of any group data
75 std::make_unique<ChannelGroupData>(*orig.mpGroupData) : nullptr;
76
77 mChannel = orig.mChannel;
78}
79
80void Track::SetName( const wxString &n )
81{
82 if ( mName != n ) {
83 mName = n;
84 Notify();
85 }
86}
87
89{
90 if (mSelected != s) {
91 mSelected = s;
92 auto pList = mList.lock();
93 if (pList)
94 pList->SelectionEvent( SharedPointer() );
95 }
96}
97
98void Track::EnsureVisible( bool modifyState )
99{
100 auto pList = mList.lock();
101 if (pList)
102 pList->EnsureVisibleEvent( SharedPointer(), modifyState );
103}
104
105void Track::Merge(const Track &orig)
106{
107 mSelected = orig.mSelected;
108}
109
111{
112 // invoke "virtual constructor" to copy track object proper:
113 auto result = Clone();
114
115 AttachedTrackObjects::ForEach([&](auto &attachment){
116 // Copy view state that might be important to undo/redo
117 attachment.CopyTo( *result );
118 });
119
120 return result;
121}
122
124{
125}
126
127
129{
130 wxASSERT(mList.lock() == NULL || this == mNode.first->get());
131 return mNode;
132}
133
135(const std::weak_ptr<TrackList> &list, TrackNodePointer node)
136{
137 // BUG: When using this function to clear an owner, we may need to clear
138 // focused track too. Otherwise focus could remain on an invisible (or deleted) track.
139 mList = list;
140 mNode = node;
141}
142
144{
145 return mIndex;
146}
147
148void Track::SetIndex(int index)
149{
150 mIndex = index;
151}
152
153void Track::SetLinkType(LinkType linkType, bool completeList)
154{
155 auto pList = mList.lock();
156 if (pList && !pList->mPendingUpdates.empty()) {
157 auto orig = pList->FindById( GetId() );
158 if (orig && orig != this) {
159 orig->SetLinkType(linkType);
160 return;
161 }
162 }
163
164 DoSetLinkType(linkType, completeList);
165
166 if (pList) {
167 pList->RecalcPositions(mNode);
168 pList->ResizingEvent(mNode);
169 }
170}
171
173{
174 if (!mpGroupData)
175 // Make on demand
176 mpGroupData = std::make_unique<ChannelGroupData>();
177 return *mpGroupData;
178}
179
181{
182 auto pTrack = this;
183 if (auto pList = GetOwner())
184 if (auto pLeader = *pList->FindLeader(pTrack))
185 pTrack = pLeader;
186 // May make on demand
187 return pTrack->MakeGroupData();
188}
189
191{
192 // May make group data on demand, but consider that logically const
193 return const_cast<Track *>(this)->GetGroupData();
194}
195
196void Track::DoSetLinkType(LinkType linkType, bool completeList)
197{
198 auto oldType = GetLinkType();
199 if (linkType == oldType)
200 // No change
201 return;
202
203 if (oldType == LinkType::None) {
204 // Becoming linked
205
206 // First ensure there is no partner
207 if (auto partner = GetLinkedTrack())
208 partner->mpGroupData.reset();
209 assert(!GetLinkedTrack());
210
211 // Change the link type
212 MakeGroupData().mLinkType = linkType;
213
214 // If this acquired a partner, it loses any old group data
215 if (auto partner = GetLinkedTrack())
216 partner->mpGroupData.reset();
217 }
218 else if (linkType == LinkType::None) {
219 // Becoming unlinked
220 assert(mpGroupData);
221 if (HasLinkedTrack()) {
222 if (auto partner = GetLinkedTrack()) {
223 // Make independent copy of group data in the partner, which should
224 // have had none
225 assert(!partner->mpGroupData);
226 partner->mpGroupData =
227 std::make_unique<ChannelGroupData>(*mpGroupData);
228 partner->mpGroupData->mLinkType = LinkType::None;
229 }
230 }
231 mpGroupData->mLinkType = LinkType::None;
232 }
233 else {
234 // Remaining linked, changing the type
235 assert(mpGroupData);
236 MakeGroupData().mLinkType = linkType;
237 }
238
239 // Assertion checks only in a debug build, does not have side effects!
240 assert(LinkConsistencyCheck(completeList));
241}
242
244{
245 mChannel = c;
246}
247
249{
250 auto pList = mList.lock();
251 if (!pList)
252 return nullptr;
253
254 if (!pList->isNull(mNode)) {
255 if (HasLinkedTrack()) {
256 auto next = pList->getNext( mNode );
257 if ( !pList->isNull( next ) )
258 return next.first->get();
259 }
260
261 if (mNode.first != mNode.second->begin()) {
262 auto prev = pList->getPrev( mNode );
263 if ( !pList->isNull( prev ) ) {
264 auto track = prev.first->get();
265 if (track && track->HasLinkedTrack())
266 return track;
267 }
268 }
269 }
270
271 return nullptr;
272}
273
274bool Track::HasLinkedTrack() const noexcept
275{
276 return mpGroupData && mpGroupData->mLinkType != LinkType::None;
277}
278
279void Track::Notify( int code )
280{
281 auto pList = mList.lock();
282 if (pList)
283 pList->DataEvent( SharedPointer(), code );
284}
285
286void Track::SyncLockAdjust(double oldT1, double newT1)
287{
288 if (newT1 > oldT1) {
289 // Insert space within the track
290
291 if (oldT1 > GetEndTime())
292 return;
293
294 auto tmp = Cut(oldT1, GetEndTime());
295
296 Paste(newT1, tmp.get());
297 }
298 else if (newT1 < oldT1) {
299 // Remove from the track
300 Clear(newT1, oldT1);
301 }
302}
303
305{
306}
307
309 : Track{ orig, std::move(a) }
310{
311}
312
314{
315}
316
318 const PlayableTrack &orig, ProtectedCreationArg &&a
319) : AudioTrack{ orig, std::move(a) }
320{
321}
322
324{
325 DoSetMute(orig.DoGetMute());
326 DoSetSolo(orig.DoGetSolo());
327 AudioTrack::Init( orig );
328}
329
330void PlayableTrack::Merge( const Track &orig )
331{
332 auto pOrig = dynamic_cast<const PlayableTrack *>(&orig);
333 wxASSERT( pOrig );
334 DoSetMute(pOrig->DoGetMute());
335 DoSetSolo(pOrig->DoGetSolo());
336 AudioTrack::Merge( *pOrig );
337}
338
340{
341 if ( DoGetMute() != m ) {
342 DoSetMute(m);
343 Notify();
344 }
345}
346
348{
349 if ( DoGetSolo() != s ) {
350 DoSetSolo(s);
351 Notify();
352 }
353}
354
356{
357 return mMute.load(std::memory_order_relaxed);
358}
359
361{
362 mMute.store(value, std::memory_order_relaxed);
363}
364
366{
367 return mSolo.load(std::memory_order_relaxed);
368}
369
371{
372 mSolo.store(value, std::memory_order_relaxed);
373}
374
375// Serialize, not with tags of its own, but as attributes within a tag.
377{
378 xmlFile.WriteAttr(wxT("mute"), DoGetMute());
379 xmlFile.WriteAttr(wxT("solo"), DoGetSolo());
381}
382
383// Return true iff the attribute is recognized.
384bool PlayableTrack::HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value)
385{
386 long nValue;
387
388 if (attr == "mute" && value.TryGet(nValue)) {
389 DoSetMute(nValue != 0);
390 return true;
391 }
392 else if (attr == "solo" && value.TryGet(nValue)) {
393 DoSetSolo(nValue != 0);
394 return true;
395 }
396
397 return AudioTrack::HandleXMLAttribute(attr, value);
398}
399
400bool Track::Any() const
401 { return true; }
402
404 { return GetSelected(); }
405
406bool Track::IsLeader() const
407{
408 return !GetLinkedTrack() || HasLinkedTrack();
409}
410
412 { return IsSelected() && IsLeader(); }
413
415(const Track *n, Track *dest)
416{
417 if (dest) {
418 dest->SetChannel(n->GetChannel());
419 dest->mpGroupData = n->mpGroupData ?
420 std::make_unique<ChannelGroupData>(*n->mpGroupData) : nullptr;
421 dest->SetName(n->GetName());
422 }
423}
424
425bool Track::LinkConsistencyFix(bool doFix, bool completeList)
426{
427 // Sanity checks for linked tracks; unsetting the linked property
428 // doesn't fix the problem, but it likely leaves us with orphaned
429 // sample blocks instead of much worse problems.
430 bool err = false;
431 if (completeList && HasLinkedTrack()) {
432 if (auto link = GetLinkedTrack()) {
433 // A linked track's partner should never itself be linked
434 if (link->HasLinkedTrack()) {
435 err = true;
436 if (doFix) {
437 wxLogWarning(
438 L"Left track %s had linked right track %s with extra right "
439 "track link.\n Removing extra link from right track.",
440 GetName(), link->GetName());
441 link->SetLinkType(LinkType::None);
442 }
443 }
444
445 // Channels should be left and right
446 if ( !( (GetChannel() == Track::LeftChannel &&
447 link->GetChannel() == Track::RightChannel) ||
449 link->GetChannel() == Track::LeftChannel) ) ) {
450 err = true;
451 if (doFix) {
452 wxLogWarning(
453 L"Track %s and %s had left/right track links out of order. "
454 "Setting tracks to not be linked.",
455 GetName(), link->GetName());
457 }
458 }
459 }
460 else {
461 err = true;
462 if (doFix) {
463 wxLogWarning(
464 L"Track %s had link to NULL track. Setting it to not be linked.",
465 GetName());
467 }
468 }
469 }
470 return ! err;
471}
472
473// TrackList
474//
475// The TrackList sends events whenever certain updates occur to the list it
476// is managing. Any other classes that may be interested in get these updates
477// should use TrackList::Subscribe().
478//
479
480// same value as in the default constructed TrackId:
481long TrackList::sCounter = -1;
482
484 [](AudacityProject &project) { return TrackList::Create( &project ); }
485};
486
488{
489 return project.AttachedObjects::Get< TrackList >( key );
490}
491
493{
494 return Get( const_cast< AudacityProject & >( project ) );
495}
496
498 : mOwner{ pOwner }
499{
500}
501
502// Factory function
503std::shared_ptr<TrackList> TrackList::Create( AudacityProject *pOwner )
504{
505 return std::make_shared<TrackList>( pOwner );
506}
507
508#if 0
510{
511 if (this != &that) {
512 this->Clear();
513 Swap(that);
514 }
515 return *this;
516}
517#endif
518
520{
521 auto SwapLOTs = [](
522 ListOfTracks &a, const std::weak_ptr< TrackList > &aSelf,
523 ListOfTracks &b, const std::weak_ptr< TrackList > &bSelf )
524 {
525 a.swap(b);
526 for (auto it = a.begin(), last = a.end(); it != last; ++it)
527 (*it)->SetOwner(aSelf, {it, &a});
528 for (auto it = b.begin(), last = b.end(); it != last; ++it)
529 (*it)->SetOwner(bSelf, {it, &b});
530 };
531
532 const auto self = shared_from_this();
533 const auto otherSelf = that.shared_from_this();
534 SwapLOTs( *this, self, that, otherSelf );
535 SwapLOTs( this->mPendingUpdates, self, that.mPendingUpdates, otherSelf );
536 mUpdaters.swap(that.mUpdaters);
537}
538
540{
541 Clear(false);
542}
543
544wxString TrackList::MakeUniqueTrackName(const wxString& baseTrackName) const
545{
546 int n = 1;
547 while(true)
548 {
549 auto name = wxString::Format("%s %d", baseTrackName, n++);
550
551 bool found {false};
552 for(const auto track : Any())
553 {
554 if(track->GetName() == name)
555 {
556 found = true;
557 break;
558 }
559 }
560 if(!found)
561 return name;
562 }
563}
564
566{
567 if ( isNull( node ) )
568 return;
569
570 Track *t;
571 int i = 0;
572
573 auto prev = getPrev( node );
574 if ( !isNull( prev ) ) {
575 t = prev.first->get();
576 i = t->GetIndex() + 1;
577 }
578
579 const auto theEnd = end();
580 for (auto n = Find( node.first->get() ); n != theEnd; ++n) {
581 t = *n;
582 t->SetIndex(i++);
583 }
584
586}
587
589{
590 BasicUI::CallAfter( [wThis = weak_from_this(), event = std::move(event)]{
591 if (auto pThis = wThis.lock())
592 pThis->Publish(event);
593 } );
594}
595
596void TrackList::SelectionEvent( const std::shared_ptr<Track> &pTrack )
597{
599}
600
601void TrackList::DataEvent( const std::shared_ptr<Track> &pTrack, int code )
602{
603 QueueEvent({
604 TrackListEvent::TRACK_DATA_CHANGE, pTrack, code });
605}
606
608 const std::shared_ptr<Track> &pTrack, bool modifyState )
609{
611 pTrack, static_cast<int>(modifyState) });
612}
613
615{
616 QueueEvent({ TrackListEvent::PERMUTED, *node.first });
617}
618
619void TrackList::DeletionEvent(std::weak_ptr<Track> node, bool duringReplace)
620{
622 { TrackListEvent::DELETION, std::move(node), duringReplace ? 1 : 0 });
623}
624
626{
627 QueueEvent({ TrackListEvent::ADDITION, *node.first });
628}
629
631{
632 QueueEvent({ TrackListEvent::RESIZING, *node.first });
633}
634
637{
638 auto it = const_cast<TrackList*>(this)->getEnd();
639 return {
640 { it, it, it, &Track::Any },
641 { it, it, it, &Track::Any }
642 };
643}
644
647{
648 auto iter = Find(pTrack);
649 while( *iter && ! ( *iter )->IsLeader() )
650 --iter;
651 return iter.Filter( &Track::IsLeader );
652}
653
655{
656 if (!track.HasLinkedTrack())
657 return false;
658 auto pOwner = track.GetOwner();
659 if (!pOwner)
660 return false;
661 auto pPartner = pOwner->GetNext(&track, false);
662 if (!pPartner)
663 return false;
664
665 // Swap channels, avoiding copying of GroupData
666 auto pData = move(track.mpGroupData);
667 assert(pData);
668 pOwner->MoveUp(pPartner);
669 pPartner->mpGroupData = move(pData);
670 pPartner->SetChannel(Track::LeftChannel);
672 return true;
673}
674
675void TrackList::Permute(const std::vector<TrackNodePointer> &permutation)
676{
677 for (const auto iter : permutation) {
678 ListOfTracks::value_type track = *iter.first;
679 erase(iter.first);
680 Track *pTrack = track.get();
681 pTrack->SetOwner(shared_from_this(),
682 { insert(ListOfTracks::end(), track), this });
683 }
684 auto n = getBegin();
687}
688
690{
691 // Linear search. Tracks in a project are usually very few.
692 // Search only the non-pending tracks.
693 auto it = std::find_if( ListOfTracks::begin(), ListOfTracks::end(),
694 [=](const ListOfTracks::value_type &ptr){ return ptr->GetId() == id; } );
695 if (it == ListOfTracks::end())
696 return {};
697 return it->get();
698}
699
700Track *TrackList::DoAddToHead(const std::shared_ptr<Track> &t)
701{
702 Track *pTrack = t.get();
703 push_front(ListOfTracks::value_type(t));
704 auto n = getBegin();
705 pTrack->SetOwner(shared_from_this(), n);
706 pTrack->SetId( TrackId{ ++sCounter } );
708 AdditionEvent(n);
709 return front().get();
710}
711
712Track *TrackList::DoAdd(const std::shared_ptr<Track> &t)
713{
714 push_back(t);
715
716 auto n = getPrev( getEnd() );
717
718 t->SetOwner(shared_from_this(), n);
719 t->SetId( TrackId{ ++sCounter } );
721 AdditionEvent(n);
722 return back().get();
723}
724
725auto TrackList::Replace(Track * t, const ListOfTracks::value_type &with) ->
726 ListOfTracks::value_type
727{
728 ListOfTracks::value_type holder;
729 if (t && with) {
730 auto node = t->GetNode();
731 t->SetOwner({}, {});
732
733 holder = *node.first;
734
735 Track *pTrack = with.get();
736 *node.first = with;
737 pTrack->SetOwner(shared_from_this(), node);
738 pTrack->SetId( t->GetId() );
739 RecalcPositions(node);
740
741 DeletionEvent(t->shared_from_this(), true);
742 AdditionEvent(node);
743 }
744 return holder;
745}
746
748{
749 auto list = track.mList.lock();
750 if (list.get() == this)
751 {
752 auto channels = TrackList::Channels(&track);
753 for (auto c : channels)
754 {
755 c->SetLinkType(Track::LinkType::None);
756 c->SetChannel(Track::ChannelType::MonoChannel);
757 }
758 }
759 else
761}
762
763bool TrackList::MakeMultiChannelTrack(Track& track, int nChannels, bool aligned)
764{
765 if (nChannels != 2)
766 return false;
767
768 auto list = track.mList.lock();
769 if (list.get() == this)
770 {
771 if (*list->FindLeader(&track) != &track)
772 return false;
773
774 auto first = list->Find(&track);
775 auto canLink = [&]() -> bool {
776 int count = nChannels;
777 for (auto it = first, end = TrackList::end(); it != end && count; ++it)
778 {
779 if ((*it)->HasLinkedTrack())
780 return false;
781 --count;
782 }
783 return count == 0;
784 }();
785
786 if (!canLink)
787 return false;
788
789 (*first)->SetChannel(Track::LeftChannel);
790 auto second = std::next(first);
791 (*second)->SetChannel(Track::RightChannel);
792 (*first)->SetLinkType(aligned ? Track::LinkType::Aligned : Track::LinkType::Group);
793 }
794 else
796 return true;
797}
798
800{
801 auto result = getEnd();
802 if (t) {
803 auto node = t->GetNode();
804 t->SetOwner({}, {});
805
806 if ( !isNull( node ) ) {
807 ListOfTracks::value_type holder = *node.first;
808
809 result = getNext( node );
810 erase(node.first);
811 if ( !isNull( result ) )
812 RecalcPositions(result);
813
814 DeletionEvent(t->shared_from_this(), false);
815 }
816 }
817 return result;
818}
819
820void TrackList::Clear(bool sendEvent)
821{
822 // Null out the back-pointers to this in tracks, in case there
823 // are outstanding shared_ptrs to those tracks, making them outlive
824 // the temporary ListOfTracks below.
825 for ( auto pTrack: *this )
826 {
827 pTrack->SetOwner({}, {});
828
829 if (sendEvent)
830 DeletionEvent(pTrack->shared_from_this(), false);
831 }
832
833 for ( auto pTrack: mPendingUpdates )
834 {
835 pTrack->SetOwner({}, {});
836
837 if (sendEvent)
838 DeletionEvent(pTrack, false);
839 }
840
841 ListOfTracks tempList;
842 tempList.swap( *this );
843
844 ListOfTracks updating;
845 updating.swap( mPendingUpdates );
846
847 mUpdaters.clear();
848}
849
851Track *TrackList::GetNext(Track * t, bool linked) const
852{
853 if (t) {
854 auto node = t->GetNode();
855 if ( !isNull( node ) ) {
856 if ( linked && t->HasLinkedTrack() )
857 node = getNext( node );
858
859 if ( !isNull( node ) )
860 node = getNext( node );
861
862 if ( !isNull( node ) )
863 return node.first->get();
864 }
865 }
866
867 return nullptr;
868}
869
870Track *TrackList::GetPrev(Track * t, bool linked) const
871{
872 if (t) {
873 TrackNodePointer prev;
874 auto node = t->GetNode();
875 if ( !isNull( node ) ) {
876 // linked is true and input track second in team?
877 if (linked) {
878 prev = getPrev( node );
879 if( !isNull( prev ) &&
880 !t->HasLinkedTrack() && t->GetLinkedTrack() )
881 // Make it the first
882 node = prev;
883 }
884
885 prev = getPrev( node );
886 if ( !isNull( prev ) ) {
887 // Back up once
888 node = prev;
889
890 // Back up twice sometimes when linked is true
891 if (linked) {
892 prev = getPrev( node );
893 if( !isNull( prev ) &&
894 !(*node.first)->HasLinkedTrack() && (*node.first)->GetLinkedTrack() )
895 node = prev;
896 }
897
898 return node.first->get();
899 }
900 }
901 }
902
903 return nullptr;
904}
905
907{
908 return GetPrev(t, true) != NULL;
909}
910
912{
913 return GetNext(t, true) != NULL;
914}
915
916// This is used when you want to swap the channel group starting
917// at s1 with that starting at s2.
918// The complication is that the tracks are stored in a single
919// linked list.
921{
922 // if a null pointer is passed in, we want to know about it
923 wxASSERT(!isNull(s1));
924 wxASSERT(!isNull(s2));
925
926 // Deal with first track in each team
927 s1 = ( * FindLeader( s1.first->get() ) )->GetNode();
928 s2 = ( * FindLeader( s2.first->get() ) )->GetNode();
929
930 // Safety check...
931 if (s1 == s2)
932 return;
933
934 // Be sure s1 is the earlier iterator
935 if ((*s1.first)->GetIndex() >= (*s2.first)->GetIndex())
936 std::swap(s1, s2);
937
938 // For saving the removed tracks
939 using Saved = std::vector< ListOfTracks::value_type >;
940 Saved saved1, saved2;
941
942 auto doSave = [&] ( Saved &saved, TrackNodePointer &s ) {
943 size_t nn = Channels( s.first->get() ).size();
944 saved.resize( nn );
945 // Save them in backwards order
946 while( nn-- )
947 saved[nn] = *s.first, s.first = erase(s.first);
948 };
949
950 doSave( saved1, s1 );
951 // The two ranges are assumed to be disjoint but might abut
952 const bool same = (s1 == s2);
953 doSave( saved2, s2 );
954 if (same)
955 // Careful, we invalidated s1 in the second doSave!
956 s1 = s2;
957
958 // Reinsert them
959 auto doInsert = [&] ( Saved &saved, TrackNodePointer &s ) {
960 Track *pTrack;
961 for (auto & pointer : saved)
962 pTrack = pointer.get(),
963 // Insert before s, and reassign s to point at the new node before
964 // old s; which is why we saved pointers in backwards order
965 pTrack->SetOwner(shared_from_this(),
966 s = { insert(s.first, pointer), this } );
967 };
968 // This does not invalidate s2 even when it equals s1:
969 doInsert( saved2, s1 );
970 // Even if s2 was same as s1, this correctly inserts the saved1 range
971 // after the saved2 range, when done after:
972 doInsert( saved1, s2 );
973
974 // Now correct the Index in the tracks, and other things
975 RecalcPositions(s1);
977}
978
980{
981 if (t) {
982 Track *p = GetPrev(t, true);
983 if (p) {
984 SwapNodes(p->GetNode(), t->GetNode());
985 return true;
986 }
987 }
988
989 return false;
990}
991
993{
994 if (t) {
995 Track *n = GetNext(t, true);
996 if (n) {
997 SwapNodes(t->GetNode(), n->GetNode());
998 return true;
999 }
1000 }
1001
1002 return false;
1003}
1004
1005bool TrackList::Contains(const Track * t) const
1006{
1007 return make_iterator_range( *this ).contains( t );
1008}
1009
1011{
1012 return begin() == end();
1013}
1014
1015size_t TrackList::size() const
1016{
1017 int cnt = 0;
1018
1019 if (!empty())
1020 cnt = getPrev( getEnd() ).first->get()->GetIndex() + 1;
1021
1022 return cnt;
1023}
1024
1025namespace {
1026 // Abstract the common pattern of the following three member functions
1027 inline double Accumulate
1028 (const TrackList &list,
1029 double (Track::*memfn)() const,
1030 double ident,
1031 const double &(*combine)(const double&, const double&))
1032 {
1033 // Default the answer to zero for empty list
1034 if (list.empty()) {
1035 return 0.0;
1036 }
1037
1038 // Otherwise accumulate minimum or maximum of track values
1039 return list.Any().accumulate(ident, combine, memfn);
1040 }
1041}
1042
1044{
1045 return Accumulate(*this, &Track::GetOffset, DBL_MAX, std::min);
1046}
1047
1049{
1050 return Accumulate(*this, &Track::GetStartTime, DBL_MAX, std::min);
1051}
1052
1054{
1055 return Accumulate(*this, &Track::GetEndTime, -DBL_MAX, std::max);
1056}
1057
1058std::shared_ptr<Track>
1060{
1061 std::shared_ptr<Track> pTrack;
1062 if (src) {
1063 pTrack = src->Clone(); // not duplicate
1064 // Share the satellites with the original, though they do not point back
1065 // to the pending track
1066 ((AttachedTrackObjects&)*pTrack) = *src; // shallow copy
1067 }
1068
1069 if (pTrack) {
1070 mUpdaters.push_back( updater );
1071 mPendingUpdates.push_back( pTrack );
1072 auto n = mPendingUpdates.end();
1073 --n;
1074 pTrack->SetOwner(shared_from_this(), {n, &mPendingUpdates});
1075 }
1076
1077 return pTrack;
1078}
1079
1080void TrackList::RegisterPendingNewTrack( const std::shared_ptr<Track> &pTrack )
1081{
1082 Add<Track>( pTrack );
1083 pTrack->SetId( TrackId{} );
1084}
1085
1087{
1088 auto pUpdater = mUpdaters.begin();
1089 for (const auto &pendingTrack : mPendingUpdates) {
1090 // Copy just a part of the track state, according to the update
1091 // function
1092 const auto &updater = *pUpdater;
1093 auto src = FindById( pendingTrack->GetId() );
1094 if (pendingTrack && src) {
1095 if (updater)
1096 updater( *pendingTrack, *src );
1097 pendingTrack->DoSetLinkType(src->GetLinkType());
1098 }
1099 ++pUpdater;
1100 }
1101}
1102
1105{
1106 for (const auto &pTrack: mPendingUpdates)
1107 pTrack->SetOwner( {}, {} );
1108 mPendingUpdates.clear();
1109 mUpdaters.clear();
1110
1111 if (pAdded)
1112 pAdded->clear();
1113
1114 // To find the first node that remains after the first deleted one
1115 TrackNodePointer node;
1116 bool foundNode = false;
1117
1118 for (auto it = ListOfTracks::begin(), stop = ListOfTracks::end();
1119 it != stop;) {
1120 if (it->get()->GetId() == TrackId{}) {
1121 do {
1122 if (pAdded)
1123 pAdded->push_back( *it );
1124 (*it)->SetOwner( {}, {} );
1125 DeletionEvent(*it, false);
1126 it = erase( it );
1127 }
1128 while (it != stop && it->get()->GetId() == TrackId{});
1129
1130 if (!foundNode && it != stop) {
1131 node = (*it)->GetNode();
1132 foundNode = true;
1133 }
1134 }
1135 else
1136 ++it;
1137 }
1138
1139 if (!empty()) {
1141 }
1142}
1143
1146{
1147 bool result = false;
1148
1149 ListOfTracks additions;
1150 ListOfTracks updates;
1151 {
1152 // Always clear, even if one of the update functions throws
1153 auto cleanup = finally( [&] { ClearPendingTracks( &additions ); } );
1155 updates.swap( mPendingUpdates );
1156 }
1157
1158 // Remaining steps must be No-fail-guarantee so that this function
1159 // gives Strong-guarantee
1160
1161 std::vector< std::shared_ptr<Track> > reinstated;
1162
1163 for (auto &pendingTrack : updates) {
1164 if (pendingTrack) {
1165 pendingTrack->AttachedTrackObjects::ForEach([&](auto &attachment){
1166 attachment.Reparent( pendingTrack );
1167 });
1168 auto src = FindById( pendingTrack->GetId() );
1169 if (src)
1170 this->Replace(src, pendingTrack), result = true;
1171 else
1172 // Perhaps a track marked for pending changes got deleted by
1173 // some other action. Recreate it so we don't lose the
1174 // accumulated changes.
1175 reinstated.push_back(pendingTrack);
1176 }
1177 }
1178
1179 // If there are tracks to reinstate, append them to the list.
1180 for (auto &pendingTrack : reinstated)
1181 if (pendingTrack)
1182 this->Add( pendingTrack ), result = true;
1183
1184 // Put the pending added tracks back into the list, preserving their
1185 // positions.
1186 bool inserted = false;
1187 ListOfTracks::iterator first;
1188 for (auto &pendingTrack : additions) {
1189 if (pendingTrack) {
1190 auto iter = ListOfTracks::begin();
1191 std::advance( iter, pendingTrack->GetIndex() );
1192 iter = ListOfTracks::insert( iter, pendingTrack );
1193 pendingTrack->SetOwner( shared_from_this(), {iter, this} );
1194 pendingTrack->SetId( TrackId{ ++sCounter } );
1195 if (!inserted) {
1196 first = iter;
1197 inserted = true;
1198 }
1199 }
1200 }
1201 if (inserted) {
1202 TrackNodePointer node{first, this};
1203 RecalcPositions(node);
1204 AdditionEvent(node);
1205 result = true;
1206 }
1207
1208 return result;
1209}
1210
1212{
1213 // Linear search. Tracks in a project are usually very few.
1214 auto pList = mList.lock();
1215 if (pList) {
1216 const auto id = GetId();
1217 const auto end = pList->mPendingUpdates.end();
1218 auto it = std::find_if(
1219 pList->mPendingUpdates.begin(), end,
1220 [=](const ListOfTracks::value_type &ptr){ return ptr->GetId() == id; } );
1221 if (it != end)
1222 return *it;
1223 }
1224 return SharedPointer();
1225}
1226
1227std::shared_ptr<const Track> Track::SubstitutePendingChangedTrack() const
1228{
1229 return const_cast<Track*>(this)->SubstitutePendingChangedTrack();
1230}
1231
1232std::shared_ptr<const Track> Track::SubstituteOriginalTrack() const
1233{
1234 auto pList = mList.lock();
1235 if (pList) {
1236 const auto id = GetId();
1237 const auto pred = [=]( const ListOfTracks::value_type &ptr ) {
1238 return ptr->GetId() == id; };
1239 const auto end = pList->mPendingUpdates.end();
1240 const auto it = std::find_if( pList->mPendingUpdates.begin(), end, pred );
1241 if (it != end) {
1242 const auto &list2 = (const ListOfTracks &) *pList;
1243 const auto end2 = list2.end();
1244 const auto it2 = std::find_if( list2.begin(), end2, pred );
1245 if ( it2 != end2 )
1246 return *it2;
1247 }
1248 }
1249 return SharedPointer();
1250}
1251
1253{
1254 static Track::TypeInfo info{
1255 { "generic", "generic", XO("Generic Track") }, false };
1256 return info;
1257}
1258
1260{
1261 return true;
1262}
1263
1265{
1266 return {};
1267}
1268
1270{
1271 return {};
1272}
1273
1274// Serialize, not with tags of its own, but as attributes within a tag.
1276 XMLWriter &xmlFile, bool includeNameAndSelected) const
1277{
1278 if (includeNameAndSelected) {
1279 xmlFile.WriteAttr(wxT("name"), GetName());
1280 xmlFile.WriteAttr(wxT("isSelected"), this->GetSelected());
1281 }
1282 AttachedTrackObjects::ForEach([&](auto &attachment){
1283 attachment.WriteXMLAttributes( xmlFile );
1284 });
1285}
1286
1287// Return true iff the attribute is recognized.
1289 const std::string_view& attr, const XMLAttributeValueView& valueView)
1290{
1291 long nValue = -1;
1292
1293 bool handled = false;
1294 AttachedTrackObjects::ForEach([&](auto &attachment){
1295 handled = handled || attachment.HandleXMLAttribute( attr, valueView );
1296 });
1297 if (handled)
1298 ;
1299 else if (attr == "name") {
1300 SetName(valueView.ToWString());
1301 return true;
1302 }
1303 else if (attr == "isSelected" && valueView.TryGet(nValue)) {
1304 this->SetSelected(nValue != 0);
1305 return true;
1306 }
1307 return false;
1308}
1309
1311{
1312 auto pList = mList.lock();
1313 if (pList) {
1314 pList->RecalcPositions(mNode);
1315 pList->ResizingEvent(mNode);
1316 }
1317}
1318
1320{
1321 static Track::TypeInfo info{
1322 { "audio", "audio", XO("Audio Track") },
1323 false, &Track::ClassTypeInfo() };
1324 return info;
1325}
1326
1328{
1329 static Track::TypeInfo info{
1330 { "playable", "playable", XO("Playable Track") },
1331 false, &AudioTrack::ClassTypeInfo() };
1332 return info;
1333}
1334
1336
1338{
1339 if ( !mPendingUpdates.empty() )
1340 return true;
1341 if (end() != std::find_if(begin(), end(), [](const Track *t){
1342 return t->GetId() == TrackId{};
1343 }))
1344 return true;
1345 return false;
1346}
1347
1349{
1350 return mpGroupData ? mpGroupData->mLinkType : LinkType::None;
1351}
1352
1354{
1355 if (auto owner = GetOwner())
1356 {
1357 auto leader = *owner->FindLeader(this);
1358 return leader != this && leader->GetLinkType() == Track::LinkType::Aligned;
1359 }
1360 return false;
1361}
1362
1363// Undo/redo handling of selection changes
1364namespace {
1367 : mpTracks{ TrackList::Create(nullptr) }
1368 {
1369 for (auto pTrack : TrackList::Get(project)) {
1370 if ( pTrack->GetId() == TrackId{} )
1371 // Don't copy a pending added track
1372 continue;
1373 mpTracks->Add(pTrack->Duplicate());
1374 }
1375 }
1376 void RestoreUndoRedoState(AudacityProject &project) override {
1377 auto &dstTracks = TrackList::Get(project);
1378 dstTracks.Clear();
1379 for (auto pTrack : mpTracks->Any())
1380 dstTracks.Add(pTrack->Duplicate());
1381 }
1382 bool CanUndoOrRedo(const AudacityProject &project) override {
1383 return !TrackList::Get(project).HasPendingTracks();
1384 }
1385 const std::shared_ptr<TrackList> mpTracks;
1386};
1387
1389 [](AudacityProject &project) -> std::shared_ptr<UndoStateExtension> {
1390 return std::make_shared<TrackListRestorer>(project);
1391 }
1392};
1393}
1394
1396{
1397 auto &exts = state.state.extensions;
1398 auto end = exts.end(),
1399 iter = std::find_if(exts.begin(), end, [](auto &pExt){
1400 return dynamic_cast<TrackListRestorer*>(pExt.get());
1401 });
1402 if (iter != end)
1403 return static_cast<TrackListRestorer*>(iter->get())->mpTracks.get();
1404 return nullptr;
1405}
wxT("CloseDown"))
Toolkit-neutral facade for basic user interface services.
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
static const auto exts
Definition: ImportAUP.cpp:56
MessageBoxException for violation of preconditions or assertions.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:448
static CommandHandlerObject & ident(AudacityProject &project)
static const AudacityProject::AttachedObjects::RegisteredFactory key
Definition: Track.cpp:483
declares abstract base class Track, TrackList, and iterators over TrackList
std::pair< ListOfTracks::iterator, ListOfTracks * > TrackNodePointer
Pairs a std::list iterator and a pointer to a list, for comparison purposes.
Definition: Track.h:49
std::list< std::shared_ptr< Track > > ListOfTracks
Definition: Track.h:43
int id
static CustomUpdaterValue updater
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Track subclass holding data representing sound (as notes, or samples, or ...)
Definition: Track.h:898
bool HandleXMLAttribute(const std::string_view &, const XMLAttributeValueView &)
Definition: Track.h:909
AudioTrack()
Definition: Track.cpp:304
static const TypeInfo & ClassTypeInfo()
Definition: Track.cpp:1319
void WriteXMLAttributes(XMLWriter &WXUNUSED(xmlFile)) const
Definition: Track.h:906
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
Utility to register hooks into a host class that attach client data.
Definition: ClientData.h:220
void ForEach(const Function &function)
Invoke function on each ClientData object that has been created in this.
Definition: ClientData.h:380
AudioTrack subclass that can also be audibly replayed by the program.
Definition: Track.h:917
bool HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value)
Definition: Track.cpp:384
std::atomic< bool > mMute
Atomic because it may be read by worker threads in playback.
Definition: Track.h:948
void DoSetSolo(bool value)
Definition: Track.cpp:370
bool DoGetMute() const
Definition: Track.cpp:355
void Init(const PlayableTrack &init)
Definition: Track.cpp:323
void Merge(const Track &init) override
Definition: Track.cpp:330
void DoSetMute(bool value)
Definition: Track.cpp:360
static const TypeInfo & ClassTypeInfo()
Definition: Track.cpp:1327
void WriteXMLAttributes(XMLWriter &xmlFile) const
Definition: Track.cpp:376
void SetSolo(bool s)
Definition: Track.cpp:347
bool DoGetSolo() const
Definition: Track.cpp:365
std::atomic< bool > mSolo
Atomic because it may be read by worker threads in playback.
Definition: Track.h:950
void SetMute(bool m)
Definition: Track.cpp:339
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:226
virtual void Merge(const Track &orig)
Definition: Track.cpp:105
bool GetSelected() const
Definition: Track.h:470
void EnsureVisible(bool modifyState=false)
Definition: Track.cpp:98
Track()
Definition: Track.cpp:45
virtual void SetSelected(bool s)
Definition: Track.cpp:88
virtual void Paste(double WXUNUSED(t), const Track *WXUNUSED(src))=0
virtual Holder Cut(double WXUNUSED(t0), double WXUNUSED(t1))=0
bool IsSelected() const
Definition: Track.cpp:403
virtual void Clear(double WXUNUSED(t0), double WXUNUSED(t1))=0
void SetChannel(ChannelType c) noexcept
Definition: Track.cpp:243
virtual ~Track()
Definition: Track.cpp:123
static void FinishCopy(const Track *n, Track *dest)
Definition: Track.cpp:415
virtual bool SupportsBasicEditing() const
Whether this track type implements cut-copy-paste; by default, true.
Definition: Track.cpp:1259
ChannelType
Definition: Track.h:282
@ LeftChannel
Definition: Track.h:283
@ RightChannel
Definition: Track.h:284
@ MonoChannel
Definition: Track.h:285
virtual double GetStartTime() const =0
static const TypeInfo & ClassTypeInfo()
Definition: Track.cpp:1252
virtual Holder Clone() const =0
bool IsSelectedLeader() const
Definition: Track.cpp:411
virtual void SyncLockAdjust(double oldT1, double newT1)
Definition: Track.cpp:286
double mOffset
Definition: Track.h:448
TrackNodePointer GetNode() const
Retrieve mNode with debug checks.
Definition: Track.cpp:128
virtual bool LinkConsistencyFix(bool doFix=true, bool completeList=true)
Check consistency of channel groups, and maybe fix it.
Definition: Track.cpp:425
ChannelGroupData & GetGroupData()
Definition: Track.cpp:180
std::shared_ptr< Track > SubstitutePendingChangedTrack()
Definition: Track.cpp:1211
std::shared_ptr< const Track > SubstituteOriginalTrack() const
Definition: Track.cpp:1232
std::shared_ptr< TrackList > GetOwner() const
Definition: Track.h:410
bool mSelected
Definition: Track.h:274
void SetLinkType(LinkType linkType, bool completeList=true)
Definition: Track.cpp:153
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:298
bool HasLinkedTrack() const noexcept
Returns true for leaders of multichannel groups.
Definition: Track.cpp:274
TrackId GetId() const
Definition: Track.h:288
virtual Holder Duplicate() const
Definition: Track.cpp:110
std::shared_ptr< Track > Holder
Definition: Track.h:369
wxString mName
Definition: Track.h:271
void SetOwner(const std::weak_ptr< TrackList > &list, TrackNodePointer node)
Update mNode when Track is added to TrackList, or removed from it.
Definition: Track.cpp:135
void Notify(int code=-1)
Definition: Track.cpp:279
void SetId(TrackId id)
Definition: Track.h:290
bool LinkConsistencyCheck(bool completeList)
Do the non-mutating part of consistency fix only and return status.
Definition: Track.h:405
bool HandleCommonXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView)
Definition: Track.cpp:1288
void SetIndex(int index)
Definition: Track.cpp:148
wxString GetName() const
Definition: Track.h:467
void Init(const Track &orig)
Definition: Track.cpp:65
virtual ConstIntervals GetIntervals() const
Report times on the track where important intervals begin and end, for UI to snap to.
Definition: Track.cpp:1264
virtual ChannelType GetChannel() const
Definition: Track.h:480
void WriteCommonXMLAttributes(XMLWriter &xmlFile, bool includeNameAndSelected=true) const
Definition: Track.cpp:1275
void AdjustPositions()
Definition: Track.cpp:1310
std::weak_ptr< TrackList > mList
Definition: Track.h:266
virtual double GetOffset() const =0
TrackNodePointer mNode
Holds iterator to self, so that TrackList::Find can be constant-time.
Definition: Track.h:269
ChannelType mChannel
Definition: Track.h:447
virtual double GetEndTime() const =0
std::vector< Interval > Intervals
Definition: Track.h:336
bool IsLeader() const
Definition: Track.cpp:406
Track * GetLinkedTrack() const
Definition: Track.cpp:248
ChannelGroupData & MakeGroupData()
Definition: Track.cpp:172
LinkType
For two tracks describes the type of the linkage.
Definition: Track.h:237
LinkType GetLinkType() const noexcept
Definition: Track.cpp:1348
int mIndex
0-based position of this track in its TrackList
Definition: Track.h:270
bool IsAlignedWithLeader() const
Returns true if the leader track has link type LinkType::Aligned.
Definition: Track.cpp:1353
void DoSetLinkType(LinkType linkType, bool completeList=true)
Definition: Track.cpp:196
std::unique_ptr< ChannelGroupData > mpGroupData
Definition: Track.h:263
std::vector< ConstInterval > ConstIntervals
Definition: Track.h:338
bool Any() const
Definition: Track.cpp:400
void SetName(const wxString &n)
Definition: Track.cpp:80
TrackId mId
Identifies the track only in-session, not persistently.
Definition: Track.h:261
int GetIndex() const
Definition: Track.cpp:143
An in-session identifier of track objects across undo states. It does not persist between sessions.
Definition: Track.h:152
Iterator over only members of a TrackList of the specified subtype, optionally filtered by a predicat...
Definition: Track.h:1007
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1339
void PermutationEvent(TrackNodePointer node)
Definition: Track.cpp:614
bool MoveUp(Track *t)
Definition: Track.cpp:979
void RegisterPendingNewTrack(const std::shared_ptr< Track > &pTrack)
Definition: Track.cpp:1080
bool empty() const
Definition: Track.cpp:1010
bool CanMoveUp(Track *t) const
Definition: Track.cpp:906
static long sCounter
Definition: Track.h:1726
bool MakeMultiChannelTrack(Track &first, int nChannels, bool aligned)
Converts channels to a multichannel track.
Definition: Track.cpp:763
static std::shared_ptr< TrackList > Create(AudacityProject *pOwner)
Definition: Track.cpp:503
double GetEndTime() const
Definition: Track.cpp:1053
bool CanMoveDown(Track *t) const
Definition: Track.cpp:911
std::vector< Updater > mUpdaters
This is in correspondence with mPendingUpdates.
Definition: Track.h:1780
static TrackList * FindUndoTracks(const UndoStackElem &state)
Definition: Track.cpp:1395
double GetMinOffset() const
Definition: Track.cpp:1043
void ResizingEvent(TrackNodePointer node)
Definition: Track.cpp:630
TrackNodePointer getBegin() const
Definition: Track.h:1684
ListOfTracks::value_type Replace(Track *t, const ListOfTracks::value_type &with)
Definition: Track.cpp:725
iterator end()
Definition: Track.h:1394
wxString MakeUniqueTrackName(const wxString &baseTrackName) const
Returns string that contains baseTrackName, but is guaranteed to be unique among other tracks in that...
Definition: Track.cpp:544
TrackList & operator=(const TrackList &)=delete
TrackKind * Add(const std::shared_ptr< TrackKind > &t)
Definition: Track.h:1567
double GetStartTime() const
Definition: Track.cpp:1048
bool MoveDown(Track *t)
Definition: Track.cpp:992
bool isNull(TrackNodePointer p) const
Definition: Track.h:1678
TrackNodePointer getNext(TrackNodePointer p) const
Move an iterator to the next node, if any; else stay at end.
Definition: Track.h:1689
size_t size() const
Definition: Track.cpp:1015
void Swap(TrackList &that)
Definition: Track.cpp:519
void AdditionEvent(TrackNodePointer node)
Definition: Track.cpp:625
void RecalcPositions(TrackNodePointer node)
Definition: Track.cpp:565
auto Find(Track *pTrack) -> TrackIter< TrackType >
Turn a pointer into a TrackIter (constant time); get end iterator if this does not own the track.
Definition: Track.h:1402
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1440
TrackNodePointer getEnd() const
Definition: Track.h:1681
bool ApplyPendingTracks()
Definition: Track.cpp:1145
std::function< void(Track &dest, const Track &src) > Updater
Definition: Track.h:1729
bool Contains(const Track *t) const
Mainly a test function. Uses a linear search, so could be slow.
Definition: Track.cpp:1005
TrackIterRange< Track > EmptyRange() const
Definition: Track.cpp:635
void EnsureVisibleEvent(const std::shared_ptr< Track > &pTrack, bool modifyState)
Definition: Track.cpp:607
void DataEvent(const std::shared_ptr< Track > &pTrack, int code)
Definition: Track.cpp:601
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:487
Track * DoAdd(const std::shared_ptr< Track > &t)
Definition: Track.cpp:712
TrackNodePointer Remove(Track *t)
Remove the Track and return an iterator to what followed it.
Definition: Track.cpp:799
static bool SwapChannels(Track &track)
If the given track is one of a pair of channels, swap them.
Definition: Track.cpp:654
iterator begin()
Definition: Track.h:1393
Track * GetNext(Track *t, bool linked=false) const
Return a track in the list that comes after Track t.
Definition: Track.cpp:851
Track * GetPrev(Track *t, bool linked=false) const
Definition: Track.cpp:870
std::shared_ptr< Track > RegisterPendingChangedTrack(Updater updater, Track *src)
Definition: Track.cpp:1059
void ClearPendingTracks(ListOfTracks *pAdded=nullptr)
Definition: Track.cpp:1104
void UpdatePendingTracks()
Definition: Track.cpp:1086
Track * DoAddToHead(const std::shared_ptr< Track > &t)
Definition: Track.cpp:700
Track * FindById(TrackId id)
Definition: Track.cpp:689
void Permute(const std::vector< TrackNodePointer > &permutation)
For use in sorting: assume each iterator points into this list, no duplications.
Definition: Track.cpp:675
TrackIter< Track > FindLeader(Track *pTrack)
Definition: Track.cpp:645
void QueueEvent(TrackListEvent event)
Definition: Track.cpp:588
void SwapNodes(TrackNodePointer s1, TrackNodePointer s2)
Definition: Track.cpp:920
TrackList(const TrackList &that)=delete
void UnlinkChannels(Track &track)
Removes linkage if track belongs to a group.
Definition: Track.cpp:747
bool HasPendingTracks() const
Definition: Track.cpp:1337
virtual ~TrackList()
Definition: Track.cpp:539
void Clear(bool sendEvent=true)
Make the list empty.
Definition: Track.cpp:820
void DeletionEvent(std::weak_ptr< Track > node, bool duringReplace)
Definition: Track.cpp:619
void SelectionEvent(const std::shared_ptr< Track > &pTrack)
Definition: Track.cpp:596
TrackNodePointer getPrev(TrackNodePointer p) const
Move an iterator to the previous node, if any; else wrap to end.
Definition: Track.h:1699
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1544
ListOfTracks mPendingUpdates
Shadow tracks holding append-recording in progress; need to put them into a list so that GetLink() wo...
Definition: Track.h:1778
Base class for extra information attached to undo/redo states.
Definition: UndoManager.h:83
A view into an attribute value. The class does not take the ownership of the data.
wxString ToWString() const
Convert the view value to wxString.
bool TryGet(bool &value) const noexcept
Try to get a boolean value from the view.
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:25
void WriteAttr(const wxString &name, const Identifier &value)
Definition: XMLWriter.h:36
PROJECT_FILE_IO_API wxString Find(const FilePath &path)
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:208
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
std::optional< LogWindowUpdater > pUpdater
Definition: LogWindow.cpp:53
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:752
double Accumulate(const TrackList &list, double(Track::*memfn)() const, double ident, const double &(*combine)(const double &, const double &))
Definition: Track.cpp:1028
UndoRedoExtensionRegistry::Entry sEntry
Definition: Track.cpp:1388
STL namespace.
LinkType mLinkType
Definition: Track.h:253
Empty argument passed to some public constructors.
Definition: Track.h:233
virtual ~TrackIntervalData()
Range between two TrackIters, usable in range-for statements, and with Visit member functions.
Definition: Track.h:1168
Notification of changes in individual tracks of TrackList, or of TrackList's composition.
Definition: Track.h:1289
@ RESIZING
Posted when some track changed its height.
Definition: Track.h:1305
@ SELECTION_CHANGE
Posted when the set of selected tracks changes.
Definition: Track.h:1292
@ DELETION
Posted when a track has been deleted from a tracklist. Also posted when one track replaces another.
Definition: Track.h:1314
@ ADDITION
Posted when a track has been added to a tracklist. Also posted when one track replaces another.
Definition: Track.h:1308
@ PERMUTED
Posted when tracks are reordered but otherwise unchanged.
Definition: Track.h:1302
@ TRACK_REQUEST_VISIBLE
Posted when a track needs to be scrolled into view.
Definition: Track.h:1298
@ TRACK_DATA_CHANGE
Posted when certain fields of a track change.
Definition: Track.h:1295
Typically statically constructed.
Definition: UndoManager.h:102
Holds one item with description and time range for the UndoManager.
Definition: UndoManager.h:117
UndoState state
Definition: UndoManager.h:128
Extensions extensions
Definition: UndoManager.h:114
void RestoreUndoRedoState(AudacityProject &project) override
Modify the project when undoing or redoing to some state in history.
Definition: Track.cpp:1376
const std::shared_ptr< TrackList > mpTracks
Definition: Track.cpp:1385
TrackListRestorer(AudacityProject &project)
Definition: Track.cpp:1366
bool CanUndoOrRedo(const AudacityProject &project) override
Whether undo or redo is now permitted; default returns true.
Definition: Track.cpp:1382