Dattorro Plateau reverb implentation in Supriya

I’ve been working with Claude Code recently, and wanted to see how well it could create a SynthDef from code in another language that isn’t SuperCollider. I decided to start with something challenging, and chose the VCV Rack implementation of Dattorro’s Plateau reverb found here. I chose that because it’s such a great sounding reverb.

The code works, but is too echoey. I was wondering if anyone is familiar with this reverb algorithm, and is willing to offer some pointers? I’ve tried adding more input diffusers and all-pass filters in the “tanks” (those aren’t in the code I included, though). That did help, but not completely. Maybe I need more? By the way, the code is Python as I use Supriya, the Python API for SuperCollider.

"""
Plateau Reverb - Dattorro 1997 Plate Reverb Algorithm
Based on Jon Dattorro's "Effect Design Part 1: Reverberator and Other Filters" (1997)
Ported from Valley Audio's Plateau VCV Rack module

=============================================================================
ALGORITHM OVERVIEW
=============================================================================

This is an implementation of Jon Dattorro's figure-8 plate reverb algorithm,
which creates a dense, lush reverb tail through cascaded allpass diffusion and
cross-coupled stereo delay tanks with modulated feedback.

The algorithm consists of these main stages:
1. Pre-delay (optional time offset before reverb starts)
2. Input bandwidth limiting (HPF + LPF on input signal)
3. Input diffusion network (4 cascaded allpass filters)
4. Modulation oscillators (sine/noise LFOs for chorus effect)
5. Stereo tank (cross-coupled feedback loops with diffusion)
6. Tank damping filters (frequency-dependent decay control)
7. Output taps (8 per channel for dense early reflections)
8. Output bandwidth limiting (DC blocking)
9. Dry/wet mix

=============================================================================
INPUT PARAMETERS
=============================================================================

pre_delay: float (0.0-0.5 seconds)
    Time offset before reverb starts. Creates gap between dry signal and
    reverb tail. Useful for simulating room size or creating special effects.

size: float (0.0-1.0)
    Scales all delay times proportionally. Controls the apparent size of the
    reverberant space. Smaller = tighter sound, larger = more spacious.
    At 1.0, delay times match Dattorro's original specification exactly.

decay: float (0.1-0.9999)
    Controls how long the reverb tail lasts. Applied quadratically for more
    intuitive control curve. Higher values = longer decay time.
    At 0.9999, decay time approaches infinity (infinite reverb).

diffusion: float (0.0-10.0)
    Controls the feedback gain in the tank allpass filters (0-0.7 actual gain).
    Higher diffusion = denser, smoother reverb tail. Lower = more discrete echoes.
    Dattorro recommends 0.7 max to avoid metallic artifacts.

input_low_cut: float (0.0-10.0 pitch, converted to Hz)
    High-pass filter on input before diffusion. Removes low frequencies from
    reverb tail. Useful for preventing muddy bass buildup.
    Pitch range: 0 = 13.75 Hz, 5 = 440 Hz, 10 = 14080 Hz

input_high_cut: float (0.0-10.0 pitch, converted to Hz)
    Low-pass filter on input before diffusion. Removes high frequencies from
    reverb tail. Creates darker, warmer reverb character.
    Pitch range: 0 = 13.75 Hz, 5 = 440 Hz, 10 = 14080 Hz

reverb_low_cut: float (0.0-10.0 pitch, converted to Hz)
    High-pass filter in feedback loop. Controls frequency-dependent decay -
    low frequencies decay faster. Prevents bass buildup in long tails.
    Applied inside the tank feedback loop for exponential effect.

reverb_high_cut: float (0.0-10.0 pitch, converted to Hz)
    Low-pass filter in feedback loop. Controls frequency-dependent decay -
    high frequencies decay faster. Creates natural damping like real rooms.
    Applied inside the tank feedback loop for exponential effect.

mod_speed: float (0.0-1.0, squared then mapped to 1-100 Hz)
    Controls LFO frequency for chorus modulation. Prevents metallic ringing
    by detuning delay lines. Dattorro spec calls for 1-2 Hz base frequency.
    0.0 = 1-2 Hz (subtle), 1.0 = 10-18 Hz (intense chorus/vibrato)

mod_depth: float (0.0-16.0)
    Maximum delay time modulation in samples (±16 samples max per Dattorro).
    Controls intensity of chorus effect. Higher = more pitch variation.
    Combined with mod_speed to create lush, moving reverb tail.

mod_shape: float (0.0-1.0)
    Crossfades between sine wave LFOs (0.0) and noise LFOs (1.0).
    Sine = smooth, periodic modulation. Noise = random, irregular modulation.
    Intermediate values blend both for complex modulation patterns.

dry: float (0.0-1.0)
    Dry signal level (original unprocessed input).
    1.0 = full dry signal, 0.0 = completely wet (reverb only).

wet: float (0.0-1.0, scaled by 10x internally)
    Wet signal level (reverb output). Scaled by 10.0 internally for gain.
    0.5 = moderate reverb mix, 1.0 = very wet mix.

=============================================================================
SIGNAL FLOW
=============================================================================

Input (stereo) → Sum to mono → Pre-delay → Input filters (HPF/LPF)
                                                    ↓
                                         Input diffusion network
                                         (4 cascaded allpass)
                                                    ↓
                                   ┌────────────────┴────────────────┐
                                   ↓                                 ↓
                         LEFT TANK (figure-8)          RIGHT TANK (figure-8)
                         ↓                                           ↓
    ┌──────────────────→ + ←─────────────┐    ┌──────────────────→ + ←─────────────┐
    │                    ↓                │    │                    ↓                │
    │                Allpass 1            │    │                Allpass 1            │
    │               (modulated)           │    │               (modulated)           │
    │                    ↓                │    │                    ↓                │
    │                Delay 1              │    │                Delay 1              │
    │              (damping filters)      │    │              (damping filters)      │
    │                    ↓                │    │                    ↓                │
    │                Allpass 2            │    │                Allpass 2            │
    │               (modulated)           │    │               (modulated)           │
    │                    ↓                │    │                    ↓                │
    │                Delay 2              │    │                Delay 2              │
    │                    ↓                │    │                    ↓                │
    └─────── decay ─────┘                │    └─────── decay ─────┘                │
             (feeds right tank)           │             (feeds left tank)            │
                                          └───────────────┬──────────────────────────┘
                                                          ↓
                                                    Output taps
                                        (8 taps per channel, ±0.6 coefficients)
                                                          ↓
                                                   DC blocking (HPF)
                                                          ↓
                                              Dry/wet mix → Output (stereo)

=============================================================================
STAGE DETAILS
=============================================================================

1. PRE-DELAY
   - Optional delay (0-500ms) before reverb starts
   - Simulates distance between sound source and reflective surfaces
   - Implemented with DelayC (cubic interpolation)

2. INPUT BANDWIDTH LIMITING
   - High-pass filter (input_low_cut) removes rumble and DC offset
   - Low-pass filter (input_high_cut) shapes tonal character
   - Applied before diffusion to prevent frequency buildup

3. INPUT DIFFUSION NETWORK
   - 4 cascaded allpass filters with coprime delays (141, 107, 379, 277 samples)
   - Creates dense early reflections from single input
   - Fixed gains: 0.75, 0.75, 0.625, 0.625 (per Dattorro spec)
   - Coprime delays prevent periodic artifacts
   - All delays scaled by 'size' parameter

4. MODULATION OSCILLATORS
   - Four independent LFO pairs (sine + noise for each tank allpass)
   - Base frequencies: 1.0, 1.5, 1.2, 1.8 Hz (per Dattorro: 1-2 Hz range)
   - Prevents comb filter resonances and metallic ringing
   - mod_shape crossfades between smooth (sine) and random (noise)
   - mod_depth controls ±16 sample excursion maximum
   - mod_speed scales frequencies 1x-10x for tempo sync or special effects

5. STEREO TANK (Figure-8 topology)
   Each tank has identical structure but different delay times:

   a) Cross-coupled input mixing
      - Left tank receives: diffused input + right tank feedback
      - Right tank receives: diffused input + left tank feedback
      - Creates stereo width through decorrelation

   b) Modulated allpass filter (decay diffusion 1)
      - Left: 672 samples, Right: 908 samples
      - Negative gain (-tank_diffusion) for inversion
      - Modulated by LFO for chorus effect
      - Creates early reflections within tank

   c) Long delay line 1
      - Left: 4453 samples (~150ms), Right: 4217 samples (~142ms)
      - Main reverb time accumulator
      - Coprime lengths prevent periodic artifacts

   d) Tank damping filters
      - Applied after delay 1, before allpass 2
      - Frequency-dependent decay (reverb_low_cut, reverb_high_cut)
      - In feedback loop = exponential effect on decay spectrum

   e) Decay scaling
      - Applied after damping to control overall reverb time
      - Quadratic curve for intuitive control (decay²)

   f) Modulated allpass filter (decay diffusion 2)
      - Left: 1800 samples, Right: 2656 samples
      - Positive gain (+tank_diffusion)
      - Modulated by different LFO for variety
      - Adds complexity to late reflections

   g) Long delay line 2
      - Left: 3720 samples (~125ms), Right: 3163 samples (~106ms)
      - Secondary reverb time accumulator
      - Output point for feedback and taps

6. OUTPUT TAPS
   - 8 delay taps per channel from various points in both tanks
   - Specific coefficients from Dattorro Table 2: ±0.6
   - Pattern per channel: +0.6, +0.6, -0.6, +0.6 (same tank)
                          -0.6, -0.6, -0.6, -0.6 (cross tank)
   - Asymmetric tap pattern creates wide stereo image
   - Cross-channel taps increase decorrelation
   - No equal weighting - specific phase relationships prevent cancellation

7. OUTPUT BANDWIDTH LIMITING
   - 20 Hz high-pass filter removes DC drift from feedback accumulation
   - Prevents speaker cone excursion and low-frequency buildup

8. DRY/WET MIX
   - Dry signal preserved in parallel
   - Wet scaled by 10.0 for gain compensation
   - Independent control of dry and wet levels

=============================================================================
TECHNICAL SPECIFICATIONS
=============================================================================

Reference Sample Rate: 29,761 Hz (Dattorro original specification)
  - All delay times specified at this rate
  - Automatically scaled to actual sample rate

Input Diffusion Gains:
  - Filters 1-2: 0.75 (fixed, per Dattorro)
  - Filters 3-4: 0.625 (fixed, per Dattorro)

Tank Diffusion Gain Range:
  - Minimum: 0.0 (no diffusion, discrete echoes)
  - Maximum: 0.7 (per Dattorro, prevents metallic artifacts)
  - Scaled from diffusion parameter (0-10 → 0-0.7)

LFO Modulation:
  - Maximum excursion: ±16 samples (per Dattorro)
  - Base frequencies: 1.0, 1.5, 1.2, 1.8 Hz
  - Four independent oscillators (different frequencies)

Delay Times (at size=1.0):
  - Input diffusion: 141, 107, 379, 277 samples
  - Left tank: 672, 4453, 1800, 3720 samples
  - Right tank: 908, 4217, 2656, 3163 samples
  - All coprime to minimize periodic artifacts

Output Tap Delays (samples at size=1.0):
  - Left from left: 266, 2974, 1913, 1996
  - Left from right: 1990, 187, 1066, 0
  - Right from right: 353, 3627, 1228, 2673
  - Right from left: 2111, 335, 121, 0

Decay Curve:
  - Input: 0.1-0.9999 (linear)
  - Applied: quadratic (1 - (1 - decay)²)
  - Creates more intuitive control response

=============================================================================
REFERENCES
=============================================================================

Jon Dattorro (1997). "Effect Design Part 1: Reverberator and Other Filters"
  Journal of the Audio Engineering Society, Vol. 45, No. 9, pp. 660-684
  https://ccrma.stanford.edu/~dattorro/EffectDesignPart1.pdf

Valley Audio Plateau VCV Rack module
  https://github.com/ValleyAudio/ValleyRackFree

Key Features of This Algorithm:
  - Figure-8 feedback topology for infinite reverb
  - Modulated allpass filters prevent metallic ringing
  - Coprime delay lengths minimize periodic artifacts
  - Cross-coupled stereo tanks create wide image
  - Frequency-dependent damping for natural decay
  - Multiple output taps for dense early reflections
  - Asymmetric tap weighting prevents phase cancellation

=============================================================================
"""

