1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-18 17:10:05 +02:00

Rewrite many iterations over tracks and channels in Project.cpp

This commit is contained in:
Paul Licameli 2018-09-10 20:23:09 -04:00
parent d01013e0d0
commit ae2ce1ad90

View File

@ -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<WaveTrack*>(pTrack);
for (const auto &clip: pWaveTrack->GetClips())
clip->MarkChanged();
}
pTrack = iter.Next();
}
for (auto pWaveTrack : GetTracks()->Any<WaveTrack>())
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<WaveTrack*>(iterWaveTrack.First());
while (pWaveTrack)
{
rate = std::max(rate, pWaveTrack->GetRate());
pWaveTrack = static_cast<WaveTrack*>(iterWaveTrack.Next());
}
return rate;
return std::max( mRate,
mTracks->Any<const WaveTrack>().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<WaveTrack>())
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<WaveTrack*>(t)->GetClips())
clip->MarkChanged();
}
t = iter.Next();
}
for (auto wt : GetTracks()->Any<WaveTrack>())
// 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<std::unique_ptr<ODTask>> newTasks;
//std::vector<ODDecodeTask*> 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<WaveTrack>()) {
//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;i<newTasks.size();i++) {
if(newTasks[i]->GetODType() & 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;i<newTasks.size();i++) {
if(newTasks[i]->GetODType() & 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<ODTask> 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<ODTask> newTask;
#ifdef EXPERIMENTAL_OD_FLAC
if(!(createdODTasks&ODTask::eODFLAC) && (odFlags & ODTask::eODFLAC)) {
newTask = std::make_unique<ODDecodeFlacTask>();
createdODTasks = createdODTasks | ODTask::eODFLAC;
}
else
if(!(createdODTasks&ODTask::eODFLAC) && (odFlags & ODTask::eODFLAC)) {
newTask = std::make_unique<ODDecodeFlacTask>();
createdODTasks = createdODTasks | ODTask::eODFLAC;
}
else
#endif
if(!(createdODTasks&ODTask::eODPCMSummary) && (odFlags & ODTask::eODPCMSummary)) {
newTask = std::make_unique<ODComputeSummaryTask>();
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<ODComputeSummaryTask>();
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;i<newTasks.size();i++)
ODManager::Instance()->AddNewTask(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<std::unique_ptr<WaveTrack::Locker>> 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<WaveTrack::Locker>(
static_cast<const WaveTrack*>(t)));
t = iter.Next();
}
for (auto wt : mLastSavedTracks->Any<WaveTrack>())
lockers.push_back(
std::make_unique<WaveTrack::Locker>(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<WaveTrack*>(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<WaveTrack>();
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<WaveTrack*>(pTrack);
auto pSavedWaveTrack = static_cast<const WaveTrack*>(pSavedTrack);
auto savedTrackRange = pSavedTrackList.Any<const WaveTrack>();
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<ODComputeSummaryTask> 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 &regions )
{
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<WaveTrack>())
{
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 &region = 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 &region = 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<WaveTrack>()
+ (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 &region = 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<WaveTrack*>(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<WaveTrack*>(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<PlayableTrack*>(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);
}
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) {
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<PlayableTrack *>(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<PlayableTrack>()) {
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<PlayableTrack *>( 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<PlayableTrack *>( 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<PlayableTrack *>( 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<PlayableTrack *>( 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<PlayableTrack>();
auto nPlayableTracks = range.size();
auto nPlaying = (range - &PlayableTrack::GetMute).size();
for (auto track : GetTracks()->Any<PlayableTrack>())
// 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<PlayableTrack *>( 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<PlayableTrack *>( 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<PlayableTrack *>( i );
if (pi) {
pi->SetSolo(!bWasSolo);
if( IsSoloSimple() )
pi->SetMute(false);
}
}
}
else
{
auto pi = dynamic_cast<PlayableTrack *>( i );
if (pi) {
pi->SetSolo(false);
for (auto leader : GetTracks()->Leaders<PlayableTrack>()) {
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<WaveTrack>())
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)