Merge pull request #710 from paulfd/reset-smoothers
Reset the smoothers
This commit is contained in:
commit
3dfc9c84f3
10 changed files with 126 additions and 25 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue