Merge pull request #710 from paulfd/reset-smoothers

Reset the smoothers
This commit is contained in:
Paul Ferrand 2021-03-21 00:51:51 +01:00 committed by GitHub
commit 3dfc9c84f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 126 additions and 25 deletions

View file

@ -157,6 +157,11 @@ absl::Span<const float> BeatClock::getRunningBeatPosition()
return absl::MakeConstSpan(runningBeatPosition_.data(), currentCycleFrames_); return absl::MakeConstSpan(runningBeatPosition_.data(), currentCycleFrames_);
} }
double BeatClock::getLastBeatPosition() const
{
return lastClientPos_.toBeats(timeSig_);
}
absl::Span<const int> BeatClock::getRunningBeatsPerBar() absl::Span<const int> BeatClock::getRunningBeatsPerBar()
{ {
fillBufferUpTo(currentCycleFrames_); fillBufferUpTo(currentCycleFrames_);
@ -175,32 +180,35 @@ void BeatClock::fillBufferUpTo(unsigned delay)
for (unsigned i = fillIdx; i < delay; ++i) for (unsigned i = fillIdx; i < delay; ++i)
beatsPerBarData[i] = sig.beatsPerBar; beatsPerBarData[i] = sig.beatsPerBar;
if (!isPlaying_) {
if (fillIdx < delay) {
fill(absl::MakeSpan(&beatNumberData[fillIdx], delay - fillIdx), 0);
fill(absl::MakeSpan(&beatNumberPosition[fillIdx], delay - fillIdx), 0.0f);
}
currentCycleFill_ = fillIdx;
return;
}
BBT clientPos = lastClientPos_; BBT clientPos = lastClientPos_;
const double beatsPerFrame = beatsPerSecond_ * samplePeriod_;
const BBT hostPos = lastHostPos_; const BBT hostPos = lastHostPos_;
bool mustApplyHostPos = mustApplyHostPos_; bool mustApplyHostPos = mustApplyHostPos_;
for (; fillIdx < delay; ++fillIdx) { if (!isPlaying_) {
clientPos = BBT::fromBeats(sig, clientPos.toBeats(sig) + beatsPerFrame);
clientPos = mustApplyHostPos ? hostPos : clientPos; clientPos = mustApplyHostPos ? hostPos : clientPos;
mustApplyHostPos = false; mustApplyHostPos = false;
// quantization to nearest for prevention of rounding errors if (fillIdx < delay) {
double beats = clientPos.toBeats(sig); double beats = clientPos.toBeats(sig);
beatNumberData[fillIdx] = dequantize<int>(quantize(beats)); // quantization to nearest for prevention of rounding errors
beatNumberPosition[fillIdx] = static_cast<float>(beats); fill(absl::MakeSpan(&beatNumberData[fillIdx], delay - fillIdx), dequantize<int>(quantize(beats)));
fill(absl::MakeSpan(&beatNumberPosition[fillIdx], delay - fillIdx), static_cast<float>(beats));
fillIdx = delay;
}
} else {
for (; fillIdx < delay; ++fillIdx) {
clientPos = BBT::fromBeats(sig, clientPos.toBeats(sig) + getBeatsPerFrame());
clientPos = mustApplyHostPos ? hostPos : clientPos;
mustApplyHostPos = false;
// quantization to nearest for prevention of rounding errors
double beats = clientPos.toBeats(sig);
beatNumberData[fillIdx] = dequantize<int>(quantize(beats));
beatNumberPosition[fillIdx] = static_cast<float>(beats);
}
} }
currentCycleFill_ = fillIdx; currentCycleFill_ = fillIdx;
lastClientPos_ = clientPos; lastClientPos_ = clientPos;
mustApplyHostPos_ = mustApplyHostPos; mustApplyHostPos_ = mustApplyHostPos;

View file

@ -102,6 +102,11 @@ public:
* @brief Set the time signature. * @brief Set the time signature.
*/ */
void setTimeSignature(unsigned delay, TimeSignature newSig); void setTimeSignature(unsigned delay, TimeSignature newSig);
/**
* @brief Get the time signature
*
*/
TimeSignature getTimeSignature() const noexcept { return timeSig_; }
/** /**
* @brief Set the time position. * @brief Set the time position.
*/ */
@ -134,6 +139,19 @@ public:
* @brief Get the time signature numerator for each frame of the current cycle. * @brief Get the time signature numerator for each frame of the current cycle.
*/ */
absl::Span<const int> getRunningBeatsPerBar(); absl::Span<const int> getRunningBeatsPerBar();
/**
* @brief Get the last BeatPosition
*
* @return float
*/
double getLastBeatPosition() const;
/**
* @brief Get the Beats Per Frame object
*
* @return float
*/
double getBeatsPerFrame() const { return beatsPerSecond_ * samplePeriod_; }
/** /**
* @brief Create a normalized phase signal for LFO which completes a * @brief Create a normalized phase signal for LFO which completes a
* period every N-th beat. * period every N-th beat.

View file

@ -57,21 +57,27 @@ void sfz::MidiState::setSampleRate(float sampleRate) noexcept
void sfz::MidiState::advanceTime(int numSamples) noexcept void sfz::MidiState::advanceTime(int numSamples) noexcept
{ {
auto clearEvents = [] (EventVector& events) { internalClock += numSamples;
flushEvents();
}
void sfz::MidiState::flushEvents() noexcept
{
auto flushEventVector = [] (EventVector& events) {
ASSERT(!events.empty()); // CC event vectors should never be empty ASSERT(!events.empty()); // CC event vectors should never be empty
events.front().value = events.back().value; events.front().value = events.back().value;
events.front().delay = 0; events.front().delay = 0;
events.resize(1); events.resize(1);
}; };
internalClock += numSamples;
for (auto& ccEvents : cc) for (auto& ccEvents : cc)
clearEvents(ccEvents); flushEventVector(ccEvents);
clearEvents(pitchEvents); flushEventVector(pitchEvents);
clearEvents(channelAftertouchEvents); flushEventVector(channelAftertouchEvents);
} }
void sfz::MidiState::setSamplesPerBlock(int samplesPerBlock) noexcept void sfz::MidiState::setSamplesPerBlock(int samplesPerBlock) noexcept
{ {
auto updateEventBufferSize = [=] (EventVector& events) { auto updateEventBufferSize = [=] (EventVector& events) {
@ -113,7 +119,7 @@ float sfz::MidiState::getLastVelocity() const noexcept
void sfz::MidiState::insertEventInVector(EventVector& events, int delay, float value) void sfz::MidiState::insertEventInVector(EventVector& events, int delay, float value)
{ {
const auto insertionPoint = absl::c_upper_bound(events, delay, MidiEventDelayComparator {}); const auto insertionPoint = absl::c_lower_bound(events, delay, MidiEventDelayComparator {});
if (insertionPoint == events.end() || insertionPoint->delay != delay) if (insertionPoint == events.end() || insertionPoint->delay != delay)
events.insert(insertionPoint, { delay, value }); events.insert(insertionPoint, { delay, value });
else else

View file

@ -130,11 +130,18 @@ public:
/** /**
* @brief Advances the internal clock of a given amount of samples. * @brief Advances the internal clock of a given amount of samples.
* You should call this at each callback. This will flush the events * You should call this at each callback. This will flush the events
* in the midistate memory. * in the midistate memory by calling flushEvents().
* *
* @param numSamples the number of samples of clock advance * @param numSamples the number of samples of clock advance
*/ */
void advanceTime(int numSamples) noexcept; void advanceTime(int numSamples) noexcept;
/**
* @brief Flush events in all states, keeping only the last one as the "base" state
*
*/
void flushEvents() noexcept;
/** /**
* @brief Get the CC value for CC number * @brief Get the CC value for CC number
* *

View file

@ -31,6 +31,12 @@ void OnePoleSmoother::setSmoothing(unsigned smoothValue, float sampleRate)
void OnePoleSmoother::reset(float value) void OnePoleSmoother::reset(float value)
{ {
filter.reset(value); filter.reset(value);
target_ = value;
}
void OnePoleSmoother::resetToTarget()
{
reset(target_);
} }
void OnePoleSmoother::process(absl::Span<const float> input, absl::Span<float> output, bool canShortcut) void OnePoleSmoother::process(absl::Span<const float> input, absl::Span<float> output, bool canShortcut)
@ -55,6 +61,8 @@ void OnePoleSmoother::process(absl::Span<const float> input, absl::Span<float> o
} else if (input.data() != output.data()) { } else if (input.data() != output.data()) {
copy<float>(input, output); copy<float>(input, output);
} }
target_ = input.back();
} }
/// ///
@ -76,6 +84,11 @@ void LinearSmoother::reset(float value)
//framesToTarget_ = 0; //framesToTarget_ = 0;
} }
void LinearSmoother::resetToTarget()
{
reset(target_);
}
void LinearSmoother::process(absl::Span<const float> input, absl::Span<float> output, bool canShortcut) void LinearSmoother::process(absl::Span<const float> input, absl::Span<float> output, bool canShortcut)
{ {
CHECK_SPAN_SIZES(input, output); CHECK_SPAN_SIZES(input, output);

View file

@ -31,6 +31,10 @@ public:
* @param value * @param value
*/ */
void reset(float value = 0.0f); void reset(float value = 0.0f);
/**
* @brief Reset to the target value (the back of the last vector passed)
*/
void resetToTarget();
/** /**
* @brief Process a span of data. Input and output can refer to the same * @brief Process a span of data. Input and output can refer to the same
* memory. * memory.
@ -47,6 +51,7 @@ public:
private: private:
bool smoothing { false }; bool smoothing { false };
OnePoleFilter<float> filter {}; OnePoleFilter<float> filter {};
float target_ { 0.0f };
}; };
/** /**
@ -70,6 +75,10 @@ public:
* @param value * @param value
*/ */
void reset(float value = 0.0f); void reset(float value = 0.0f);
/**
* @brief Reset to the target value (the back of the last vector passed)
*/
void resetToTarget();
/** /**
* @brief Process a span of data. Input and output can refer to the same * @brief Process a span of data. Input and output can refer to the same
* memory. * memory.

View file

@ -258,6 +258,7 @@ void Synth::Impl::clear()
groupOpcodes_.clear(); groupOpcodes_.clear();
unknownOpcodes_.clear(); unknownOpcodes_.clear();
modificationTime_ = absl::nullopt; modificationTime_ = absl::nullopt;
playheadMoved_ = false;
// set default controllers // set default controllers
// midistate is reset above // midistate is reset above
@ -917,6 +918,12 @@ void Synth::renderBlock(AudioSpan<float> buffer) noexcept
BeatClock& bc = impl.resources_.beatClock; BeatClock& bc = impl.resources_.beatClock;
bc.beginCycle(numFrames); bc.beginCycle(numFrames);
if (impl.playheadMoved_ && impl.resources_.beatClock.isPlaying()) {
impl.resources_.midiState.flushEvents();
impl.genController_->resetSmoothers();
impl.playheadMoved_ = false;
}
{ // Clear effect busses { // Clear effect busses
ScopedTiming logger { callbackBreakdown.effects }; ScopedTiming logger { callbackBreakdown.effects };
for (auto& bus : impl.effectBuses_) { for (auto& bus : impl.effectBuses_) {
@ -1305,7 +1312,16 @@ void Synth::timePosition(int delay, int bar, double barBeat)
Impl& impl = *impl_; Impl& impl = *impl_;
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
impl.resources_.beatClock.setTimePosition(delay, BBT(bar, barBeat)); const auto newPosition = BBT(bar, barBeat);
const auto newBeatPosition = newPosition.toBeats(impl.resources_.beatClock.getTimeSignature());
const auto currentBeatPosition = impl.resources_.beatClock.getLastBeatPosition();
const auto positionDifference = std::abs(newBeatPosition - currentBeatPosition);
const auto threshold = 2 * impl.resources_.beatClock.getBeatsPerFrame();
if (positionDifference > threshold)
impl.playheadMoved_ = true;
impl.resources_.beatClock.setTimePosition(delay, newPosition);
} }
void Synth::playbackState(int delay, int playbackState) void Synth::playbackState(int delay, int playbackState)

View file

@ -309,6 +309,8 @@ struct Synth::Impl final: public Parser::Listener {
client.setReceiveCallback(broadcastReceiver); client.setReceiveCallback(broadcastReceiver);
return client; return client;
} }
bool playheadMoved_ { false };
}; };
} // namespace sfz } // namespace sfz

View file

@ -16,6 +16,7 @@
namespace sfz { namespace sfz {
struct ControllerSource::Impl { struct ControllerSource::Impl {
float getLastTransformedValue(uint16_t cc, uint8_t curve) const noexcept;
double sampleRate_ = config::defaultSampleRate; double sampleRate_ = config::defaultSampleRate;
Resources* res_ = nullptr; Resources* res_ = nullptr;
absl::flat_hash_map<ModKey, Smoother> smoother_; absl::flat_hash_map<ModKey, Smoother> smoother_;
@ -31,6 +32,22 @@ ControllerSource::~ControllerSource()
{ {
} }
float ControllerSource::Impl::getLastTransformedValue(uint16_t cc, uint8_t curveIndex) const noexcept
{
ASSERT(res_);
const Curve& curve = res_->curves.getCurve(curveIndex);
const auto lastCCValue = res_->midiState.getCCValue(cc);
return curve.evalNormalized(lastCCValue);
}
void ControllerSource::resetSmoothers()
{
for (auto& item : impl_->smoother_) {
const ModKey::Parameters p = item.first.parameters();
item.second.reset(impl_->getLastTransformedValue(p.cc, p.curve));
}
}
void ControllerSource::setSampleRate(double sampleRate) void ControllerSource::setSampleRate(double sampleRate)
{ {
if (impl_->sampleRate_ == sampleRate) if (impl_->sampleRate_ == sampleRate)
@ -58,6 +75,7 @@ void ControllerSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId, u
if (p.smooth > 0) { if (p.smooth > 0) {
Smoother s; Smoother s;
s.setSmoothing(p.smooth, impl_->sampleRate_); s.setSmoothing(p.smooth, impl_->sampleRate_);
s.reset(impl_->getLastTransformedValue(p.cc, p.curve));
impl_->smoother_[sourceKey] = s; impl_->smoother_[sourceKey] = s;
} }
else { else {

View file

@ -21,6 +21,10 @@ public:
void init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay) override; void init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay) override;
void generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl::Span<float> buffer) override; void generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl::Span<float> buffer) override;
/**
* @brief Reset the smoothers.
*/
void resetSmoothers();
private: private:
struct Impl; struct Impl;
std::unique_ptr<Impl> impl_; std::unique_ptr<Impl> impl_;