@maruaudio/dsp/modulation/borrowed-rotary is a generic two-rotor speaker approximation. A mono feed (L + R)/2 is split at 800 Hz by RBJ Butterworth sections of @maru/filters-biquad, the low branch inverted so the neutral sum is flat. Each band goes through its own caller-owned ("borrowed") ring and is read back with a sinusoidal delay around 2.5 ms, an amplitude dip and a pan:
| Rotor | Speed | Delay swing | Amplitude dip | Pan |
|---|---|---|---|---|
| Horn (high band) | Rate | ±25 % × depth | up to 45 % × depth | ±0.9 × depth × width |
| Drum (low band) | 0.86 × Rate | ±10 % × depth | up to 30 % × depth | ±0.45 × depth × width |
Parameters
configureBorrowedRotary(state, rateHz, depth, width, balance, smooth) uses clampFiniteWithFallback (@maru/math-range): rateHz 0.1 to 10 Hz (fallback and initial 0.8), depth and width 0 to 1 (1), balance 0 to 1 (0.5, unity band gains). With smooth false every value jumps to its target. Otherwise the horn follows Rate with 0.6 s acceleration and 0.9 s deceleration time constants, the drum with 1.8 s and 2.4 s, and depth, width and balance ramp with 30 ms constants. Rate changes keep the phases.
Lifecycle
initBorrowedRotary(rate) (8 to 192 kHz, fallback 48 kHz) and two prepared initFractionalFeedbackDelayNode rings outside the callback → configure → borrowedRotaryTick(state, hornRing, drumRing, left, right), reading outLeft and outRight → resetBorrowedRotary (filters, phases and ramps) together with clearing both rings. Ticks allocate nothing and do no I/O.
When to use it
A rotating-speaker effect on organ, keys or guitar in a chain that owns its delay rings. It is not a calibrated cabinet or the Drawbars module's graph.
Limits
- Stereo in and out, but summed to mono first, so antiphase input cancels.
- Rings shorter than 3.125 ms plus two frames are refused: both outputs are 0 and the rings are untouched. The rings hold bands, not channels; do not share them.
- Depth 0 stops motion but keeps the feed, crossover and 2.5 ms delay; width 0 makes both outputs equal.
- Wet only. Input is clamped to ±16 (non-finite → 0); peaks are not normalized.