From ae2ce1ad90aa7e0b78b9e7638a8fcb8e647d533a Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 10 Sep 2018 20:23:09 -0400 Subject: [PATCH] Rewrite many iterations over tracks and channels in Project.cpp --- src/Project.cpp | 569 ++++++++++++++++-------------------------------- 1 file changed, 193 insertions(+), 376 deletions(-) diff --git a/src/Project.cpp b/src/Project.cpp index 43f2e2f79..65c82bda2 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -1392,18 +1392,9 @@ void AudacityProject::RedrawProject(const bool bForceWaveTracks /*= false*/) FixScrollbars(); if (bForceWaveTracks && GetTracks()) { - TrackListIterator iter(GetTracks()); - Track* pTrack = iter.First(); - while (pTrack) - { - if (pTrack->GetKind() == Track::Wave) - { - WaveTrack* pWaveTrack = static_cast(pTrack); - for (const auto &clip: pWaveTrack->GetClips()) - clip->MarkChanged(); - } - pTrack = iter.Next(); - } + for (auto pWaveTrack : GetTracks()->Any()) + for (const auto &clip: pWaveTrack->GetClips()) + clip->MarkChanged(); } mTrackPanel->Refresh(false); } @@ -1604,17 +1595,8 @@ void AudacityProject::AS_SetSelectionFormat(const NumericFormatId & format) double AudacityProject::SSBL_GetRate() const { // Return maximum of project rate and all track rates. - double rate = mRate; - - TrackListOfKindIterator iterWaveTrack(Track::Wave, mTracks.get()); - WaveTrack *pWaveTrack = static_cast(iterWaveTrack.First()); - while (pWaveTrack) - { - rate = std::max(rate, pWaveTrack->GetRate()); - pWaveTrack = static_cast(iterWaveTrack.Next()); - } - - return rate; + return std::max( mRate, + mTracks->Any().max( &WaveTrack::GetRate ) ); } const NumericFormatId & AudacityProject::SSBL_GetFrequencySelectionFormatName() @@ -2648,13 +2630,8 @@ void AudacityProject::OnCloseWindow(wxCloseEvent & event) // in memory. After it's locked, DELETE the data structure so that // there's no memory leak. if (mLastSavedTracks) { - TrackListIterator iter(mLastSavedTracks.get()); - Track *t = iter.First(); - while (t) { - if (t->GetKind() == Track::Wave) - ((WaveTrack *) t)->CloseLock(); - t = iter.Next(); - } + for (auto wt : mLastSavedTracks->Any()) + wt->CloseLock(); mLastSavedTracks->Clear(); // sends an event mLastSavedTracks.reset(); @@ -3169,12 +3146,9 @@ void AudacityProject::OpenFile(const wxString &fileNameArg, bool addtohistory) // the version saved on disk will be preserved until the // user selects Save(). - Track *t; - TrackListIterator iter(GetTracks()); mLastSavedTracks = TrackList::Create(); - t = iter.First(); - while (t) { + for (auto t : GetTracks()->Any()) { if (t->GetErrorOpening()) { wxLogWarning( @@ -3225,11 +3199,10 @@ void AudacityProject::OpenFile(const wxString &fileNameArg, bool addtohistory) } mLastSavedTracks->Add(t->Duplicate()); - t = iter.Next(); } InitialState(); - mTrackPanel->SetFocusedTrack(iter.First()); + mTrackPanel->SetFocusedTrack(*GetTracks()->Any().begin()); HandleResize(); mTrackPanel->Refresh(false); mTrackPanel->Update(); // force any repaint to happen now, @@ -3310,17 +3283,11 @@ void AudacityProject::OpenFile(const wxString &fileNameArg, bool addtohistory) else if (status & FSCKstatus_CHANGED) { // Mark the wave tracks as changed and redraw. - TrackListIterator iter(GetTracks()); - Track *t = iter.First(); - while (t) { - if (t->GetKind() == Track::Wave) - { - // Only wave tracks have a notion of "changed". - for (const auto &clip: static_cast(t)->GetClips()) - clip->MarkChanged(); - } - t = iter.Next(); - } + for (auto wt : GetTracks()->Any()) + // Only wave tracks have a notion of "changed". + for (const auto &clip: wt->GetClips()) + clip->MarkChanged(); + mTrackPanel->Refresh(true); // Vaughan, 2010-08-20: This was bogus, as all the actions in DirManager::ProjectFSCK @@ -3350,10 +3317,8 @@ void AudacityProject::OpenFile(const wxString &fileNameArg, bool addtohistory) // may have spared the files at the expense of leaked memory). But // here is a better way to accomplish the intent, doing like what happens // when the project closes: - TrackListOfKindIterator iter(Track::Wave, mTracks.get() ); - for ( Track *pTrack = iter.First(); pTrack; pTrack = iter.Next() ) { - static_cast< WaveTrack* >(pTrack)->CloseLock(); - } + for ( auto pTrack : mTracks->Any< WaveTrack >() ) + pTrack->CloseLock(); mTracks->Clear(); //mTracks->Clear(true); @@ -3438,55 +3403,47 @@ void AudacityProject::EnqueueODTasks() //OD***Blocks. if(ODManager::HasLoadedODFlag()) { - Track *tr; - TrackListIterator triter(GetTracks()); - tr = triter.First(); - std::vector> newTasks; //std::vector decodeTasks; unsigned int createdODTasks=0; - while (tr) { - if (tr->GetKind() == Track::Wave) { - //check the track for blocks that need decoding. - //There may be more than one type e.g. FLAC/FFMPEG/lame - unsigned int odFlags; - odFlags=((WaveTrack*)tr)->GetODFlags(); + for (auto wt : GetTracks()->Any()) { + //check the track for blocks that need decoding. + //There may be more than one type e.g. FLAC/FFMPEG/lame + unsigned int odFlags = wt->GetODFlags(); - //add the track to the already created tasks that correspond to the od flags in the wavetrack. - for(unsigned int i=0;iGetODType() & odFlags) - newTasks[i]->AddWaveTrack((WaveTrack*)tr); - } + //add the track to the already created tasks that correspond to the od flags in the wavetrack. + for(unsigned int i=0;iGetODType() & odFlags) + newTasks[i]->AddWaveTrack(wt); + } - //create whatever NEW tasks we need to. - //we want at most one instance of each class for the project - while((odFlags|createdODTasks) != createdODTasks) - { - std::unique_ptr newTask; + //create whatever NEW tasks we need to. + //we want at most one instance of each class for the project + while((odFlags|createdODTasks) != createdODTasks) + { + std::unique_ptr newTask; #ifdef EXPERIMENTAL_OD_FLAC - if(!(createdODTasks&ODTask::eODFLAC) && (odFlags & ODTask::eODFLAC)) { - newTask = std::make_unique(); - createdODTasks = createdODTasks | ODTask::eODFLAC; - } - else + if(!(createdODTasks&ODTask::eODFLAC) && (odFlags & ODTask::eODFLAC)) { + newTask = std::make_unique(); + createdODTasks = createdODTasks | ODTask::eODFLAC; + } + else #endif - if(!(createdODTasks&ODTask::eODPCMSummary) && (odFlags & ODTask::eODPCMSummary)) { - newTask = std::make_unique(); - createdODTasks= createdODTasks | ODTask::eODPCMSummary; - } - else { - wxPrintf("unrecognized OD Flag in block file.\n"); - //TODO:ODTODO: display to user. This can happen when we build audacity on a system that doesnt have libFLAC - break; - } - if(newTask) - { - newTask->AddWaveTrack((WaveTrack*)tr); - newTasks.push_back(std::move(newTask)); - } + if(!(createdODTasks&ODTask::eODPCMSummary) && (odFlags & ODTask::eODPCMSummary)) { + newTask = std::make_unique(); + createdODTasks = createdODTasks | ODTask::eODPCMSummary; + } + else { + wxPrintf("unrecognized OD Flag in block file.\n"); + //TODO:ODTODO: display to user. This can happen when we build audacity on a system that doesnt have libFLAC + break; + } + if(newTask) + { + newTask->AddWaveTrack(wt); + newTasks.push_back(std::move(newTask)); } } - tr = triter.Next(); } for(unsigned int i=0;iAddNewTask(std::move(newTasks[i])); @@ -3927,9 +3884,7 @@ bool AudacityProject::DoSave (const bool fromSaveAs, // Some confirmation dialogs if (!bWantSaveCopy) { - TrackListIterator iter(GetTracks()); - bool bHasTracks = (iter.First() != NULL); - if (!bHasTracks) + if ( ! GetTracks()->Any() ) { if (GetUndoManager()->UnsavedChanges() && mEmptyCanBeDirty) { int result = AudacityMessageBox(_("Your project is now empty.\nIf saved, the project will have no tracks.\n\nTo save any previously open tracks:\nClick 'No', Edit > Undo until all tracks\nare open, then File > Save Project.\n\nSave anyway?"), @@ -4073,15 +4028,9 @@ bool AudacityProject::DoSave (const bool fromSaveAs, std::vector> lockers; if (mLastSavedTracks) { lockers.reserve(mLastSavedTracks->size()); - TrackListIterator iter(mLastSavedTracks.get()); - Track *t = iter.First(); - while (t) { - if (t->GetKind() == Track::Wave) - lockers.push_back( - std::make_unique( - static_cast(t))); - t = iter.Next(); - } + for (auto wt : mLastSavedTracks->Any()) + lockers.push_back( + std::make_unique(wt)); } // This renames the project directory, and moves or copies @@ -4138,17 +4087,13 @@ bool AudacityProject::DoSave (const bool fromSaveAs, mLastSavedTracks->Clear(); mLastSavedTracks = TrackList::Create(); - TrackListIterator iter(GetTracks()); - Track *t = iter.First(); - while (t) { + for (auto t : GetTracks()->Any()) { mLastSavedTracks->Add(t->Duplicate()); //only after the xml has been saved we can mark it saved. //thus is because the OD blockfiles change on background thread while this is going on. - // if(dupT->GetKind() == Track::Wave) - // ((WaveTrack*)dupT)->MarkSaved(); - - t = iter.Next(); + // if(const auto wt = track_cast(dupT)) + // wt->MarkSaved(); } GetUndoManager()->StateSaved(); @@ -4188,38 +4133,36 @@ bool AudacityProject::SaveCopyWaveTracks(const wxString & strProjectPathName, // Some of this is similar to code in ExportMultiple::ExportMultipleByTrack // but that code is really tied into the dialogs. - // Copy the tracks because we're going to do some state changes before exporting. - Track* pTrack; - WaveTrack* pWaveTrack; - TrackListOfKindIterator iter(Track::Wave, GetTracks()); - unsigned int numWaveTracks = 0; + // Copy the tracks because we're going to do some state changes before exporting. + unsigned int numWaveTracks = 0; auto ppSavedTrackList = TrackList::Create(); auto &pSavedTrackList = *ppSavedTrackList; - for (pTrack = iter.First(); pTrack != NULL; pTrack = iter.Next()) + auto trackRange = GetTracks()->Any(); + for (auto pWaveTrack : trackRange) { numWaveTracks++; - pWaveTrack = (WaveTrack*)pTrack; pSavedTrackList.Add(mTrackFactory->DuplicateWaveTrack(*pWaveTrack)); } auto cleanup = finally( [&] { // Restore the saved track states and clean up. - TrackListIterator savedTrackIter(&pSavedTrackList); - Track *pSavedTrack; - for (pTrack = iter.First(), pSavedTrack = savedTrackIter.First(); - ((pTrack != NULL) && (pSavedTrack != NULL)); - pTrack = iter.Next(), pSavedTrack = savedTrackIter.Next()) - { - pWaveTrack = static_cast(pTrack); - auto pSavedWaveTrack = static_cast(pSavedTrack); + auto savedTrackRange = pSavedTrackList.Any(); + auto ppSavedTrack = savedTrackRange.begin(); + for (auto ppTrack = trackRange.begin(); - pWaveTrack->SetSelected(pSavedTrack->GetSelected()); + *ppTrack && *ppSavedTrack; + + ++ppTrack, ++ppSavedTrack) + { + auto pWaveTrack = *ppTrack; + auto pSavedWaveTrack = *ppSavedTrack; + pWaveTrack->SetSelected(pSavedWaveTrack->GetSelected()); pWaveTrack->SetMute(pSavedWaveTrack->GetMute()); pWaveTrack->SetSolo(pSavedWaveTrack->GetSolo()); - pWaveTrack->SetGain(((WaveTrack*)pSavedTrack)->GetGain()); - pWaveTrack->SetPan(((WaveTrack*)pSavedTrack)->GetPan()); + pWaveTrack->SetGain(pSavedWaveTrack->GetGain()); + pWaveTrack->SetPan(pSavedWaveTrack->GetPan()); } } ); @@ -4228,10 +4171,8 @@ bool AudacityProject::SaveCopyWaveTracks(const wxString & strProjectPathName, return true; // Okay, now some bold state-faking to default values. - for (pTrack = iter.First(); pTrack != NULL; pTrack = iter.Next()) + for (auto pWaveTrack : trackRange) { - pWaveTrack = (WaveTrack*)pTrack; - pWaveTrack->SetSelected(false); pWaveTrack->SetMute(false); pWaveTrack->SetSolo(false); @@ -4254,34 +4195,25 @@ bool AudacityProject::SaveCopyWaveTracks(const wxString & strProjectPathName, mStrOtherNamesArray.clear(); Exporter theExporter; - Track* pRightTrack; wxFileName uniqueTrackFileName; - for (pTrack = iter.First(); ((pTrack != NULL) && bSuccess); pTrack = iter.Next()) + for (auto pTrack : (trackRange + &Track::IsLeader)) { - if (pTrack->GetKind() == Track::Wave) - { - SelectionStateChanger changer{ GetSelectionState(), *GetTracks() }; - pTrack->SetSelected(true); - if (pTrack->GetLinked()) - { - pRightTrack = iter.Next(); - pRightTrack->SetSelected(true); - } - else - pRightTrack = NULL; + SelectionStateChanger changer{ GetSelectionState(), *GetTracks() }; + auto channels = TrackList::Channels(pTrack); - uniqueTrackFileName = wxFileName(strDataDirPathName, pTrack->GetName(), extension); - FileNames::MakeNameUnique(mStrOtherNamesArray, uniqueTrackFileName); - bSuccess = - theExporter.Process(this, pRightTrack ? 2 : 1, - fileFormat, uniqueTrackFileName.GetFullPath(), true, - pTrack->GetStartTime(), pTrack->GetEndTime()); + for (auto channel : channels) + channel->SetSelected(true); + uniqueTrackFileName = wxFileName(strDataDirPathName, pTrack->GetName(), extension); + FileNames::MakeNameUnique(mStrOtherNamesArray, uniqueTrackFileName); + bSuccess = + theExporter.Process(this, channels.size(), + fileFormat, uniqueTrackFileName.GetFullPath(), true, + pTrack->GetStartTime(), pTrack->GetEndTime()); - if (!bSuccess) - // If only some exports succeed, the cleanup is not done here - // but trusted to the caller - break; - } + if (!bSuccess) + // If only some exports succeed, the cleanup is not done here + // but trusted to the caller + break; } return bSuccess; @@ -4778,12 +4710,10 @@ void AudacityProject::PopState(const UndoState &state) TrackList *const tracks = state.tracks.get(); mTracks->Clear(); - TrackListIterator iter(tracks); - Track *t = iter.First(); bool odUsed = false; std::unique_ptr computeTask; - while (t) + for (auto t : tracks->Any()) { auto copyTrack = mTracks->Add(t->Duplicate()); @@ -4809,8 +4739,6 @@ void AudacityProject::PopState(const UndoState &state) computeTask->AddWaveTrack(wt); } }); - - t = iter.Next(); } //add the task. @@ -4847,8 +4775,8 @@ void AudacityProject::UpdateLyrics() if (!mLyricsWindow) return; - TrackListOfKindIterator iter(Track::Label, GetTracks()); - LabelTrack* pLabelTrack = (LabelTrack*)(iter.First()); // Lyrics come from only the first label track. + // Lyrics come from only the first label track. + auto pLabelTrack = *GetTracks()->Any< const LabelTrack >().begin(); if (!pLabelTrack) return; @@ -4924,15 +4852,10 @@ void AudacityProject::ClearClipboard() void AudacityProject::Clear() { - TrackListIterator iter(GetTracks()); - - Track *n = iter.First(); - - while (n) { + for (auto n : GetTracks()->Any()) { if (n->GetSelected() || n->IsSyncLockSelected()) { n->Clear(mViewInfo.selectedRegion.t0(), mViewInfo.selectedRegion.t1()); } - n = iter.Next(); } double seconds = mViewInfo.selectedRegion.duration(); @@ -5202,22 +5125,16 @@ void AudacityProject::OnTimer(wxTimerEvent& WXUNUSED(event)) //regions memory need to be deleted by the caller void AudacityProject::GetRegionsByLabel( Regions ®ions ) { - TrackListIterator iter(GetTracks()); - Track *n; - //determine labeled regions - for( n = iter.First(); n; n = iter.Next() ) - if( n->GetKind() == Track::Label && n->GetSelected() ) + for (auto lt : GetTracks()->Selected< LabelTrack >()) { + for (int i = 0; i < lt->GetNumLabels(); i++) { - LabelTrack *lt = ( LabelTrack* )n; - for( int i = 0; i < lt->GetNumLabels(); i++ ) - { - const LabelStruct *ls = lt->GetLabel( i ); - if( ls->selectedRegion.t0() >= mViewInfo.selectedRegion.t0() && - ls->selectedRegion.t1() <= mViewInfo.selectedRegion.t1() ) - regions.push_back(Region(ls->getT0(), ls->getT1())); - } + const LabelStruct *ls = lt->GetLabel(i); + if (ls->selectedRegion.t0() >= mViewInfo.selectedRegion.t0() && + ls->selectedRegion.t1() <= mViewInfo.selectedRegion.t1()) + regions.push_back(Region(ls->getT0(), ls->getT1())); } + } //anything to do ? if( regions.size() == 0 ) @@ -5256,35 +5173,22 @@ void AudacityProject::EditByLabel( EditFunction action, if( regions.size() == 0 ) return; - TrackListIterator iter(GetTracks()); - Track *n; - bool allTracks = true; - // if at least one wave track is selected // apply only on the selected track - for( n = iter.First(); n; n = iter.Next() ) - if( n->GetKind() == Track::Wave && n->GetSelected() ) - { - allTracks = false; - break; - } + const bool allTracks = (GetTracks()->Selected< WaveTrack >()).empty(); //Apply action on wavetracks starting from //labeled regions in the end. This is to correctly perform //actions like 'Delete' which collapse the track area. - n = iter.First(); - while (n) + for (auto wt : GetTracks()->Any()) { - if ((n->GetKind() == Track::Wave) && - (allTracks || n->GetSelected() || (bSyncLockedTracks && n->IsSyncLockSelected()))) + if (allTracks || wt->GetSelected() || (bSyncLockedTracks && wt->IsSyncLockSelected())) { - WaveTrack *wt = ( WaveTrack* )n; for (int i = (int)regions.size() - 1; i >= 0; i--) { const Region ®ion = regions.at(i); (wt->*action)(region.start, region.end); } } - n = iter.Next(); } } @@ -5302,18 +5206,9 @@ void AudacityProject::EditClipboardByLabel( EditDestFunction action ) if( regions.size() == 0 ) return; - TrackListIterator iter(GetTracks()); - Track *n; - bool allTracks = true; - // if at least one wave track is selected // apply only on the selected track - for( n = iter.First(); n; n = iter.Next() ) - if( n->GetKind() == Track::Wave && n->GetSelected() ) - { - allTracks = false; - break; - } + const bool allTracks = (GetTracks()->Selected< WaveTrack >()).empty(); ClearClipboard(); @@ -5323,47 +5218,46 @@ void AudacityProject::EditClipboardByLabel( EditDestFunction action ) //Apply action on wavetracks starting from //labeled regions in the end. This is to correctly perform //actions like 'Cut' which collapse the track area. - for( n = iter.First(); n; n = iter.Next() ) - { - if( n->GetKind() == Track::Wave && ( allTracks || n->GetSelected() ) ) - { - WaveTrack *wt = ( WaveTrack* )n; - // This track accumulates the needed clips, right to left: - Track::Holder merged; - for( int i = (int)regions.size() - 1; i >= 0; i-- ) - { - const Region ®ion = regions.at(i); - auto dest = ( wt->*action )( region.start, region.end ); - if( dest ) - { - MenuCommandHandler::FinishCopy( wt, dest.get() ); - if( !merged ) - merged = std::move(dest); - else - { - // Paste to the beginning; unless this is the first region, - // offset the track to account for time between the regions - if (i < (int)regions.size() - 1) - merged->Offset( - regions.at(i + 1).start - region.end); - // dest may have a placeholder clip at the end that is - // removed when pasting, which is okay because we proceed - // right to left. Any placeholder already in merged is kept. - // Only the rightmost placeholder is important in the final - // result. - merged->Paste( 0.0 , dest.get() ); - } - } - else // nothing copied but there is a 'region', so the 'region' must be a 'point label' so offset + for(auto wt : + GetTracks()->Any() + + (allTracks ? &Track::Any : &Track::IsSelected) + ) { + // This track accumulates the needed clips, right to left: + Track::Holder merged; + for( int i = (int)regions.size() - 1; i >= 0; i-- ) + { + const Region ®ion = regions.at(i); + auto dest = ( wt->*action )( region.start, region.end ); + if( dest ) + { + MenuCommandHandler::FinishCopy( wt, dest.get() ); + if( !merged ) + merged = std::move(dest); + else + { + // Paste to the beginning; unless this is the first region, + // offset the track to account for time between the regions if (i < (int)regions.size() - 1) - if (merged) - merged->Offset( - regions.at(i + 1).start - region.end); + merged->Offset( + regions.at(i + 1).start - region.end); + + // dest may have a placeholder clip at the end that is + // removed when pasting, which is okay because we proceed + // right to left. Any placeholder already in merged is kept. + // Only the rightmost placeholder is important in the final + // result. + merged->Paste( 0.0 , dest.get() ); + } } - if( merged ) - newClipboard.Add( std::move(merged) ); + else // nothing copied but there is a 'region', so the 'region' must be a 'point label' so offset + if (i < (int)regions.size() - 1) + if (merged) + merged->Offset( + regions.at(i + 1).start - region.end); } + if( merged ) + newClipboard.Add( std::move(merged) ); } // Survived possibility of exceptions. Commit changes to the clipboard now. @@ -5737,11 +5631,8 @@ void AudacityProject::SetTrackGain(WaveTrack * wt, LWSlider * slider) wxASSERT(wt); float newValue = slider->Get(); - // Assume linked track is wave or null - const auto link = static_cast(wt->GetLink()); - wt->SetGain(newValue); - if (link) - link->SetGain(newValue); + for (auto channel : TrackList::Channels(wt)) + channel->SetGain(newValue); PushState(_("Adjusted gain"), _("Gain"), UndoPush::CONSOLIDATE); @@ -5753,11 +5644,8 @@ void AudacityProject::SetTrackPan(WaveTrack * wt, LWSlider * slider) wxASSERT(wt); float newValue = slider->Get(); - // Assume linked track is wave or null - const auto link = static_cast(wt->GetLink()); - wt->SetPan(newValue); - if (link) - link->SetPan(newValue); + for (auto channel : TrackList::Channels(wt)) + channel->SetPan(newValue); PushState(_("Adjusted Pan"), _("Pan"), UndoPush::CONSOLIDATE); @@ -5781,7 +5669,6 @@ void AudacityProject::RemoveTrack(Track * toRemove) } wxString name = toRemove->GetName(); - Track *partner = toRemove->GetLink(); auto playable = dynamic_cast(toRemove); if (playable) @@ -5792,14 +5679,14 @@ void AudacityProject::RemoveTrack(Track * toRemove) pMixerBoard->RemoveTrackCluster(playable); // Will remove partner shown in same cluster. } - mTracks->Remove(toRemove); - if (partner) { - mTracks->Remove(partner); - } - - if (toRemoveWasFocused) { + auto channels = TrackList::Channels(toRemove); + // Be careful to post-increment over positions that get erased! + auto &iter = channels.first; + while (iter != channels.end()) + mTracks->Remove( * iter++ ); + + if (toRemoveWasFocused) mTrackPanel->SetFocusedTrack(newFocus); - } PushState( wxString::Format(_("Removed track '%s.'"), @@ -5813,82 +5700,42 @@ void AudacityProject::RemoveTrack(Track * toRemove) void AudacityProject::HandleTrackMute(Track *t, const bool exclusive) { + // Whatever t is, replace with lead channel + t = *GetTracks()->FindLeader(t); + // "exclusive" mute means mute the chosen track and unmute all others. - if (exclusive) - { - TrackListIterator iter(GetTracks()); - Track *it = iter.First(); - while (it) { - auto i = dynamic_cast(it); - if (i) { - if (i == t) { - i->SetMute(true); - if(i->GetLinked()) { // also mute the linked track - it = iter.Next(); - i->SetMute(true); - } - } - else { - i->SetMute(false); - } - i->SetSolo(false); - } - it = iter.Next(); + if (exclusive) { + for (auto leader : GetTracks()->Leaders()) { + const auto group = TrackList::Channels(leader); + bool chosen = (t == leader); + for (auto channel : group) + channel->SetMute( chosen ), + channel->SetSolo( false ); } } - else - { + else { // Normal click toggles this track. auto pt = dynamic_cast( t ); if (!pt) return; - pt->SetMute(!pt->GetMute()); - if(t->GetLinked()) // set mute the same on both, if a pair - { - bool muted = pt->GetMute(); - TrackListIterator iter(GetTracks()); - Track *i = iter.First(); - while (i != t) { // search for this track - i = iter.Next(); - } - i = iter.Next(); // get the next one, since linked - auto pi = dynamic_cast( i ); - if (pi) - pi->SetMute(muted); // and mute it as well - } + bool wasMute = pt->GetMute(); + for (auto channel : TrackList::Channels(pt)) + channel->SetMute( !wasMute ); if (IsSoloSimple() || IsSoloNone()) { - TrackListIterator iter(GetTracks()); - Track *i = iter.First(); - int nPlaying=0; - int nPlayableTracks =0; - // We also set a solo indicator if we have just one track / stereo pair playing. // in a group of more than one playable tracks. // otherwise clear solo on everything. - while (i) { - auto pi = dynamic_cast( i ); - if (pi) { - nPlayableTracks++; - if( !pi->GetMute()) - { - nPlaying += 1; - if(i->GetLinked()) - i = iter.Next(); // don't count this one as it is linked - } - } - i = iter.Next(); - } - i = iter.First(); - while (i) { - auto pi = dynamic_cast( i ); - if (pi) - pi->SetSolo( (nPlaying==1) && (nPlayableTracks > 1 ) && !pi->GetMute() ); // will set both of a stereo pair - i = iter.Next(); - } + auto range = GetTracks()->Leaders(); + auto nPlayableTracks = range.size(); + auto nPlaying = (range - &PlayableTrack::GetMute).size(); + + for (auto track : GetTracks()->Any()) + // will set both of a stereo pair + track->SetSolo( (nPlaying==1) && (nPlayableTracks > 1 ) && !track->GetMute() ); } } ModifyState(true); @@ -5896,11 +5743,15 @@ void AudacityProject::HandleTrackMute(Track *t, const bool exclusive) // Type of solo (standard or simple) follows the set preference, unless // alternate == true, which causes the opposite behavior. -void AudacityProject::HandleTrackSolo(Track *const t, const bool alternate) +void AudacityProject::HandleTrackSolo(Track *t, const bool alternate) { + // Whatever t is, replace with lead channel + t = *GetTracks()->FindLeader(t); + const auto pt = dynamic_cast( t ); if (!pt) return; + bool bWasSolo = pt->GetSolo(); bool bSoloMultiple = !IsSoloSimple() ^ alternate; @@ -5911,55 +5762,28 @@ void AudacityProject::HandleTrackSolo(Track *const t, const bool alternate) // when in standard radio button mode. if ( bSoloMultiple ) { - pt->SetSolo( !pt->GetSolo() ); - if(t->GetLinked()) - { - bool soloed = pt->GetSolo(); - TrackListIterator iter(GetTracks()); - Track *i = iter.First(); - while (i != t) { // search for this track - i = iter.Next(); - } - i = iter.Next(); // get the next one, since linked - auto pi = dynamic_cast( i ); - if (pi) - pi->SetSolo(soloed); // and solo it as well - } + for (auto channel : TrackList::Channels(pt)) + channel->SetSolo( !bWasSolo ); } else { // Normal click solo this track only, mute everything else. // OR unmute and unsolo everything. - TrackListIterator iter(GetTracks()); - Track *i = iter.First(); - bool bWasSolo = pt->GetSolo(); - while (i) { - if( i==t ) - { - pt->SetSolo(!bWasSolo); - if( IsSoloSimple() ) - pt->SetMute(false); - if(t->GetLinked()) - { - i = iter.Next(); - auto pi = dynamic_cast( i ); - if (pi) { - pi->SetSolo(!bWasSolo); - if( IsSoloSimple() ) - pi->SetMute(false); - } - } - } - else - { - auto pi = dynamic_cast( i ); - if (pi) { - pi->SetSolo(false); + for (auto leader : GetTracks()->Leaders()) { + const auto group = TrackList::Channels(leader); + bool chosen = (t == leader); + for (auto channel : group) { + if (chosen) { + channel->SetSolo( !bWasSolo ); if( IsSoloSimple() ) - pi->SetMute(!bWasSolo); + channel->SetMute( false ); + } + else { + channel->SetSolo( false ); + if( IsSoloSimple() ) + channel->SetMute( !bWasSolo ); } } - i = iter.Next(); } } ModifyState(true); @@ -6043,13 +5867,8 @@ void AudacityProject::ResetProjectToEmpty() { // in memory. After it's locked, DELETE the data structure so that // there's no memory leak. if (mLastSavedTracks) { - TrackListIterator iter(mLastSavedTracks.get()); - Track *t = iter.First(); - while (t) { - if (t->GetKind() == Track::Wave) - ((WaveTrack *)t)->CloseLock(); - t = iter.Next(); - } + for (auto t : mLastSavedTracks->Any()) + t->CloseLock(); mLastSavedTracks->Clear(); // sends an event mLastSavedTracks.reset(); @@ -6105,11 +5924,9 @@ bool AudacityProject::SaveFromTimerRecording(wxFileName fnFile) { // Does the project have any tracks? bool AudacityProject::ProjectHasTracks() { - // These two lines test for an 'empty' project. + // Test for an 'empty' project. // of course it could still have a history at this stage. - TrackListIterator iter2(GetTracks()); - bool bHasTracks = (iter2.First() != NULL); - return bHasTracks; + return ! ( GetTracks()->Any() ).empty(); } wxString AudacityProject::GetHoursMinsString(int iMinutes)