// 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)` 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 #include #include #include 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 constexpr auto make_voice_space_entries() noexcept { constexpr std::size_t N = EngineT::kVoiceSpaceCount; std::array 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 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(-1); } } // namespace nisps