@maruaudio/dsp/modulation/stereo-ring-modulator multiplies each channel of a stereo pair by a carrier and outputs only the product:
outLeft = fround(left × carrierX) // base frequency
outRight = fround(right × carrierY) // base × ratio, offset by a phaseBoth carriers come from one f32 master phase of @maru/oscillators-lissajous-duo, so the right lane's integer ratio and phase offset stay phase locked. The carrier is a sine, or a PolyBLEP saw or square. The output is wet only; the caller owns the dry/wet blend.
Parameters
| Setter | Range the setter enforces | Default |
|---|---|---|
setStereoRingModulatorFrequency | 0.1 Hz to stereoRingModulatorMaximumFrequency(rate); NaN → 0.1 Hz | 220 Hz |
setStereoRingModulatorWaveform | Clamped to 0 to 2, rounded; NaN → 0: sine, saw, square | Sine |
setStereoRingModulatorRightRatio | Integer 1 to 4 (denominator 1); NaN → 1 | 1 |
setStereoRingModulatorPhaseOffset | 0 to 180 degrees of the right carrier; NaN → 0 | 0° |
The frequency ceiling is min(2000 Hz, 0.45 × rate / 4), so a four-times right lane stays below 0.45 × the sample rate: 900 Hz at 8 kHz. The init rate is clamped to 8 to 384 kHz, and a non-finite rate uses 48 kHz.
Lifecycle
initStereoRingModulatorNode → setters → stereoRingModulatorNodeTick(state, left, right), reading outLeft and outRight → resetStereoRingModulatorNode, which zeroes the master phase and outputs and keeps the configuration. A frequency change keeps the running phase. Ticks allocate nothing and do no I/O.
When to use it
Sum-and-difference sidebands on a voice or synth, with the right carrier at a multiple of the left one or in phase opposition.
Limits
- Stereo in, stereo out, one frame per tick; no audio history, tail or delay.
- Waveform, ratio and phase changes apply on the next sample with no fade; a product with live discrete controls schedules its own transition.
- Only the carrier fundamental is bounded. The sum and difference sidebands with the input are not oversampled or band-limited.