31#include <wx/textfile.h>
41#pragma warning( disable : 4786 )
57: vrulerSize( orig.vrulerSize )
75 std::make_unique<ChannelGroupData>(*orig.
mpGroupData) :
nullptr;
92 auto pList =
mList.lock();
100 auto pList =
mList.lock();
113 auto result =
Clone();
117 attachment.CopyTo( *result );
130 wxASSERT(
mList.lock() == NULL ||
this ==
mNode.first->get());
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);
167 pList->RecalcPositions(
mNode);
168 pList->ResizingEvent(
mNode);
176 mpGroupData = std::make_unique<ChannelGroupData>();
184 if (
auto pLeader = *pList->FindLeader(pTrack))
187 return pTrack->MakeGroupData();
199 if (linkType == oldType)
208 partner->mpGroupData.reset();
216 partner->mpGroupData.reset();
225 assert(!partner->mpGroupData);
226 partner->mpGroupData =
249 auto pList =
mList.lock();
253 if (!pList->isNull(
mNode)) {
255 auto next = pList->getNext(
mNode );
256 if ( !pList->isNull( next ) )
257 return next.first->get();
261 auto prev = pList->getPrev(
mNode );
262 if ( !pList->isNull( prev ) ) {
263 auto track = prev.first->get();
264 if (track && track->HasLinkedTrack())
280 auto pList =
mList.lock();
295 Paste(newT1, tmp.get());
297 else if (newT1 < oldT1) {
337 return mMute.load(std::memory_order_relaxed);
342 mMute.store(value, std::memory_order_relaxed);
347 return mSolo.load(std::memory_order_relaxed);
352 mSolo.store(value, std::memory_order_relaxed);
368 if (attr ==
"mute" && value.
TryGet(nValue)) {
372 else if (attr ==
"solo" && value.
TryGet(nValue)) {
400 std::make_unique<ChannelGroupData>(*n->
mpGroupData) :
nullptr;
414 if (link->HasLinkedTrack()) {
418 L
"Left track %s had linked right track %s with extra right "
419 "track link.\n Removing extra link from right track.",
433 L
"Track %s and %s had left/right track links out of order. "
434 "Setting tracks to not be linked.",
444 L
"Track %s had link to NULL track. Setting it to not be linked.",
469 return project.AttachedObjects::Get<
TrackList >(
key );
485 return std::make_shared<TrackList>( pOwner );
502 ListOfTracks &a,
const std::weak_ptr< TrackList > &aSelf,
503 ListOfTracks &b,
const std::weak_ptr< TrackList > &bSelf )
506 for (
auto it = a.begin(), last = a.end(); it != last; ++it)
507 (*it)->SetOwner(aSelf, {it, &a});
508 for (
auto it = b.begin(), last = b.end(); it != last; ++it)
509 (*it)->SetOwner(bSelf, {it, &b});
512 const auto self = shared_from_this();
513 const auto otherSelf = that.shared_from_this();
514 SwapLOTs( *
this, self, that, otherSelf );
515 SwapLOTs( this->mPendingUpdates, self, that.mPendingUpdates, otherSelf );
516 mUpdaters.swap(that.mUpdaters);
529 auto name = wxString::Format(
"%s %d", baseTrackName, n++);
532 for(
const auto track :
Any())
534 if(track->GetName() ==
name)
555 t = prev.first->get();
559 const auto theEnd =
end();
560 for (
auto n =
Find( node.first->get() ); n != theEnd; ++n) {
571 if (
auto pThis = wThis.lock())
572 pThis->Publish(event);
588 const std::shared_ptr<Track> &pTrack,
bool modifyState )
591 pTrack,
static_cast<int>(modifyState) });
603 node.second && node.first != node.second->end()
632 auto iter =
Find(pTrack);
633 while( *iter && ! ( *iter )->IsLeader() )
645 auto pPartner = pOwner->GetNext(&track,
false);
652 pOwner->MoveUp(pPartner);
653 pPartner->mpGroupData = move(pData);
661 for (
const auto iter : permutation) {
662 ListOfTracks::value_type track = *iter.first;
664 Track *pTrack = track.get();
665 pTrack->
SetOwner(shared_from_this(),
678 [=](
const ListOfTracks::value_type &ptr){
return ptr->GetId() ==
id; } );
686 Track *pTrack = t.get();
687 push_front(ListOfTracks::value_type(t));
689 pTrack->
SetOwner(shared_from_this(), n);
693 return front().get();
702 t->SetOwner(shared_from_this(), n);
710 ListOfTracks::value_type
712 ListOfTracks::value_type holder;
714 auto node = t->GetNode();
717 holder = *node.first;
719 Track *pTrack = with.get();
721 pTrack->
SetOwner(shared_from_this(), node);
722 pTrack->
SetId( t->GetId() );
723 RecalcPositions(node);
733 auto list = track.
mList.lock();
734 if (list.get() ==
this)
737 for (
auto c : channels)
740 c->SetChannel(Track::ChannelType::MonoChannel);
752 auto list = track.
mList.lock();
753 if (list.get() ==
this)
755 if (*list->FindLeader(&track) != &track)
758 auto first = list->Find(&track);
759 auto canLink = [&]() ->
bool {
760 int count = nChannels;
763 if ((*it)->HasLinkedTrack())
774 auto second = std::next(first);
791 ListOfTracks::value_type holder = *node.first;
809 for (
auto pTrack: *
this )
810 pTrack->SetOwner( {}, {} );
812 pTrack->SetOwner( {}, {} );
815 tempList.swap( *
this );
839 return node.first->get();
870 !(*node.first)->HasLinkedTrack() && (*node.first)->GetLinkedTrack() )
874 return node.first->get();
884 return GetPrev(t,
true) != NULL;
889 return GetNext(t,
true) != NULL;
903 s1 = ( *
FindLeader( s1.first->get() ) )->GetNode();
904 s2 = ( *
FindLeader( s2.first->get() ) )->GetNode();
911 if ((*s1.first)->GetIndex() >= (*s2.first)->GetIndex())
915 using Saved = std::vector< ListOfTracks::value_type >;
916 Saved saved1, saved2;
919 size_t nn =
Channels( s.first->get() ).size();
923 saved[nn] = *s.first, s.first = erase(s.first);
926 doSave( saved1, s1 );
928 const bool same = (s1 == s2);
929 doSave( saved2, s2 );
937 for (
auto & pointer : saved)
938 pTrack = pointer.get(),
941 pTrack->
SetOwner(shared_from_this(),
942 s = { insert(s.first, pointer),
this } );
945 doInsert( saved2, s1 );
948 doInsert( saved1, s2 );
1005 double (
Track::*memfn)()
const,
1007 const double &(*combine)(
const double&,
const double&))
1015 return list.
Any().accumulate(
ident, combine, memfn);
1034std::shared_ptr<Track>
1037 std::shared_ptr<Track> pTrack;
1039 pTrack = src->
Clone();
1058 Add<Track>( pTrack );
1069 auto src =
FindById( pendingTrack->GetId() );
1070 if (pendingTrack && src) {
1072 updater( *pendingTrack, *src );
1073 pendingTrack->DoSetLinkType(src->GetLinkType());
1083 pTrack->SetOwner( {}, {} );
1092 bool foundNode =
false;
1096 if (it->get()->GetId() ==
TrackId{}) {
1099 pAdded->push_back( *it );
1100 (*it)->SetOwner( {}, {} );
1103 while (it != stop && it->get()->GetId() ==
TrackId{});
1105 if (!foundNode && it != stop) {
1106 node = (*it)->GetNode();
1123 bool result =
false;
1137 std::vector< std::shared_ptr<Track> > reinstated;
1139 for (
auto &pendingTrack : updates) {
1141 pendingTrack->AttachedTrackObjects::ForEach([&](
auto &attachment){
1142 attachment.Reparent( pendingTrack );
1144 auto src =
FindById( pendingTrack->GetId() );
1146 this->
Replace(src, pendingTrack), result =
true;
1151 reinstated.push_back(pendingTrack);
1156 for (
auto &pendingTrack : reinstated)
1158 this->
Add( pendingTrack ), result =
true;
1162 bool inserted =
false;
1163 ListOfTracks::iterator first;
1164 for (
auto &pendingTrack : additions) {
1167 std::advance( iter, pendingTrack->GetIndex() );
1168 iter = ListOfTracks::insert( iter, pendingTrack );
1169 pendingTrack->SetOwner( shared_from_this(), {iter,
this} );
1190 auto pList =
mList.lock();
1192 const auto id =
GetId();
1193 const auto end = pList->mPendingUpdates.end();
1194 auto it = std::find_if(
1195 pList->mPendingUpdates.begin(),
end,
1196 [=](
const ListOfTracks::value_type &ptr){ return ptr->GetId() == id; } );
1210 auto pList =
mList.lock();
1212 const auto id =
GetId();
1213 const auto pred = [=](
const ListOfTracks::value_type &ptr ) {
1214 return ptr->GetId() ==
id; };
1215 const auto end = pList->mPendingUpdates.end();
1216 const auto it = std::find_if( pList->mPendingUpdates.begin(),
end, pred );
1219 const auto end2 = list2.end();
1220 const auto it2 = std::find_if( list2.begin(), end2, pred );
1231 {
"generic",
"generic",
XO(
"Generic Track") },
false };
1252 XMLWriter &xmlFile,
bool includeNameAndSelected)
const
1254 if (includeNameAndSelected) {
1259 attachment.WriteXMLAttributes( xmlFile );
1269 bool handled =
false;
1271 handled = handled || attachment.HandleXMLAttribute( attr, valueView );
1275 else if (attr ==
"name") {
1279 else if (attr ==
"isSelected" && valueView.
TryGet(nValue)) {
1288 auto pList =
mList.lock();
1290 pList->RecalcPositions(
mNode);
1291 pList->ResizingEvent(
mNode);
1298 {
"audio",
"audio",
XO(
"Audio Track") },
1306 {
"playable",
"playable",
XO(
"Playable Track") },
1333 auto leader = *owner->FindLeader(
this);
Toolkit-neutral facade for basic user interface services.
const TranslatableString name
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)
static CommandHandlerObject & ident(AudacityProject &project)
static const AudacityProject::AttachedObjects::RegisteredFactory key
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.
std::list< std::shared_ptr< Track > > ListOfTracks
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
bool HandleXMLAttribute(const std::string_view &, const XMLAttributeValueView &)
static const TypeInfo & ClassTypeInfo()
void WriteXMLAttributes(XMLWriter &WXUNUSED(xmlFile)) const
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Utility to register hooks into a host class that attach client data.
void ForEach(const Function &function)
Invoke function on each ClientData object that has been created in this.
AudioTrack subclass that can also be audibly replayed by the program.
bool HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value)
std::atomic< bool > mMute
Atomic because it may be read by worker threads in playback.
void DoSetSolo(bool value)
void Init(const PlayableTrack &init)
void Merge(const Track &init) override
void DoSetMute(bool value)
static const TypeInfo & ClassTypeInfo()
void WriteXMLAttributes(XMLWriter &xmlFile) const
std::atomic< bool > mSolo
Atomic because it may be read by worker threads in playback.
Abstract base class for an object holding data associated with points on a time axis.
virtual void Merge(const Track &orig)
void EnsureVisible(bool modifyState=false)
virtual void SetSelected(bool s)
virtual void Paste(double WXUNUSED(t), const Track *WXUNUSED(src))=0
virtual Holder Cut(double WXUNUSED(t0), double WXUNUSED(t1))=0
virtual void Clear(double WXUNUSED(t0), double WXUNUSED(t1))=0
void SetChannel(ChannelType c) noexcept
static void FinishCopy(const Track *n, Track *dest)
virtual bool SupportsBasicEditing() const
Whether this track type implements cut-copy-paste; by default, true.
virtual double GetStartTime() const =0
static const TypeInfo & ClassTypeInfo()
virtual Holder Clone() const =0
bool IsSelectedLeader() const
virtual void SyncLockAdjust(double oldT1, double newT1)
TrackNodePointer GetNode() const
Retrieve mNode with debug checks.
virtual bool LinkConsistencyFix(bool doFix=true, bool completeList=true)
Check consistency of channel groups, and maybe fix it.
ChannelGroupData & GetGroupData()
std::shared_ptr< Track > SubstitutePendingChangedTrack()
std::shared_ptr< const Track > SubstituteOriginalTrack() const
std::shared_ptr< TrackList > GetOwner() const
void SetLinkType(LinkType linkType, bool completeList=true)
std::shared_ptr< Subclass > SharedPointer()
bool HasLinkedTrack() const noexcept
Returns true for leaders of multichannel groups.
virtual Holder Duplicate() const
std::shared_ptr< Track > Holder
void SetOwner(const std::weak_ptr< TrackList > &list, TrackNodePointer node)
Update mNode when Track is added to TrackList, or removed from it.
bool LinkConsistencyCheck(bool completeList)
Do the non-mutating part of consistency fix only and return status.
bool HandleCommonXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView)
void Init(const Track &orig)
virtual ConstIntervals GetIntervals() const
Report times on the track where important intervals begin and end, for UI to snap to.
virtual ChannelType GetChannel() const
void WriteCommonXMLAttributes(XMLWriter &xmlFile, bool includeNameAndSelected=true) const
std::weak_ptr< TrackList > mList
virtual double GetOffset() const =0
TrackNodePointer mNode
Holds iterator to self, so that TrackList::Find can be constant-time.
virtual double GetEndTime() const =0
std::vector< Interval > Intervals
Track * GetLinkedTrack() const
ChannelGroupData & MakeGroupData()
LinkType
For two tracks describes the type of the linkage.
LinkType GetLinkType() const noexcept
int mIndex
0-based position of this track in its TrackList
bool IsAlignedWithLeader() const
Returns true if the leader track has link type LinkType::Aligned.
void DoSetLinkType(LinkType linkType, bool completeList=true)
std::unique_ptr< ChannelGroupData > mpGroupData
std::vector< ConstInterval > ConstIntervals
void SetName(const wxString &n)
TrackId mId
Identifies the track only in-session, not persistently.
An in-session identifier of track objects across undo states. It does not persist between sessions.
Iterator over only members of a TrackList of the specified subtype, optionally filtered by a predicat...
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
void PermutationEvent(TrackNodePointer node)
void RegisterPendingNewTrack(const std::shared_ptr< Track > &pTrack)
bool CanMoveUp(Track *t) const
bool MakeMultiChannelTrack(Track &first, int nChannels, bool aligned)
Converts channels to a multichannel track.
static std::shared_ptr< TrackList > Create(AudacityProject *pOwner)
double GetEndTime() const
bool CanMoveDown(Track *t) const
std::vector< Updater > mUpdaters
This is in correspondence with mPendingUpdates.
double GetMinOffset() const
void ResizingEvent(TrackNodePointer node)
TrackNodePointer getBegin() const
ListOfTracks::value_type Replace(Track *t, const ListOfTracks::value_type &with)
wxString MakeUniqueTrackName(const wxString &baseTrackName) const
Returns string that contains baseTrackName, but is guaranteed to be unique among other tracks in that...
TrackList & operator=(const TrackList &)=delete
TrackKind * Add(const std::shared_ptr< TrackKind > &t)
double GetStartTime() const
bool isNull(TrackNodePointer p) const
TrackNodePointer getNext(TrackNodePointer p) const
Move an iterator to the next node, if any; else stay at end.
void Swap(TrackList &that)
void AdditionEvent(TrackNodePointer node)
void RecalcPositions(TrackNodePointer node)
auto Find(Track *pTrack) -> TrackIter< TrackType >
Turn a pointer into a TrackIter (constant time); get end iterator if this does not own the track.
auto Any() -> TrackIterRange< TrackType >
TrackNodePointer getEnd() const
bool ApplyPendingTracks()
std::function< void(Track &dest, const Track &src) > Updater
bool Contains(const Track *t) const
Mainly a test function. Uses a linear search, so could be slow.
TrackIterRange< Track > EmptyRange() const
void EnsureVisibleEvent(const std::shared_ptr< Track > &pTrack, bool modifyState)
void DataEvent(const std::shared_ptr< Track > &pTrack, int code)
static TrackList & Get(AudacityProject &project)
Track * DoAdd(const std::shared_ptr< Track > &t)
TrackNodePointer Remove(Track *t)
Remove the Track and return an iterator to what followed it.
static bool SwapChannels(Track &track)
If the given track is one of a pair of channels, swap them.
Track * GetNext(Track *t, bool linked=false) const
Return a track in the list that comes after Track t.
Track * GetPrev(Track *t, bool linked=false) const
std::shared_ptr< Track > RegisterPendingChangedTrack(Updater updater, Track *src)
void ClearPendingTracks(ListOfTracks *pAdded=nullptr)
void UpdatePendingTracks()
Track * DoAddToHead(const std::shared_ptr< Track > &t)
Track * FindById(TrackId id)
void Permute(const std::vector< TrackNodePointer > &permutation)
For use in sorting: assume each iterator points into this list, no duplications.
TrackIter< Track > FindLeader(Track *pTrack)
void QueueEvent(TrackListEvent event)
void SwapNodes(TrackNodePointer s1, TrackNodePointer s2)
TrackList(const TrackList &that)=delete
void UnlinkChannels(Track &track)
Removes linkage if track belongs to a group.
bool HasPendingTracks() const
void DeletionEvent(TrackNodePointer node={})
void Clear(bool sendEvent=true)
Make the list empty.
void SelectionEvent(const std::shared_ptr< Track > &pTrack)
TrackNodePointer getPrev(TrackNodePointer p) const
Move an iterator to the previous node, if any; else wrap to end.
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
ListOfTracks mPendingUpdates
Shadow tracks holding append-recording in progress; need to put them into a list so that GetLink() wo...
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...
void WriteAttr(const wxString &name, const Identifier &value)
wxString Find(const FilePath &path)
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for, if Traits<Type>::iterated_type is defined.
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for, if Traits<Type>::iterated_type is defined.
std::optional< LogWindowUpdater > pUpdater
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
double Accumulate(const TrackList &list, double(Track::*memfn)() const, double ident, const double &(*combine)(const double &, const double &))
virtual ~TrackIntervalData()
Range between two TrackIters, usable in range-for statements, and with Visit member functions.
Notification of changes in individual tracks of TrackList, or of TrackList's composition.
@ RESIZING
Posted when some track changed its height.
@ SELECTION_CHANGE
Posted when the set of selected tracks changes.
@ DELETION
Posted when a track has been deleted from a tracklist. Also posted when one track replaces another.
@ ADDITION
Posted when a track has been added to a tracklist. Also posted when one track replaces another.
@ PERMUTED
Posted when tracks are reordered but otherwise unchanged.
@ TRACK_REQUEST_VISIBLE
Posted when a track needs to be scrolled into view.
@ TRACK_DATA_CHANGE
Posted when certain fields of a track change.