From d2a18f01e3e4330cd504b5b2f048471992d963ee Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 11 Jan 2017 20:49:38 -0500 Subject: [PATCH 1/4] 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 2/4] 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 3/4] 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 4/4] 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);