Retire the zone system: one capture = one parameter set, and re-seam the engine and Sample face into bands
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "shell/instrument/reasampler_embed.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -62,15 +63,6 @@ std::string sampleLabel(const std::vector<SampleChoice>& samples, const std::str
|
||||
}
|
||||
#endif
|
||||
|
||||
// Projects the performance map into the strip's minimal zone shape (key ranges only).
|
||||
// Kept shell-side because it reads PerformanceMap; embed_strip stays free of it.
|
||||
std::vector<EmbedZone> toEmbedZones(const PerformanceMap& map) {
|
||||
std::vector<EmbedZone> out;
|
||||
out.reserve(map.zones.size());
|
||||
for (const PerformanceZone& z : map.zones) out.push_back(EmbedZone{z.lowNote, z.highNote});
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
tresult PLUGIN_API ReaSamplerEmbed::queryInterface(const TUID iid, void** obj) {
|
||||
@@ -80,26 +72,37 @@ tresult PLUGIN_API ReaSamplerEmbed::queryInterface(const TUID iid, void** obj) {
|
||||
return kNoInterface;
|
||||
}
|
||||
|
||||
// The loaded capture id + effective root, both cheap in-process accessors.
|
||||
void ReaSamplerEmbed::refreshLoaded() {
|
||||
loadedId_ = processor_->selectedSampleId();
|
||||
const InstrumentParams params = processor_->instrumentParams();
|
||||
if (params.rootOverride) {
|
||||
rootNote_ = *params.rootOverride;
|
||||
} else if (const SelectedSample* ref = findRef(processor_->sampleRefs(), loadedId_)) {
|
||||
rootNote_ = ref->rootNote;
|
||||
} else {
|
||||
rootNote_ = 60;
|
||||
}
|
||||
}
|
||||
|
||||
void ReaSamplerEmbed::refresh() {
|
||||
if (!processor_) {
|
||||
samples_.clear();
|
||||
map_.zones.clear();
|
||||
selectedZone_ = -1;
|
||||
loadedId_.clear();
|
||||
rootNote_ = 60;
|
||||
return;
|
||||
}
|
||||
auto banks = processor_->bridge().readReasamplerExtState(reasampler::kProjExtBanksKey);
|
||||
samples_ = banks ? listSamples(*banks) : std::vector<SampleChoice>{};
|
||||
map_ = processor_->performanceMap();
|
||||
if (selectedZone_ >= static_cast<int>(map_.zones.size())) selectedZone_ = -1;
|
||||
refreshLoaded();
|
||||
}
|
||||
|
||||
void ReaSamplerEmbed::maybeRefresh() {
|
||||
if (!processor_) { refresh(); return; } // clears state; cheap
|
||||
|
||||
// The performance map is a cheap in-process accessor, and the editor may edit zones
|
||||
// with no bank-content change — always re-snapshot it so an edit reflects immediately.
|
||||
map_ = processor_->performanceMap();
|
||||
if (selectedZone_ >= static_cast<int>(map_.zones.size())) selectedZone_ = -1;
|
||||
// The loaded capture + root are cheap in-process accessors, and the editor may change
|
||||
// either with no bank-content change — always re-snapshot so an edit reflects at once.
|
||||
refreshLoaded();
|
||||
|
||||
// The expensive part is the bank-blob bridge read: gate it on the bank-generation
|
||||
// stamp, re-reading only when it changed (or on the first paint). A project with no
|
||||
@@ -157,9 +160,6 @@ TPtrInt ReaSamplerEmbed::embed_message(int msg, TPtrInt parm2, TPtrInt parm3) {
|
||||
#ifdef _WIN32
|
||||
case REAPER_FXEMBED_WM_PAINT:
|
||||
return paint(parm2, parm3) ? 1 : 0;
|
||||
case REAPER_FXEMBED_WM_LBUTTONDOWN:
|
||||
// Selection at most: map the click to a zone; force a redraw if it changed.
|
||||
return onMouseDown(parm3) ? REAPER_FXEMBED_RETNOTIFY_INVALIDATE : 0;
|
||||
#endif
|
||||
default:
|
||||
return 0; // unhandled messages (cursor, wheel, hittest) fall through
|
||||
@@ -186,45 +186,35 @@ bool ReaSamplerEmbed::paint(TPtrInt bitmap, TPtrInt drawInfo) {
|
||||
|
||||
const EmbedLayout layout = layoutEmbed(w, h);
|
||||
|
||||
if (map_.zones.empty()) {
|
||||
// No opt-in zones authored: a faint band so the strip reads as "present, no zones"
|
||||
// — the default single-capture face lives in the editor.
|
||||
if (loadedId_.empty()) {
|
||||
// Nothing loaded: a faint band so the strip reads as "present, silent" — the pick
|
||||
// affordance lives in the editor.
|
||||
LICE_FillRect(bmp, layout.keymap.x, layout.keymap.y, layout.keymap.width,
|
||||
layout.keymap.height, toLice(roleColor(Role::BgCell)), 0.5f, 0);
|
||||
const std::string label = version::vstPluginName() + // channel-derived
|
||||
(samples_.empty() ? " (bank empty)" : " (no zones)");
|
||||
(samples_.empty() ? " (bank empty)" : " (pick a capture)");
|
||||
const Rect labelR = Rect::ltrb(layout.keymap.x + 4, layout.keymap.y, layout.keymap.right(),
|
||||
layout.keymap.bottom());
|
||||
text(bmp, toKitBox(labelR), label.c_str(), Font::Label, Role::TextPrimary, Align::Left);
|
||||
} else {
|
||||
// Each zone draws as a segment (first-match order, matching selection/playback),
|
||||
// colored by its key span's spectral hue so it reads as the same spectrum as the
|
||||
// editor's keyboard strip. The selected zone lifts to accent-primary + a static glow.
|
||||
for (int i = 0; i < static_cast<int>(map_.zones.size()); ++i) {
|
||||
const PerformanceZone& z = map_.zones[i];
|
||||
const Rect r = zoneSegmentRect(layout, z.lowNote, z.highNote);
|
||||
if (r.width <= 0) continue;
|
||||
const bool sel = (i == selectedZone_);
|
||||
if (sel) {
|
||||
// Static glow halo, then the crisp accent-primary fill.
|
||||
LICE_FillRect(bmp, r.x - 2, r.y, r.width + 4, r.height,
|
||||
toLice(roleColor(Role::AccentHot)), 0.30f, 0);
|
||||
LICE_FillRect(bmp, r.x, r.y, r.width, r.height,
|
||||
toLice(roleColor(Role::AccentPrimary)), 1.0f, 0);
|
||||
} else {
|
||||
const double t = ((z.lowNote + z.highNote) * 0.5) / 127.0;
|
||||
LICE_FillRect(bmp, r.x, r.y, r.width, r.height,
|
||||
toLice(spectralColor(t)), 0.65f, 0);
|
||||
}
|
||||
LICE_DrawRect(bmp, r.x, r.y, r.width - 1, r.height - 1,
|
||||
toLice(roleColor(Role::LineHairline)), 1.0f, 0);
|
||||
// Label when wide enough to read; the selected (accent-fill) segment labels in
|
||||
// bg/base for contrast, the rest in text/primary.
|
||||
if (r.width >= 24) {
|
||||
const Rect lr = Rect::ltrb(r.x + 3, r.y, r.right() - 2, r.bottom());
|
||||
text(bmp, toKitBox(lr), sampleLabel(samples_, z.sampleId).c_str(),
|
||||
Font::Label, sel ? Role::BgBase : Role::TextPrimary, Align::Left);
|
||||
}
|
||||
// The loaded capture spans the whole keyboard, drawn in its root's spectral hue so
|
||||
// the strip reads as the same spectrum as the editor's keyboard strip; the root key
|
||||
// lifts to accent-primary with a static glow ("this is where it plays at unity").
|
||||
const Rect span = keySpanRect(layout, 0, 127);
|
||||
LICE_FillRect(bmp, span.x, span.y, span.width, span.height,
|
||||
toLice(spectralColor(rootNote_ / 127.0)), 0.65f, 0);
|
||||
LICE_DrawRect(bmp, span.x, span.y, span.width - 1, span.height - 1,
|
||||
toLice(roleColor(Role::LineHairline)), 1.0f, 0);
|
||||
const Rect root = keySpanRect(layout, rootNote_, rootNote_);
|
||||
const int rw = (std::max)(2, root.width);
|
||||
LICE_FillRect(bmp, root.x - 2, root.y, rw + 4, root.height,
|
||||
toLice(roleColor(Role::AccentHot)), 0.30f, 0);
|
||||
LICE_FillRect(bmp, root.x, root.y, rw, root.height,
|
||||
toLice(roleColor(Role::AccentPrimary)), 1.0f, 0);
|
||||
if (span.width >= 24) {
|
||||
const Rect lr = Rect::ltrb(span.x + 3, span.y, span.right() - 2, span.bottom());
|
||||
text(bmp, toKitBox(lr), sampleLabel(samples_, loadedId_).c_str(), Font::Label,
|
||||
Role::TextPrimary, Align::Left);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,19 +233,6 @@ bool ReaSamplerEmbed::paint(TPtrInt bitmap, TPtrInt drawInfo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReaSamplerEmbed::onMouseDown(TPtrInt drawInfo) {
|
||||
auto* di = reinterpret_cast<const REAPER_FXEMBED_DrawInfo*>(drawInfo);
|
||||
if (!di || di->width <= 0 || di->height <= 0) return false;
|
||||
refresh();
|
||||
const EmbedLayout layout = layoutEmbed(di->width, di->height);
|
||||
const std::vector<EmbedZone> zones = toEmbedZones(map_);
|
||||
const int hit = zoneAtPoint(layout, zones.data(), static_cast<int>(zones.size()),
|
||||
di->mouse_x, di->mouse_y);
|
||||
if (hit == selectedZone_) return false; // no change -> no redraw
|
||||
selectedZone_ = hit;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
} // namespace reasampler::vst
|
||||
|
||||
Reference in New Issue
Block a user