feat: run the per-voice filter between the pitch and amp stages, with its own deck
Params ride the one parameter set; payload v8 -> v9, off by default. Deck composition moves to a pure deck_groups module in pitch -> filter -> amp order.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "core/audio/peaks.h"
|
||||
#include "core/instrument/engine/filter/voice_filter.h"
|
||||
#include "core/instrument/engine/velocity_curve.h"
|
||||
|
||||
namespace reasampler {
|
||||
@@ -90,15 +91,38 @@ struct PitchEnvParams {
|
||||
double peakSemitones = 0.0; // signed depth at the peak
|
||||
};
|
||||
|
||||
// Per-voice resonant filter, off by default (enabled=false -> the render path skips it
|
||||
// entirely -> bit-identical to the un-filtered engine). Holds the filter module's OWN
|
||||
// normalized control positions verbatim rather than a parallel set, so no control range is
|
||||
// re-derived here; `filter_params.h` owns every law that maps them to Hz/Q/depth.
|
||||
//
|
||||
// The three modulation depths all land in that same normalized cutoff domain and sum before a
|
||||
// single clamp: `modAmount` scales the per-frame filter envelope, `velAmount` scales the
|
||||
// note-on velocity through `velocityCurve`, and `keyTrack` moves cutoff by octaves per octave
|
||||
// above the root. All three are zero/neutral by default.
|
||||
struct FilterParams {
|
||||
bool enabled = false;
|
||||
instrument::engine::filter::FilterSettings settings;
|
||||
double modAmount = 0.0; // bipolar [-1,+1], envelope -> cutoff
|
||||
double velAmount = 0.0; // bipolar [-1,+1], velocity -> cutoff
|
||||
double keyTrack = 0.0; // octaves of cutoff per octave of (note - root)
|
||||
AdsrParams env; // the same staged AHDSR the amp runs; frames
|
||||
// Shapes velocity before velAmount scales it. Linear rather than the amp's flat() default
|
||||
// because a flat curve under a depth control would make every velocity the same offset;
|
||||
// the no-op at rest is velAmount == 0, not the curve.
|
||||
VelocityCurve velocityCurve = VelocityCurve::linear();
|
||||
};
|
||||
|
||||
// Bundle a voice reads at start(). Defaults reproduce the bare engine (Gate, hold-0 AHDSR,
|
||||
// Varispeed, pitch envelope off) — core regression tests rely on this; the Preserve product
|
||||
// default is layered on at (de)serialization, see kDefaultPitchEngine.
|
||||
// Varispeed, pitch envelope off, filter off) — core regression tests rely on this; the
|
||||
// Preserve product default is layered on at (de)serialization, see kDefaultPitchEngine.
|
||||
struct PlayParams {
|
||||
PlayMode playMode = PlayMode::Gate;
|
||||
AdsrParams adsr;
|
||||
TriggerParams trigger;
|
||||
PitchEngine pitchEngine = PitchEngine::Varispeed;
|
||||
PitchEnvParams pitchEnv;
|
||||
FilterParams filter;
|
||||
};
|
||||
|
||||
// [start, end) frames, half-open. A zero-length loop (start == end) is the "no sustain loop"
|
||||
|
||||
Reference in New Issue
Block a user