diff --git a/src/effects/ChangePitch.cpp b/src/effects/ChangePitch.cpp index 9762a7681..a78236cba 100644 --- a/src/effects/ChangePitch.cpp +++ b/src/effects/ChangePitch.cpp @@ -247,7 +247,7 @@ bool EffectChangePitch::Process() // eliminate the next line: mSemitones = m_dSemitonesChange; #endif - return EffectSoundTouch::ProcessWithTimeWarper(initer, warper); + return EffectSoundTouch::ProcessWithTimeWarper(initer, warper, true); } } diff --git a/src/effects/ChangeTempo.cpp b/src/effects/ChangeTempo.cpp index c3450b60e..5988e1b21 100644 --- a/src/effects/ChangeTempo.cpp +++ b/src/effects/ChangeTempo.cpp @@ -217,7 +217,7 @@ bool EffectChangeTempo::Process() double mT1Dashed = mT0 + (mT1 - mT0)/(m_PercentChange/100.0 + 1.0); RegionTimeWarper warper{ mT0, mT1, std::make_unique(mT0, mT0, mT1, mT1Dashed ) }; - success = EffectSoundTouch::ProcessWithTimeWarper(initer, warper); + success = EffectSoundTouch::ProcessWithTimeWarper(initer, warper, false); } if(success) diff --git a/src/effects/SoundTouchEffect.cpp b/src/effects/SoundTouchEffect.cpp index ceafadc5d..5161a5027 100644 --- a/src/effects/SoundTouchEffect.cpp +++ b/src/effects/SoundTouchEffect.cpp @@ -20,6 +20,7 @@ effect that uses SoundTouch to do its processing (ChangeTempo #include #include "../LabelTrack.h" +#include "../WaveClip.h" #include "../WaveTrack.h" #include "../NoteTrack.h" #include "TimeWarper.h" @@ -67,7 +68,9 @@ bool EffectSoundTouch::ProcessNoteTrack(NoteTrack *nt, const TimeWarper &warper) } #endif -bool EffectSoundTouch::ProcessWithTimeWarper(InitFunction initer, const TimeWarper &warper) +bool EffectSoundTouch::ProcessWithTimeWarper(InitFunction initer, + const TimeWarper &warper, + bool preserveLength) { // Assumes that mSoundTouch has already been initialized // by the subclass for subclass-specific parameters. The @@ -85,6 +88,7 @@ bool EffectSoundTouch::ProcessWithTimeWarper(InitFunction initer, const TimeWarp this->CopyInputTracks(true); bool bGoodResult = true; + mPreserveLength = preserveLength; mCurTrackNum = 0; m_maxNewLength = 0.0; @@ -107,14 +111,9 @@ bool EffectSoundTouch::ProcessWithTimeWarper(InitFunction initer, const TimeWarp if (!leftTrack->GetSelected()) return fallthrough(); - //Get start and end times from track - mCurT0 = leftTrack->GetStartTime(); - mCurT1 = leftTrack->GetEndTime(); - - //Set the current bounds to whichever left marker is - //greater and whichever right marker is less - mCurT0 = wxMax(mT0, mCurT0); - mCurT1 = wxMin(mT1, mCurT1); + //Get start and end times from selection + mCurT0 = mT0; + mCurT1 = mT1; // Process only if the right marker is to the right of the left marker if (mCurT1 > mCurT0) { @@ -250,9 +249,8 @@ bool EffectSoundTouch::ProcessOne(WaveTrack *track, outputTrack->Flush(); } - // Take the output track and insert it in place of the original - // sample data - track->ClearAndPaste(mCurT0, mCurT1, outputTrack.get(), false, true, &warper); + // Transfer output samples to the original + Finalize(track, outputTrack.get(), warper); double newLength = outputTrack->GetEndTime(); m_maxNewLength = wxMax(m_maxNewLength, newLength); @@ -345,12 +343,10 @@ bool EffectSoundTouch::ProcessStereo( outputRightTrack->Flush(); } - // Take the output tracks and insert in place of the original - // sample data. - leftTrack->ClearAndPaste( - mCurT0, mCurT1, outputLeftTrack.get(), false, true, &warper); - rightTrack->ClearAndPaste( - mCurT0, mCurT1, outputRightTrack.get(), false, true, &warper); + // Transfer output samples to the original + Finalize(leftTrack, outputLeftTrack.get(), warper); + Finalize(rightTrack, outputRightTrack.get(), warper); + // Track the longest result length double newLength = outputLeftTrack->GetEndTime(); @@ -384,4 +380,57 @@ bool EffectSoundTouch::ProcessStereoResults(const size_t outputCount, return true; } +void EffectSoundTouch::Finalize(WaveTrack* orig, WaveTrack* out, const TimeWarper &warper) +{ + if (mPreserveLength) { + auto newLen = out->GetNumSamples(); + auto oldLen = out->TimeToLongSamples(mCurT1) - out->TimeToLongSamples(mCurT0); + + // Pad output track to original length since SoundTouch may remove samples + if (newLen < oldLen) { + out->InsertSilence(out->LongSamplesToTime(newLen - 1), + out->LongSamplesToTime(oldLen - newLen)); + } + // Trim output track to original length since SoundTouch may add extra samples + else if (newLen > oldLen) { + out->Trim(0, out->LongSamplesToTime(oldLen)); + } + } + + // Silenced samples will be inserted in gaps between clips, so capture where these + // gaps are for later deletion + std::vector> gaps; + double last = 0.0; + auto clips = orig->SortedClipArray(); + auto front = clips.front(); + auto back = clips.back(); + for (auto &clip : clips) { + auto st = clip->GetStartTime(); + auto et = clip->GetEndTime(); + + if (st >= mCurT0 || et < mCurT1) { + if (mCurT0 < st && clip == front) { + gaps.push_back(std::make_pair(mCurT0, st)); + } + if (mCurT1 > et && clip == back) { + gaps.push_back(std::make_pair(et, mCurT1)); + } + if (last >= mCurT0) { + gaps.push_back(std::make_pair(last, st)); + } + } + last = et; + } + + // Take the output track and insert it in place of the original sample data + orig->ClearAndPaste(mCurT0, mCurT1, out, true, true, &warper); + + // Finally, recreate the gaps + for (auto gap : gaps) { + auto st = orig->LongSamplesToTime(orig->TimeToLongSamples(gap.first)); + auto et = orig->LongSamplesToTime(orig->TimeToLongSamples(gap.second)); + orig->SplitDelete(warper.Warp(st), warper.Warp(et)); + } +} + #endif // USE_SOUNDTOUCH diff --git a/src/effects/SoundTouchEffect.h b/src/effects/SoundTouchEffect.h index 6b64ba557..8dc4d6c7f 100644 --- a/src/effects/SoundTouchEffect.h +++ b/src/effects/SoundTouchEffect.h @@ -49,7 +49,9 @@ protected: // Effect implementation using InitFunction = std::function< void(soundtouch::SoundTouch *soundtouch) >; - bool ProcessWithTimeWarper(InitFunction initer, const TimeWarper &warper); + bool ProcessWithTimeWarper(InitFunction initer, + const TimeWarper &warper, + bool preserveLength); std::unique_ptr mSoundTouch; double mCurT0; @@ -69,6 +71,9 @@ private: bool ProcessStereoResults(const size_t outputCount, WaveTrack* outputLeftTrack, WaveTrack* outputRightTrack); + void Finalize(WaveTrack* orig, WaveTrack* out, const TimeWarper &warper); + + bool mPreserveLength; int mCurTrackNum;