Retire the zone system: one capture = one parameter set, and re-seam the engine and Sample face into bands

This commit is contained in:
2026-07-30 07:15:54 -04:00
parent a689fb75eb
commit 8d4ccbf841
61 changed files with 5416 additions and 8008 deletions
+98 -52
View File
@@ -551,11 +551,10 @@ target_link_libraries(card_drag PUBLIC drag_out bank_grid)
# ---------------------------------------------------------------------------
# 2v) Pure sampler_core library — NO VST3, NO REAPER, NO SWELL. The HEART of the
# Phase S MIDI-playback instrument (S3 / D3): polyphonic voice allocation with
# bounded stealing, an ADSR amplitude envelope, a key/velocity keymap with
# (note, velocity) -> zone resolution, and repitch/interpolation from a root note
# with loop-point-aware sustain. The mirror of bank_model / peaks / bank_book,
# tested hard outside any host. Lives under core/instrument/engine/ but
# MIDI-playback instrument (D3): polyphonic voice allocation with bounded stealing,
# the three envelope evaluators, and repitch/interpolation from a root note with
# loop-point-aware sustain over ONE loaded capture. The mirror of bank_model / peaks /
# bank_book, tested hard outside any host. Lives under core/instrument/engine/ but
# links NEITHER SDK — the plain-data boundary is enforced structurally: the test
# target below links only sampler_core (+ its peaks dep for the AudioSample alias,
# the one house precedent wav_codec also relies on). The VST3 shell (shell/instrument/
@@ -573,12 +572,19 @@ target_link_libraries(pitch_shift PUBLIC peaks)
# velocity_curve (S-VIEW-9) — the pure velocity->amp transfer curve (eval + editing/clamp/inverse
# map). NO VST3/REAPER/SWELL/vendor and DELIBERATELY no editor_geometry (its hit-test takes an
# explicit pixel box, not a Rect) so the engine can depend on it WITHOUT gaining a transitive
# dependency on the editor's layout types. sampler_core depends on it (KeyZone carries a
# VelocityCurve; Voice::start eval's it). Mirror of pitch_shift's role, one layer below the engine.
# dependency on the editor's layout types. play_params.h carries one (SampleData holds the
# curve; Voice::start eval's it). Mirror of pitch_shift's role, one layer below the engine.
add_library(velocity_curve STATIC src/core/instrument/engine/velocity_curve.cpp)
target_include_directories(velocity_curve PUBLIC src)
add_library(sampler_core STATIC src/core/instrument/engine/sampler_core.cpp)
# Two TUs on the engine's own responsibility seam: voice.cpp is the per-NOTE half (note-on
# setup, the Preserve ring prime, legato retune), voice_engine.cpp the note routing /
# allocation / stealing / mono stack / panic / block render. The per-SAMPLE render half is
# inline in voice.h (with the envelope evaluators in envelopes.h) precisely so this TU
# boundary costs the hot path nothing — see voice.h's header.
add_library(sampler_core STATIC
src/core/instrument/engine/voice.cpp
src/core/instrument/engine/voice_engine.cpp)
target_include_directories(sampler_core PUBLIC src)
target_link_libraries(sampler_core PUBLIC peaks pitch_shift velocity_curve)
@@ -817,48 +823,67 @@ add_test(NAME velocity_curve_tests COMMAND velocity_curve_tests)
# ---------------------------------------------------------------------------
# 2i) Pure VST3-instrument helpers (Phase S1) — NO VST3, NO REAPER, NO SWELL/LICE.
# editor_geometry: the IPlugView LICE editor's rectangle layout + hit-test math
# (mirror of mode_switch/bank_grid). bridge_marshal: the REAPER VST-host bridge
# read marshalling — GetProjExtState result decode + the ONE grow-loop retry
# policy (readProjExtStateGrowing, Q-W5 rider T2-04) shared by the VST bridge
# read AND the extension's persist/usage_scan ext-state reads (hence linked into
# reaper_reasampler too). Both are unit-tested outside the DAW;
# the VST3 shell (shell/instrument/*) that draws/routes/invokes is DAW-verified.
# editor_geometry: the shared geometry vocabulary every instrument UI module speaks
# (the `Rect` alias + contains()) — header-only, hence INTERFACE; the Sample face's
# own layout lives in sample_bands/sample_chrome below. bridge_marshal: the REAPER
# VST-host bridge read marshalling — GetProjExtState result decode + the ONE
# grow-loop retry policy (readProjExtStateGrowing, Q-W5 rider T2-04) shared by the
# VST bridge read AND the extension's persist/usage_scan ext-state reads (hence
# linked into reaper_reasampler too); unit-tested outside the DAW, while the VST3
# shell (shell/instrument/*) that draws/routes/invokes is DAW-verified.
# ---------------------------------------------------------------------------
add_library(editor_geometry STATIC src/core/instrument/ui/editor_geometry.cpp)
target_include_directories(editor_geometry PUBLIC src)
add_library(editor_geometry INTERFACE)
target_include_directories(editor_geometry INTERFACE src)
# sample_bands — THE band-stack allocator: the ONE module that owns the Sample face's
# vertical inventory (chrome / two-lane waveform / decks) plus the waveform band's lane
# split. A shared READ-ONLY surface for every band owner; a band's interior module lays out
# inside the rect it is handed and never re-allocates the stack. NEITHER SDK.
add_library(sample_bands STATIC src/core/instrument/ui/sample_bands.cpp)
target_include_directories(sample_bands PUBLIC src)
target_link_libraries(sample_bands PUBLIC editor_geometry)
# sample_chrome — the CHROME band's interior: the toolbar row (title + Browse) over the
# control row (root strip, preview, velocity knob cell, curve button, channel toggle). Reads
# the band rect from sample_bands; owns no vertical inventory of its own. NEITHER SDK.
add_library(sample_chrome STATIC src/core/instrument/ui/sample_chrome.cpp)
target_include_directories(sample_chrome PUBLIC src)
target_link_libraries(sample_chrome PUBLIC sample_bands)
add_library(bridge_marshal STATIC src/core/instrument/map/bridge_marshal.cpp)
target_include_directories(bridge_marshal PUBLIC src)
# embed_strip (Phase S6) — PURE layout + hit-test for the embedded TCP/MCP strip: the
# 128-key span -> zone-segment rects, point -> zone selection, and the level-band fill.
# The mirror of editor_geometry (whose Rect + contains() it reuses); unit-tested outside
# the DAW, while the embed shell (src/shell/instrument/reasampler_embed.cpp) marshals REAPER's embed
# messages (paint bitmap + mouse coords) into it. Links editor_geometry for the shared Rect.
# embed_strip (Phase S6) — PURE layout for the embedded TCP/MCP strip: the 128-key span ->
# key-span rects (the loaded capture's full span, and its root marker) and the level-band
# fill. Read-only, so no hit-test. Unit-tested outside the DAW, while the embed shell
# (src/shell/instrument/reasampler_embed.cpp) marshals REAPER's embed messages (the paint
# bitmap) into it. Links editor_geometry for the shared Rect.
add_library(embed_strip STATIC src/core/instrument/ui/embed_strip.cpp)
target_include_directories(embed_strip PUBLIC src)
target_link_libraries(embed_strip PUBLIC editor_geometry)
# sample_map (Phase S4; RESOLUTION half since Q-W2v) — PURE mapping logic for the
# instrument: the live bank blob -> selected sample (via the SHARED bank_book JSON parse,
# NOT a second parser), interleaved->mono downmix (the channel policy), the Tier-0/zoned
# keymap builds, and the refs/performance resolution. Links the three pure modules it
# composes — bank_book (shared JSON), wav_codec (shared WAV parse), and sampler_core (the
# Keymap/SampleData it yields) — and NEITHER SDK. The VST3 shell (reasampler_processor)
# does the bridge read + file I/O off the audio thread, then calls these; the process
# callback stays allocation-free. The ComponentState codec is component_state_io below.
# NOT a second parser), interleaved->mono downmix (the channel policy), the one parameter
# set's override-beats-intrinsic fold, and the SampleData build. Links the pure modules it
# composes — bank_book (shared JSON), wav_codec (shared WAV parse), velocity_curve (the
# curve field play_params.h carries) — and NEITHER SDK. NOT the voice engine: since the
# build's product is plain SampleData, the engine's object code is no longer a dependency.
# The VST3 shell (reasampler_processor) does the bridge read + file I/O off the audio
# thread, then calls these; the process callback stays allocation-free. The ComponentState
# codec is component_state_io below.
add_library(sample_map STATIC src/core/instrument/map/sample_map.cpp)
target_include_directories(sample_map PUBLIC src)
target_link_libraries(sample_map PUBLIC bank_book wav_codec sampler_core)
target_link_libraries(sample_map PUBLIC bank_book wav_codec velocity_curve peaks)
# component_state_io (Q-W2v split of sample_map, T4-13 ≡ T2-07) — the ComponentState
# ENVELOPE + zones-payload binary codec (envelope v1..v11, zones payload v1..v7, every
# lift preserved byte-identically). Split so the codec — which grows on every envelope
# bump and is shared with the EXTENSION's preset-blob path (instrument_drop) — links
# WITHOUT the voice engine: its deps are velocity_curve (the per-zone curve field) and
# master_gain (the v8 wire cap) only; sampler_core/pitch_shift object code never enters
# the extension binary. Its own test target linking exactly these is the structural proof.
# ENVELOPE + params-payload binary codec (envelope v1..v11, params payload v1..v8, every
# lift preserved byte-identically; v1..v7 are the retired zone lists, read via the
# adopt-zone-one migration). Split so the codec — which grows on every envelope bump and is
# shared with the EXTENSION's preset-blob path (instrument_drop) — links WITHOUT the voice
# engine: its deps are velocity_curve (the curve field) and master_gain (the v8 wire cap)
# only; sampler_core/pitch_shift object code never enters the extension binary. Its own test
# target linking exactly these is the structural proof.
add_library(component_state_io STATIC src/core/instrument/map/component_state_io.cpp)
target_include_directories(component_state_io PUBLIC src)
target_link_libraries(component_state_io PUBLIC velocity_curve master_gain)
@@ -872,10 +897,9 @@ add_library(capture_browser STATIC src/core/instrument/ui/capture_browser.cpp)
target_include_directories(capture_browser PUBLIC src)
target_link_libraries(capture_browser PUBLIC editor_geometry)
# keyboard_strip (Phase S10) — PURE key-span<->pixel mapping, root marker, zone-bar rects +
# edge-grab hit regions, and the drag-delta note resolver for the capture-first editor's
# keyboard strip (single-capture root-set) and the opt-in Zones panel (S10-Z). The mirror of
# embed_strip; links editor_geometry for the shared Rect. NEITHER SDK.
# keyboard_strip (Phase S10) — PURE key-span<->pixel mapping, root marker, and the
# drag-delta note resolver for the editor's keyboard strip (root display + root-set). The
# mirror of embed_strip; links editor_geometry for the shared Rect. NEITHER SDK.
add_library(keyboard_strip STATIC src/core/instrument/ui/keyboard_strip.cpp)
target_include_directories(keyboard_strip PUBLIC src)
target_link_libraries(keyboard_strip PUBLIC editor_geometry)
@@ -908,7 +932,7 @@ target_link_libraries(bank_sync PRIVATE wire)
# metrics/cell rect) which pulls editor_geometry transitively. NEITHER SDK.
add_library(browser_scroll STATIC src/core/instrument/ui/browser_scroll.cpp)
target_include_directories(browser_scroll PUBLIC src)
target_link_libraries(browser_scroll PUBLIC capture_browser)
target_link_libraries(browser_scroll PUBLIC capture_browser sample_chrome)
# note_entry (Phase S12) — PURE text->clamped-MIDI-note parse for the direct numeric entry of
# a zone's low/high/root (decimal integer OR note name under the C4==60 convention, clamped to
@@ -974,9 +998,16 @@ target_link_libraries(curve_popup PUBLIC editor_geometry)
add_library(master_gain STATIC src/core/instrument/engine/master_gain.cpp)
target_include_directories(master_gain PUBLIC src)
add_executable(editor_geometry_tests tests/test_editor_geometry.cpp)
target_link_libraries(editor_geometry_tests PRIVATE editor_geometry)
add_test(NAME editor_geometry_tests COMMAND editor_geometry_tests)
# sample_bands: the band-stack allocator's vertical inventory, asserted as pure geometry
# (chrome / two-lane waveform / deck row) independent of any paint call — the contract the
# band owners downstream read.
add_executable(sample_bands_tests tests/test_sample_bands.cpp)
target_link_libraries(sample_bands_tests PRIVATE sample_bands)
add_test(NAME sample_bands_tests COMMAND sample_bands_tests)
add_executable(sample_chrome_tests tests/test_sample_chrome.cpp)
target_link_libraries(sample_chrome_tests PRIVATE sample_chrome)
add_test(NAME sample_chrome_tests COMMAND sample_chrome_tests)
add_executable(bridge_marshal_tests tests/test_bridge_marshal.cpp)
target_link_libraries(bridge_marshal_tests PRIVATE bridge_marshal)
@@ -1253,16 +1284,26 @@ if(WIN32 AND EXISTS "${VST3_SDK}/public.sdk/source/main/pluginfactory.cpp")
src/shell/instrument/reasampler_processor.cpp
src/shell/instrument/processor_state.cpp
src/shell/instrument/processor_reload.cpp
# The editor family (Q-W2v, T4-11): eight face-axis TUs — session/bridge state,
# param plumbing, paint x2 (Sample | Browse+Zone), input x2 (same axis), platform;
# the eighth (editor_layout) hoisted PURE into core/instrument/ui/editor_geometry
# + browser_scroll (T2-06). Shared internals: editor_internal.h (no TU).
# The editor family, split on the Sample face's BAND axis: session/bridge state,
# param plumbing + the shared band-layout resolve, then paint and input in matching
# sets — dispatch, chrome, waveform, decks — plus the two band-independent surfaces
# (the Browse modal and the velocity-curve popup) and the platform/window TU. The
# layout math itself is PURE (core/instrument/ui/sample_bands + sample_chrome +
# browser_scroll). Shared internals: editor_internal.h (no TU).
src/shell/instrument/editor_session.cpp
src/shell/instrument/editor_controls.cpp
src/shell/instrument/editor_paint_sample.cpp
src/shell/instrument/editor_paint_browse_zone.cpp
src/shell/instrument/editor_input_sample.cpp
src/shell/instrument/editor_input_browse_zone.cpp
src/shell/instrument/editor_paint.cpp
src/shell/instrument/editor_paint_chrome.cpp
src/shell/instrument/editor_paint_waveform.cpp
src/shell/instrument/editor_paint_deck.cpp
src/shell/instrument/editor_paint_browse.cpp
src/shell/instrument/editor_paint_curve.cpp
src/shell/instrument/editor_input.cpp
src/shell/instrument/editor_input_chrome.cpp
src/shell/instrument/editor_input_waveform.cpp
src/shell/instrument/editor_input_deck.cpp
src/shell/instrument/editor_input_browse.cpp
src/shell/instrument/editor_input_curve.cpp
src/shell/instrument/editor_platform.cpp
src/shell/instrument/reasampler_embed.cpp
src/shell/instrument/reaper_bridge.cpp
@@ -1288,6 +1329,10 @@ if(WIN32 AND EXISTS "${VST3_SDK}/public.sdk/source/main/pluginfactory.cpp")
# app_version: ext_keys.h's channel-derived namespace accessor (V4) delegates to it, so
# the instrument reads the SAME namespace the extension writes; its PUBLIC include dir
# (build/generated) carries version_generated.h for the channel bit.
# sampler_core: the voice engine the processor drives (sample_map no longer pulls it —
# its build yields plain SampleData — so the module links it directly.)
# sample_bands + sample_chrome: the band-stack allocator the Sample face's three band
# TUs read, and the chrome band's interior geometry.
# capture_browser + keyboard_strip (S10): the pure card-grid/tab + keyboard-strip
# geometry the capture-first editor draws + hit-tests against; both link editor_geometry
# transitively (shared Rect).
@@ -1317,7 +1362,8 @@ if(WIN32 AND EXISTS "${VST3_SDK}/public.sdk/source/main/pluginfactory.cpp")
# sample_usage (pS-usage): the usage-record wire + publish plan the processor's
# reloadInstrument publishes through the bridge (the one sanctioned VST-side write).
target_link_libraries(reasampler_vst PRIVATE vst3_sdk editor_geometry bridge_marshal
sample_map component_state_io capture_paths embed_strip app_version capture_browser keyboard_strip
sampler_core sample_map component_state_io capture_paths embed_strip app_version
capture_browser keyboard_strip sample_bands sample_chrome
waveform_view bank_sync browser_scroll note_entry param_slider
theme component_geometry bank_grid trigger_seam envelope_overlay envelope_edit
knob_deck curve_popup master_gain sample_usage file_bytes)