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
+25 -25
View File
@@ -17,37 +17,37 @@
#include "public.sdk/source/vst/vstsinglecomponenteffect.h"
#include "shell/instrument/reaper_bridge.h"
#include "core/instrument/map/sample_map.h" // PerformanceMap (the instrument's owned zoned keymap)
#include "core/instrument/map/component_state_io.h" // ComponentState codec (Q-W2v split)
#include "core/instrument/engine/sampler_core.h"
#include "core/instrument/map/sample_map.h" // InstrumentParams (the one parameter set)
#include "core/instrument/map/component_state_io.h" // ComponentState codec
#include "core/instrument/engine/voice_engine.h"
namespace reasampler::vst {
using instrument::map::ComponentState;
using instrument::map::PerformanceMap;
using instrument::map::InstrumentParams;
using instrument::map::SampleRefs;
using instrument::map::kPreviewVelocityDefault;
class ReaSamplerEmbed; // embedded TCP/MCP UI shell (owned below; see queryInterface)
// Decoded keymap + the voice engine playing it. The engine holds references into the
// keymap, so both must live/die together at a stable address — heap-allocated,
// The decoded capture + the voice engine playing it. The engine holds a reference to the
// sample, so both must live/die together at a stable address — heap-allocated,
// non-copyable, non-movable. process() only ever reads this through an atomic pointer.
struct LoadedInstrument {
Keymap keymap;
SampleData sample;
VoiceEngine engine;
std::uint64_t installedAt = 0; // reloadGeneration_ at which this was installed into live_
// Takeover declick is on by default here (product default; the pure core defaults it
// off): any voice restart (mono retrigger, legato, poly steal, preview) ramps instead
// of clicking.
LoadedInstrument(Keymap km, std::size_t maxVoices,
LoadedInstrument(SampleData sd, std::size_t maxVoices,
std::uint64_t gen, std::size_t preserveVoiceCap = 0,
std::int64_t preserveWindowFrames = 0,
VoiceMode voiceMode = VoiceMode::Poly,
MonoTrigger monoTrigger = MonoTrigger::Retrigger)
: keymap(std::move(km)),
engine(maxVoices, keymap, preserveVoiceCap, preserveWindowFrames,
: sample(std::move(sd)),
engine(maxVoices, sample, preserveVoiceCap, preserveWindowFrames,
voiceMode, monoTrigger, /*takeoverDeclick=*/true),
installedAt(gen) {}
@@ -111,7 +111,7 @@ public:
return static_cast<double>(embedPeak_.load(std::memory_order_relaxed));
}
// Resolves selection/zones against the instance-owned SampleRefs, decodes each WAV
// Resolves the selection against the instance-owned SampleRefs, decodes its WAV
// off-thread, and publishes the built instrument via atomic swap — no bank read
// required. When the bank blob is readable it's first folded into the refs table
// (refreshRefsFromBank; the browser's copy-the-ref-in + recapture-sync mechanism). A
@@ -140,16 +140,16 @@ public:
// The live host sample rate latched from setupProcessing; the editor's envelope overlay
// shares this time base. 0.0 before setupProcessing runs.
double sampleRate() const { return sampleRate_; }
// The single-capture selection id (guarded by selectionMutex_, never read on the audio
// thread): the default face's pick when the performance map is empty; a non-empty map
// supersedes it. Empty id -> silence, no first-sample fallback.
// The loaded capture's id (guarded by selectionMutex_, never read on the audio thread).
// Empty id -> silence, no first-sample fallback.
std::string selectedSampleId();
void setSelectedSampleId(const std::string& id);
// The performance map (zoned keymap). UI thread, guarded by performanceMutex_; never
// read on the audio thread — reloadInstrument bakes it into the Keymap off-thread.
PerformanceMap performanceMap();
void setPerformanceMap(const PerformanceMap& map);
// The one parameter set governing that capture. UI thread, guarded by paramsMutex_;
// never read on the audio thread — reloadInstrument bakes it into the SampleData
// off-thread.
InstrumentParams instrumentParams();
void setInstrumentParams(const InstrumentParams& params);
// Per-instance channel mode (mono | stereo), guarded by channelModeMutex_, never read
// on the audio thread. Decode policy only (downmix vs L/R split) — the output bus is
@@ -203,7 +203,7 @@ private:
void retireIdleDrain();
// Light voice-param rebuild: rebuilds the engine around a copy of the live instrument's
// already-decoded Keymap (no bridge/disk) and publishes through the same drain-slot
// already-decoded SampleData (no bridge/disk) and publishes through the same drain-slot
// swap as a full reload. No-op when nothing is loaded. Off the audio thread only.
void rebuildVoiceEngine();
@@ -253,15 +253,15 @@ private:
std::vector<std::unique_ptr<LoadedInstrument>> graveyard_; // drained on reclaim + setActive(false) + terminate
std::mutex reloadMutex_; // serializes off-thread reloads + graveyard access
// The single-capture selection id ("" = no pick -> silence). Off-thread only, not read
// on the audio thread.
// The loaded capture's id ("" = no pick -> silence). Off-thread only, not read on the
// audio thread.
std::mutex selectionMutex_;
std::string selectedSampleId_;
// The performance map (zoned keymap). Off-thread only; reloadInstrument bakes it into
// the Keymap under the reload lock, never read directly on the audio thread.
std::mutex performanceMutex_;
PerformanceMap performanceMap_;
// The one parameter set. Off-thread only; reloadInstrument bakes it into the SampleData
// under the reload lock, never read directly on the audio thread.
std::mutex paramsMutex_;
InstrumentParams params_;
// Instance-owned sample refs: path + intrinsics per referenced sample. Refreshed
// opportunistically from the bank blob when readable; never a bank dependency for