instrument: deliver continuous playback params live to sounding voices via a seqlock block, holding normalized stage position across time edits

This commit is contained in:
2026-07-30 21:03:05 -04:00
parent 7bd911d58b
commit 1dade0bfcf
25 changed files with 1352 additions and 66 deletions
+33
View File
@@ -186,7 +186,40 @@ static void testBipolarKnobLawRoundTripsAndIsExactAtCentre() {
CHECK(deckNormFromBipolar(3.0) == 1.0);
}
static void testLiveRoutingCoversEveryContinuousPlaybackControl() {
// The live set: the six filter tone/modulation knobs, plus every stage time and stage
// level on all three envelopes.
const DeckParam live[] = {
DeckParam::kFilterMorph, DeckParam::kFilterCutoff, DeckParam::kFilterQ,
DeckParam::kFilterDrive, DeckParam::kFilterModAmt, DeckParam::kFilterKeyTrack,
DeckParam::kAttack, DeckParam::kHold, DeckParam::kDecay, DeckParam::kSustain,
DeckParam::kRelease,
DeckParam::kFilterEnvAttack, DeckParam::kFilterEnvHold, DeckParam::kFilterEnvDecay,
DeckParam::kFilterEnvSustain, DeckParam::kFilterEnvRelease,
DeckParam::kPitchEnvAttack, DeckParam::kPitchEnvDecay, DeckParam::kPitchEnvDepth,
};
for (DeckParam p : live) CHECK(isLiveDeckParam(p));
// Everything else reloads or rebuilds. kFilterVel and kKeyTrack are the two that look
// continuous but feed values a voice latches at note-on (the velocity-curve result and
// the pitch ratio) — making them live would re-gain or retune a note already struck.
const DeckParam reloads[] = {
DeckParam::kPlayMode, DeckParam::kPitchEngine, DeckParam::kPitchEnvEnable,
DeckParam::kFilterEnable, DeckParam::kFilterLaw, DeckParam::kFilterVel,
DeckParam::kKeyTrack, DeckParam::kTrigLength, DeckParam::kTrigFadeIn,
DeckParam::kTrigFadeOut, DeckParam::kVoiceCount, DeckParam::kVoiceMode,
DeckParam::kMonoTrigger, DeckParam::kMasterGain,
};
for (DeckParam p : reloads) CHECK(!isLiveDeckParam(p));
// Every id in the space is classified by one of the two lists above, so a control added
// later cannot slip through unclassified.
const std::size_t classified = (sizeof(live) + sizeof(reloads)) / sizeof(DeckParam);
CHECK(classified == static_cast<std::size_t>(DeckParam::kCount));
}
int main() {
testLiveRoutingCoversEveryContinuousPlaybackControl();
testDeckReadsPitchThenFilterThenAmpLeftToRight();
testFilterGroupCarriesItsFiveToneControlsPlusModulation();
testAmpGroupWidthSurvivesAGateTriggerFlip();