import math
from supriya import synthdef
from supriya.ugens import (
    In,
    Out,
    AllpassC,
    DelayC,
    LPF,
    HPF,
    LinExp,
    LocalIn,
    LocalOut,
    SinOsc,
    LFNoise1,
    LinLin,
)

# Dattorro reference sample rate
DATTORRO_SR = 29761.0

# Fixed coefficients from Dattorro paper (input diffusion stays fixed)
INPUT_DIFFUSION_1 = 0.75
INPUT_DIFFUSION_2 = 0.625
# Tank diffusion max from Dattorro paper
MAX_TANK_DIFFUSION = 0.7

# LFO max excursion (samples)
LFO_MAX_EXCURSION = 16.0


def gain_to_decay_time(gain, delay_time):
    """
    Convert allpass gain coefficient to SuperCollider decay_time (RT60).

    decay_time = delay_time * -60 / (20 * log10(|gain|))
    """
    if abs(gain) < 0.001:
        return 0.001
    if abs(gain) >= 1.0:
        return 100.0  # Very long decay

    return delay_time * -60.0 / (20.0 * math.log10(abs(gain)))


def pitch_to_frequency(pitch):
    """
    Convert pitch value (0-10) to frequency in Hz using LinExp.
    Uses Valley Audio's formula: freq = 440.0 * 2^(pitch - 5.0)

    Range: pitch 0 → 13.75 Hz, pitch 5 → 440 Hz, pitch 10 → 14080 Hz

    Using LinExp for safe exponential mapping without power operations.
    """
    # Map pitch (0-10) to frequency (13.75-14080 Hz) using exponential curve
    # LinExp maps linearly from input range to exponentially from output range
    return LinExp.kr(
        source=pitch,
        input_minimum=0.0,
        input_maximum=10.0,
        output_minimum=13.75,
        output_maximum=14080.0
    )


