feat(capture): offline auto/manual tail via pure TailMode mapping
Add TailMode{None,Auto,Manual} + derived -72dB TRIMEND + 8s cap in render_settings;
wire into OfflineRenderBackend with snapshot/restore of the tail settings; add
CAPTURE_ITEM_TAIL/CAPTURE_TRACK_TAIL variant actions (plain actions stay exact-bounds).
This commit is contained in:
+75
-11
@@ -3,10 +3,59 @@
|
||||
|
||||
#include "render_settings.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
|
||||
namespace reasampler {
|
||||
|
||||
double autoTrimEndRatio() {
|
||||
// Amplitude ratio = 10^(dB/20). Derived from kAutoTrimThresholdDb so the dB is
|
||||
// the single source of truth (header ~3062: RENDER_TRIMEND is an amplitude ratio,
|
||||
// "0.5 means -6.02 dB"). For -72 dB this is ~= 0.00025119.
|
||||
return std::pow(10.0, kAutoTrimThresholdDb / 20.0);
|
||||
}
|
||||
|
||||
TailRenderSettings tailRenderSettingsFor(TailMode mode, double manualTailMs) {
|
||||
TailRenderSettings t;
|
||||
switch (mode) {
|
||||
case TailMode::None:
|
||||
// Exact bounds — byte-identical to the pre-tail no-tail capture. Tail off,
|
||||
// disable-all normalize (the current default), no trim.
|
||||
t.tailFlag = kTailFlagNone;
|
||||
t.tailMs = 0.0;
|
||||
t.normalize = kNormalizeDisableAll;
|
||||
t.trimEnd = 0.0;
|
||||
return t;
|
||||
|
||||
case TailMode::Auto:
|
||||
// Generous 8 s tail, then SURGICAL normalize: ONLY the trim-ending-silence
|
||||
// bit (32768) — every other postprocessing bit clear. A fixed-threshold
|
||||
// trailing-silence trim is a pure boundary decision (it scales/limits/fades
|
||||
// nothing), so it re-introduces none of the coloring the disable-all bit
|
||||
// guarded against, and two identical requests trim at the identical sample
|
||||
// -> bit-identical repeats hold (spec §surgical normalize).
|
||||
t.tailFlag = kTailFlagCustomBounds;
|
||||
t.tailMs = kMaxTailMs;
|
||||
t.normalize = kNormalizeTrimEnd;
|
||||
t.trimEnd = autoTrimEndRatio();
|
||||
return t;
|
||||
|
||||
case TailMode::Manual:
|
||||
// Fixed tail, no trim -> keep the disable-all normalize exactly as the
|
||||
// no-tail path does. Clamp to the 8 s cap even here: the runaway guard
|
||||
// applies whether the length came from the Auto default or an explicit
|
||||
// request (spec §Manual override). Negative requests floor to 0.
|
||||
t.tailFlag = kTailFlagCustomBounds;
|
||||
t.tailMs = std::clamp(manualTailMs, 0.0, kMaxTailMs);
|
||||
t.normalize = kNormalizeDisableAll;
|
||||
t.trimEnd = 0.0;
|
||||
return t;
|
||||
}
|
||||
// Unreachable for a valid enum; fail closed to exact bounds (never a stray tail).
|
||||
return t;
|
||||
}
|
||||
|
||||
RenderSettingsChoice renderSettingsFor(SourceMode mode, double /*wetDry*/) {
|
||||
// `wetDry` is accepted so CaptureRequest.wetDry remains the seam for future
|
||||
// dry work (M10 null test), but it does not affect this mapping. FX scoping is
|
||||
@@ -130,23 +179,38 @@ RazorRange razorUnionBounds(const std::vector<RazorRange>& ranges) {
|
||||
}
|
||||
|
||||
const std::vector<CaptureActionDef>& captureActionTable() {
|
||||
// Built once (function-local static): two SCOPE actions. Tail OFF for all
|
||||
// (exact bounds). Ids are FOREVER-STABLE — never edit a shipped string. Each
|
||||
// action infers its range (razor-else-time) at fire time and enforces its
|
||||
// FX-scope invariant via fxBypassPlanFor. The M7 CAPTURE_TRACKS_WET /
|
||||
// CAPTURE_ITEMS_WET / CAPTURE_RAZOR_WET ids are RETIRED (mirror-unregistered in
|
||||
// main.cpp); the CAPTURE_MASTER scope action is REMOVED (its id is likewise
|
||||
// mirror-unregistered) — to capture the master you render a track.
|
||||
// Built once (function-local static): two SCOPE actions x two tail variants.
|
||||
// The None rows are exact bounds (byte-identical to today); the …_TAIL rows are
|
||||
// the paired TailMode::Auto "…with tail" variants (Daniel's lean over silently
|
||||
// flipping the exact-bounds default — spec §Open questions). Ids are FOREVER-
|
||||
// STABLE — never edit a shipped string. Each action infers its range
|
||||
// (razor-else-time) at fire time and enforces its FX-scope invariant via
|
||||
// fxBypassPlanFor. The M7 CAPTURE_TRACKS_WET / CAPTURE_ITEMS_WET /
|
||||
// CAPTURE_RAZOR_WET ids are RETIRED (mirror-unregistered in main.cpp); the
|
||||
// CAPTURE_MASTER scope action is REMOVED (its id is likewise mirror-
|
||||
// unregistered) — to capture the master you render a track.
|
||||
static const std::vector<CaptureActionDef> table = {
|
||||
// Item scope — item/take FX only. NEW forever-stable id.
|
||||
// Item scope, exact bounds — item/take FX only.
|
||||
{"CEREBELLUM_REASAMPLER_CAPTURE_ITEM",
|
||||
"ReaSampler: capture selected item(s)", "item",
|
||||
CaptureScope::Item},
|
||||
CaptureScope::Item, TailMode::None},
|
||||
|
||||
// Track scope — item FX + the track's own FX. NEW forever-stable id.
|
||||
// Track scope, exact bounds — item FX + the track's own FX.
|
||||
{"CEREBELLUM_REASAMPLER_CAPTURE_TRACK",
|
||||
"ReaSampler: capture selected track(s)", "track",
|
||||
CaptureScope::Track},
|
||||
CaptureScope::Track, TailMode::None},
|
||||
|
||||
// Item scope with Auto tail — captures the take-FX decay past the range end,
|
||||
// trimmed to -72 dB. NEW forever-stable id.
|
||||
{"CEREBELLUM_REASAMPLER_CAPTURE_ITEM_TAIL",
|
||||
"ReaSampler: capture selected item(s) with tail", "item",
|
||||
CaptureScope::Item, TailMode::Auto},
|
||||
|
||||
// Track scope with Auto tail — captures the track's own reverb/delay decay
|
||||
// past the range end, trimmed to -72 dB. NEW forever-stable id.
|
||||
{"CEREBELLUM_REASAMPLER_CAPTURE_TRACK_TAIL",
|
||||
"ReaSampler: capture selected track(s) with tail", "track",
|
||||
CaptureScope::Track, TailMode::Auto},
|
||||
};
|
||||
return table;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user