diff --git a/libraries/lib-stretching-sequence/ClipSegment.cpp b/libraries/lib-stretching-sequence/ClipSegment.cpp index 25094f910880..d8a7698f647b 100644 --- a/libraries/lib-stretching-sequence/ClipSegment.cpp +++ b/libraries/lib-stretching-sequence/ClipSegment.cpp @@ -12,7 +12,6 @@ #include "ClipInterface.h" #include "SampleFormat.h" #include "StaffPadTimeAndPitch.h" - #include #include #include @@ -44,28 +43,47 @@ ClipSegment::ClipSegment( : mTotalNumSamplesToProduce { GetTotalNumSamplesToProduce( clip, durationToDiscard) } , mSource { clip, durationToDiscard, direction } + , mPreserveFormants { clip.GetPitchAndSpeedPreset() == + PitchAndSpeedPreset::OptimizeForVoice } + , mCentShift { clip.GetCentShift() } , mStretcher { std::make_unique( clip.GetRate(), clip.GetWidth(), mSource, GetStretchingParameters(clip)) } , mOnSemitoneShiftChangeSubscription { clip.SubscribeToCentShiftChange( [this](int cents) { - std::lock_guard lock(mStretcherMutex); - mStretcher->OnCentShiftChange(cents); + mCentShift = cents; + mUpdateCentShift = true; }) } , mOnFormantPreservationChangeSubscription { clip.SubscribeToPitchAndSpeedPresetChange( [this](PitchAndSpeedPreset preset) { - std::lock_guard lock(mStretcherMutex); - mStretcher->OnFormantPreservationChange( - preset == PitchAndSpeedPreset::OptimizeForVoice); + mPreserveFormants = + preset == PitchAndSpeedPreset::OptimizeForVoice; + mUpdateFormantPreservation = true; }) } { } +ClipSegment::~ClipSegment() +{ + mOnSemitoneShiftChangeSubscription.Reset(); + mOnFormantPreservationChangeSubscription.Reset(); +} + size_t ClipSegment::GetFloats(float* const* buffers, size_t numSamples) { - std::lock_guard lock(mStretcherMutex); + // Check if formant preservation of pitch shift needs to be updated. + // This approach is not immune to a race condition, but it is unlikely and + // not critical, as it would only affect one playback pass, during which the + // user could easily correct the mistake if needed. On the other hand, we + // cannot trust that the observer subscriptions do not get called after + // destruction of this object, so better not do anything too sophisticated + // there. + if (mUpdateFormantPreservation.exchange(false)) + mStretcher->OnFormantPreservationChange(mPreserveFormants); + if (mUpdateCentShift.exchange(false)) + mStretcher->OnCentShiftChange(mCentShift); const auto numSamplesToProduce = limitSampleBufferSize( numSamples, mTotalNumSamplesToProduce - mTotalNumSamplesProduced); mStretcher->GetSamples(buffers, numSamplesToProduce); diff --git a/libraries/lib-stretching-sequence/ClipSegment.h b/libraries/lib-stretching-sequence/ClipSegment.h index afe675ff794f..e54c4a6d64e7 100644 --- a/libraries/lib-stretching-sequence/ClipSegment.h +++ b/libraries/lib-stretching-sequence/ClipSegment.h @@ -14,8 +14,8 @@ #include "ClipTimeAndPitchSource.h" #include "Observer.h" #include "PlaybackDirection.h" +#include #include -#include class ClipInterface; class TimeAndPitchInterface; @@ -31,6 +31,7 @@ class STRETCHING_SEQUENCE_API ClipSegment final : public AudioSegment { public: ClipSegment(ClipInterface&, double durationToDiscard, PlaybackDirection); + ~ClipSegment() override; // AudioSegment size_t GetFloats(float* const* buffers, size_t numSamples) override; @@ -41,7 +42,10 @@ class STRETCHING_SEQUENCE_API ClipSegment final : public AudioSegment const sampleCount mTotalNumSamplesToProduce; sampleCount mTotalNumSamplesProduced = 0; ClipTimeAndPitchSource mSource; - std::mutex mStretcherMutex; + bool mPreserveFormants; + int mCentShift; + std::atomic mUpdateFormantPreservation = false; + std::atomic mUpdateCentShift = false; // Careful that this guy is constructed after `mSource`, which it refers to // in its ctor. // todo(mhodgkinson) make this safe. diff --git a/libraries/lib-stretching-sequence/ClipTimeAndPitchSource.cpp b/libraries/lib-stretching-sequence/ClipTimeAndPitchSource.cpp index 86b9d1419ad6..2b1219f94dcf 100644 --- a/libraries/lib-stretching-sequence/ClipTimeAndPitchSource.cpp +++ b/libraries/lib-stretching-sequence/ClipTimeAndPitchSource.cpp @@ -80,6 +80,11 @@ void ClipTimeAndPitchSource::Pull( // become a reasonably small value again :) -sampleCount { numSamplesToRead }; } + else + { + for (auto i = 0u; i < mClip.GetWidth(); ++i) + std::fill(buffers[i], buffers[i] + samplesPerChannel, 0.f); + } } size_t ClipTimeAndPitchSource::GetWidth() const