-
Notifications
You must be signed in to change notification settings - Fork 0
Enable Hermite reads; add orbit read guard, soft-clip writes, and cadence thresholds #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -88,6 +88,14 @@ float computeTempoDelaySamples(float sampleRate, float tempoBpm, float noteDivis | |||||||||||||||||||||||||||||
| float wrapUnitParam(float value, float fallback) { | ||||||||||||||||||||||||||||||
| return isFiniteSafe(value) ? wrapPosFloat(value, 1.0f, 1.0f) : fallback; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| float readDelayWrapped(const DelayLine& delay, float readPos) { | ||||||||||||||||||||||||||||||
| #if defined(ORBIT_DELAY_ENABLE_HERMITE) | ||||||||||||||||||||||||||||||
| return delay.readAbsoluteHermiteWrapped(readPos); | ||||||||||||||||||||||||||||||
| #else | ||||||||||||||||||||||||||||||
| return delay.readAbsoluteLinearWrapped(readPos); | ||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } // namespace | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| void OrbitDelayCore::applyPendingParamsIfNeeded() { | ||||||||||||||||||||||||||||||
|
|
@@ -375,7 +383,8 @@ bool OrbitDelayCore::advanceCadence() { | |||||||||||||||||||||||||||||
| void OrbitDelayCore::maybeApplyLowpassCutoff(float smoothedToneHz) { | ||||||||||||||||||||||||||||||
| const float clampedToneHz = clampf(smoothedToneHz, 300.0f, clampf(0.49f * sampleRate_, 300.0f, 12000.0f)); | ||||||||||||||||||||||||||||||
| const float delta = std::fabs(clampedToneHz - appliedToneHz_); | ||||||||||||||||||||||||||||||
| if (lowpassDirty_ || heavyParamCadenceHit_ || delta >= kLowpassUpdateDeltaHz) { | ||||||||||||||||||||||||||||||
| const bool cadenceAndAudible = heavyParamCadenceHit_ && delta >= kLowpassCadenceUpdateDeltaHz; | ||||||||||||||||||||||||||||||
| if (lowpassDirty_ || delta >= kLowpassUpdateDeltaHz || cadenceAndAudible) { | ||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Reintroduce a cadence-based fallback for small tone deltas; after this change, if Useful? React with 👍 / 👎.
Comment on lines
+386
to
+387
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cadence thresholds are currently ineffective due to condition dominance
One concrete way to restore cadence-gated behavior- const bool cadenceAndAudible = heavyParamCadenceHit_ && delta >= kLowpassCadenceUpdateDeltaHz;
- if (lowpassDirty_ || delta >= kLowpassUpdateDeltaHz || cadenceAndAudible) {
+ const bool cadenceAndAudible = heavyParamCadenceHit_ && delta >= kLowpassCadenceUpdateDeltaHz;
+ if (lowpassDirty_ || cadenceAndAudible) {
@@
- const bool cadenceAndAudible = heavyParamCadenceHit_ && delta >= kDiffuserCadenceUpdateDelta;
- if (diffuserDirty_ || delta >= kDiffuserUpdateDelta || cadenceAndAudible) {
+ const bool cadenceAndAudible = heavyParamCadenceHit_ && delta >= kDiffuserCadenceUpdateDelta;
+ if (diffuserDirty_ || cadenceAndAudible) {Also applies to: 398-399 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| const float lowpassQ = feedbackLowpassQ(); | ||||||||||||||||||||||||||||||
| lowpassL_.setParams(clampedToneHz, lowpassQ); | ||||||||||||||||||||||||||||||
| lowpassR_.setParams(clampedToneHz, lowpassQ); | ||||||||||||||||||||||||||||||
|
|
@@ -386,7 +395,8 @@ void OrbitDelayCore::maybeApplyLowpassCutoff(float smoothedToneHz) { | |||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| void OrbitDelayCore::maybeApplyDiffuserAmount(float smoothedSmear) { | ||||||||||||||||||||||||||||||
| const float delta = std::fabs(smoothedSmear - appliedSmear_); | ||||||||||||||||||||||||||||||
| if (diffuserDirty_ || heavyParamCadenceHit_ || delta >= kDiffuserUpdateDelta) { | ||||||||||||||||||||||||||||||
| const bool cadenceAndAudible = heavyParamCadenceHit_ && delta >= kDiffuserCadenceUpdateDelta; | ||||||||||||||||||||||||||||||
| if (diffuserDirty_ || delta >= kDiffuserUpdateDelta || cadenceAndAudible) { | ||||||||||||||||||||||||||||||
|
Comment on lines
+398
to
+399
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the lowpass logic, the cadence check here is redundant because
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Keep a cadence-triggered path for small smear adjustments; with this condition, once Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||||||
| const float clamped = clampf(smoothedSmear, 0.0f, 1.0f); | ||||||||||||||||||||||||||||||
| diffuserL_.setAmount(clamped); | ||||||||||||||||||||||||||||||
| diffuserR_.setAmount(clamped); | ||||||||||||||||||||||||||||||
|
|
@@ -618,21 +628,21 @@ float OrbitDelayCore::processChannelFast(float input, | |||||||||||||||||||||||||||||
| if (readMode_ == ReadMode::AccidentalReverse) { | ||||||||||||||||||||||||||||||
| const float readPos = wrapPosFloat(static_cast<float>(delay.writePos) - params.tempoDelaySamples + spread + lfoSamples, delaySize, invDelaySize); | ||||||||||||||||||||||||||||||
| baseReadPos = readPos; | ||||||||||||||||||||||||||||||
| #if defined(ORBIT_DELAY_ENABLE_HERMITE) | ||||||||||||||||||||||||||||||
| wet = delay.readAbsoluteHermiteWrapped(readPos); | ||||||||||||||||||||||||||||||
| #else | ||||||||||||||||||||||||||||||
| wet = delay.readAbsoluteLinearWrapped(readPos); | ||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||
| wet = readDelayWrapped(delay, readPos); | ||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||
| float readPos = params.orbit * static_cast<float>(delay.writePos) + params.offsetSamples + spread + lfoSamples; | ||||||||||||||||||||||||||||||
| readPos = wrapPosFloat(readPos, delaySize, invDelaySize); | ||||||||||||||||||||||||||||||
| baseReadPos = readPos; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| #if defined(ORBIT_DELAY_ENABLE_HERMITE) | ||||||||||||||||||||||||||||||
| wet = delay.readAbsoluteHermiteWrapped(readPos); | ||||||||||||||||||||||||||||||
| #else | ||||||||||||||||||||||||||||||
| wet = delay.readAbsoluteLinearWrapped(readPos); | ||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||
| const float writePos = static_cast<float>(delay.writePos); | ||||||||||||||||||||||||||||||
| const float maxGuardBySize = 0.25f * delaySize; | ||||||||||||||||||||||||||||||
| const float guardSamples = clampf(maxGuardBySize, kOrbitReadGuardMinSamples, kOrbitReadGuardMaxSamples); | ||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||||||||||||||||||||||||||
| const float distanceForward = wrapPosFloat(readPos - writePos, delaySize, invDelaySize); | ||||||||||||||||||||||||||||||
| if (distanceForward < guardSamples || distanceForward > (delaySize - guardSamples)) { | ||||||||||||||||||||||||||||||
|
Comment on lines
+638
to
+640
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Clamp Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||||||
| readPos = wrapPosFloat(writePos + guardSamples, delaySize, invDelaySize); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
Comment on lines
+637
to
+642
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard size can collapse reads onto/near write position for small delay buffers With Suggested fix- const float maxGuardBySize = 0.25f * delaySize;
- const float guardSamples = clampf(maxGuardBySize, kOrbitReadGuardMinSamples, kOrbitReadGuardMaxSamples);
+ const float maxGuardBySize = 0.25f * delaySize;
+ const float requestedGuard = clampf(maxGuardBySize, kOrbitReadGuardMinSamples, kOrbitReadGuardMaxSamples);
+ const float maxSafeGuard = clampf(0.5f * (delaySize - 1.0f), 1.0f, kOrbitReadGuardMaxSamples);
+ const float guardSamples = clampf(requestedGuard, 1.0f, maxSafeGuard);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| baseReadPos = readPos; | ||||||||||||||||||||||||||||||
| wet = readDelayWrapped(delay, readPos); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if (!isFiniteSafe(wet)) { | ||||||||||||||||||||||||||||||
|
|
@@ -682,7 +692,8 @@ float OrbitDelayCore::processChannelFast(float input, | |||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| delay.write(toBuffer); | ||||||||||||||||||||||||||||||
| const float toBufferClipped = softClipPolynomial(toBuffer); | ||||||||||||||||||||||||||||||
| delay.write(toBufferClipped); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const float out = (sanitizedInput * (1.0f - params.mix) + filteredWet * params.mix) * params.outputGain; | ||||||||||||||||||||||||||||||
| return isFiniteSafe(out) ? out : 0.0f; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for cadence-driven updates is redundant and likely inverted. Currently,
kLowpassUpdateDeltaHzis 20.0 andkLowpassCadenceUpdateDeltaHzis 80.0. Sincedelta >= 80.0impliesdelta >= 20.0, thecadenceAndAudiblecondition is dead code—the filter will update every sample as soon as the delta exceeds 20.0, regardless of the cadence hit.Furthermore, this change prevents the filter from ever reaching its final target if the smoother stops moving while the delta is less than 20Hz (whereas previously it would sync on the next cadence hit). To achieve the goal of reducing updates while maintaining accuracy, you should update immediately for large changes and on cadence for smaller ones.