@synthdef()
def plateau_reverb(
    in_bus: int = 2,
    out_bus: int = 0,
    # Valley Audio parameter ranges (matched exactly)
    pre_delay: float = 0.0,  # 0.0-0.5 seconds
    size: float = 0.5,  # 0.0-1.0 scale
    decay: float = 0.54995,  # 0.1-0.9999
    diffusion: float = 10.0,  # 0.0-10.0
    input_low_cut: float = 10.0,  # 0.0-10.0 pitch (converted to Hz)
    input_high_cut: float = 10.0,  # 0.0-10.0 pitch (converted to Hz)
    reverb_low_cut: float = 10.0,  # 0.0-10.0 pitch (converted to Hz)
    reverb_high_cut: float = 10.0,  # 0.0-10.0 pitch (converted to Hz)
    mod_speed: float = 0.0,  # 0.0-1.0 (squared, then 1-100 Hz)
    mod_depth: float = 0.5,  # 0.0-16.0
    mod_shape: float = 0.5,  # 0.0-1.0 (0=sine, 1=noise)
    dry: float = 1.0,  # 0.0-1.0
    wet: float = 0.5,  # 0.0-1.0
):

    # Apply quadratic decay curve (from Plateau.cpp)
    decay_factor = 1.0 - decay
    decay_scaled = 1.0 - (decay_factor * decay_factor)

    # Apply wet gain scaling (multiply by 10.0 from Plateau.cpp)
    wet_scaled = wet * 10.0

    # Scale tank diffusion: 0-10 → 0-0.7 (max from Dattorro paper)
    tank_diffusion = (diffusion / 10.0) * MAX_TANK_DIFFUSION

    # Convert damping pitch values (0-10) to Hz
    input_low_cut_hz = pitch_to_frequency(input_low_cut)
    input_high_cut_hz = pitch_to_frequency(input_high_cut)
    reverb_low_cut_hz = pitch_to_frequency(reverb_low_cut)
    reverb_high_cut_hz = pitch_to_frequency(reverb_high_cut)

    # Read stereo input
    input_signal = In.ar(bus=in_bus, channel_count=2)
    dry_signal = input_signal

    # Convert stereo to mono for processing
    mono_input = (input_signal[0] + input_signal[1]) * 0.5

    # Pre-delay (up to 0.5 seconds)
    if pre_delay > 0.001:
        pre_delayed = DelayC.ar(source=mono_input, maximum_delay_time=0.5, delay_time=pre_delay)
    else:
        pre_delayed = mono_input

    # Input bandwidth filtering
    filtered = pre_delayed
    if input_low_cut_hz > 20.0:
        filtered = HPF.ar(source=filtered, frequency=input_low_cut_hz)
    if input_high_cut_hz < 20000.0:
        filtered = LPF.ar(source=filtered, frequency=input_high_cut_hz)

    # INPUT DIFFUSION: 4 allpass filters in series
    # Delay times from Dattorro paper: 141, 107, 379, 277 samples
    scale = size

    # Diffuser 1: 141 samples, gain = 0.75
    d1_time = (141.0 / DATTORRO_SR) * scale
    d1_decay = gain_to_decay_time(INPUT_DIFFUSION_1, d1_time)
    diffuser_1 = AllpassC.ar(
        source=filtered,
        maximum_delay_time=0.1,
        delay_time=d1_time,
        decay_time=d1_decay
    )

    # Diffuser 2: 107 samples, gain = 0.75
    d2_time = (107.0 / DATTORRO_SR) * scale
    d2_decay = gain_to_decay_time(INPUT_DIFFUSION_1, d2_time)
    diffuser_2 = AllpassC.ar(
        source=diffuser_1,
        maximum_delay_time=0.1,
        delay_time=d2_time,
        decay_time=d2_decay
    )

    # Diffuser 3: 379 samples, gain = 0.625
    d3_time = (379.0 / DATTORRO_SR) * scale
    d3_decay = gain_to_decay_time(INPUT_DIFFUSION_2, d3_time)
    diffuser_3 = AllpassC.ar(
        source=diffuser_2,
        maximum_delay_time=0.2,
        delay_time=d3_time,
        decay_time=d3_decay
    )

    # Diffuser 4: 277 samples, gain = 0.625
    d4_time = (277.0 / DATTORRO_SR) * scale
    d4_decay = gain_to_decay_time(INPUT_DIFFUSION_2, d4_time)
    diffuser_4 = AllpassC.ar(
        source=diffuser_3,
        maximum_delay_time=0.2,
        delay_time=d4_time,
        decay_time=d4_decay
    )

    # diffused_input = diffuser_8
    diffused_input = diffuser_4

    # MODULATION OSCILLATORS
    # Four different base LFO frequencies: 1.0, 1.5, 1.2, 1.8 Hz (per Dattorro paper: 1-2 Hz)
    # mod_speed scales from 1x (at 0.0) to 10x (at 1.0) the base frequencies
    # This gives a range of 1-2 Hz (default) up to 10-18 Hz (max)
    mod_scale = 1.0 + (mod_speed * 9.0)

    lfo_1 = SinOsc.kr(frequency=1.0 * mod_scale)
    lfo_2 = SinOsc.kr(frequency=1.5 * mod_scale)
    lfo_3 = SinOsc.kr(frequency=1.2 * mod_scale)
    lfo_4 = SinOsc.kr(frequency=1.8 * mod_scale)

    # Noise modulators (doubled frequency for variety)
    noise_1 = LFNoise1.kr(frequency=2.0 * mod_scale)
    noise_2 = LFNoise1.kr(frequency=3.0 * mod_scale)
    noise_3 = LFNoise1.kr(frequency=2.4 * mod_scale)
    noise_4 = LFNoise1.kr(frequency=3.6 * mod_scale)

    # Interpolate between sine and noise based on mod_shape
    mod_1 = LinLin.kr(
        source=mod_shape,
        input_minimum=0.0,
        input_maximum=1.0,
        output_minimum=lfo_1,
        output_maximum=noise_1
    )
    mod_2 = LinLin.kr(
        source=mod_shape,
        input_minimum=0.0,
        input_maximum=1.0,
        output_minimum=lfo_2,
        output_maximum=noise_2
    )
    mod_3 = LinLin.kr(
        source=mod_shape,
        input_minimum=0.0,
        input_maximum=1.0,
        output_minimum=lfo_3,
        output_maximum=noise_3
    )
    mod_4 = LinLin.kr(
        source=mod_shape,
        input_minimum=0.0,
        input_maximum=1.0,
        output_minimum=lfo_4,
        output_maximum=noise_4
    )

    # Scale modulation depth (Valley Audio range: 0-16, maps to ±16 samples)
    mod_depth_samples = (mod_depth / 16.0) * (LFO_MAX_EXCURSION / DATTORRO_SR) * scale

    mod_1_scaled = mod_1 * mod_depth_samples
    mod_2_scaled = mod_2 * mod_depth_samples
    mod_3_scaled = mod_3 * mod_depth_samples
    mod_4_scaled = mod_4 * mod_depth_samples

    # STEREO TANK with feedback using LocalIn/LocalOut
    # 2 channels of feedback
    feedback = LocalIn.ar(channel_count=2)

    # ============ LEFT TANK ============
    # Mix input with right channel feedback (cross-feedback)
    # Note: decay is already applied in the feedback signal
    left_input = diffused_input + feedback[1]

    # Decay diffusion 1 (modulated allpass) - 672 samples, gain = -tank_diffusion
    left_apf_1_delay = (672.0 / DATTORRO_SR) * scale + mod_1_scaled
    left_apf_1_decay = gain_to_decay_time(tank_diffusion, (672.0 / DATTORRO_SR) * scale)
    # Negative gain: invert input, apply allpass, invert output
    left_apf_1 = AllpassC.ar(
        source=-left_input,
        maximum_delay_time=0.2,
        delay_time=left_apf_1_delay,
        decay_time=left_apf_1_decay
    )
    left_apf_1 = -left_apf_1

    # Delay line 1 - 4453 samples
    left_delay_1 = DelayC.ar(
        source=left_apf_1,
        maximum_delay_time=0.5,
        delay_time=(4453.0 / DATTORRO_SR) * scale
    )

    # Damping filters (post-delay1, pre-APF2)
    if reverb_high_cut_hz < 20000.0:
        left_delay_1 = LPF.ar(source=left_delay_1, frequency=reverb_high_cut_hz)
    if reverb_low_cut_hz > 20.0:
        left_delay_1 = HPF.ar(source=left_delay_1, frequency=reverb_low_cut_hz)

    left_delay_1 = left_delay_1 * decay_scaled

    # Decay diffusion 2 (modulated allpass) - 1800 samples, gain = +tank_diffusion
    left_apf_2_delay = (1800.0 / DATTORRO_SR) * scale + mod_2_scaled
    left_apf_2_decay = gain_to_decay_time(tank_diffusion, (1800.0 / DATTORRO_SR) * scale)
    left_apf_2 = AllpassC.ar(
        source=left_delay_1,
        maximum_delay_time=0.2,
        delay_time=left_apf_2_delay,
        decay_time=left_apf_2_decay
    )

    # Delay line 2 - 3720 samples
    left_delay_2 = DelayC.ar(
        source=left_apf_2,
        maximum_delay_time=0.5,
        delay_time=(3720.0 / DATTORRO_SR) * scale
    )

    # Output for feedback (attenuated by decay)
    left_feedback = left_delay_2 * decay_scaled

    # ============ RIGHT TANK ============
    # Mix input with left channel feedback (cross-feedback)
    # Note: decay is already applied in the feedback signal
    right_input = diffused_input + feedback[0]

    # Decay diffusion 1 (modulated allpass) - 908 samples, gain = -tank_diffusion
    right_apf_1_delay = (908.0 / DATTORRO_SR) * scale + mod_3_scaled
    right_apf_1_decay = gain_to_decay_time(tank_diffusion, (908.0 / DATTORRO_SR) * scale)
    # Negative gain: invert input, apply allpass, invert output
    right_apf_1 = AllpassC.ar(
        source=-right_input,
        maximum_delay_time=0.2,
        delay_time=right_apf_1_delay,
        decay_time=right_apf_1_decay
    )
    right_apf_1 = -right_apf_1

    # Delay line 1 - 4217 samples
    right_delay_1 = DelayC.ar(
        source=right_apf_1,
        maximum_delay_time=0.5,
        delay_time=(4217.0 / DATTORRO_SR) * scale
    )

    # Damping filters (post-delay1, pre-APF2)
    if reverb_high_cut_hz < 20000.0:
        right_delay_1 = LPF.ar(source=right_delay_1, frequency=reverb_high_cut_hz)
    if reverb_low_cut_hz > 20.0:
        right_delay_1 = HPF.ar(source=right_delay_1, frequency=reverb_low_cut_hz)

    right_delay_1 = right_delay_1 * decay_scaled

    # Decay diffusion 2 (modulated allpass) - 2656 samples, gain = +tank_diffusion
    right_apf_2_delay = (2656.0 / DATTORRO_SR) * scale + mod_4_scaled
    right_apf_2_decay = gain_to_decay_time(tank_diffusion, (2656.0 / DATTORRO_SR) * scale)
    right_apf_2 = AllpassC.ar(
        source=right_delay_1,
        maximum_delay_time=0.2,
        delay_time=right_apf_2_delay,
        decay_time=right_apf_2_decay
    )

    # Delay line 2 - 3163 samples
    right_delay_2 = DelayC.ar(
        source=right_apf_2,
        maximum_delay_time=0.5,
        delay_time=(3163.0 / DATTORRO_SR) * scale
    )

    # Output for feedback (attenuated by decay)
    right_feedback = right_delay_2 * decay_scaled

    # Close the feedback loop (send decay-attenuated signals)
    LocalOut.ar(source=[left_feedback, right_feedback])

    # OUTPUT TAPS - 8 per channel with cross-channel routing
    # Tap coefficients from Dattorro Table 2: 0.6 with specific +/- signs

    # Left output: +0.6, +0.6, -0.6, +0.6 (left), -0.6, -0.6, -0.6, -0.6 (right cross)
    left_wet = (
        # From left channel
        0.6 * DelayC.ar(source=left_apf_1, maximum_delay_time=0.3, delay_time=(266.0 / DATTORRO_SR) * scale) +
        0.6 * DelayC.ar(source=left_apf_1, maximum_delay_time=0.3, delay_time=(2974.0 / DATTORRO_SR) * scale) +
        -0.6 * DelayC.ar(source=left_delay_1, maximum_delay_time=0.5, delay_time=(1913.0 / DATTORRO_SR) * scale) +
        0.6 * DelayC.ar(source=left_apf_2, maximum_delay_time=0.3, delay_time=(1996.0 / DATTORRO_SR) * scale) +
        # From right channel (cross-taps)
        -0.6 * DelayC.ar(source=right_delay_1, maximum_delay_time=0.5, delay_time=(1990.0 / DATTORRO_SR) * scale) +
        -0.6 * DelayC.ar(source=right_apf_2, maximum_delay_time=0.3, delay_time=(187.0 / DATTORRO_SR) * scale) +
        -0.6 * DelayC.ar(source=right_delay_2, maximum_delay_time=0.5, delay_time=(1066.0 / DATTORRO_SR) * scale) +
        -0.6 * right_delay_2
    )

    # Right output: +0.6, +0.6, -0.6, +0.6 (right), -0.6, -0.6, -0.6, -0.6 (left cross)
    right_wet = (
        # From right channel
        0.6 * DelayC.ar(source=right_apf_1, maximum_delay_time=0.3, delay_time=(353.0 / DATTORRO_SR) * scale) +
        0.6 * DelayC.ar(source=right_apf_1, maximum_delay_time=0.3, delay_time=(3627.0 / DATTORRO_SR) * scale) +
        -0.6 * DelayC.ar(source=right_delay_1, maximum_delay_time=0.5, delay_time=(1228.0 / DATTORRO_SR) * scale) +
        0.6 * DelayC.ar(source=right_apf_2, maximum_delay_time=0.3, delay_time=(2673.0 / DATTORRO_SR) * scale) +
        # From left channel (cross-taps)
        -0.6 * DelayC.ar(source=left_delay_2, maximum_delay_time=0.5, delay_time=(2111.0 / DATTORRO_SR) * scale) +
        -0.6 * DelayC.ar(source=left_delay_2, maximum_delay_time=0.5, delay_time=(335.0 / DATTORRO_SR) * scale) +
        -0.6 * DelayC.ar(source=left_delay_2, maximum_delay_time=0.5, delay_time=(121.0 / DATTORRO_SR) * scale) +
        -0.6 * left_delay_2
    )

    # DC blocking on outputs (20 Hz HPF)
    left_wet = HPF.ar(source=left_wet, frequency=20.0)
    right_wet = HPF.ar(source=right_wet, frequency=20.0)

    # Combine dry and wet (wet is scaled by 10.0)
    left_out = (dry_signal[0] * dry) + (left_wet * wet_scaled)
    right_out = (dry_signal[1] * dry) + (right_wet * wet_scaled)

    # Output stereo signal
    Out.ar(bus=out_bus, source=[left_out, right_out])


Did you compare yours to Alik’s:

https://www.youtube.com/watch?v=AnmY5LFHSVk&list=PLXCUkMwOEWQuPvyeeZQn76vywqZrXLiiT&index=3

from a number of years ago?

Sam

Oh, great. I’ll check it out. Thanks!