# tunnels

A phase-distortion bass instrument for jungle / Reese workflows.
CZ engine. Mono, glide, legato. Two flagship presets:
`belleville pure` (1988 sound) and `dread pressure` (mid-90s
filtered/distorted Reese), plus a wider palette hinting at other
hands behind the lineage.

See `DESIGN.md` for the audio target and `PLAN.md` for the engineering plan.

## Install

Grab the latest [release](https://github.com/lewisflude/tunnels/releases).
Builds are not signed or notarized — they're functional, but you'll need to
click past one OS warning the first time.

### macOS — `Tunnels-macos-<version>.pkg`

Installs the AU into `/Library/Audio/Plug-Ins/Components`, the VST3 into
`/Library/Audio/Plug-Ins/VST3`, and the standalone into `/Applications`.

**Don't double-click.** Gatekeeper will say *"Apple could not verify…"* and
refuse to open it. Instead:

1. In Finder, **right-click (or Control-click) the `.pkg`** → **Open**.
2. Confirm **Open** in the dialog that appears.
3. Step through the installer normally.

### Windows — `Tunnels-windows-<version>.exe`

Inno Setup wizard. Requires admin to write the VST3 to
`C:\Program Files\Common Files\VST3`.

SmartScreen will show *"Windows protected your PC"*. Click **More info** →
**Run anyway**.

### Linux

- Debian / Ubuntu — `sudo dpkg -i tunnels_<version>_amd64.deb`. Installs the
  VST3 to `/usr/lib/vst3`.
- Other distros — extract `Tunnels-linux-<version>.tar.gz` and run
  `./install.sh` to drop the VST3 into `~/.vst3`.

Restart your DAW after installing so it rescans plug-in folders.

## Build

The dev shell ships `just` as the task runner. `just` lists everything;
`just <recipe>` runs it.

```sh
nix develop          # dev shell (toolchain, deps, just)
just build           # configure + build plugin (VST3 + AU + Standalone) + tests
just test            # run the unit test binary
just verify          # build + ctest + pluginval — full pre-push gate
```

Underneath, `just` just calls `cmake`/`ctest`/the `tools/` scripts, so the
raw commands still work if you prefer them:

```sh
cmake -B build -G Ninja -DBUILD_TESTING=ON
cmake --build build
ctest --test-dir build
```

Sanitisers:

```sh
just asan            # build-asan/ tree with ASan + UBSan, runs ctest
```

## Validation

```sh
just pluginval       # strictness 10 across all formats
just auval           # macOS AU validation
just render          # reference-render diff against tests/refs/
just render-refs     # regenerate tests/refs/*.wav (commit intentionally)
```

Generate the reference WAVs once with `just render-refs`, then commit them;
subsequent `just render` runs diff against the checked-in WAVs.

## Layout

- `src/PluginProcessor.{h,cpp}` — APVTS layout, processor, held-note stack
- `src/PluginEditor.{h,cpp}` — single-screen custom editor + LookAndFeel
- `src/dsp/CZWaveforms.h` — eight CZ phase-distortion shapes (CZ-faithful)
- `src/dsp/PDLine.h` — single PD oscillator + DCW envelope
- `src/dsp/SubOscillator.h` — sine sub at −1 oct
- `src/dsp/Drive.h` — `tanh` saturation, 0..24 dB, unity make-up at 0 dB
- `src/dsp/LadderFilter.h` — 24 dB ZDF ladder LP, `tanh` feedback
- `src/dsp/PDVoice.{h,cpp}` — single voice, oversampled saturated stage
- `presets/*.xml` — eight shipped presets, embedded as BinaryData
- `tests/` — `juce::UnitTest` suite + reference-render harness
- `tools/` — `pluginval.sh`, `auval.sh`, `render.sh`

## Quality bar (PLAN ↔ status)

- `ctest` — green
- pluginval / auval / render harness — wired up, run pre-merge by user
- aliasing budget at 48 kHz / 2× oversampling — 146.7 dB SNR (PLAN: ≥ 60 dB)
- filter at full reso + full drive over 10 s — no NaN
- state round-trip — bit-identical, unknown future params ignored cleanly
- 60 s random-MIDI render — no NaN, no out-of-range samples
- preset switch while held — smoothed audible-rate params; discrete params
  take effect on next noteOn (per PLAN)
