Build with this node

Start with a prompt in Studio, or copy it into your own coding agent.

Ask Studio

@maruaudio/dsp/reverbs/schroeder is a mono algorithmic reverb with the Freeverb topology: four parallel damped feedback combs into two serial Schroeder allpasses. The input enters at 0.2, the comb outputs sum at 0.25 and the allpasses use feedback 0.5. Comb lengths are 1557, 1617, 1491 and 1422 frames and allpass lengths 225 and 556, scaled from 44.1 kHz. Each comb's damping filter is y = delayed × (1 − d) + y₋₁ × d, and every stored sample is rounded to f32. The output is x × (1 − mix) + wet × mix.

Parameters

SetterMapping as codedDefault
setReverbRoomSizeFeedback 0.72 + clamp(size, 0, 1) × 0.22 (0.72 to 0.94)0.72
setReverbDampingDamping 0.05 + clamp(d, 0, 1) × 0.45 (0.05 to 0.5)0.05
setReverbMix0 to 10.35

Lifecycle

initReverbNode(rate) allocates every line → prepareReverbNodeSampleRate re-derives lengths inside those lines, keeping history → setters → reverbNodeTick → resetReverbNode, which clears the whole tail and keeps parameters. Size the state for the highest rate it will be prepared at; a longer length is clamped to the allocation. For live replacement, clearReverbNodeStep(state, cursor, budget) clears at most 256 stores per call until it returns reverbNodeStoreCount(state), then finalizeReverbNodeClear resets indices and filters. Ticks allocate nothing.

When to use it

A small, fixed-cost room or plate-like tail on a mono voice or send, or the core of @maru/reverbs-stereo-schroeder.

Limits

  • Mono sample tick; the output is a double, not rounded to f32.
  • Feedback stays below unity (at most 0.94).
  • Parameter changes apply at once with no smoothing.
  • The clamps are the kernel node's conditional clamp, not the math/range policy.

Implementations

Rust Output verified: The build passed and its output matches the reference implementation.

Verified on Apple Silicon macOS native and Node WASM at 8, 44.1, 48, 96 and 192 kHz. Output and state match the TypeScript reference, with no observed allocations, reallocations or deallocations during the tested processing, control and reset calls. Incremental clearing, browser hosts, plugins, other operating systems and realtime deadlines have not been verified. Performance has not been measured.

View target checks and scope

Algorithm properties and proofs

Lean 4 checks specific properties of this algorithm’s mathematical model. Each model result applies within its stated domain and assumptions.

5 Lean 4 results: 4 proofs in their stated domains; 1 refused rewrite.

Proved in the stated domain.For every in-range index and every power-of-two length, wrapping by mask gives the same next index as wrapping by compare.32-bit unsigned words, index below the length, length a power of two below 2^31
Limits and assumptions
  • About a Lean model of boundedIndex, which is not exported and has no hash link to the shipped code. The mask is illegal on non-power-of-two lines such as the Schroeder comb lengths, so it does not apply to the code Maru ships; the proof also trusts bv_decide's checker running as native code.
Theorem
MaruLean.RingWrap.mask_eq_compare
Recorded axioms
propext, Classical.choice, Quot.sound, RingWrap.mask_eq_compare._native.bv_decide.ax_1_7
Manifest reviewed
Proved in the stated domain.For every index below a non-zero length, wrapping by unsigned remainder gives the same next index as wrapping by compare.32-bit unsigned words, index below the length
Limits and assumptions
  • About a Lean model of boundedIndex, not linked to the shipped code by a hash check, and it trusts bv_decide's checker running as native code. Once the index has left the line the substitution is refused.
Theorem
MaruLean.RingWrap.mod_eq_compare
Recorded axioms
propext, Classical.choice, Quot.sound, RingWrap.mod_eq_compare._native.bv_decide.ax_1_7
Manifest reviewed
Proved in the stated domain.The 32-bit compare wrap equals the unbounded integer wrap for every in-range index and every length below 2^32.natural numbers, index below the length, length below 2^32
Limits and assumptions
  • About a Lean model of boundedIndex; the step from a JavaScript number to a natural number is not proved here.
Theorem
MaruLean.RingWrap.compare_toNat
Recorded axioms
propext, Quot.sound
Manifest reviewed
Show 2 more Lean 4 results
Rewrite refused.A mask on a length-1000 line, a mask or modulo once the index has left the line, and modulo on a zero length each give a different index than compare.one concrete 32-bit input per rewrite
Limits and assumptions
  • Each witness is one input; it shows a guard a compiler rule must keep, not which inputs real modules reach.
  • Axioms are not pinned by a #guard_msgs check; `#print axioms` output is unguarded.
  • This record also covers MaruLean.RingWrap.mask_refused_out_of_range, MaruLean.RingWrap.mod_refused_out_of_range, MaruLean.RingWrap.mod_refused_zero_length; axioms describe MaruLean.RingWrap.mask_refused_non_power_of_two only.
Theorem
MaruLean.RingWrap.mask_refused_non_power_of_two
Manifest reviewed
Proved in the stated domain.In the hand-counted model, the mask ring wrap does not dominate the compare wrap, so the model makes no prediction for that pair.counting model
Limits and assumptions
  • Hand-counted operations for C kernels; the model says nothing here, and measurement decides speed.
  • Axioms are not pinned by a #guard_msgs check; `#print axioms` output is unguarded.
Theorem
MaruLean.CostModel.mask_dominates_nothing_against_compare
Manifest reviewed
Explore proofs alongside performance

Parameters

ParameterTypeSetter
Room size roomSizefloatsetReverbRoomSize
Damping dampingfloatsetReverbDamping
Mix mixfloatsetReverbMix

Ports

  • In ininput · audio · mono
  • Out outoutput · audio · mono

One sample per reverbNodeTick call.

Dependencies

None: this node imports no other node.

Dependents

Used by