// deck_groups.cpp — see deck_groups.h. Pure data; no host types. #include "core/instrument/ui/deck_groups.h" #include namespace reasampler::instrument::ui { namespace { int id(DeckParam p) { return static_cast(p); } double clamp(double v, double lo, double hi) { return v < lo ? lo : (v > hi ? hi : v); } } // namespace double deckBipolarFromNorm(double norm) { return clamp(norm, 0.0, 1.0) * 2.0 - 1.0; } double deckNormFromBipolar(double value) { return clamp(value, -1.0, 1.0) * 0.5 + 0.5; } std::vector sampleDeckGroups(PlayMode playMode) { std::vector out; { DeckGroupDesc pitch; pitch.id = kGroupPitch; pitch.captionWidth = 38; pitch.captionToggle = {id(DeckParam::kPitchEngine), 48}; pitch.cellIds = {id(DeckParam::kKeyTrack)}; out.push_back(std::move(pitch)); } { DeckGroupDesc penv; penv.id = kGroupPitchEnv; penv.captionWidth = 58; penv.captionToggle = {id(DeckParam::kPitchEnvEnable), 32}; penv.cellIds = {id(DeckParam::kPitchEnvAttack), id(DeckParam::kPitchEnvDecay), id(DeckParam::kPitchEnvDepth)}; out.push_back(std::move(penv)); } { // Tone shaping left-to-right, then the three modulation depths that all target cutoff. DeckGroupDesc filter; filter.id = kGroupFilter; filter.captionWidth = 46; filter.captionToggle = {id(DeckParam::kFilterEnable), 32}; filter.cellIds = {id(DeckParam::kFilterMorph), id(DeckParam::kFilterCutoff), id(DeckParam::kFilterQ), id(DeckParam::kFilterDrive), id(DeckParam::kFilterModAmt), id(DeckParam::kFilterVel), id(DeckParam::kFilterKeyTrack)}; filter.rowToggle = {id(DeckParam::kFilterLaw), 44}; out.push_back(std::move(filter)); } { DeckGroupDesc fenv; fenv.id = kGroupFilterEnv; fenv.captionWidth = 66; fenv.cellIds = {id(DeckParam::kFilterEnvAttack), id(DeckParam::kFilterEnvHold), id(DeckParam::kFilterEnvDecay), id(DeckParam::kFilterEnvSustain), id(DeckParam::kFilterEnvRelease)}; out.push_back(std::move(fenv)); } { DeckGroupDesc amp; amp.id = kGroupAmpEnv; amp.captionWidth = 78; amp.captionToggle = {id(DeckParam::kPlayMode), 44}; if (playMode == PlayMode::Gate) { amp.cellIds = {id(DeckParam::kAttack), id(DeckParam::kHold), id(DeckParam::kDecay), id(DeckParam::kSustain), id(DeckParam::kRelease)}; } else { // Trigger, time-ordered left-to-right (Fade In / Length % / Fade Out — matches // the drawn envelope), plus two blanks (see knob_deck.h's blank-cell contract). amp.cellIds = {id(DeckParam::kTrigFadeIn), id(DeckParam::kTrigLength), id(DeckParam::kTrigFadeOut), -1, -1}; } out.push_back(std::move(amp)); } { DeckGroupDesc voice; voice.id = kGroupVoice; voice.captionWidth = 38; voice.captionToggle = {id(DeckParam::kVoiceMode), 40}; voice.cellIds = {id(DeckParam::kVoiceCount)}; voice.rowToggle = {id(DeckParam::kMonoTrigger), 44}; out.push_back(std::move(voice)); } { DeckGroupDesc master; master.id = kGroupMaster; master.captionWidth = 46; master.cellIds = {id(DeckParam::kMasterGain)}; out.push_back(std::move(master)); } return out; } bool isLiveDeckParam(DeckParam id) { switch (id) { case DeckParam::kAttack: case DeckParam::kHold: case DeckParam::kDecay: case DeckParam::kSustain: case DeckParam::kRelease: case DeckParam::kPitchEnvAttack: case DeckParam::kPitchEnvDecay: case DeckParam::kPitchEnvDepth: case DeckParam::kFilterMorph: case DeckParam::kFilterCutoff: case DeckParam::kFilterQ: case DeckParam::kFilterDrive: case DeckParam::kFilterModAmt: case DeckParam::kFilterKeyTrack: case DeckParam::kFilterEnvAttack: case DeckParam::kFilterEnvHold: case DeckParam::kFilterEnvDecay: case DeckParam::kFilterEnvSustain: case DeckParam::kFilterEnvRelease: return true; // Listed rather than defaulted so a newly added control is a COMPILE error here (the // -Wswitch gate is GCC/Clang; MSVC's C4062 is off at this project's warning level) // instead of silently defaulting to non-live. Reasons live in the header. case DeckParam::kPlayMode: case DeckParam::kPitchEngine: case DeckParam::kTrigLength: case DeckParam::kTrigFadeIn: case DeckParam::kTrigFadeOut: case DeckParam::kPitchEnvEnable: case DeckParam::kKeyTrack: case DeckParam::kFilterEnable: case DeckParam::kFilterVel: case DeckParam::kFilterLaw: case DeckParam::kVoiceCount: case DeckParam::kVoiceMode: case DeckParam::kMonoTrigger: case DeckParam::kMasterGain: case DeckParam::kCount: // not a control return false; } return false; // unreachable for a valid enumerator; silences a warning. } bool liveCommitFor(LiveDragKind kind, int paramId, PlayMode playMode) { switch (kind) { case LiveDragKind::kDeckKnob: return paramId >= 0 && paramId < static_cast(DeckParam::kCount) && isLiveDeckParam(static_cast(paramId)); case LiveDragKind::kEnvNode: return playMode == PlayMode::Gate; case LiveDragKind::kOther: return false; } return false; } } // namespace reasampler::instrument::ui