Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.1.0] - 2026-07-14

### Added

- Project bootstrap: README, license, contributing guide, architecture and build docs, ADRs, and CI workflow.
- DSP core: initial working Nave signal path with unit tests.
- DSP core: initial working Nave signal path (Convolution -> LoCut -> HiCut -> Dry/Wet Mix -> Level) with unit tests.
- **IR Blend**: a second, independently loadable impulse response slot (IR B) and an `IR Blend` parameter that crossfades between IR A and IR B (e.g. two cabs, or two mic positions on the same cab). Defaults to 0% (IR A only), bit-identical to the v0.1 single-IR signal path.
- **Inter-IR phase alignment**: loading IR B automatically time-shifts it so its transient onset lines up with IR A's, preventing comb-filtering when the two are blended together (`src/dsp/IrAlignment.{h,cpp}`).
- **Distance**: a simulated mic-to-cab distance control (post-convolution, pre-LoCut/HiCut) combining a proximity-effect low-shelf cut and a high-frequency "air absorption" high-shelf cut, both scaling with the parameter. Defaults to 0% ("off"), the same explicit-bypass-at-the-extreme pattern used by LoCut/HiCut, so the default state stays a true passthrough.
- Editor: "Load IR B..."/"Default" controls and an IR B file-name label alongside the existing IR A controls, plus IR Blend and Distance knobs.
- Broadened Catch2 test coverage: sample-rate sweep (44.1-192 kHz) null and finite-output tests, mono/stereo/unsupported bus-layout tests, long-run (2000-block and 300-block-with-loaded-IRs) NaN/Inf stability soak tests, and full unit coverage for IR Blend, Distance, and IR-onset-alignment behaviour.
- `docs/manual.md`: a full user manual (signal flow, parameter reference, usage tips).

### Deferred

