Files
reasampler/CLAUDE.md
T
daniel 43ca5e4069 Docs: reflect Phase D5 landing; close out Phase D
Archive D5 (mode switch) to COMPLETED.md and remove the completed Phase D
section from PLAN.md; add mode_switch to CLAUDE.md architecture + targets.
2026-07-23 12:47:13 -04:00

7.5 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repo identity and current state

The CMake project and binary are now named reaper_reasampler. This is ReaSampler — a per-project audio sample-bank capture tool. The MPE modules (mpe_model, mpe_view) have been removed. M0M4 are complete: bank_model (Sample metadata + BankIndex with JSON round-trip), peaks (waveform min/max bin computation), the capture shell's offline-render path (DAW-verified spike: master-mix / time-selection capture to a project-relative bank at 32-bit float WAV, non-destructive), and persist (BankIndex ↔ project ext state: SetProjExtState/GetProjExtState under namespace "reasampler", GUID-based project identity, project-relative path resolution, Save-As bank relocation with copy semantics) are all implemented and tested. The remaining REAPER-facing shells (insert, bank_panel, actions) and the realtime backend are being built out per CONTEXT.md / PLAN.md. The discipline — pure REAPER-free testable core split from REAPER-facing shells — is preserved throughout.

CONTEXT.md is the authoritative spec and build roadmap. Read it first for any non-trivial task. Every REAPER API name cited there is correct-by-intent; verify argument order, types, and flag values against vendor/reaper-sdk/sdk/reaper_plugin_functions.h before use.

One-time submodule setup

git submodule update --init

Vendors two submodules (see .gitmodules):

  • vendor/reaper-sdksdk/reaper_plugin.h, sdk/reaper_plugin_functions.h, SWELL headers
  • vendor/WDL — WDL utilities and the SWELL cross-platform Win32 layer

Build and test

cmake -B build -S .
cmake --build build
ctest --test-dir build

Seven targets:

Target Kind Purpose
bank_model_tests executable Pure unit tests for bank_model — no REAPER, no DAW.
peaks_tests executable Pure unit tests for peaks — no REAPER, no DAW.
capture_paths_tests executable Pure unit tests for capture_paths — no REAPER, no DAW.
view_mode_model_tests executable Pure unit tests for view_mode_model — no REAPER, no DAW.
view_tree_tests executable Pure unit tests for view_tree — no REAPER, no DAW.
mode_switch_tests executable Pure unit tests for mode_switch — no REAPER, no DAW.
reaper_reasampler loadable module The actual extension binary (.dll / .dylib / .so).

macOS / Linux: SWELL dialog resources

src/resource.rc must be pre-processed by SWELL's resgen once per platform:

php vendor/WDL/WDL/swell/mac_resgen.php src/resource.rc   # macOS; Linux reuses the output

Add the generated file to the appropriate APPLE / Linux target_sources block in CMakeLists.txt. The SWS extension build is the canonical reference for this step.

Install / reload

There is no hot-reload. Copy the built binary into REAPER's UserPlugins/ folder (Options → Show REAPER resource path) and restart REAPER. Extensions load at startup only.

Architecture: the load-bearing split

Pure core (no REAPER types, unit-testable outside the DAW):

  • bank_modelSample metadata struct + BankIndex (add/remove/query/tier/dedup-by-hash + JSON round-trip). Test it hard — it is the heart.
  • peaks — waveform min/max bin computation from raw PCM. Fed a known signal, asserts envelope. Does not depend on REAPER's peak API.
  • view_mode_model — Design View mode system: mode registry, GUID-keyed membership, folder-tree-aware visibility derivation, snapshot-based park/restore planner, JSON round-trip. Mirror of bank_model for the Design View phase.
  • view_tree — pure I_FOLDERDEPTH→FolderTree helper for the Design View shell; no REAPER types at the boundary.
  • mode_switch — REAPER-free segment layout + hit-test math for the bank_panel's Design View mode switch; divides a header rectangle into N equal segments and hit-tests a point to a segment. Mirror of bank_grid.

REAPER-facing shells:

  • captureICaptureBackend interface; OfflineRenderBackend (deterministic default) and RealtimeRecordBackend. Input: CaptureRequest. Output: finished file + populated Sample handed to bank_model.
  • insert — placement via InsertMedia; conform-to-project-tempo is an explicit opt-in flag, never silent stretching.
  • bank_panel — docked LICE-drawn grid: thumbnails, audition, multi-select, keyboard navigation.
  • persist — project ext state (SetProjExtState / GetProjExtState, namespace "reasampler") ↔ bank_model JSON + ViewModeModel JSON ("view_state" key); project-relative path resolution.
  • view — Design View shell: reads the folder tree via view_tree, snapshots flag values before parking, drives hide + CPU-park on inactive-mode leaves (B_SHOWINTCP/B_SHOWINMIXER/B_MAINSEND/I_FXEN + per-FX offline) and derived visibility on parents; restores from snapshot. Never touches master or B_MUTE/I_SOLO.
  • track_guid — shared MediaTrack* → canonical GUID-string formatter; single source of truth for membership keys used by both the view shell and the actions layer.
  • actions — registers the capture/placement/slot action family and the Design View action family (toggle active mode, activate Arrange/Design, tag/untag selected tracks, show-both); routes each to the modules above via the command_id/gaccel/hookcommand contract.

REAPER extension contract (src/main.cpp)

  • Exactly one translation unit defines REAPERAPI_IMPLEMENT — that is main.cpp. Every other .cpp includes reaper_plugin_functions.h without the define and gets extern declarations for the global API function pointers.
  • REAPER dlopen()s any reaper_*.dll|dylib|so found in UserPlugins/ and calls the ReaperPluginEntry export (produced by REAPER_PLUGIN_ENTRYPOINT). rec->GetFunc resolves API pointers; rec->Register plugs extension callbacks in.
  • Action registration pattern (preserve this for all new actions):
    1. rec->Register("command_id", (void*)"STABLE_FOREVER_STRING") — mints a persistent command id. Never change this string after shipping; user keybindings key off it.
    2. rec->Register("gaccel", &accel) — puts the action in the Actions list.
    3. rec->Register("hookcommand", ...) — receives every action fired; claim only your own id, return false otherwise.
    4. On unload (rec == nullptr), mirror-unregister everything with the same strings prefixed by '-'.

The load-bearing principle

Capture and placement are separate acts. Capturing audio writes a file to the bank and adds an index entry. It never puts an item in the arrange view. Placement is a distinct, on-demand action (insert module / InsertMedia). Any code path that auto-inserts a capture into the timeline violates the purpose of the tool and must be rejected in review.

Precision invariants — required before any feature ships

  • Null test: a dry offline capture of a range, re-inserted at its source position, nulls to silence against the source. Ship as a verification action.
  • Bit-identical repeats: identical offline capture requests produce identical files.
  • Non-destructive: capture never mutates source items or tracks; the realtime backend's temp track is created and removed cleanly, and source routing is restored.
  • Exact bounds: no rounding of the requested range; no added silence unless a tail is explicitly requested; channel count preserved (no silent stereo fold).
  • Relative paths only in the persisted BankIndex.