Files
reasampler/CLAUDE.md
T
2026-07-21 20:48:01 -04:00

5.4 KiB

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 on-disk scaffold is named reaper_mpeview (CMake project name, binary prefix, source filenames). CONTEXT.md repurposes it into ReaSampler — a per-project audio sample-bank capture tool. The existing MPE modules (mpe_model, mpe_view) are being replaced by the sampler module set (bank_model, peaks, capture, insert, bank_panel, persist, actions). The discipline — pure REAPER-free testable core split from REAPER-facing shells — is being preserved, not replaced. When the code says "mpe" and the spec says "sampler", this is why.

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

Two targets:

Target Kind Purpose
mpe_model_tests executable Pure unit tests — no REAPER, no DAW. This is the fast iteration loop.
reaper_mpeview 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.

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; project-relative path resolution.
  • actions — registers the capture/placement/slot action family and routes each to the modules above.

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.