Phase S FB1 (r11): Sample-view recomposition — knob deck + elastic full-width hero + curve popup w/ right-click delete + post-mixer master gain (ComponentState v8)
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#include "bank_sync.h" // S9/S8 pure decisions: parseBankGeneration, consumeDecision
|
||||
#include "capture_paths.h" // resolveBankFile (shared M4 path resolution)
|
||||
#include "ext_keys.h" // kProjExtBanksKey / kProjExtBankGenKey / kProjExtAssignKey (shared wire contract)
|
||||
#include "master_gain.h" // masterGainMaxLinear (FB1 post-mixer gain clamp)
|
||||
#include "reasampler_editor.h"
|
||||
#include "reasampler_embed.h" // S6 embed shell + IReaperUIEmbedInterface (its iid DEF'd there)
|
||||
#include "sample_map.h" // selectSample, resolvePerformance, buildZonedKeymap, state (de)ser
|
||||
@@ -224,6 +225,10 @@ tresult PLUGIN_API ReaSamplerProcessor::setState(IBStream* state) {
|
||||
voiceMode_ = cs.voiceMode;
|
||||
monoTrigger_ = cs.monoTrigger;
|
||||
}
|
||||
// FB1: restore the post-mixer master gain (v8; older blobs lift to unity in
|
||||
// deserializeComponentState — pre-FB1 output). One atomic store; the audio thread picks
|
||||
// it up at the next block start.
|
||||
setMasterGainLinear(cs.masterGainLinear);
|
||||
// Rebuild from the restored state (off-thread — setState is a load-time call).
|
||||
reloadFromBank();
|
||||
return kResultOk;
|
||||
@@ -252,6 +257,7 @@ tresult PLUGIN_API ReaSamplerProcessor::getState(IBStream* state) {
|
||||
state_out.voiceMode = voiceMode_;
|
||||
state_out.monoTrigger = monoTrigger_;
|
||||
}
|
||||
state_out.masterGainLinear = masterGainLinear(); // FB1: persist the post-mixer gain (v8)
|
||||
const std::vector<std::uint8_t> bytes = serializeComponentState(state_out);
|
||||
if (!bytes.empty()) {
|
||||
const tresult wr = state->write(const_cast<std::uint8_t*>(bytes.data()),
|
||||
@@ -351,6 +357,16 @@ void ReaSamplerProcessor::setMonoTrigger(MonoTrigger trigger) {
|
||||
rebuildVoiceEngine();
|
||||
}
|
||||
|
||||
void ReaSamplerProcessor::setMasterGainLinear(double linear) {
|
||||
// Clamp to the control's legal span (the master_gain taper: 0 = -inf/silence, cap =
|
||||
// +24 dB). One relaxed atomic store — the audio thread reads it at the next block start;
|
||||
// no rebuild, no lock (a post-sum output trim is not a keymap fact).
|
||||
if (!(linear >= 0.0)) linear = 0.0; // also catches NaN
|
||||
const double maxLin = masterGainMaxLinear();
|
||||
if (linear > maxLin) linear = maxLin;
|
||||
masterGain_.store(static_cast<float>(linear), std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void ReaSamplerProcessor::previewNoteOn(int note) {
|
||||
if (note < 0) note = 0;
|
||||
if (note > 127) note = 127;
|
||||
@@ -877,6 +893,16 @@ tresult PLUGIN_API ReaSamplerProcessor::process(ProcessData& data) {
|
||||
drain->engine.render(ch0, ch1, static_cast<std::size_t>(frames));
|
||||
drain->preview.render(ch0, ch1, static_cast<std::size_t>(frames));
|
||||
}
|
||||
// FB1 post-mixer master gain: ONE relaxed load per block, applied AFTER the voice sum
|
||||
// (engine + drain + preview) and BEFORE the extra-channel mirror + peak, so the mirror
|
||||
// and the level indicator both see the actual output. A cheap multiply — no per-voice
|
||||
// cost, no alloc, no lock (RT discipline).
|
||||
{
|
||||
const float g = masterGain_.load(std::memory_order_relaxed);
|
||||
if (g != 1.f) {
|
||||
for (int32 i = 0; i < frames; ++i) { ch0[i] *= g; ch1[i] *= g; }
|
||||
}
|
||||
}
|
||||
// Any channels beyond the first two mirror ch0 (defensive — REAPER negotiates 1 or 2).
|
||||
for (int32 ch = 2; ch < out.numChannels; ++ch) {
|
||||
if (float* buf = out.channelBuffers32[ch]) {
|
||||
@@ -904,6 +930,14 @@ tresult PLUGIN_API ReaSamplerProcessor::process(ProcessData& data) {
|
||||
drain->engine.render(ch0, static_cast<std::size_t>(frames));
|
||||
drain->preview.render(ch0, static_cast<std::size_t>(frames));
|
||||
}
|
||||
// FB1 post-mixer master gain (mono path) — same contract as the stereo branch above:
|
||||
// post-sum, pre-peak/replicate, one relaxed load, RT-safe.
|
||||
{
|
||||
const float g = masterGain_.load(std::memory_order_relaxed);
|
||||
if (g != 1.f) {
|
||||
for (int32 i = 0; i < frames; ++i) ch0[i] *= g;
|
||||
}
|
||||
}
|
||||
float peak = 0.f;
|
||||
for (int32 i = 0; i < frames; ++i) {
|
||||
const float a = ch0[i] < 0.f ? -ch0[i] : ch0[i];
|
||||
|
||||
Reference in New Issue
Block a user