- **IR browser + bundled IR library** (tracked in issue #1, left open): shipping a curated, bundled set of cabinet IRs requires either licensed real-world captures (an asset-sourcing/licensing task, not a DSP task) or synthetic placeholder IRs that could be mistaken for real captures - neither was implemented in this pass. IR Blend, Distance emulation, and inter-IR phase alignment - the DSP-engineering parts of that issue - are implemented; see the issue comment for the full rationale.
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ Per-repo working memory for Claude Code sessions on this plugin. Part of the **M
## What this is
Nave is the "cabinet IR loader / convolution (guitar & bass)" member of the suite. AU / VST3 / Standalone, JUCE 8.

## Status (v0.1 — bootstrap complete)
Core DSP working, **22 Catch2 tests green**, CI (macOS + Windows, pluginval strictness 10 + auval) green. GUI is a functional v0.1 slider editor (custom LookAndFeel is roadmap M3). No signing yet (roadmap M4). Open work is tracked in this repo's GitHub **milestones/issues**.
## Status (v0.1.0M1 DSP completion & test coverage done)
Core DSP working, **50 Catch2 tests green**, CI (macOS + Windows, pluginval strictness 10 + auval) green. GUI is a functional v0.1 slider editor (custom LookAndFeel is roadmap M3). No signing yet (roadmap M4). M1 added IR Blend (dual-IR crossfade with inter-IR phase alignment) and simulated mic Distance; the "bundled IR library" part of M1's DSP issue is deferred (asset-sourcing/licensing, not a DSP task — see the issue comment on #1). Open work is tracked in this repo's GitHub **milestones/issues**.

## DSP
Nave is a cabinet IR loader built around juce::dsp::Convolution constructed with its default zero-latency, uniformly-partitioned configuration (Convolution::Latency{0}), chosen because reamping IRs are short and the workflow is latency-sensitive. Signal chain: Convolution (loaded IR or a default 1-sample unit-impulse) -> LoCut HPF -> HiCut LPF -> juce::dsp::DryWetMixer (delay-compensated, currently 0 samples) -> Level trim, matching the spec's stated order. LoCut's minimum (20 Hz) and HiCut's maximum (20 kHz) are treated as explicit "off"/bypass positions where the engine skips that filter's IIR processing entirely (not just an extreme cutoff) — this was necessary because even a 2nd-order Butterworth many octaves outside a test tone still imposes enough phase shift to fail a strict -80 dBFS sample-domain null; skipping it entirely gives a true bit-accurate passthrough at the default state. The IR file's absolute path is persisted as a plain ValueTree property on apvts.state (not an APVTS float parameter), round-tripping automatically through copyState()/replaceState(); file I/O only ever happens off the audio thread (editor FileChooser callback or setStateInformation, both message-thread/session-load contexts).
Nave is a cabinet IR loader built around two independent juce::dsp::Convolution slots (IR A, IR B), each constructed with the default zero-latency, uniformly-partitioned configuration (Convolution::Latency{0}), chosen because reamping IRs are short and the workflow is latency-sensitive. Signal chain: Convolution (crossfade of IR A/IR B via the IR Blend parameter) -> Distance (simulated mic-to-cab distance: proximity low-shelf + air-absorption high-shelf) -> LoCut HPF -> HiCut LPF -> juce::dsp::DryWetMixer (delay-compensated, currently 0 samples) -> Level trim. LoCut's minimum (20 Hz), HiCut's maximum (20 kHz), and Distance's minimum (0%) are each treated as explicit "off"/bypass positions where the engine skips that filter's IIR processing entirely (not just an extreme cutoff/gain) — this was necessary because even a 2nd-order Butterworth many octaves outside a test tone still imposes enough phase shift to fail a strict -80 dBFS sample-domain null; skipping it entirely gives a true bit-accurate passthrough at the default state (IR Blend defaults to 0%, i.e. IR A only, which is bit-identical to the pre-M1 single-IR path). Loading IR B applies inter-IR phase alignment (src/dsp/IrAlignment.{h,cpp}: onset detection via a relative-threshold crossing, then a time-domain shift) against IR A's most recently loaded onset, so blending the two never introduces comb-filtering from a timing mismatch. Both IR files' absolute paths are persisted as plain ValueTree properties on apvts.state (not APVTS float parameters), round-tripping automatically through copyState()/replaceState(); file I/O only ever happens off the audio thread (editor FileChooser callbacks or setStateInformation, both message-thread/session-load contexts).

## Build & test
```sh
Expand Down
40 changes: 28 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,55 @@
<!-- ==BEGIN BODY== (plugin engineer: replace this block with What it is / Features / Signal flow / Roadmap) -->
## What it is

Nave is a cabinet impulse-response (IR) loader built on JUCE 8, aimed at reamping guitar and bass DI tracks: load a cab (or full-rig) IR captured from a real speaker/mic setup and Nave convolves it with your DI signal using a zero-latency partitioned convolution engine, then shapes the result with a pair of post-convolution filters, a dry/wet mix, and an output trim. With no IR loaded, Nave runs a unit-impulse (delta) IR - mathematically a passthrough - so it is a valid, transparent effect straight out of the box.
Nave is a cabinet impulse-response (IR) loader built on JUCE 8, aimed at reamping guitar and bass DI tracks: load a cab (or full-rig) IR captured from a real speaker/mic setup and Nave convolves it with your DI signal using a zero-latency partitioned convolution engine, then shapes the result with a simulated mic-distance control, a pair of post-convolution filters, a dry/wet mix, and an output trim. With no IR loaded, Nave runs a unit-impulse (delta) IR - mathematically a passthrough - so it is a valid, transparent effect straight out of the box. See [`docs/manual.md`](docs/manual.md) for the full user manual.

## Features (v0.1 scope)
## Features

- **IR loading** - load any WAV/AIFF impulse response via a file chooser; loading happens off the audio thread and never blocks or allocates during playback
- **Zero-latency convolution** - `juce::dsp::Convolution`'s zero-latency uniformly partitioned algorithm, so Nave never adds plugin delay compensation overhead
- **IR loading, two independent slots** - load any WAV/AIFF impulse response into IR A and/or IR B via file choosers; loading happens off the audio thread and never blocks or allocates during playback
- **IR Blend** - crossfades between IR A and IR B (e.g. two cabs, or two mic positions on the same cab), with automatic inter-IR phase alignment so blending never introduces comb-filtering from a timing mismatch between the two IRs
- **Zero-latency convolution** - `juce::dsp::Convolution`'s zero-latency uniformly partitioned algorithm for both IR slots, so Nave never adds plugin delay compensation overhead
- **Distance** - simulated mic-to-cab distance (reduced proximity-effect bass + high-frequency air-absorption darkening as the value increases); an explicit "off" position at its default
- **LoCut** - post-convolution high-pass, 20 Hz - 800 Hz (default 20 Hz, an explicit "off"/bypassed position), removes low-end mud
- **HiCut** - post-convolution low-pass, 2 kHz - 20 kHz (default 20 kHz, also an explicit "off" position), tames fizz
- **Mix** - dry/wet, default 100% (fully wet) - a cabinet IR is normally run fully in the signal path
- **Level** - output trim, -24 dB to +24 dB
- Full state save/recall via `AudioProcessorValueTreeState`, including the loaded IR's file path
- Full state save/recall via `AudioProcessorValueTreeState`, including both loaded IRs' file paths

## Signal flow

```
Input --> Convolution (loaded IR or default delta) --> LoCut (HPF, 20-800 Hz) --> HiCut (LPF, 2-20 kHz)
|
Output <-- Level (output trim) <-- Mix <--------------+
Input --> Convolution (crossfade of IR A / IR B) --> Distance --> LoCut (HPF, 20-800 Hz) --> HiCut (LPF, 2-20 kHz)
|
Output <-- Level (output trim) <-- Mix <------------------------ +
^
|
delay-compensated dry path
```

See [`docs/architecture.md`](docs/architecture.md) for the full breakdown, including the convolution/latency strategy, the filter-bypass-at-range-extremes design, and IR file state handling.
See [`docs/architecture.md`](docs/architecture.md) for the full breakdown, including the convolution/latency strategy, the filter-bypass-at-range-extremes design, inter-IR phase alignment, and IR file state handling.

## Parameters

| Parameter | Range | Default | Unit | Description |
|---|---|---|---|---|
| LoCut | 20 – 800 | 20 (off) | Hz | Post-convolution high-pass filter; bypassed entirely at its minimum. |
| HiCut | 2000 – 20000 | 20000 (off) | Hz | Post-convolution low-pass filter; bypassed entirely at its maximum. |
| IR Blend | 0 – 100 | 0 (IR A only) | % | Crossfades between IR A and IR B. |
| Distance | 0 – 100 | 0 (off) | % | Simulated mic-to-cab distance coloration; bypassed entirely at its minimum. |
| Mix | 0 – 100 | 100 (fully wet) | % | Dry/wet blend against the original input. |
| Level | -24 – +24 | 0 | dB | Output trim, applied last. |

Full musical context and usage tips: [`docs/manual.md`](docs/manual.md).

## Roadmap

| Milestone | Description | Status |
|---|---|---|
| M0 | Bootstrap - project skeleton, CI, docs | Done |
| M1 | DSP core - IR loading, convolution, LoCut/HiCut/Mix/Level signal path, latency reporting, unit tests | Done |
| M2 | Custom GUI | Planned |
| M3 | Release engineering - signing, notarization, installers, v1.0.0 | Planned |
| M1 | DSP completion & test coverage - IR Blend, Distance emulation, inter-IR phase alignment, broadened Catch2 suite | Done (IR browser + bundled IR library deferred - see issue tracker) |
| M2 | Presets & state recall - preset system, factory presets | Planned |
| M3 | GUI & accessibility - custom LookAndFeel, accessibility pass | Planned |
| M4 | Release - code signing, notarization, installers, v1.0.0 | Planned |
<!-- ==END BODY== -->

## Installation
Expand Down
Loading
Loading