refactor(nisps): delete dead engine/mode mass
Phase 1 group 3 (L3, L9, L4, L5, L6, ST1). - L3: nisps/modes/voice_space.hpp — entirely dead, no includes anywhere. - L4: deleted SawOsc and SquareOsc. KEPT SineOsc — a verifier caught that the original reviewer's grep missed its live consumer (the firmware selftest); re-confirmed here before touching the file. - L9: removed the no-op VoiceSpace enum/table/setter boilerplate from the five engines with no real voice spaces; kept it on PAFSynth, VerbFX and ChannelStrip, which have real ones. Every engine member was checked against nisps/wasm/bindings.cpp, firmware/ and tests/ for callers first. - L5: ModeBase::input_dirty_ was write-only state — deleted the flag rather than making it gate inference, which would have been a behaviour change. - L6: VerbFXEngine's delay_to_verb_ (computed 12x per block, never read), enable_reverb_, enable_delay_to_reverb_ and the unused set_enable_* setters. - ST1: rewrote the four engine header comment blocks that described implementations which do not exist. L7 (MEMLCeliumEngine's inert feedback path) is deliberately NOT done — tracing git history showed feedbackGain went 0.1f (live) -> "0; //0.1f" (explicitly muted, value preserved) -> dropped entirely in the port. That is a muted feature, not dead weight, and deleting it would silently lose it. Left intact pending an operator decision; see the phase report. Gates: run-all-tests.sh ALL GREEN.
This commit is contained in:
parent
e37f16739e
commit
ea588e79cd
10 changed files with 31 additions and 158 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
// nisps/dsp/osc.hpp — oscillators.
|
// nisps/dsp/osc.hpp — oscillators.
|
||||||
//
|
//
|
||||||
// SineOsc, SawOsc, SquareOsc — basic waveforms. Cheap, naive sine via
|
// SineOsc — naive sine via sinf(phase * TWO_PI), used by the firmware
|
||||||
// sinf(phase * TWO_PI). For high-fidelity needs, replace with a wavetable.
|
// selftest's audio sweep. For high-fidelity needs, replace with a wavetable.
|
||||||
//
|
//
|
||||||
// PAFOperator — Phase-Aligned Formant operator. Direct port of `maxiPAFOperator`
|
// PAFOperator — Phase-Aligned Formant operator. Direct port of `maxiPAFOperator`
|
||||||
// in memllib. Maintains a static gauss/cauchy table populated on first
|
// in memllib. Maintains a static gauss/cauchy table populated on first
|
||||||
|
|
@ -47,41 +47,6 @@ class SineOsc {
|
||||||
float phase_ = 0.f;
|
float phase_ = 0.f;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SawOsc {
|
|
||||||
public:
|
|
||||||
SawOsc() noexcept = default;
|
|
||||||
void setup(float sample_rate) noexcept { inv_sr_ = 1.f / sample_rate; }
|
|
||||||
|
|
||||||
// Naive saw — aliases above ~Nyquist/2; fine for sub-bass / LFO use.
|
|
||||||
NISPS_HOT NISPS_FORCE_INLINE float saw(float frequency) noexcept {
|
|
||||||
const float y = (phase_ * 2.f) - 1.f;
|
|
||||||
phase_ += inv_sr_ * frequency;
|
|
||||||
if (phase_ >= 1.f) phase_ -= 1.f;
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
float inv_sr_ = 1.f / 48000.f;
|
|
||||||
float phase_ = 0.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SquareOsc {
|
|
||||||
public:
|
|
||||||
SquareOsc() noexcept = default;
|
|
||||||
void setup(float sample_rate) noexcept { inv_sr_ = 1.f / sample_rate; }
|
|
||||||
|
|
||||||
NISPS_HOT NISPS_FORCE_INLINE float square(float frequency) noexcept {
|
|
||||||
const float y = phase_ < 0.5f ? -1.f : 1.f;
|
|
||||||
phase_ += inv_sr_ * frequency;
|
|
||||||
if (phase_ >= 1.f) phase_ -= 1.f;
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
float inv_sr_ = 1.f / 48000.f;
|
|
||||||
float phase_ = 0.f;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Phase-Aligned Formant operator — port of maxiPAFOperator. The Gaussian and
|
// Phase-Aligned Formant operator — port of maxiPAFOperator. The Gaussian and
|
||||||
// Cauchy lookup tables are generated once at first call to `init()` (file-
|
// Cauchy lookup tables are generated once at first call to `init()` (file-
|
||||||
// local static; safe in single-threaded firmware audio path).
|
// local static; safe in single-threaded firmware audio path).
|
||||||
|
|
|
||||||
|
|
@ -94,12 +94,6 @@ class AnalysisEngine {
|
||||||
static constexpr std::size_t param_count() noexcept { return 0u; }
|
static constexpr std::size_t param_count() noexcept { return 0u; }
|
||||||
static constexpr std::string_view engine_id() noexcept { return "analysis"; }
|
static constexpr std::string_view engine_id() noexcept { return "analysis"; }
|
||||||
|
|
||||||
enum class VoiceSpace : std::size_t { None = 0 };
|
|
||||||
static constexpr std::size_t kVoiceSpaceCount = 0u;
|
|
||||||
static constexpr std::array<std::string_view, 0u> kVoiceSpaceNames = {};
|
|
||||||
void set_voice_space(VoiceSpace) noexcept {}
|
|
||||||
VoiceSpace voice_space() const noexcept { return VoiceSpace::None; }
|
|
||||||
|
|
||||||
struct Features {
|
struct Features {
|
||||||
float pitch = 0.f;
|
float pitch = 0.f;
|
||||||
float aperiodicity = 0.f;
|
float aperiodicity = 0.f;
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@
|
||||||
// implementations.
|
// implementations.
|
||||||
//
|
//
|
||||||
// Includes:
|
// Includes:
|
||||||
// - NoOpEngine: silent passthrough. Two uses:
|
// - NoOpEngine: silent passthrough (engine_id() == "thru"), used directly
|
||||||
// (1) the standalone "thru" engine for the SoundAnalysisMIDI mode (audio
|
// as the mode-level EngineT wherever a mode produces no audio itself —
|
||||||
// is analysed but not synthesised), exposed via engine_id()=="thru";
|
// SoundAnalysisMIDIMode (audio is analysed via a separately-composed
|
||||||
// (2) composed inside sequencer-only engines (BreakOr, Elysiamorf) whose
|
// AnalysisEngine member, not synthesised) and ExternalSynthMIDIMode
|
||||||
// MIDI/I2C event emission lives outside the audio path. Internal
|
// (joystick -> MLP -> MIDI CC only, no audio path). BreakOrEngine and
|
||||||
// composition uses the type directly, not the engine_id lookup.
|
// ElysiamorfEngine are sequencer-only engines that also return silence
|
||||||
|
// from process(), but each implements that directly — neither composes
|
||||||
|
// NoOpEngine.
|
||||||
// - Helper macros / static_asserts to verify each concrete engine satisfies
|
// - Helper macros / static_asserts to verify each concrete engine satisfies
|
||||||
// `nisps::AudioEngine` at compile time.
|
// `nisps::AudioEngine` at compile time.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// nisps/engines/breakor.hpp — 8-track ratio-sequencer.
|
// nisps/engines/breakor.hpp — 8-track ratio-sequencer.
|
||||||
//
|
//
|
||||||
// `process()` returns silence; the engine's job is to emit MIDI/I2C events on
|
// `process()` returns silence directly; the engine's job is to emit
|
||||||
// each tick. Wraps a NoOpEngine for the audio path. Stream 4/6 will hook
|
// NoteOn/NoteOff/Clock events on each tick via `pop_events()`. BreakOrMode
|
||||||
// `pop_events()` into the firmware's MIDI/I2C output.
|
// (mode layer) drains these into its ControlEvent ring for platform glue
|
||||||
|
// (firmware MIDI/I2C, browser WebMIDI) to forward.
|
||||||
//
|
//
|
||||||
// Param layout: 8 tracks × 7 ratio-seq params each = 56 params.
|
// Param layout: 8 tracks × 7 ratio-seq params each = 56 params.
|
||||||
// per track: [ratio0, ratio1, ratio2, phasorMul, phaseOff, ampRatio0, ampRatio1]
|
// per track: [ratio0, ratio1, ratio2, phasorMul, phaseOff, ampRatio0, ampRatio1]
|
||||||
|
|
@ -34,12 +35,6 @@ class BreakOrEngine {
|
||||||
static constexpr std::size_t param_count() noexcept { return kNParams; }
|
static constexpr std::size_t param_count() noexcept { return kNParams; }
|
||||||
static constexpr std::string_view engine_id() noexcept { return "breakor"; }
|
static constexpr std::string_view engine_id() noexcept { return "breakor"; }
|
||||||
|
|
||||||
enum class VoiceSpace : std::size_t { None = 0, Count = 0 };
|
|
||||||
static constexpr std::size_t kVoiceSpaceCount = 0u;
|
|
||||||
static constexpr std::array<std::string_view, 0u> kVoiceSpaceNames = {};
|
|
||||||
void set_voice_space(VoiceSpace) noexcept {}
|
|
||||||
VoiceSpace voice_space() const noexcept { return VoiceSpace::None; }
|
|
||||||
|
|
||||||
enum class EventKind : std::uint8_t { NoteOn, NoteOff, Clock };
|
enum class EventKind : std::uint8_t { NoteOn, NoteOff, Clock };
|
||||||
struct Event {
|
struct Event {
|
||||||
EventKind kind;
|
EventKind kind;
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,6 @@ class ElysiamorfEngine {
|
||||||
static constexpr std::size_t param_count() noexcept { return kNParams; }
|
static constexpr std::size_t param_count() noexcept { return kNParams; }
|
||||||
static constexpr std::string_view engine_id() noexcept { return "elysiamorf"; }
|
static constexpr std::string_view engine_id() noexcept { return "elysiamorf"; }
|
||||||
|
|
||||||
enum class VoiceSpace : std::size_t { None = 0, Count = 0 };
|
|
||||||
static constexpr std::size_t kVoiceSpaceCount = 0u;
|
|
||||||
static constexpr std::array<std::string_view, 0u> kVoiceSpaceNames = {};
|
|
||||||
void set_voice_space(VoiceSpace) noexcept {}
|
|
||||||
VoiceSpace voice_space() const noexcept { return VoiceSpace::None; }
|
|
||||||
|
|
||||||
enum class EventKind : std::uint8_t { CC, Clock };
|
enum class EventKind : std::uint8_t { CC, Clock };
|
||||||
struct Event {
|
struct Event {
|
||||||
EventKind kind;
|
EventKind kind;
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,10 @@
|
||||||
// Voice 1 (3 PAF operators): base freq, detune1/2, 3× cf, 3× bw, 3× shift,
|
// Voice 1 (3 PAF operators): base freq, detune1/2, 3× cf, 3× bw, 3× shift,
|
||||||
// amp ADSR, pitch envelope, pitch emphasis. (~20 params)
|
// amp ADSR, pitch envelope, pitch emphasis. (~20 params)
|
||||||
//
|
//
|
||||||
// The sequencer fires note events that trigger V0/V1 envelopes. The actual
|
// The sequencer fires note events internally to trigger V0/V1 envelopes.
|
||||||
// internal "RatioSeq" tick uses the sequencer-side params; we expose
|
// `pop_events()` exposes that same NoteOn/NoteOff/Clock stream (as BreakOr
|
||||||
// `pop_events()` for stream 4/6 to consume MIDI/I2C if desired.
|
// and Elysiamorf do), but no mode currently drains it — MEMLCeliumMode is a
|
||||||
|
// pure synth with no MIDI/I2C output wired for this engine.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
@ -41,12 +42,6 @@ class MEMLCeliumEngine {
|
||||||
static constexpr std::size_t param_count() noexcept { return kNParams; }
|
static constexpr std::size_t param_count() noexcept { return kNParams; }
|
||||||
static constexpr std::string_view engine_id() noexcept { return "memlcelium"; }
|
static constexpr std::string_view engine_id() noexcept { return "memlcelium"; }
|
||||||
|
|
||||||
enum class VoiceSpace : std::size_t { Direct = 0, Count = 1 };
|
|
||||||
static constexpr std::size_t kVoiceSpaceCount = 1u;
|
|
||||||
static constexpr std::array<std::string_view, kVoiceSpaceCount> kVoiceSpaceNames = {"Direct"};
|
|
||||||
void set_voice_space(VoiceSpace) noexcept {}
|
|
||||||
VoiceSpace voice_space() const noexcept { return VoiceSpace::Direct; }
|
|
||||||
|
|
||||||
void setup(float sample_rate) noexcept {
|
void setup(float sample_rate) noexcept {
|
||||||
sample_rate_ = sample_rate;
|
sample_rate_ = sample_rate;
|
||||||
for (auto* op : {&v0_paf0_, &v0_paf1_, &v0_paf2_,
|
for (auto* op : {&v0_paf0_, &v0_paf1_, &v0_paf2_,
|
||||||
|
|
|
||||||
|
|
@ -157,11 +157,9 @@ class VerbFXEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_enable_filterbank(bool v) noexcept { enable_filterbank_ = v; }
|
void set_enable_filterbank(bool v) noexcept { enable_filterbank_ = v; }
|
||||||
void set_enable_reverb(bool v) noexcept { enable_reverb_ = v; }
|
|
||||||
void set_enable_short_delay(bool v) noexcept { enable_short_delay_ = v; }
|
void set_enable_short_delay(bool v) noexcept { enable_short_delay_ = v; }
|
||||||
void set_enable_medium_delay(bool v) noexcept { enable_medium_delay_ = v; }
|
void set_enable_medium_delay(bool v) noexcept { enable_medium_delay_ = v; }
|
||||||
void set_enable_long_delay(bool v) noexcept { enable_long_delay_ = v; }
|
void set_enable_long_delay(bool v) noexcept { enable_long_delay_ = v; }
|
||||||
void set_enable_delay_to_reverb(bool v) noexcept { enable_delay_to_reverb_ = v; }
|
|
||||||
void set_wet_dry_override(float v) noexcept { wet_dry_ = v; }
|
void set_wet_dry_override(float v) noexcept { wet_dry_ = v; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -239,7 +237,6 @@ class VerbFXEngine {
|
||||||
filterbank_res_linear(p, 19.f);
|
filterbank_res_linear(p, 19.f);
|
||||||
common_delays_linear(p);
|
common_delays_linear(p);
|
||||||
verb_vs_delay_ = p[43];
|
verb_vs_delay_ = p[43];
|
||||||
delay_to_verb_ = p[44] * 0.99f;
|
|
||||||
delay_morph_ = p[45];
|
delay_morph_ = p[45];
|
||||||
delay_blend_ = p[46];
|
delay_blend_ = p[46];
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +256,7 @@ class VerbFXEngine {
|
||||||
ddelay_feedback1_ = p[40] * 0.99f;
|
ddelay_feedback1_ = p[40] * 0.99f;
|
||||||
ddelay_time2_ = 10.f + p[41] * 501.f;
|
ddelay_time2_ = 10.f + p[41] * 501.f;
|
||||||
ddelay_feedback2_ = p[42] * 0.99f;
|
ddelay_feedback2_ = p[42] * 0.99f;
|
||||||
verb_vs_delay_ = p[43]; delay_to_verb_ = p[44] * 0.99f;
|
verb_vs_delay_ = p[43];
|
||||||
delay_morph_ = p[45]; delay_blend_ = p[46];
|
delay_morph_ = p[45]; delay_blend_ = p[46];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,7 +279,7 @@ class VerbFXEngine {
|
||||||
ddelay_feedback1_ = p[40] * p[40] * 0.98f;
|
ddelay_feedback1_ = p[40] * p[40] * 0.98f;
|
||||||
ddelay_time2_ = 10.f + p[41] * 501.f;
|
ddelay_time2_ = 10.f + p[41] * 501.f;
|
||||||
ddelay_feedback2_ = p[42] * p[42] * 0.98f;
|
ddelay_feedback2_ = p[42] * p[42] * 0.98f;
|
||||||
verb_vs_delay_ = p[43]; delay_to_verb_ = p[44] * 0.99f;
|
verb_vs_delay_ = p[43];
|
||||||
delay_morph_ = p[45]; delay_blend_ = p[46];
|
delay_morph_ = p[45]; delay_blend_ = p[46];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -302,7 +299,6 @@ class VerbFXEngine {
|
||||||
ddelay_time2_ = 10.f + std::sqrt(p[41]) * 501.f;
|
ddelay_time2_ = 10.f + std::sqrt(p[41]) * 501.f;
|
||||||
ddelay_feedback2_ = std::sqrt(p[42]) * 0.98f;
|
ddelay_feedback2_ = std::sqrt(p[42]) * 0.98f;
|
||||||
verb_vs_delay_ = p[43] * p[43];
|
verb_vs_delay_ = p[43] * p[43];
|
||||||
delay_to_verb_ = std::sqrt(p[44]) * 0.99f;
|
|
||||||
delay_morph_ = p[45]; delay_blend_ = p[46];
|
delay_morph_ = p[45]; delay_blend_ = p[46];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -322,7 +318,6 @@ class VerbFXEngine {
|
||||||
ddelay_time2_ = 10.f + p[41] * 501.f;
|
ddelay_time2_ = 10.f + p[41] * 501.f;
|
||||||
ddelay_feedback2_ = std::sqrt(p[42]) * 0.95f;
|
ddelay_feedback2_ = std::sqrt(p[42]) * 0.95f;
|
||||||
verb_vs_delay_ = p[43] * p[43];
|
verb_vs_delay_ = p[43] * p[43];
|
||||||
delay_to_verb_ = std::sqrt(p[44]) * 0.99f;
|
|
||||||
delay_morph_ = p[45]; delay_blend_ = p[46];
|
delay_morph_ = p[45]; delay_blend_ = p[46];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,7 +340,7 @@ class VerbFXEngine {
|
||||||
ddelay_feedback1_ = p[40] * p[40] * 0.98f;
|
ddelay_feedback1_ = p[40] * p[40] * 0.98f;
|
||||||
ddelay_time2_ = 10.f + p[41] * p[41] * 501.f;
|
ddelay_time2_ = 10.f + p[41] * p[41] * 501.f;
|
||||||
ddelay_feedback2_ = p[42] * p[42] * 0.98f;
|
ddelay_feedback2_ = p[42] * p[42] * 0.98f;
|
||||||
verb_vs_delay_ = p[43]; delay_to_verb_ = p[44] * 0.99f;
|
verb_vs_delay_ = p[43];
|
||||||
delay_morph_ = p[45]; delay_blend_ = p[46];
|
delay_morph_ = p[45]; delay_blend_ = p[46];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -364,7 +359,7 @@ class VerbFXEngine {
|
||||||
: (1.f + v * v * 19.f);
|
: (1.f + v * v * 19.f);
|
||||||
}
|
}
|
||||||
common_delays_linear(p);
|
common_delays_linear(p);
|
||||||
verb_vs_delay_ = p[43]; delay_to_verb_ = p[44] * 0.99f;
|
verb_vs_delay_ = p[43];
|
||||||
delay_morph_ = p[45]; delay_blend_ = p[46];
|
delay_morph_ = p[45]; delay_blend_ = p[46];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,7 +387,7 @@ class VerbFXEngine {
|
||||||
ddelay_feedback1_ = std::sqrt(p[40]) * 0.98f;
|
ddelay_feedback1_ = std::sqrt(p[40]) * 0.98f;
|
||||||
ddelay_time2_ = 10.f + p[41] * 501.f;
|
ddelay_time2_ = 10.f + p[41] * 501.f;
|
||||||
ddelay_feedback2_ = std::sqrt(p[42]) * 0.98f;
|
ddelay_feedback2_ = std::sqrt(p[42]) * 0.98f;
|
||||||
verb_vs_delay_ = p[43]; delay_to_verb_ = p[44] * 0.99f;
|
verb_vs_delay_ = p[43];
|
||||||
delay_morph_ = p[45]; delay_blend_ = p[46];
|
delay_morph_ = p[45]; delay_blend_ = p[46];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -416,7 +411,7 @@ class VerbFXEngine {
|
||||||
: (1.f + v * v * 19.f);
|
: (1.f + v * v * 19.f);
|
||||||
}
|
}
|
||||||
common_delays_linear(p);
|
common_delays_linear(p);
|
||||||
verb_vs_delay_ = p[43]; delay_to_verb_ = p[44] * 0.99f;
|
verb_vs_delay_ = p[43];
|
||||||
delay_morph_ = p[45]; delay_blend_ = p[46];
|
delay_morph_ = p[45]; delay_blend_ = p[46];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -437,7 +432,7 @@ class VerbFXEngine {
|
||||||
: (1.f + std::sqrt(v) * 25.f);
|
: (1.f + std::sqrt(v) * 25.f);
|
||||||
}
|
}
|
||||||
common_delays_linear(p);
|
common_delays_linear(p);
|
||||||
verb_vs_delay_ = p[43]; delay_to_verb_ = p[44] * 0.99f;
|
verb_vs_delay_ = p[43];
|
||||||
delay_morph_ = p[45]; delay_blend_ = p[46];
|
delay_morph_ = p[45]; delay_blend_ = p[46];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -453,7 +448,7 @@ class VerbFXEngine {
|
||||||
for (std::size_t i = 0u; i < 8u; ++i) fb_freqs_[i] = harm_bases[i] + p[21 + i] * 40.f;
|
for (std::size_t i = 0u; i < 8u; ++i) fb_freqs_[i] = harm_bases[i] + p[21 + i] * 40.f;
|
||||||
filterbank_res_sqrt(p, 19.f);
|
filterbank_res_sqrt(p, 19.f);
|
||||||
common_delays_linear(p);
|
common_delays_linear(p);
|
||||||
verb_vs_delay_ = p[43]; delay_to_verb_ = p[44] * 0.99f;
|
verb_vs_delay_ = p[43];
|
||||||
delay_morph_ = p[45]; delay_blend_ = p[46];
|
delay_morph_ = p[45]; delay_blend_ = p[46];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -492,17 +487,15 @@ class VerbFXEngine {
|
||||||
float ddelay_time_ = 0.f, ddelay_feedback_ = 0.f;
|
float ddelay_time_ = 0.f, ddelay_feedback_ = 0.f;
|
||||||
float ddelay_time1_ = 0.f, ddelay_feedback1_ = 0.f;
|
float ddelay_time1_ = 0.f, ddelay_feedback1_ = 0.f;
|
||||||
float ddelay_time2_ = 0.f, ddelay_feedback2_ = 0.f;
|
float ddelay_time2_ = 0.f, ddelay_feedback2_ = 0.f;
|
||||||
float verb_vs_delay_ = 0.f, delay_to_verb_ = 0.f;
|
float verb_vs_delay_ = 0.f;
|
||||||
float delay_morph_ = 0.5f, delay_blend_ = 0.f;
|
float delay_morph_ = 0.5f, delay_blend_ = 0.f;
|
||||||
float filter_bank_delay_xfade_ = 0.f;
|
float filter_bank_delay_xfade_ = 0.f;
|
||||||
float wet_dry_ = 0.5f;
|
float wet_dry_ = 0.5f;
|
||||||
|
|
||||||
bool enable_filterbank_ = true;
|
bool enable_filterbank_ = true;
|
||||||
bool enable_reverb_ = true;
|
|
||||||
bool enable_short_delay_ = true;
|
bool enable_short_delay_ = true;
|
||||||
bool enable_medium_delay_ = true;
|
bool enable_medium_delay_ = true;
|
||||||
bool enable_long_delay_ = true;
|
bool enable_long_delay_ = true;
|
||||||
bool enable_delay_to_reverb_ = true;
|
|
||||||
|
|
||||||
VoiceSpace voice_space_ = VoiceSpace::Default;
|
VoiceSpace voice_space_ = VoiceSpace::Default;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// nisps/engines/xiasri.hpp — Reverb + delays + pitch-shift FX engine.
|
// nisps/engines/xiasri.hpp — Reverb + delays + pitch-shift FX engine.
|
||||||
//
|
//
|
||||||
// Mirrors firmware XIASRIAudioApp. Direct (no voice space) NN→param mapping —
|
// Mirrors firmware XIASRIAudioApp. Direct NN→param mapping — the firmware
|
||||||
// the firmware ignored its voice-space slot and read smoothParams[] inline.
|
// ignored its voice-space slot and read smoothParams[] inline, so this
|
||||||
// We expose a single "Direct" voice space for schema parity.
|
// engine has no VoiceSpace machinery at all: the 24 NN outputs feed the
|
||||||
|
// smoother directly.
|
||||||
//
|
//
|
||||||
// Pipeline (per sample):
|
// Pipeline (per sample):
|
||||||
// pitch_shift → DC-blocker → 6-allpass + 2-comb reverb tail → 4 parallel
|
// pitch_shift → DC-blocker → 6-allpass + 2-comb reverb tail → 4 parallel
|
||||||
|
|
@ -36,13 +37,6 @@ class XIASRIEngine {
|
||||||
static constexpr std::size_t param_count() noexcept { return kNParams; }
|
static constexpr std::size_t param_count() noexcept { return kNParams; }
|
||||||
static constexpr std::string_view engine_id() noexcept { return "xiasri"; }
|
static constexpr std::string_view engine_id() noexcept { return "xiasri"; }
|
||||||
|
|
||||||
enum class VoiceSpace : std::size_t { Direct = 0, Count = 1 };
|
|
||||||
static constexpr std::size_t kVoiceSpaceCount = 1u;
|
|
||||||
static constexpr std::array<std::string_view, kVoiceSpaceCount> kVoiceSpaceNames = {"Direct"};
|
|
||||||
|
|
||||||
void set_voice_space(VoiceSpace) noexcept {} // no-op for parity
|
|
||||||
VoiceSpace voice_space() const noexcept { return VoiceSpace::Direct; }
|
|
||||||
|
|
||||||
void setup(float sample_rate) noexcept {
|
void setup(float sample_rate) noexcept {
|
||||||
sample_rate_ = sample_rate;
|
sample_rate_ = sample_rate;
|
||||||
smoother_.setup(150.f, sample_rate);
|
smoother_.setup(150.f, sample_rate);
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,6 @@ class ModeBase {
|
||||||
if (value < 0.f) value = 0.f;
|
if (value < 0.f) value = 0.f;
|
||||||
else if (value > 1.f) value = 1.f;
|
else if (value > 1.f) value = 1.f;
|
||||||
input_channels_[idx] = value;
|
input_channels_[idx] = value;
|
||||||
input_dirty_ = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Input neutralization (single/double controller toggle) ----
|
// ---- Input neutralization (single/double controller toggle) ----
|
||||||
|
|
@ -175,7 +174,6 @@ class ModeBase {
|
||||||
NISPS_FORCE_INLINE void set_input_pinned(std::size_t idx, bool pinned) noexcept {
|
NISPS_FORCE_INLINE void set_input_pinned(std::size_t idx, bool pinned) noexcept {
|
||||||
if (idx >= NInputs) return;
|
if (idx >= NInputs) return;
|
||||||
input_pinned_[idx] = pinned;
|
input_pinned_[idx] = pinned;
|
||||||
input_dirty_ = true;
|
|
||||||
}
|
}
|
||||||
NISPS_FORCE_INLINE bool is_input_pinned(std::size_t idx) const noexcept {
|
NISPS_FORCE_INLINE bool is_input_pinned(std::size_t idx) const noexcept {
|
||||||
return idx < NInputs && input_pinned_[idx];
|
return idx < NInputs && input_pinned_[idx];
|
||||||
|
|
@ -184,7 +182,6 @@ class ModeBase {
|
||||||
if (v < 0.f) v = 0.f;
|
if (v < 0.f) v = 0.f;
|
||||||
else if (v > 1.f) v = 1.f;
|
else if (v > 1.f) v = 1.f;
|
||||||
pin_value_ = v;
|
pin_value_ = v;
|
||||||
input_dirty_ = true;
|
|
||||||
}
|
}
|
||||||
float pin_value() const noexcept { return pin_value_; }
|
float pin_value() const noexcept { return pin_value_; }
|
||||||
|
|
||||||
|
|
@ -226,7 +223,6 @@ class ModeBase {
|
||||||
engine_.set_params(ml_.outputs());
|
engine_.set_params(ml_.outputs());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input_dirty_ = false;
|
|
||||||
if constexpr (requires(Derived& d) { d.on_post_inference(); }) {
|
if constexpr (requires(Derived& d) { d.on_post_inference(); }) {
|
||||||
static_cast<Derived&>(*this).on_post_inference();
|
static_cast<Derived&>(*this).on_post_inference();
|
||||||
}
|
}
|
||||||
|
|
@ -325,7 +321,6 @@ class ModeBase {
|
||||||
std::array<float, NInputs> input_channels_{};
|
std::array<float, NInputs> input_channels_{};
|
||||||
std::array<bool, NInputs> input_pinned_{}; // false => live
|
std::array<bool, NInputs> input_pinned_{}; // false => live
|
||||||
float pin_value_ = 0.5f; // neutral
|
float pin_value_ = 0.5f; // neutral
|
||||||
bool input_dirty_ = false;
|
|
||||||
std::size_t voice_space_idx_ = 0u;
|
std::size_t voice_space_idx_ = 0u;
|
||||||
RingBuffer<ControlEvent, kModeEventBufferSize> events_{};
|
RingBuffer<ControlEvent, kModeEventBufferSize> events_{};
|
||||||
// Adaptive-learning state (Jolt + OU). Declared AFTER events_ so the
|
// Adaptive-learning state (Jolt + OU). Declared AFTER events_ so the
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
// nisps/modes/voice_space.hpp — Voice-space binding helpers.
|
|
||||||
//
|
|
||||||
// In this codebase the *voice-space code* (the lambda that maps NN outputs
|
|
||||||
// to engine state fields) lives INSIDE each engine — see e.g.
|
|
||||||
// `PAFSynthEngine::apply_quad_detune` etc. Engines expose a
|
|
||||||
// `set_voice_space(EngineT::VoiceSpace)` method to switch which mapping is
|
|
||||||
// active; the next `set_params(span<float>)` call routes through the
|
|
||||||
// selected voice space.
|
|
||||||
//
|
|
||||||
// The mode layer therefore only needs to:
|
|
||||||
// 1. Tell the engine which voice space is active.
|
|
||||||
// 2. Surface the voice-space NAMES (for UI dropdowns / schema parity).
|
|
||||||
//
|
|
||||||
// `VoiceSpaceEntry` is a thin value used by `ParamSchema::voice_spaces` and
|
|
||||||
// for static introspection (e.g. matching schema names against
|
|
||||||
// `EngineT::kVoiceSpaceNames`). We keep it constexpr-friendly.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <span>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace nisps {
|
|
||||||
|
|
||||||
struct VoiceSpaceEntry {
|
|
||||||
std::string_view name;
|
|
||||||
std::size_t index;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build a constexpr list of {name, index} from the engine's static name table.
|
|
||||||
template <typename EngineT>
|
|
||||||
constexpr auto make_voice_space_entries() noexcept {
|
|
||||||
constexpr std::size_t N = EngineT::kVoiceSpaceCount;
|
|
||||||
std::array<VoiceSpaceEntry, N> entries{};
|
|
||||||
for (std::size_t i = 0u; i < N; ++i) {
|
|
||||||
entries[i] = VoiceSpaceEntry{EngineT::kVoiceSpaceNames[i], i};
|
|
||||||
}
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look up a voice-space index by name on an engine. Returns SIZE_MAX if not
|
|
||||||
// found. Modes use this to translate schema voice-space strings to the
|
|
||||||
// engine's enum index when loading a default.
|
|
||||||
template <typename EngineT>
|
|
||||||
constexpr std::size_t find_voice_space(std::string_view name) noexcept {
|
|
||||||
for (std::size_t i = 0u; i < EngineT::kVoiceSpaceCount; ++i) {
|
|
||||||
if (EngineT::kVoiceSpaceNames[i] == name) return i;
|
|
||||||
}
|
|
||||||
return static_cast<std::size_t>(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nisps
|
|
||||||
Loading…
Reference in a new issue