From f276373f3cc60bdd7980652f339a76809a47407c Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 25 Dec 2017 16:22:24 -0500 Subject: [PATCH 01/10] Remove GetLink(ed) in clip-moving functions --- src/Menus.cpp | 122 ++++++++++++++++++++++++++------------------------ src/Menus.h | 2 +- 2 files changed, 65 insertions(+), 59 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 2b895b3c9..f5e7b6b4f 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -3856,13 +3856,14 @@ double MenuCommandHandler::OnClipMove auto t0 = selectedRegion.t0(); - state.capturedClip = wt->GetClipAtTime( t0 ); - if (state.capturedClip == nullptr && track->GetLinked() && track->GetLink()) { - // the clips in the right channel may be different from the left - track = track->GetLink(); - wt = static_cast(track); - state.capturedClip = wt->GetClipAtTime(t0); + // Find the first channel that has a clip at time t0 + for (auto channel : TrackList::Channels(wt) ) { + if( nullptr != (state.capturedClip = channel->GetClipAtTime( t0 )) ) { + wt = channel; + break; + } } + if (state.capturedClip == nullptr) return 0.0; @@ -6906,34 +6907,29 @@ int MenuCommandHandler::FindClips std::vector results; int nTracksSearched = 0; - int trackNum = 1; - for (auto track : tracks->Leaders()) { - if (track->GetKind() == Track::Wave && (!anyWaveTracksSelected || track->GetSelected())) { - auto waveTrack = static_cast(track); - bool stereoAndDiff = waveTrack->GetLinked() && !ChannelsHaveSameClipBoundaries(waveTrack); + auto leaders = tracks->Leaders(); + auto range = leaders.Filter(); + if (anyWaveTracksSelected) + range = range + &Track::GetSelected; + for (auto waveTrack : range) { + bool stereoAndDiff = ChannelsHaveDifferentClipBoundaries(waveTrack); - auto result = next ? FindNextClip(project, waveTrack, t0, t1) : - FindPrevClip(project, waveTrack, t0, t1); + auto range = stereoAndDiff + ? TrackList::Channels( waveTrack ) + : TrackList::SingletonRange( waveTrack ); + + for ( auto wt : range ) { + auto result = next ? FindNextClip(project, wt, t0, t1) : + FindPrevClip(project, wt, t0, t1); if (result.found) { - result.trackNum = trackNum; + result.trackNum = + 1 + std::distance( leaders.begin(), leaders.find( waveTrack ) ); result.channel = stereoAndDiff; results.push_back(result); } - if (stereoAndDiff) { - auto waveTrack2 = static_cast(track->GetLink()); - auto result = next ? FindNextClip(project, waveTrack2, t0, t1) : - FindPrevClip(project, waveTrack2, t0, t1); - if (result.found) { - result.trackNum = trackNum; - result.channel = stereoAndDiff; - results.push_back(result); - } - } - - nTracksSearched++; } - trackNum++; + nTracksSearched++; } @@ -6974,14 +6970,13 @@ int MenuCommandHandler::FindClips return nTracksSearched; // can be used for screen reader messages if required } -// Whether the two channels of a stereo track have the same clips -bool MenuCommandHandler::ChannelsHaveSameClipBoundaries(const WaveTrack* wt) -{ - bool sameClips = false; - - if (wt->GetLinked() && wt->GetLink()) { - auto& left = wt->GetClips(); - auto& right = static_cast(wt->GetLink())->GetClips(); +namespace { + bool TwoChannelsHaveSameBoundaries + ( const WaveTrack *first, const WaveTrack *second ) + { + bool sameClips = false; + auto& left = first->GetClips(); + auto& right = second->GetClips(); if (left.size() == right.size()) { sameClips = true; for (unsigned int i = 0; i < left.size(); i++) { @@ -6992,9 +6987,24 @@ bool MenuCommandHandler::ChannelsHaveSameClipBoundaries(const WaveTrack* wt) } } } + return sameClips; + } +} + +bool MenuCommandHandler::ChannelsHaveDifferentClipBoundaries( + const WaveTrack* wt) +{ + // This is quadratic in the number of channels + auto channels = TrackList::Channels(wt); + while (!channels.empty()) { + auto channel = *channels.first++; + for (auto other : channels) { + if (!TwoChannelsHaveSameBoundaries(channel, other)) + return true; + } } - return sameClips; + return false; } void MenuCommandHandler::OnSelectPrevClip(const CommandContext &context) @@ -8120,34 +8130,29 @@ int MenuCommandHandler::FindClipBoundaries std::vector results; int nTracksSearched = 0; - int trackNum = 1; - for (auto track : tracks->Leaders()) { - if (track->GetKind() == Track::Wave && (!anyWaveTracksSelected || track->GetSelected())) { - auto waveTrack = static_cast(track); - bool stereoAndDiff = waveTrack->GetLinked() && !ChannelsHaveSameClipBoundaries(waveTrack); + auto leaders = tracks->Leaders(); + auto range = leaders.Filter(); + if (anyWaveTracksSelected) + range = range + &Track::GetSelected; + for (auto waveTrack : range) { + bool stereoAndDiff = ChannelsHaveDifferentClipBoundaries(waveTrack); - auto result = next ? FindNextClipBoundary(waveTrack, time) : - FindPrevClipBoundary(waveTrack, time); + auto range = stereoAndDiff + ? TrackList::Channels( waveTrack ) + : TrackList::SingletonRange(waveTrack); + + for (auto wt : range) { + auto result = next ? FindNextClipBoundary(wt, time) : + FindPrevClipBoundary(wt, time); if (result.nFound > 0) { - result.trackNum = trackNum; + result.trackNum = + 1 + std::distance( leaders.begin(), leaders.find( waveTrack ) ); result.channel = stereoAndDiff; results.push_back(result); } - if (stereoAndDiff) { - waveTrack = static_cast(track->GetLink()); - result = next ? FindNextClipBoundary(waveTrack, time) : - FindPrevClipBoundary(waveTrack, time); - if (result.nFound > 0) { - result.trackNum = trackNum; - result.channel = stereoAndDiff; - results.push_back(result); - } - } - - nTracksSearched++; } - trackNum++; + nTracksSearched++; } @@ -8215,7 +8220,8 @@ wxString MenuCommandHandler::FoundTrack::ComposeTrackName() const : name; auto longName = shortName; if (channel) { - if ( waveTrack->GetLinked() ) + // TODO: more-than-two-channels-message + if ( waveTrack->IsLeader() ) /* i18n-hint: given the name of a track, specify its left channel */ longName = wxString::Format(_("%s left"), shortName); else diff --git a/src/Menus.h b/src/Menus.h index 094208ad1..06181eed4 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -357,7 +357,7 @@ FoundClip FindPrevClip int FindClips (AudacityProject &project, double t0, double t1, bool next, std::vector& results); -bool ChannelsHaveSameClipBoundaries(const WaveTrack* wt); +bool ChannelsHaveDifferentClipBoundaries(const WaveTrack* wt); void OnSelectPrevClip(const CommandContext &context ); void OnSelectNextClip(const CommandContext &context ); void OnSelectClip(AudacityProject &project, bool next); From 4c75175e4120fb045a0a1c583829eed8facfe8e3 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sat, 7 Jan 2017 22:07:23 -0500 Subject: [PATCH 02/10] Remove GetLink(ed) in Normalize effect... ... It's much simpler and should be easy to generalize, but what would be appropriate messages? --- src/effects/Normalize.cpp | 183 +++++++++++++++++--------------------- 1 file changed, 83 insertions(+), 100 deletions(-) diff --git a/src/effects/Normalize.cpp b/src/effects/Normalize.cpp index af9d5b369..fef42cebd 100644 --- a/src/effects/Normalize.cpp +++ b/src/effects/Normalize.cpp @@ -219,10 +219,6 @@ bool EffectNormalize::Process() //Iterate over each track this->CopyInputTracks(); // Set up mOutputTracks. bool bGoodResult = true; - SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks.get()); - WaveTrack *track = (WaveTrack *) iter.First(); - WaveTrack *prevTrack; - prevTrack = track; double progress = 0; wxString topMsg; if(mDC && mGain) @@ -234,8 +230,10 @@ bool EffectNormalize::Process() else if(!mDC && !mGain) topMsg = _("Not doing anything...\n"); // shouldn't get here - while (track) { + for ( auto track : mOutputTracks->Selected< WaveTrack >() + + ( mStereoInd ? &Track::Any : &Track::IsLeader ) ) { //Get start and end times from track + // PRL: No accounting for multiple channels? double trackStart = track->GetStartTime(); double trackEnd = track->GetEndTime(); @@ -244,121 +242,106 @@ bool EffectNormalize::Process() mCurT0 = mT0 < trackStart? trackStart: mT0; mCurT1 = mT1 > trackEnd? trackEnd: mT1; + auto range = mStereoInd + ? TrackList::SingletonRange(track) + : TrackList::Channels(track); + // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { - wxString msg; - auto trackName = track->GetName(); + wxString trackName = track->GetName(); - if(!track->GetLinked() || mStereoInd) - msg = - topMsg + wxString::Format( _("Analyzing: %s"), trackName ); + float extent; +#ifdef EXPERIMENTAL_R128_NORM + if (mUseLoudness) + // Loudness: use sum of both tracks. + // As a result, stereo tracks appear about 3 LUFS louder, + // as specified. + extent = 0; else - msg = - topMsg + wxString::Format( _("Analyzing first track of stereo pair: %s"), trackName ); - float offset, extent; - bGoodResult = AnalyseTrack(track, msg, progress, offset, extent); - if (!bGoodResult ) - break; - if(!track->GetLinked() || mStereoInd) { +#endif + // Will compute a maximum + extent = std::numeric_limits::lowest(); + std::vector offsets; + + wxString msg; + if (range.size() == 1) // mono or 'stereo tracks independently' - if( (extent > 0) && mGain ) - { - mMult = ratio / extent; + msg = topMsg + + wxString::Format( _("Analyzing: %s"), trackName ); + else + msg = topMsg + + // TODO: more-than-two-channels-message + wxString::Format( _("Analyzing first track of stereo pair: %s"), trackName); + + // Analysis loop over channels collects offsets and extent + for (auto channel : range) { + float offset, extent2; + bGoodResult = + AnalyseTrack( channel, msg, progress, offset, extent2 ); + if ( ! bGoodResult ) + goto break2; #ifdef EXPERIMENTAL_R128_NORM - if(mUseLoudness) { - // LUFS is defined as -0.691 dB + 10*log10(sum(channels)) - mMult /= 0.8529037031; - // LUFS are related to square values so the multiplier must be the root. - mMult = sqrt(ratio / extent); - } -#endif - } + if (mUseLoudness) + extent += extent2; else - mMult = 1.0; - - msg = - topMsg + wxString::Format( _("Processing: %s"), trackName ); - - if(track->GetLinked() || prevTrack->GetLinked()) // only get here if there is a linked track but we are processing independently - msg = - topMsg + wxString::Format( _("Processing stereo channels independently: %s"), trackName ); - - if (!ProcessOne(track, msg, progress, offset)) - { - bGoodResult = false; - break; - } +#endif + extent = std::max( extent, extent2 ); + offsets.push_back(offset); + // TODO: more-than-two-channels-message + msg = topMsg + + wxString::Format( _("Analyzing second track of stereo pair: %s"), trackName ); } - else { - // we have a linked stereo track - // so we need to find it's min, max and offset - // as they are needed to calc the multiplier for both tracks - - track = (WaveTrack *) iter.Next(); // get the next one - msg = - topMsg + wxString::Format( _("Analyzing second track of stereo pair: %s"), trackName ); - - float offset2, extent2; - bGoodResult = AnalyseTrack(track, msg, progress, offset2, extent2); - if ( !bGoodResult ) - break; + // Compute the multiplier using extent + if( (extent > 0) && mGain ) { + mMult = ratio / extent; #ifdef EXPERIMENTAL_R128_NORM - if (mUseLoudness) { - // Loudness: use sum of both tracks. - // As a result, stereo tracks appear about 3 LUFS louder, as specified. - extent = extent + extent2; + if(mUseLoudness) { + // PRL: See commit 9cbb67a for the origin of the next line, + // which has no effect because mMult is again overwritten. What + // was the intent? + // LUFS is defined as -0.691 dB + 10*log10(sum(channels)) - extent *= 0.8529037031; + mMult /= 0.8529037031; + // LUFS are related to square values so the multiplier must be the root. + mMult = sqrt(ratio / extent); } - else { - // Peak: use maximum of both tracks. - extent = fmax(extent, extent2); - } -#else - // Peak: use maximum of both tracks. - extent = fmax(extent, extent2); #endif + } + else + mMult = 1.0; - if( (extent > 0) && mGain ) - { - mMult = ratio / extent; // we need to use this for both linked tracks -#ifdef EXPERIMENTAL_R128_NORM - if(mUseLoudness) { - // LUFS are related to square values so the multiplier must be the root. - mMult = sqrt(mMult); - } -#endif - } + if (range.size() == 1) { + if (TrackList::Channels(track).size() == 1) + // really mono + msg = topMsg + + wxString::Format( _("Processing: %s"), trackName ); else - mMult = 1.0; + //'stereo tracks independently' + // TODO: more-than-two-channels-message + msg = topMsg + + wxString::Format( _("Processing stereo channels independently: %s"), trackName); + } + else + msg = topMsg + + // TODO: more-than-two-channels-message + wxString::Format( _("Processing first track of stereo pair: %s"), trackName); - track = (WaveTrack *) iter.Prev(); // go back to the first linked one - msg = - topMsg + wxString::Format( _("Processing first track of stereo pair: %s"), trackName ); - - if (!ProcessOne(track, msg, progress, offset)) - { - bGoodResult = false; - break; - } - track = (WaveTrack *) iter.Next(); // go to the second linked one - msg = - topMsg + wxString::Format( _("Processing second track of stereo pair: %s"), trackName ); - - if (!ProcessOne(track, msg, progress, offset2)) - { - bGoodResult = false; - break; - } + // Use multiplier in the second, processing loop over channels + auto pOffset = offsets.begin(); + for (auto channel : range) { + if (false == + (bGoodResult = ProcessOne(channel, msg, progress, *pOffset++)) ) + goto break2; + // TODO: more-than-two-channels-message + msg = topMsg + + wxString::Format( _("Processing second track of stereo pair: %s"), trackName); } } - - //Iterate to the next track - prevTrack = track; - track = (WaveTrack *) iter.Next(); } + break2: + this->ReplaceProcessedTracks(bGoodResult); return bGoodResult; } From a68184f81dba3f08199e80daea907ae6c7cfaffa Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 12 Jan 2017 12:47:07 -0500 Subject: [PATCH 03/10] Remove GetLink(ed) in track panel drawing and refresh --- src/TrackPanel.cpp | 126 ++++++++++++++++++++++----------------------- src/TrackPanel.h | 18 ++++--- 2 files changed, 73 insertions(+), 71 deletions(-) diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index db2755daa..5183189a1 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -1078,23 +1078,17 @@ void TrackPanel::RefreshTrack(Track *trk, bool refreshbacking) if (!trk) return; - Track *link = trk->GetLink(); - - if (link && !trk->GetLinked()) { - trk = link; - link = trk->GetLink(); - } + trk = *GetTracks()->FindLeader(trk); + auto height = + TrackList::Channels(trk).sum( &Track::GetHeight ) + - kTopInset - kShadowThickness; // subtract insets and shadows from the rectangle, but not border // This matters because some separators do paint over the border wxRect rect(kLeftInset, -mViewInfo->vpos + trk->GetY() + kTopInset, GetRect().GetWidth() - kLeftInset - kRightInset - kShadowThickness, - trk->GetHeight() - kTopInset - kShadowThickness); - - if (link) { - rect.height += link->GetHeight(); - } + height); if( refreshbacking ) { @@ -1184,41 +1178,24 @@ void TrackPanel::DrawEverythingElse(TrackPanelDrawingContext &context, trackRect.y = t->GetY() - mViewInfo->vpos; trackRect.height = t->GetHeight(); - // If this track is linked to the next one, display a common - // border for both, otherwise draw a normal border - wxRect rect = trackRect; - bool skipBorder = false; - Track *l = t->GetLink(); - - if (l && t->GetLinked()) { - rect.height += l->GetHeight(); - } - else if (l && trackRect.y >= 0) { - skipBorder = true; - } - + auto leaderTrack = *GetTracks()->FindLeader( t ); // If the previous track is linked to this one but isn't on the screen // (and thus would have been skipped) we need to draw that track's border // instead. - const Track *borderTrack = t; - wxRect borderRect = rect; + bool drawBorder = (t == leaderTrack || trackRect.y < 0); - if (l && !t->GetLinked() && trackRect.y < 0) - { - borderTrack = l; + if (drawBorder) { + wxRect teamRect = trackRect; + teamRect.y = leaderTrack->GetY() - mViewInfo->vpos; + // danger with pending tracks? + teamRect.height = + TrackList::Channels(leaderTrack) + .sum( &Track::GetHeight ); - borderRect = trackRect; - borderRect.y = l->GetY() - mViewInfo->vpos; - borderRect.height = l->GetHeight(); - - borderRect.height += t->GetHeight(); - } - - if (!skipBorder) { if (mAx->IsFocused(t)) { - focusRect = borderRect; + focusRect = teamRect; } - DrawOutside(context, borderTrack, borderRect); + DrawOutside(context, leaderTrack, teamRect); } // Believe it or not, we can speed up redrawing if we don't @@ -1675,7 +1652,9 @@ void TrackInfo::Status1DrawFunction /// stereo and what sample rate it's using. auto rate = wt ? wt->GetRate() : 44100.0; wxString s; - if (!wt || (wt->GetLinked())) + if (!pTrack || TrackList::Channels(pTrack).size() > 1) + // TODO: more-than-two-channels-message + // more appropriate strings s = _("Stereo, %dHz"); else { if (wt->GetChannel() == Track::MonoChannel) @@ -1714,6 +1693,15 @@ void TrackPanel::DrawOutside wxRect rect = rec; DrawOutsideOfTrack(context, t, rect); + { + auto channels = TrackList::Channels(t); + // omit last (perhaps, only) channel + --channels.second; + for (auto channel : channels) + // draw the sash below this channel + DrawSash(channel, dc, rect); + } + // Now exclude left, right, and top insets rect.x += kLeftInset; rect.y += kTopInset; @@ -1733,7 +1721,16 @@ void TrackPanel::DrawOutside // TrackArtist::DrawSyncLockTiles(dc, tileFill); //} - DrawBordersAroundTrack(t, dc, rect, labelw, vrul); + DrawBordersAroundTrack(dc, rect, vrul); + { + auto channels = TrackList::Channels(t); + // omit last (perhaps, only) channel + --channels.second; + for (auto channel : channels) + // draw the sash below this channel + DrawBordersAroundSash(channel, dc, rect, labelw); + } + DrawShadow(t, dc, rect); } @@ -1777,16 +1774,17 @@ void TrackPanel::DrawOutsideOfTrack side.x += side.width - kRightInset; side.width = kRightInset; dc->DrawRectangle(side); +} - // Area between tracks of stereo group - if (t->GetLinked()) { - // Paint the channel separator over (what would be) the shadow of the top - // channel, and the top inset of the bottom channel - side = rect; - side.y += t->GetHeight() - kShadowThickness; - side.height = kTopInset + kShadowThickness; - dc->DrawRectangle(side); - } +void TrackPanel::DrawSash(const Track * t, wxDC * dc, const wxRect & rect) +{ + // Area between channels of a group + // Paint the channel separator over (what would be) the shadow of this + // channel, and the top inset of the following channel + wxRect side = rect; + side.y = t->GetY() - mViewInfo->vpos + t->GetHeight() - kShadowThickness; + side.height = kTopInset + kShadowThickness; + dc->DrawRectangle(side); } void TrackPanel::SetBackgroundCell @@ -1979,8 +1977,8 @@ void TrackPanel::VerticalScroll( float fracPosition){ // Given rectangle excludes the insets left, right, and top // Draw a rectangular border and also a vertical separator of track controls // from the rest (ruler and proper track area) -void TrackPanel::DrawBordersAroundTrack(const Track * t, wxDC * dc, - const wxRect & rect, const int labelw, +void TrackPanel::DrawBordersAroundTrack(wxDC * dc, + const wxRect & rect, const int vrul) { // Border around track and label area @@ -1991,21 +1989,21 @@ void TrackPanel::DrawBordersAroundTrack(const Track * t, wxDC * dc, rect.width - kShadowThickness, rect.height - kShadowThickness); + // between vruler and TrackInfo AColor::Line(*dc, vrul, rect.y, vrul, rect.y + rect.height - 1); +} - // The lines at bottom of 1st track and top of second track of stereo group - // Possibly replace with DrawRectangle to add left border. - if (t->GetLinked()) { - // The given rect has had the top inset subtracted - int h1 = rect.y + t->GetHeight() - kTopInset; - // h1 is the top coordinate of the second tracks' rectangle - // Draw (part of) the bottom border of the top channel and top border of the bottom - // At left it extends between the vertical rulers too - // These lines stroke over what is otherwise "border" of each channel - AColor::Line(*dc, labelw, h1 - kBottomMargin, rect.x + rect.width - 1, h1 - kBottomMargin); - AColor::Line(*dc, labelw, h1 + kTopInset, rect.x + rect.width - 1, h1 + kTopInset); - } +void TrackPanel::DrawBordersAroundSash(const Track * t, wxDC * dc, + const wxRect & rect, const int labelw) +{ + int h1 = t->GetY() - mViewInfo->vpos + t->GetHeight(); + // h1 is the top coordinate of the following channel's rectangle + // Draw (part of) the bottom border of the top channel and top border of the bottom + // At left it extends between the vertical rulers too + // These lines stroke over what is otherwise "border" of each channel + AColor::Line(*dc, labelw, h1 - kBottomMargin, rect.x + rect.width - 1, h1 - kBottomMargin); + AColor::Line(*dc, labelw, h1 + kTopInset, rect.x + rect.width - 1, h1 + kTopInset); } // Given rectangle has insets subtracted left, right, and top diff --git a/src/TrackPanel.h b/src/TrackPanel.h index 6a80ac5ab..91e99acc4 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -373,16 +373,20 @@ protected: void DrawEverythingElse(TrackPanelDrawingContext &context, const wxRegion & region, const wxRect & clip); - void DrawOutside - (TrackPanelDrawingContext &context, - const Track *t, const wxRect & rec); + void DrawOutside( + TrackPanelDrawingContext &context, + const Track *leaderTrack, const wxRect & teamRect); void HighlightFocusedTrack (wxDC* dc, const wxRect &rect); void DrawShadow (const Track *t, wxDC* dc, const wxRect & rect); - void DrawBordersAroundTrack(const Track *t, wxDC* dc, const wxRect & rect, const int labelw, const int vrul); - void DrawOutsideOfTrack - (TrackPanelDrawingContext &context, - const Track *t, const wxRect & rect); + void DrawBordersAroundTrack(wxDC* dc, const wxRect & rect, + const int vrul); + void DrawBordersAroundSash (const Track *t, wxDC* dc, const wxRect & rect, + const int labelw); + void DrawOutsideOfTrack ( + TrackPanelDrawingContext &context, + const Track *t, const wxRect & rect); + void DrawSash (const Track *t, wxDC* dc, const wxRect & rect); public: // Set the object that performs catch-all event handling when the pointer From 518df314ebca6b8fad99c628a996d246614fd319 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 13 Sep 2018 10:17:59 -0400 Subject: [PATCH 04/10] Rewrite GetLink(ed) in vamp effects --- src/effects/vamp/VampEffect.cpp | 40 ++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/effects/vamp/VampEffect.cpp b/src/effects/vamp/VampEffect.cpp index bb6e95b12..e326594c3 100644 --- a/src/effects/vamp/VampEffect.cpp +++ b/src/effects/vamp/VampEffect.cpp @@ -299,25 +299,25 @@ bool VampEffect::Init() { mRate = 0.0; - for (auto left : inputTracks()->Leaders< const WaveTrack >() ) - { - if (mRate == 0.0) - { - mRate = left->GetRate(); - } - - if (left->GetLinked()) - { - auto right = static_cast( left->GetLink() ); + // PRL: this loop checked that channels of a track have the same rate, + // but there was no check that all tracks have one rate, and only the first + // is remembered in mRate. Is that correct? + for (auto leader : inputTracks()->Leaders()) { + auto channelGroup = TrackList::Channels( leader ); + auto rate = (*channelGroup.first++) -> GetRate(); + for(auto channel : channelGroup) { + if (rate != channel->GetRate()) // PRL: Track rate might not match individual clip rates. // So is this check not adequate? - if (left->GetRate() != right->GetRate()) - { - Effect::MessageBox(_("Sorry, Vamp Plug-ins cannot be run on stereo tracks where the individual channels of the track do not match.")); - return false; + { + // TODO: more-than-two-channels-message + Effect::MessageBox(_("Sorry, Vamp Plug-ins cannot be run on stereo tracks where the individual channels of the track do not match.")); + return false; } } + if (mRate == 0.0) + mRate = rate; } if (mRate <= 0.0) @@ -361,22 +361,26 @@ bool VampEffect::Process() std::vector> addedTracks; - for (auto left : inputTracks()->Leaders< const WaveTrack >() ) + for (auto leader : inputTracks()->Leaders()) { + auto channelGroup = TrackList::Channels(leader); + auto left = *channelGroup.first++; + sampleCount lstart, rstart = 0; sampleCount len; GetSamples(left, &lstart, &len); unsigned channels = 1; - const WaveTrack *right{}; - if (left->GetLinked()) + const WaveTrack *right = *channelGroup.first++; + if (right) { - right = static_cast< const WaveTrack* >( left->GetLink() ); channels = 2; GetSamples(right, &rstart, &len); } + // TODO: more-than-two-channels + size_t step = mPlugin->getPreferredStepSize(); size_t block = mPlugin->getPreferredBlockSize(); From 48ae1eb3c98d5a9542ee5a25889f6a9afb0cf47e Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 20 Apr 2017 11:45:18 -0400 Subject: [PATCH 05/10] Rewrite GetLink(ed) in SoundTouch --- src/effects/SoundTouchEffect.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/effects/SoundTouchEffect.cpp b/src/effects/SoundTouchEffect.cpp index dd77863c9..dba8b3e83 100644 --- a/src/effects/SoundTouchEffect.cpp +++ b/src/effects/SoundTouchEffect.cpp @@ -120,10 +120,10 @@ bool EffectSoundTouch::ProcessWithTimeWarper(const TimeWarper &warper) // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { - if (leftTrack->GetLinked()) { + // TODO: more-than-two-channels + auto channels = TrackList::Channels(leftTrack); + if (auto rightTrack = * ++ channels.begin()) { double t; - // Assume linked track is wave - WaveTrack* rightTrack = static_cast(leftTrack->GetLink()); //Adjust bounds by the right tracks markers t = rightTrack->GetStartTime(); From 22c85dd99c882a2d16d3050c86c761859d5422bd Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 20 Apr 2017 11:52:28 -0400 Subject: [PATCH 06/10] Rewrite GetLink(ed) in SBSMS --- src/effects/SBSMSEffect.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/effects/SBSMSEffect.cpp b/src/effects/SBSMSEffect.cpp index ec9023553..a0e1cac0e 100644 --- a/src/effects/SBSMSEffect.cpp +++ b/src/effects/SBSMSEffect.cpp @@ -252,11 +252,11 @@ bool EffectSBSMS::Process() auto start = leftTrack->TimeToLongSamples(mCurT0); auto end = leftTrack->TimeToLongSamples(mCurT1); - WaveTrack* rightTrack = NULL; - if (leftTrack->GetLinked()) { + // TODO: more-than-two-channels + WaveTrack *rightTrack = + * ++ TrackList::Channels(leftTrack).begin(); + if (rightTrack) { double t; - // Assume linked track is wave or null - rightTrack = static_cast(leftTrack->GetLink()); //Adjust bounds by the right tracks markers t = rightTrack->GetStartTime(); From d2a18f01e3e4330cd504b5b2f048471992d963ee Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 11 Jan 2017 20:49:38 -0500 Subject: [PATCH 07/10] Remove use of GetLink(ed) in AudacityProject::OnPaste --- src/Menus.cpp | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index f5e7b6b4f..451fc82b6 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -5710,6 +5710,7 @@ void MenuCommandHandler::OnPaste(const CommandContext &context) bool bPastedSomething = false; auto pC = clipTrackRange.begin(); + size_t nnChannels, ncChannels; while (*pN && *pC) { auto n = *pN; auto c = *pC; @@ -5764,15 +5765,34 @@ void MenuCommandHandler::OnPaste(const CommandContext &context) _("Pasting one type of track into another is not allowed.") }; - // When trying to copy from stereo to mono track, show error and exit - // TODO: Automatically offer user to mix down to mono (unfortunately - // this is not easy to implement - if (c->GetLinked() && !n->GetLinked()) - // Throw, so that any previous changes to the project in this loop - // are discarded. - throw SimpleMessageBoxException{ - _("Copying stereo audio into a mono track is not allowed.") - }; + // We should need this check only each time we visit the leading + // channel + if ( n->IsLeader() ) { + wxASSERT( c->IsLeader() ); // the iteration logic should ensure this + + auto cChannels = TrackList::Channels(c); + ncChannels = cChannels.size(); + auto nChannels = TrackList::Channels(n); + nnChannels = nChannels.size(); + + // When trying to copy from stereo to mono track, show error and exit + // TODO: Automatically offer user to mix down to mono (unfortunately + // this is not easy to implement + if (ncChannels > nnChannels) + { + if (ncChannels > 2) { + // TODO: more-than-two-channels-message + // Re-word the error message + } + // else + + // Throw, so that any previous changes to the project in this loop + // are discarded. + throw SimpleMessageBoxException{ + _("Copying stereo audio into a mono track is not allowed.") + }; + } + } if (!ff) ff = n; @@ -5807,11 +5827,17 @@ void MenuCommandHandler::OnPaste(const CommandContext &context) } ); + --nnChannels; + --ncChannels; + // When copying from mono to stereo track, paste the wave form // to both channels - if (n->GetLinked() && !c->GetLinked()) + // TODO: more-than-two-channels + // This will replicate the last pasted channel as many times as needed + while (nnChannels > 0 && ncChannels == 0) { n = * ++ pN; + --nnChannels; n->TypeSwitch( [&](WaveTrack *wn){ @@ -5827,7 +5853,7 @@ void MenuCommandHandler::OnPaste(const CommandContext &context) ); } - if (bAdvanceClipboard){ + if (bAdvanceClipboard) { prevClip = c; c = * ++ pC; } From ecc869bdbeca5421b7527bda786a99ae0fcc0319 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 11 Jan 2017 14:00:31 -0500 Subject: [PATCH 08/10] Remove GetLink(ed) in TrackPanel resizing code --- src/TrackPanelResizeHandle.cpp | 71 +++++++++++++++++----------------- src/TrackPanelResizeHandle.h | 4 +- src/TrackPanelResizerCell.cpp | 2 +- 3 files changed, 37 insertions(+), 40 deletions(-) diff --git a/src/TrackPanelResizeHandle.cpp b/src/TrackPanelResizeHandle.cpp index 0135fbbf9..089387dc7 100644 --- a/src/TrackPanelResizeHandle.cpp +++ b/src/TrackPanelResizeHandle.cpp @@ -26,6 +26,8 @@ Paul Licameli split from TrackPanel.cpp HitTestPreview TrackPanelResizeHandle::HitPreview(bool bLinked) { + // TODO: more-than-two-channels-message + static wxCursor resizeCursor{ wxCURSOR_SIZENS }; /// When in the resize area we can adjust size or relative size. @@ -59,40 +61,34 @@ UIHandle::Result TrackPanelResizeHandle::Click } TrackPanelResizeHandle::TrackPanelResizeHandle -( const std::shared_ptr &track, int y, const AudacityProject *pProject ) +( const std::shared_ptr &track, int y ) : mpTrack{ track } , mMouseClickY( y ) { - auto tracks = pProject->GetTracks(); - auto prev = * -- tracks->Find(track.get()); - auto next = * ++ tracks->Find(track.get()); + // TODO: more-than-two-channels //STM: Determine whether we should rescale one or two tracks - if (prev && prev->GetLink() == track.get()) { - // mpTrack is the lower track - mInitialTrackHeight = track->GetHeight(); - mInitialActualHeight = track->GetActualHeight(); - mInitialMinimized = track->GetMinimized(); - mInitialUpperTrackHeight = prev->GetHeight(); - mInitialUpperActualHeight = prev->GetActualHeight(); - mMode = IsResizingBelowLinkedTracks; + auto channels = TrackList::Channels(track.get()); + auto last = *channels.rbegin(); + mInitialTrackHeight = last->GetHeight(); + mInitialActualHeight = last->GetActualHeight(); + mInitialMinimized = last->GetMinimized(); + + if (channels.size() > 1) { + auto first = *channels.begin(); + + mInitialUpperTrackHeight = first->GetHeight(); + mInitialUpperActualHeight = first->GetActualHeight(); + + if (track.get() == *channels.rbegin()) + // capturedTrack is the lowest track + mMode = IsResizingBelowLinkedTracks; + else + // capturedTrack is not the lowest track + mMode = IsResizingBetweenLinkedTracks; } - else if (next && track->GetLink() == next) { - // mpTrack is the upper track - mInitialTrackHeight = next->GetHeight(); - mInitialActualHeight = next->GetActualHeight(); - mInitialMinimized = next->GetMinimized(); - mInitialUpperTrackHeight = track->GetHeight(); - mInitialUpperActualHeight = track->GetActualHeight(); - mMode = IsResizingBetweenLinkedTracks; - } - else { - // DM: Save the initial mouse location and the initial height - mInitialTrackHeight = track->GetHeight(); - mInitialActualHeight = track->GetActualHeight(); - mInitialMinimized = track->GetMinimized(); + else mMode = IsResizing; - } } UIHandle::Result TrackPanelResizeHandle::Drag @@ -113,23 +109,24 @@ UIHandle::Result TrackPanelResizeHandle::Drag // This used to be in HandleResizeClick(), but simply clicking // on a resize border would switch the minimized state. if (pTrack->GetMinimized()) { - Track *link = pTrack->GetLink(); + auto channels = TrackList::Channels( pTrack.get() ); + for (auto channel : channels) { + channel->SetHeight(channel->GetHeight()); + channel->SetMinimized(false); + } - pTrack->SetHeight(pTrack->GetHeight()); - pTrack->SetMinimized(false); - - if (link) { - link->SetHeight(link->GetHeight()); - link->SetMinimized(false); + if (channels.size() > 1) { // Initial values must be reset since they weren't based on the // minimized heights. - mInitialUpperTrackHeight = link->GetHeight(); - mInitialTrackHeight = pTrack->GetHeight(); + mInitialUpperTrackHeight = (*channels.begin())->GetHeight(); + mInitialTrackHeight = (*channels.rbegin())->GetHeight(); } } // Common pieces of code for MONO_WAVE_PAN and otherwise. auto doResizeBelow = [&] (Track *prev, bool WXUNUSED(vStereo)) { + // TODO: more-than-two-channels + double proportion = static_cast < double >(mInitialTrackHeight) / (mInitialTrackHeight + mInitialUpperTrackHeight); @@ -150,6 +147,8 @@ UIHandle::Result TrackPanelResizeHandle::Drag }; auto doResizeBetween = [&] (Track *next, bool WXUNUSED(vStereo)) { + // TODO: more-than-two-channels + int newUpperTrackHeight = mInitialUpperTrackHeight + delta; int newTrackHeight = mInitialTrackHeight - delta; diff --git a/src/TrackPanelResizeHandle.h b/src/TrackPanelResizeHandle.h index 50071c641..47ffc8022 100644 --- a/src/TrackPanelResizeHandle.h +++ b/src/TrackPanelResizeHandle.h @@ -22,9 +22,7 @@ class TrackPanelResizeHandle final : public UIHandle TrackPanelResizeHandle(const TrackPanelResizeHandle&) = delete; public: - explicit TrackPanelResizeHandle - ( const std::shared_ptr &pTrack, int y, - const AudacityProject *pProject ); + explicit TrackPanelResizeHandle( const std::shared_ptr &pTrack, int y ); TrackPanelResizeHandle &operator=(const TrackPanelResizeHandle&) = default; diff --git a/src/TrackPanelResizerCell.cpp b/src/TrackPanelResizerCell.cpp index 65db0b55e..970bfba66 100644 --- a/src/TrackPanelResizerCell.cpp +++ b/src/TrackPanelResizerCell.cpp @@ -28,7 +28,7 @@ std::vector TrackPanelResizerCell::HitTest auto pTrack = mpTrack.lock(); if (pTrack) { auto result = std::make_shared( - pTrack, st.state.m_y, pProject ); + pTrack, st.state.m_y ); result = AssignUIHandlePtr(mResizeHandle, result); results.push_back(result); } From c107fb298bfc413b19d607ca3ccc9153cabe768e Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 11 Jan 2017 17:27:00 -0500 Subject: [PATCH 09/10] Remove GetLink(ed) in channel manipulation menu items --- .../wavetrack/ui/WaveTrackControls.cpp | 94 ++++++++++--------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp index 99ebfc8ed..9723d2e22 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp @@ -562,6 +562,8 @@ protected: void OnChannelChange(wxCommandEvent & event); void OnMergeStereo(wxCommandEvent & event); + // TODO: more-than-two-channels + // How should we define generalized channel manipulation operations? void SplitStereo(bool stereo); void OnSwapChannels(wxCommandEvent & event); @@ -603,23 +605,28 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData) (display == WaveTrack::Spectrum) && !bAudioBusy); AudacityProject *const project = ::GetActiveProject(); + TrackList *const tracks = project->GetTracks(); bool unsafe = EffectManager::Get().RealtimeIsActive() && project->IsAudioActive(); - const bool isMono = !pTrack->GetLink(); + auto nChannels = TrackList::Channels(pTrack).size(); + const bool isMono = ( nChannels == 1 ); + const bool isStereo = ( nChannels == 2 ); + // Maybe more than stereo tracks some time? + if ( isMono ) { mpData = static_cast(pUserData); WaveTrack *const pTrack = static_cast(mpData->pTrack); - TrackList *const tracks = project->GetTracks(); auto next = * ++ tracks->Find(pTrack); if (isMono) { const bool canMakeStereo = - (next && !next->GetLinked() - && pTrack->GetKind() == Track::Wave - && next->GetKind() == Track::Wave); + (next && + TrackList::Channels(next).size() == 1 && + track_cast(next)); + pMenu->Enable(OnMergeStereoID, canMakeStereo && !unsafe); int itemId; @@ -647,8 +654,8 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData) return end != std::find(checkedIds.begin(), end, id); }); - - pMenu->Enable(OnSwapChannelsID, !isMono && !unsafe); + // Enable this only for properly stereo tracks: + pMenu->Enable(OnSwapChannelsID, isStereo && !unsafe); pMenu->Enable(OnSplitStereoID, !isMono && !unsafe); #ifndef EXPERIMENTAL_DA @@ -836,11 +843,16 @@ void WaveTrackMenuTable::OnChannelChange(wxCommandEvent & event) /// Merge two tracks into one stereo track ?? void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &) { + AudacityProject *const project = ::GetActiveProject(); + const auto tracks = project->GetTracks(); + WaveTrack *const pTrack = static_cast(mpData->pTrack); wxASSERT(pTrack); + + auto partner = static_cast< WaveTrack * > + ( *tracks->Find( pTrack ).advance( 1 ) ); + pTrack->SetLinked(true); - // Assume partner is wave or null - const auto partner = static_cast(pTrack->GetLink()); if (partner) { // Set partner's parameters to match target. @@ -884,51 +896,40 @@ void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &) mpData->result = RefreshCode::RefreshAll; } -/// Split a stereo track into two tracks... +/// Split a stereo track (or more-than-stereo?) into two (or more) tracks... void WaveTrackMenuTable::SplitStereo(bool stereo) { WaveTrack *const pTrack = static_cast(mpData->pTrack); wxASSERT(pTrack); + AudacityProject *const project = ::GetActiveProject(); + auto channels = TrackList::Channels( pTrack ); - if (stereo) - pTrack->SetPanFromChannelType(); - pTrack->SetChannel(Track::MonoChannel); - // Assume partner is present, and is wave - const auto partner = static_cast(pTrack->GetLink()); - wxASSERT(partner); - if (!partner) - return; - - if (partner) - { + int totalHeight = 0; + int nChannels = 0; + for (auto channel : channels) { // Keep original stereo track name. - partner->SetName(pTrack->GetName()); + channel->SetName(pTrack->GetName()); if (stereo) - partner->SetPanFromChannelType(); - partner->SetChannel(Track::MonoChannel); + channel->SetPanFromChannelType(); + channel->SetChannel(Track::MonoChannel); //On Demand - have each channel add its own. - if (ODManager::IsInstanceCreated() && partner->GetKind() == Track::Wave) - ODManager::Instance()->MakeWaveTrackIndependent(partner); + if (ODManager::IsInstanceCreated()) + ODManager::Instance()->MakeWaveTrackIndependent(channel); + //make sure no channel is smaller than its minimum height + if (channel->GetHeight() < channel->GetMinimizedHeight()) + channel->SetHeight(channel->GetMinimizedHeight()); + totalHeight += channel->GetHeight(); + ++nChannels; } pTrack->SetLinked(false); - //make sure neither track is smaller than its minimum height - if (pTrack->GetHeight() < pTrack->GetMinimizedHeight()) - pTrack->SetHeight(pTrack->GetMinimizedHeight()); - if (partner) - { - if (partner->GetHeight() < partner->GetMinimizedHeight()) - partner->SetHeight(partner->GetMinimizedHeight()); + int averageHeight = totalHeight / nChannels; + for (auto channel : channels) // Make tracks the same height - if (pTrack->GetHeight() != partner->GetHeight()) - { - pTrack->SetHeight((pTrack->GetHeight() + partner->GetHeight()) / 2.0); - partner->SetHeight(pTrack->GetHeight()); - } - } + channel->SetHeight( averageHeight ); mpData->result = RefreshCode::RefreshAll; } @@ -939,14 +940,19 @@ void WaveTrackMenuTable::OnSwapChannels(wxCommandEvent &) AudacityProject *const project = ::GetActiveProject(); WaveTrack *const pTrack = static_cast(mpData->pTrack); - // Assume partner is wave or null - const auto partner = static_cast(pTrack->GetLink()); + auto channels = TrackList::Channels( pTrack ); + if (channels.size() != 2) + return; + Track *const focused = project->GetTrackPanel()->GetFocusedTrack(); - const bool hasFocus = - (focused == pTrack || focused == partner); + const bool hasFocus = channels.contains( focused ); + + auto first = *channels.begin(); + auto partner = *channels.rbegin(); SplitStereo(false); - pTrack->SetChannel(Track::RightChannel); + + first->SetChannel(Track::RightChannel); partner->SetChannel(Track::LeftChannel); TrackList *const tracks = project->GetTracks(); From 4aa990e8359adf365c2e7340a77a4ba4d3a3829d Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 16 Apr 2017 16:36:46 -0400 Subject: [PATCH 10/10] Remove GetLink(ed) in various other places --- src/AudioIO.cpp | 41 +++++++++++------- src/Menus.cpp | 9 ++-- src/Mix.cpp | 75 +++++++++++++++------------------ src/MixerBoard.cpp | 5 ++- src/Screenshot.cpp | 24 ++++++----- src/effects/Effect.cpp | 70 +++++++++++++----------------- src/effects/StereoToMono.cpp | 64 ++++++++++++---------------- src/effects/StereoToMono.h | 2 - src/effects/nyquist/Nyquist.cpp | 10 +++-- 9 files changed, 144 insertions(+), 156 deletions(-) diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index a99c4a759..1d2b60130 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -1891,20 +1891,17 @@ int AudioIO::StartStream(const TransportTracks &tracks, // group determination should mimic what is done in audacityAudioCallback() // when calling RealtimeProcess(). int group = 0; - for (size_t i = 0, cnt = mPlaybackTracks.size(); i < cnt; i++) + for (size_t i = 0, cnt = mPlaybackTracks.size(); i < cnt;) { const WaveTrack *vt = mPlaybackTracks[i].get(); - unsigned chanCnt = 1; - if (vt->GetLinked()) - { - i++; - chanCnt++; - } + // TODO: more-than-two-channels + unsigned chanCnt = TrackList::Channels(vt).size(); + i += chanCnt; // Setup for realtime playback at the rate of the realtime // stream, not the rate of the track. - em.RealtimeAddProcessor(group++, chanCnt, mRate); + em.RealtimeAddProcessor(group++, std::min(2u, chanCnt), mRate); } } @@ -4992,26 +4989,38 @@ int AudioIO::AudioCallback(const void *inputBuffer, void *outputBuffer, bool drop = false; bool dropQuickly = false; - bool linkFlag = false; for (unsigned t = 0; t < numPlaybackTracks; t++) { WaveTrack *vt = mPlaybackTracks[t].get(); chans[chanCnt] = vt; - if ( linkFlag ) { - linkFlag = false; + // TODO: more-than-two-channels + auto nextTrack = + t + 1 < numPlaybackTracks + ? mPlaybackTracks[t + 1].get() + : nullptr; + bool firstChannel = vt->IsLeader(); + bool lastChannel = !nextTrack || nextTrack->IsLeader(); + + if ( ! firstChannel ) dropQuickly = dropQuickly && doneMicrofading( *vt ); - } else { drop = dropTrack( *vt ); - linkFlag = vt->GetLinked(); selected = vt->GetSelected(); - // If we have a mono track, clear the right channel - if (!linkFlag) + if ( lastChannel ) { + // TODO: more-than-two-channels +#if 1 + // If we have a mono track, clear the right channel memset(tempBufs[1], 0, framesPerBuffer * sizeof(float)); +#else + // clear any other channels + for ( size_t c = chanCnt + 1; c < numPlaybackChannels; ++c ) + memset(tempBufs[c], 0, framesPerBuffer * sizeof(float)); +#endif + } dropQuickly = drop && doneMicrofading( *vt ); } @@ -5055,7 +5064,7 @@ int AudioIO::AudioCallback(const void *inputBuffer, void *outputBuffer, maxLen = std::max(maxLen, len); - if (linkFlag) + if ( ! lastChannel ) { continue; } diff --git a/src/Menus.cpp b/src/Menus.cpp index 451fc82b6..f7441b662 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -2318,13 +2318,12 @@ CommandFlag MenuCommandHandler::GetUpdateFlags flags |= PlayableTracksExistFlag; if (t->GetSelected()) { flags |= TracksSelectedFlag; - if (t->GetLinked()) { + // TODO: more-than-two-channels + if (TrackList::Channels(t).size() > 1) { flags |= StereoRequiredFlag; } - else { - flags |= WaveTracksSelectedFlag; - flags |= AudioTracksSelectedFlag; - } + flags |= WaveTracksSelectedFlag; + flags |= AudioTracksSelectedFlag; } if( t->GetEndTime() > t->GetStartTime() ) flags |= HasWaveDataFlag; diff --git a/src/Mix.cpp b/src/Mix.cpp index 57638e414..49b622164 100644 --- a/src/Mix.cpp +++ b/src/Mix.cpp @@ -49,28 +49,23 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory, uLeft.reset(), uRight.reset(); // This function was formerly known as "Quick Mix". - const Track *t; bool mono = false; /* flag if output can be mono without loosing anything*/ bool oneinput = false; /* flag set to true if there is only one input track (mono or stereo) */ - TrackListIterator iter(tracks); - SelectedTrackListOfKindIterator usefulIter(Track::Wave, tracks); + const auto trackRange = tracks->Selected< const WaveTrack >(); + auto first = *trackRange.begin(); // this only iterates tracks which are relevant to this function, i.e. // selected WaveTracks. The tracklist is (confusingly) the list of all // tracks in the project int numWaves = 0; /* number of wave tracks in the selection */ int numMono = 0; /* number of mono, centre-panned wave tracks in selection*/ - t = iter.First(); - while (t) { - if (t->GetSelected() && t->GetKind() == Track::Wave) { - numWaves++; - float pan = ((WaveTrack*)t)->GetPan(); - if (t->GetChannel() == Track::MonoChannel && pan == 0) - numMono++; - } - t = iter.Next(); + for(auto wt : trackRange) { + numWaves++; + float pan = wt->GetPan(); + if (wt->GetChannel() == Track::MonoChannel && pan == 0) + numMono++; } if (numMono == numWaves) @@ -88,45 +83,42 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory, double tstart, tend; // start and end times for one track. WaveTrackConstArray waveArray; - t = iter.First(); - while (t) { - if (t->GetSelected() && t->GetKind() == Track::Wave) { - waveArray.push_back(Track::Pointer(t)); - tstart = t->GetStartTime(); - tend = t->GetEndTime(); - if (tend > mixEndTime) - mixEndTime = tend; - // try and get the start time. If the track is empty we will get 0, - // which is ambiguous because it could just mean the track starts at - // the beginning of the project, as well as empty track. The give-away - // is that an empty track also ends at zero. + for(auto wt : trackRange) { + waveArray.push_back( Track::Pointer< const WaveTrack >( wt ) ); + tstart = wt->GetStartTime(); + tend = wt->GetEndTime(); + if (tend > mixEndTime) + mixEndTime = tend; + // try and get the start time. If the track is empty we will get 0, + // which is ambiguous because it could just mean the track starts at + // the beginning of the project, as well as empty track. The give-away + // is that an empty track also ends at zero. - if (tstart != tend) { - // we don't get empty tracks here - if (!gotstart) { - // no previous start, use this one unconditionally - mixStartTime = tstart; - gotstart = true; - } else if (tstart < mixStartTime) - mixStartTime = tstart; // have a start, only make it smaller - } // end if start and end are different - } // end if track is a selected WaveTrack. - /** @TODO: could we not use a SelectedTrackListOfKindIterator here? */ - t = iter.Next(); + if (tstart != tend) { + // we don't get empty tracks here + if (!gotstart) { + // no previous start, use this one unconditionally + mixStartTime = tstart; + gotstart = true; + } else if (tstart < mixStartTime) + mixStartTime = tstart; // have a start, only make it smaller + } // end if start and end are different } /* create the destination track (NEW track) */ - if ((numWaves == 1) || ((numWaves == 2) && (usefulIter.First()->GetLink() != NULL))) + if (numWaves == TrackList::Channels(first).size()) oneinput = true; // only one input track (either 1 mono or one linked stereo pair) auto mixLeft = trackFactory->NewWaveTrack(format, rate); if (oneinput) - mixLeft->SetName(usefulIter.First()->GetName()); /* set name of output track to be the same as the sole input track */ + mixLeft->SetName(first->GetName()); /* set name of output track to be the same as the sole input track */ else mixLeft->SetName(_("Mix")); mixLeft->SetOffset(mixStartTime); + + // TODO: more-than-two-channels decltype(mixLeft) mixRight{}; if (mono) { mixLeft->SetChannel(Track::MonoChannel); @@ -134,10 +126,11 @@ void MixAndRender(TrackList *tracks, TrackFactory *trackFactory, else { mixRight = trackFactory->NewWaveTrack(format, rate); if (oneinput) { - if (usefulIter.First()->GetLink() != NULL) // we have linked track - mixRight->SetName(usefulIter.First()->GetLink()->GetName()); /* set name to match input track's right channel!*/ + auto channels = TrackList::Channels(first); + if (channels.size() > 1) + mixRight->SetName((*channels.begin().advance(1))->GetName()); /* set name to match input track's right channel!*/ else - mixRight->SetName(usefulIter.First()->GetName()); /* set name to that of sole input channel */ + mixRight->SetName(first->GetName()); /* set name to that of sole input channel */ } else mixRight->SetName(_("Mix")); diff --git a/src/MixerBoard.cpp b/src/MixerBoard.cpp index c7d61b1a6..d69866b38 100644 --- a/src/MixerBoard.cpp +++ b/src/MixerBoard.cpp @@ -334,9 +334,10 @@ WaveTrack *MixerTrackCluster::GetWave() const WaveTrack *MixerTrackCluster::GetRight() const { + // TODO: more-than-two-channels auto left = GetWave(); if (left) - return static_cast(left->GetLink()); + return * ++ TrackList::Channels(left).begin(); else return nullptr; } @@ -981,9 +982,9 @@ void MixerBoard::UpdateTrackClusters() size_t nClusterCount = mMixerTrackClusters.size(); unsigned int nClusterIndex = 0; MixerTrackCluster* pMixerTrackCluster = NULL; - Track* pTrack; for (auto pPlayableTrack: mTracks->Leaders()) { + // TODO: more-than-two-channels auto spTrack = Track::Pointer( pPlayableTrack ); if (nClusterIndex < nClusterCount) { diff --git a/src/Screenshot.cpp b/src/Screenshot.cpp index a3ecf1d02..c75e7fb04 100644 --- a/src/Screenshot.cpp +++ b/src/Screenshot.cpp @@ -703,16 +703,20 @@ void ScreenFrame::OnOneHour(wxCommandEvent & WXUNUSED(event)) void ScreenFrame::SizeTracks(int h) { - TrackListIterator iter(mContext.GetProject()->GetTracks()); - for (Track * t = iter.First(); t; t = iter.Next()) { - if (t->GetKind() == Track::Wave) { - if (t->GetLink()) { - t->SetHeight(h); - } - else { - t->SetHeight(h*2); - } - } + // h is the height for a channel + // Set the height of a mono track twice as high + + // TODO: more-than-two-channels + // If there should be more-than-stereo tracks, this makes + // each channel as high as for a stereo channel + + auto tracks = mContext.GetProject()->GetTracks(); + for (auto t : tracks->Leaders()) { + auto channels = TrackList::Channels(t); + auto nChannels = channels.size(); + auto height = nChannels == 1 ? 2 * h : h; + for (auto channel : channels) + channel->SetHeight(height); } mContext.GetProject()->RedrawProject(); } diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 35884602f..f340989ca 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -1359,10 +1359,9 @@ bool Effect::ProcessPass() if (!left->GetSelected()) return fallthrough(); - WaveTrack *right; sampleCount len; sampleCount leftStart; - sampleCount rightStart; + sampleCount rightStart = 0; if (!isGenerator) { @@ -1376,48 +1375,37 @@ bool Effect::ProcessPass() mSampleCnt = left->TimeToLongSamples(mDuration); } - mNumChannels = 1; + mNumChannels = 0; + WaveTrack *right{}; - if (left->GetChannel() == Track::LeftChannel) - { - map[0] = ChannelNameFrontLeft; - } - else if (left->GetChannel() == Track::RightChannel) - { - map[0] = ChannelNameFrontRight; - } - else - { - map[0] = ChannelNameMono; - } - map[1] = ChannelNameEOL; - - right = NULL; - rightStart = 0; - if (left->GetLinked() && multichannel) - { - // Assume linked track is wave - right = static_cast(left->GetLink()); - if (!isGenerator) - { - GetSamples(right, &rightStart, &len); - } - clear = false; - mNumChannels = 2; - - if (right->GetChannel() == Track::LeftChannel) - { - map[1] = ChannelNameFrontLeft; - } - else if (right->GetChannel() == Track::RightChannel) - { - map[1] = ChannelNameFrontRight; - } + // Iterate either over one track which could be any channel, + // or if multichannel, then over all channels of left, + // which is a leader. + for (auto channel : + TrackList::Channels(left).StartingWith(left)) { + if (channel->GetChannel() == Track::LeftChannel) + map[mNumChannels] = ChannelNameFrontLeft; + else if (channel->GetChannel() == Track::RightChannel) + map[mNumChannels] = ChannelNameFrontRight; else - { - map[1] = ChannelNameMono; + map[mNumChannels] = ChannelNameMono; + + ++ mNumChannels; + map[mNumChannels] = ChannelNameEOL; + + if (! multichannel) + break; + + if (mNumChannels == 2) { + // TODO: more-than-two-channels + right = channel; + clear = false; + if (!isGenerator) + GetSamples(right, &rightStart, &len); + + // Ignore other channels + break; } - map[2] = ChannelNameEOL; } // Let the client know the sample rate diff --git a/src/effects/StereoToMono.cpp b/src/effects/StereoToMono.cpp index b80f5f4a8..3cff69ae9 100644 --- a/src/effects/StereoToMono.cpp +++ b/src/effects/StereoToMono.cpp @@ -75,47 +75,40 @@ bool EffectStereoToMono::Process() this->CopyInputTracks(); // Set up mOutputTracks. bool bGoodResult = true; - auto trackRange = mOutputTracks->Selected< WaveTrack >(); - mLeftTrack = *trackRange.first; + auto trackRange = mOutputTracks->SelectedLeaders< WaveTrack >(); bool refreshIter = false; - if(mLeftTrack) - { - // create a NEW WaveTrack to hold all of the output - AudacityProject *p = GetActiveProject(); - mOutTrack = p->GetTrackFactory()->NewWaveTrack(floatSample, mLeftTrack->GetRate()); - } - int count = 0; while ( trackRange.first != trackRange.second ) { mLeftTrack = *trackRange.first; - if (mLeftTrack->GetLinked()) { + auto channels = TrackList::Channels( mLeftTrack ); + if (channels.size() != 2) { + // TODO: more-than-two-channels + ++ trackRange.first; + continue; + } - // Assume linked track is wave - mRightTrack = * ++ trackRange.first; + mRightTrack = * channels.rbegin(); - if ((mLeftTrack->GetRate() == mRightTrack->GetRate())) { - auto leftTrackStart = mLeftTrack->TimeToLongSamples(mLeftTrack->GetStartTime()); - auto rightTrackStart = mRightTrack->TimeToLongSamples(mRightTrack->GetStartTime()); - mStart = wxMin(leftTrackStart, rightTrackStart); + if ((mLeftTrack->GetRate() == mRightTrack->GetRate())) { + auto leftTrackStart = mLeftTrack->TimeToLongSamples(mLeftTrack->GetStartTime()); + auto rightTrackStart = mRightTrack->TimeToLongSamples(mRightTrack->GetStartTime()); + mStart = wxMin(leftTrackStart, rightTrackStart); - auto leftTrackEnd = mLeftTrack->TimeToLongSamples(mLeftTrack->GetEndTime()); - auto rightTrackEnd = mRightTrack->TimeToLongSamples(mRightTrack->GetEndTime()); - mEnd = wxMax(leftTrackEnd, rightTrackEnd); + auto leftTrackEnd = mLeftTrack->TimeToLongSamples(mLeftTrack->GetEndTime()); + auto rightTrackEnd = mRightTrack->TimeToLongSamples(mRightTrack->GetEndTime()); + mEnd = wxMax(leftTrackEnd, rightTrackEnd); - bGoodResult = ProcessOne(count); - if (!bGoodResult) - break; + bGoodResult = ProcessOne(count); + if (!bGoodResult) + break; - mOutTrack->Clear(mOutTrack->GetStartTime(), mOutTrack->GetEndTime()); - - // The right channel has been deleted, so we must restart from the beginning - refreshIter = true; - } + // The right channel has been deleted, so we must restart from the beginning + refreshIter = true; } if (refreshIter) { - trackRange = mOutputTracks->Selected< WaveTrack >(); + trackRange = mOutputTracks->SelectedLeaders< WaveTrack >(); refreshIter = false; } else @@ -128,11 +121,6 @@ bool EffectStereoToMono::Process() return bGoodResult; } -void EffectStereoToMono::End() -{ - mOutTrack.reset(); -} - bool EffectStereoToMono::ProcessOne(int count) { float curLeftFrame; @@ -145,6 +133,10 @@ bool EffectStereoToMono::ProcessOne(int count) Floats rightBuffer{ idealBlockLen }; bool bResult = true; + AudacityProject *p = GetActiveProject(); + auto outTrack = + p->GetTrackFactory()->NewWaveTrack(floatSample, mLeftTrack->GetRate()); + while (index < mEnd) { bResult &= mLeftTrack->Get((samplePtr)leftBuffer.get(), floatSample, index, idealBlockLen); bResult &= mRightTrack->Get((samplePtr)rightBuffer.get(), floatSample, index, idealBlockLen); @@ -156,15 +148,15 @@ bool EffectStereoToMono::ProcessOne(int count) curMonoFrame = (curLeftFrame + curRightFrame) / 2.0; leftBuffer[i] = curMonoFrame; } - mOutTrack->Append((samplePtr)leftBuffer.get(), floatSample, limit); + outTrack->Append((samplePtr)leftBuffer.get(), floatSample, limit); if (TrackProgress(count, 2.*(index.as_double() / (mEnd - mStart).as_double()))) return false; } double minStart = wxMin(mLeftTrack->GetStartTime(), mRightTrack->GetStartTime()); mLeftTrack->Clear(mLeftTrack->GetStartTime(), mLeftTrack->GetEndTime()); - mOutTrack->Flush(); - mLeftTrack->Paste(minStart, mOutTrack.get()); + outTrack->Flush(); + mLeftTrack->Paste(minStart, outTrack.get()); mLeftTrack->SetLinked(false); mRightTrack->SetLinked(false); mLeftTrack->SetChannel(Track::MonoChannel); diff --git a/src/effects/StereoToMono.h b/src/effects/StereoToMono.h index 3f7889a5f..f55a9d082 100644 --- a/src/effects/StereoToMono.h +++ b/src/effects/StereoToMono.h @@ -41,7 +41,6 @@ public: // Effect implementation bool Process() override; - void End() override; bool IsHidden() override; private: @@ -54,7 +53,6 @@ private: sampleCount mEnd; WaveTrack *mLeftTrack; WaveTrack *mRightTrack; - std::unique_ptr mOutTrack; }; #endif diff --git a/src/effects/nyquist/Nyquist.cpp b/src/effects/nyquist/Nyquist.cpp index e8f025df3..83bd96f62 100644 --- a/src/effects/nyquist/Nyquist.cpp +++ b/src/effects/nyquist/Nyquist.cpp @@ -769,7 +769,7 @@ bool NyquistEffect::Process() Effect::MessageBox(message, wxOK | wxCENTRE | wxICON_EXCLAMATION, _("Nyquist Error")); } - auto trackRange = mOutputTracks->Selected< WaveTrack >(); + auto trackRange = mOutputTracks->Selected< WaveTrack >() + &Track::IsLeader; // Keep track of whether the current track is first selected in its sync-lock group // (we have no idea what the length of the returned audio will be, so we have @@ -785,10 +785,14 @@ bool NyquistEffect::Process() if (bOnePassTool) { } else { - if (mCurTrack[0]->GetLinked()) { + auto channels = TrackList::Channels(mCurTrack[0]); + if (channels.size() > 1) { + // TODO: more-than-two-channels + // Pay attention to consistency of mNumSelectedChannels + // with the running tally made by this loop! mCurNumChannels = 2; - mCurTrack[1] = * ++ iter; + mCurTrack[1] = * ++ channels.first; if (mCurTrack[1]->GetRate() != mCurTrack[0]->GetRate()) { Effect::MessageBox(_("Sorry, cannot apply effect on stereo tracks where the tracks don't match."), wxOK | wxCENTRE);