From fee0f284fe21b5f7d60f1e7de4f3bbff8133aa98 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Tue, 18 Sep 2018 12:02:37 -0400 Subject: [PATCH] Rewrite many iterations over tracks and channels in src/toolbars --- src/toolbars/ControlToolBar.cpp | 139 +++----------- src/toolbars/TranscriptionToolBar.cpp | 267 +++++++++++--------------- 2 files changed, 141 insertions(+), 265 deletions(-) diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index 8f0723036..038fa4453 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -451,7 +451,6 @@ void ControlToolBar::Repaint( wxDC *dc ) void ControlToolBar::EnableDisableButtons() { AudacityProject *p = GetActiveProject(); - bool tracks = false; bool paused = mPause->IsDown(); bool playing = mPlay->IsDown(); @@ -459,15 +458,7 @@ void ControlToolBar::EnableDisableButtons() bool busy = gAudioIO->IsBusy(); // Only interested in audio type tracks - if (p) { - TrackListIterator iter( p->GetTracks() ); - for (Track *t = iter.First(); t; t = iter.Next()) { - if (dynamic_cast(t)) { - tracks = true; - break; - } - } - } + bool tracks = p && p->GetTracks()->Any(); // PRL: PlayableTrack ? if (p) { TranscriptionToolBar *const playAtSpeedTB = p->GetTranscriptionToolBar(); @@ -599,18 +590,11 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion, p->mLastPlayMode = mode; - bool hasaudio = false; - TrackListIterator iter(t); - for (Track *trk = iter.First(); trk; trk = iter.Next()) { - if (trk->GetKind() == Track::Wave -#ifdef EXPERIMENTAL_MIDI_OUT - || (trk->GetKind() == Track::Note && useMidi) -#endif - ) { - hasaudio = true; - break; - } - } + bool hasaudio; + if (useMidi) + hasaudio = ! p->GetTracks()->Any().empty(); + else + hasaudio = ! p->GetTracks()->Any().empty(); double latestEnd = (playWhiteSpace)? t1 : t->GetEndTime(); @@ -925,28 +909,13 @@ WaveTrackArray ControlToolBar::ChooseExistingRecordingTracks( return {}; auto trackList = p->GetTracks(); - TrackListIterator it(trackList); std::vector channelCounts; WaveTrackArray candidates; - for (Track *tt = it.First(); tt; tt = it.Next()) { - // Eliminate certain tracks from consideration - if (tt->GetKind() != Track::Wave) - continue; - WaveTrack *candidate = static_cast(tt); - if (candidate->GetLink() && !candidate->GetLinked()) - // Don't re-consider right channel apart from the left - continue; - if (selectedOnly && !candidate->GetSelected()) - continue; - + const auto range = trackList->Leaders(); + for ( auto candidate : selectedOnly ? range + &Track::IsSelected : range ) { // count channels in this track - unsigned nChannels = 0; - // This is written with odd seeming generality, looking forward to - // the rewrite that removes assumption of at-most-stereo - for (auto channel = candidate; channel; - channel = channel->GetLinked() - ? static_cast(channel->GetLink()) : nullptr) - ++nChannels; + const auto channels = TrackList::Channels( candidate ); + unsigned nChannels = channels.size(); if (strictRules && nChannels > recordingChannels) { // The recording would under-fill this track's channels @@ -967,9 +936,7 @@ WaveTrackArray ControlToolBar::ChooseExistingRecordingTracks( candidates.begin() + nOldChannels); } channelCounts.push_back(nChannels); - for (auto channel = candidate; channel; - channel = channel->GetLinked() - ? static_cast(channel->GetLink()) : nullptr) { + for ( auto channel : channels ) { candidates.push_back(Track::Pointer(channel)); if(candidates.size() == recordingChannels) // Done! @@ -1017,41 +984,20 @@ void ControlToolBar::OnRecord(wxCommandEvent &evt) if (t1 == t0) t1 = DBL_MAX; - double allt0 = t0; - WaveTrackArray existingTracks; - double selt0 = t0; if (appendRecord) { - TrackList *trackList = p->GetTracks(); - TrackListIterator it(trackList); - - // Find the maximum end time of selected and all wave tracks - for (Track *tt = it.First(); tt; tt = it.Next()) { - if (tt->GetKind() == Track::Wave) { - WaveTrack *wt = static_cast(tt); - if (wt->GetEndTime() > allt0) - allt0 = wt->GetEndTime(); - - if (wt->GetSelected()) { - if (wt->GetEndTime() > selt0) - selt0 = wt->GetEndTime(); - } - } - } + const auto trackRange = p->GetTracks()->Any< const WaveTrack >(); // Try to find wave tracks to record into. (If any are selected, // try to choose only from them; else if wave tracks exist, may record into any.) existingTracks = ChooseExistingRecordingTracks(*p, true); - if (!existingTracks.empty()) { - // selt0 is now: max(selection-start, end-of-selected-wavetracks) - t0 = selt0; - } + if ( !existingTracks.empty() ) + t0 = std::max( t0, + ( trackRange + &Track::IsSelected ).max( &Track::GetEndTime ) ); else { - // allt0 is: max(selection-start, end-of-all-tracks) - // Use end time of all wave tracks if recording to non-selected - t0 = allt0; existingTracks = ChooseExistingRecordingTracks(*p, false); + t0 = std::max( t0, trackRange.max( &Track::GetEndTime ) ); // If suitable tracks still not found, will record into NEW ones, // but the choice of t0 does not depend on that. } @@ -1203,16 +1149,9 @@ bool ControlToolBar::DoRecord(AudacityProject &project, bool recordingNameCustom, useTrackNumber, useDateStamp, useTimeStamp; wxString defaultTrackName, defaultRecordingTrackName; - // Count the tracks. - int numTracks = 0; - - TrackList *trackList = p->GetTracks(); - TrackListIterator it(trackList); - for (Track *tt = it.First(); tt; tt = it.Next()) { - if (tt->GetKind() == Track::Wave && !tt->GetLinked()) - numTracks++; - } - numTracks++; + // Count the tracks. + const auto trackList = p->GetTracks(); + auto numTracks = trackList->Leaders().size(); auto recordingChannels = std::max(1L, gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2)); @@ -1242,7 +1181,7 @@ bool ControlToolBar::DoRecord(AudacityProject &project, wxString nameSuffix = wxString(wxT("")); if (useTrackNumber) { - nameSuffix += wxString::Format(wxT("%d"), numTracks + c); + nameSuffix += wxString::Format(wxT("%d"), 1 + numTracks + c); } if (useDateStamp) { @@ -1400,40 +1339,20 @@ void ControlToolBar::SetupCutPreviewTracks(double WXUNUSED(playStart), double cu ClearCutPreviewTracks(); AudacityProject *p = GetActiveProject(); if (p) { - auto cutPreviewTracks = TrackList::Create(); - // Find first selected track (stereo or mono) and duplicate it - const Track *track1 = NULL, *track2 = NULL; - TrackListIterator it(p->GetTracks()); - for (Track *t = it.First(); t; t = it.Next()) - { - if (t->GetSelected() && - (t->GetKind() == Track::Wave -#ifdef EXPERIMENTAL_MIDI_OUT - || t->GetKind() == Track::Note -#endif - )) - { - track1 = t; - track2 = t->GetLink(); + auto trackRange = p->GetTracks()->Selected(); + if( !trackRange.empty() ) { + auto cutPreviewTracks = TrackList::Create(); + for (const auto track1 : trackRange) { // Duplicate and change tracks // Clear has a very small chance of throwing - auto new1 = track1->Duplicate(); - new1->Clear(cutStart, cutEnd); - decltype(new1) new2{}; - if (track2) - { - new2 = track2->Duplicate(); - new2->Clear(cutStart, cutEnd); - } - // use NOTHROW-GUARANTEE: - cutPreviewTracks->Add(std::move(new1)); - if (track2) - cutPreviewTracks->Add(std::move(new2)); + auto newTrack = track1->Duplicate(); + newTrack->Clear(cutStart, cutEnd); + cutPreviewTracks->Add(std::move(newTrack)); } - } - if( track1 ) + // use NOTHROW-GUARANTEE: mCutPreviewTracks = cutPreviewTracks; + } } } diff --git a/src/toolbars/TranscriptionToolBar.cpp b/src/toolbars/TranscriptionToolBar.cpp index c7ce39010..ee40bc73a 100644 --- a/src/toolbars/TranscriptionToolBar.cpp +++ b/src/toolbars/TranscriptionToolBar.cpp @@ -279,14 +279,7 @@ void TranscriptionToolBar::EnableDisableButtons() AudacityProject *p = GetActiveProject(); if (!p) return; // Is anything selected? - bool selection = false; - TrackListIterator iter(p->GetTracks()); - for (Track *t = iter.First(); t; t = iter.Next()) - if (t->GetSelected()) { - selection = true; - break; - } - selection &= (p->GetSel0() < p->GetSel1()); + auto selection = p->GetSel0() < p->GetSel1() && p->GetTracks()->Selected(); mButtons[TTB_Calibrate]->SetEnabled(selection); #endif @@ -458,13 +451,9 @@ void TranscriptionToolBar::PlayAtSpeed(bool looped, bool cutPreview) // VariSpeed play reuses Scrubbing. bool bFixedSpeedPlay = !gPrefs->ReadBool(wxT("/AudioIO/VariSpeedPlay"), true); // Scrubbing doesn't support note tracks, but the fixed-speed method using time tracks does. - TrackListIterator iter(p->GetTracks()); - for (Track *t = iter.First(); t; t = iter.Next()) { - if (t->GetKind() == Track::Note) { - bFixedSpeedPlay = true; - break; - } - } + if (p->GetTracks()->Any()) + bFixedSpeedPlay = true; + // Scrubbing only supports straight through play. // So if looped or cutPreview, we have to fall back to fixed speed. bFixedSpeedPlay = bFixedSpeedPlay || looped || cutPreview; @@ -560,10 +549,7 @@ void TranscriptionToolBar::OnStartOn(wxCommandEvent & WXUNUSED(event)) mVk->AdjustThreshold(GetSensitivity()); AudacityProject *p = GetActiveProject(); - TrackList *tl = p->GetTracks(); - TrackListOfKindIterator iter(Track::Wave, tl); - - Track *t = iter.First(); //Make a track + auto t = *p->GetTracks()->Any< const WaveTrack >().begin(); if(t ) { auto wt = static_cast(t); sampleCount start, len; @@ -593,11 +579,8 @@ void TranscriptionToolBar::OnStartOff(wxCommandEvent & WXUNUSED(event)) mVk->AdjustThreshold(GetSensitivity()); AudacityProject *p = GetActiveProject(); - TrackList *tl = p->GetTracks(); - TrackListOfKindIterator iter(Track::Wave, tl); - SetButton(false, mButtons[TTB_StartOff]); - Track *t = iter.First(); //Make a track + auto t = *p->GetTracks()->Any< const WaveTrack >().begin(); if(t) { auto wt = static_cast(t); sampleCount start, len; @@ -628,10 +611,7 @@ void TranscriptionToolBar::OnEndOn(wxCommandEvent & WXUNUSED(event)) mVk->AdjustThreshold(GetSensitivity()); AudacityProject *p = GetActiveProject(); - TrackList *tl = p->GetTracks(); - TrackListOfKindIterator iter(Track::Wave, tl); - - Track *t = iter.First(); //Make a track + auto t = *p->GetTracks()->Any< const WaveTrack >().begin(); if(t) { auto wt = static_cast(t); sampleCount start, len; @@ -665,10 +645,8 @@ void TranscriptionToolBar::OnEndOff(wxCommandEvent & WXUNUSED(event)) } mVk->AdjustThreshold(GetSensitivity()); AudacityProject *p = GetActiveProject(); - TrackList *tl = p->GetTracks(); - TrackListOfKindIterator iter(Track::Wave, tl); - Track *t = iter.First(); //Make a track + auto t = *p->GetTracks()->Any< const WaveTrack >().begin(); if(t) { auto wt = static_cast(t); sampleCount start, len; @@ -706,30 +684,25 @@ void TranscriptionToolBar::OnSelectSound(wxCommandEvent & WXUNUSED(event)) TrackList *tl = p->GetTracks(); - TrackListOfKindIterator iter(Track::Wave, tl); + if(auto wt = *tl->Any().begin()) { + sampleCount start, len; + GetSamples(wt, &start, &len); - Track *t = iter.First(); //Make a track - if(t) - { - auto wt = static_cast(t); - sampleCount start, len; - GetSamples(wt, &start, &len); + //Adjust length to end if selection is null + //if(len == 0) + //len = wt->GetSequence()->GetNumSamples()-start; - //Adjust length to end if selection is null - //if(len == 0) - //len = wt->GetSequence()->GetNumSamples()-start; + double rate = wt->GetRate(); + auto newstart = mVk->OffBackward(*wt, start, start); + auto newend = + mVk->OffForward(*wt, start + len, (int)(tl->GetEndTime() * rate)); - double rate = wt->GetRate(); - auto newstart = mVk->OffBackward(*wt, start, start); - auto newend = - mVk->OffForward(*wt, start + len, (int)(tl->GetEndTime() * rate)); + //reset the selection bounds. + p->SetSel0(newstart.as_double() / rate); + p->SetSel1(newend.as_double() / rate); + p->RedrawProject(); - //reset the selection bounds. - p->SetSel0(newstart.as_double() / rate); - p->SetSel1(newend.as_double() / rate); - p->RedrawProject(); - - } + } SetButton(false,mButtons[TTB_SelectSound]); } @@ -748,29 +721,24 @@ void TranscriptionToolBar::OnSelectSilence(wxCommandEvent & WXUNUSED(event)) TrackList *tl = p->GetTracks(); - TrackListOfKindIterator iter(Track::Wave, tl); + if(auto wt = *tl->Any().begin()) { + sampleCount start, len; + GetSamples(wt, &start, &len); - Track *t = iter.First(); //Make a track - if(t) - { - auto wt = static_cast(t); - sampleCount start, len; - GetSamples(wt, &start, &len); + //Adjust length to end if selection is null + //if(len == 0) + //len = wt->GetSequence()->GetNumSamples()-start; + double rate = wt->GetRate(); + auto newstart = mVk->OnBackward(*wt, start, start); + auto newend = + mVk->OnForward(*wt, start + len, (int)(tl->GetEndTime() * rate)); - //Adjust length to end if selection is null - //if(len == 0) - //len = wt->GetSequence()->GetNumSamples()-start; - double rate = wt->GetRate(); - auto newstart = mVk->OnBackward(*wt, start, start); - auto newend = - mVk->OnForward(*wt, start + len, (int)(tl->GetEndTime() * rate)); + //reset the selection bounds. + p->SetSel0(newstart.as_double() / rate); + p->SetSel1(newend.as_double() / rate); + p->RedrawProject(); - //reset the selection bounds. - p->SetSel0(newstart.as_double() / rate); - p->SetSel1(newend.as_double() / rate); - p->RedrawProject(); - - } + } SetButton(false,mButtons[TTB_SelectSilence]); @@ -790,26 +758,21 @@ void TranscriptionToolBar::OnCalibrate(wxCommandEvent & WXUNUSED(event)) AudacityProject *p = GetActiveProject(); TrackList *tl = p->GetTracks(); - TrackListOfKindIterator iter(Track::Wave, tl); - Track *t = iter.First(); //Get a track + if(auto wt = *tl->Any().begin()) { + sampleCount start, len; + GetSamples(wt, &start, &len); - if(t) - { - auto wt = static_cast(t); - sampleCount start, len; - GetSamples(wt, &start, &len); + mVk->CalibrateNoise(*wt, start, len); + mVk->AdjustThreshold(3); - mVk->CalibrateNoise(*wt, start, len); - mVk->AdjustThreshold(3); + mButtons[TTB_StartOn]->Enable(); + mButtons[TTB_StartOff]->Enable(); + mButtons[TTB_EndOn]->Enable(); + mButtons[TTB_EndOff]->Enable(); + //mThresholdSensitivity->Set(3); - mButtons[TTB_StartOn]->Enable(); - mButtons[TTB_StartOff]->Enable(); - mButtons[TTB_EndOn]->Enable(); - mButtons[TTB_EndOff]->Enable(); - //mThresholdSensitivity->Set(3); - - SetButton(false,mButtons[TTB_Calibrate]); - } + SetButton(false,mButtons[TTB_Calibrate]); + } mButtons[TTB_StartOn]->Enable(); mButtons[TTB_StartOff]->Enable(); @@ -844,77 +807,71 @@ void TranscriptionToolBar::OnAutomateSelection(wxCommandEvent & WXUNUSED(event)) mVk->AdjustThreshold(GetSensitivity()); AudacityProject *p = GetActiveProject(); TrackList *tl = p->GetTracks(); - TrackListOfKindIterator iter(Track::Wave, tl); + if(auto wt = *tl->Any().begin()) { + sampleCount start, len; + GetSamples(wt, &start, &len); - Track *t = iter.First(); //Make a track - if(t) + //Adjust length to end if selection is null + if(len == 0) { - auto wt = static_cast(t); - sampleCount start, len; - GetSamples(wt, &start, &len); - - //Adjust length to end if selection is null - if(len == 0) - { - len = start; - start = 0; - } - sampleCount lastlen = 0; - double newStartPos, newEndPos; - - - //This is the minumum word size in samples (.05 is 50 ms) - int minWordSize = (int)(wt->GetRate() * .05); - - //Continue until we have processed the entire - //region, or we are making no progress. - while(len > 0 && lastlen != len) - { - - lastlen = len; - - auto newStart = mVk->OnForward(*wt, start, len); - - //JKC: If no start found then don't add any labels. - if( newStart==start) - break; - - //Adjust len by the NEW start position - len -= (newStart - start); - - //Adjust len by the minimum word size - len -= minWordSize; - - - - //OK, now we have found a NEW starting point. A 'word' should be at least - //50 ms long, so jump ahead minWordSize - - auto newEnd = - mVk->OffForward(*wt, newStart + minWordSize, len); - - //If newEnd didn't move, we should give up, because - // there isn't another end before the end of the selection. - if(newEnd == (newStart + minWordSize)) - break; - - - //Adjust len by the NEW word end - len -= (newEnd - newStart); - - //Calculate the start and end of the words, in seconds - newStartPos = newStart.as_double() / wt->GetRate(); - newEndPos = newEnd.as_double() / wt->GetRate(); - - - //Increment - start = newEnd; - - p->DoAddLabel(SelectedRegion(newStartPos, newEndPos)); - p->RedrawProject(); - } - SetButton(false, mButtons[TTB_AutomateSelection]); + len = start; + start = 0; } + sampleCount lastlen = 0; + double newStartPos, newEndPos; + + //This is the minumum word size in samples (.05 is 50 ms) + int minWordSize = (int)(wt->GetRate() * .05); + + //Continue until we have processed the entire + //region, or we are making no progress. + while(len > 0 && lastlen != len) + { + + lastlen = len; + + auto newStart = mVk->OnForward(*wt, start, len); + + //JKC: If no start found then don't add any labels. + if( newStart==start) + break; + + //Adjust len by the NEW start position + len -= (newStart - start); + + //Adjust len by the minimum word size + len -= minWordSize; + + + + //OK, now we have found a NEW starting point. A 'word' should be at least + //50 ms long, so jump ahead minWordSize + + auto newEnd = + mVk->OffForward(*wt, newStart + minWordSize, len); + + //If newEnd didn't move, we should give up, because + // there isn't another end before the end of the selection. + if(newEnd == (newStart + minWordSize)) + break; + + + //Adjust len by the NEW word end + len -= (newEnd - newStart); + + //Calculate the start and end of the words, in seconds + newStartPos = newStart.as_double() / wt->GetRate(); + newEndPos = newEnd.as_double() / wt->GetRate(); + + + //Increment + start = newEnd; + + p->DoAddLabel(SelectedRegion(newStartPos, newEndPos)); + p->RedrawProject(); + } + SetButton(false, mButtons[TTB_AutomateSelection]); + } } void TranscriptionToolBar::OnMakeLabel(wxCommandEvent & WXUNUSED(event))