From 7624ded5121352d9442d212a79ab05bce29796b3 Mon Sep 17 00:00:00 2001 From: bicarus-dev <202771338+bicarus-dev@users.noreply.github.com> Date: Sat, 20 Sep 2025 03:40:18 -0700 Subject: [PATCH] pc: add option to translate absolute analog values to fader input (#375) ## Link to GitHub Issue, if one exists n/a ## Description of change Add a new option that allows absolute position analog input, such as SDVX controller knobs and IIDX turntables, to be bound as input for Polaris Chord faders. In practice this means turning a knob counter-clockwise results in fader being held left (relative to velocity), and vice versa. ## Testing Tested using FauceTwo and a mouse. --- src/spice2x/games/pc/bi2x_hook.cpp | 76 +++++++++++++++++++++++++++++- src/spice2x/games/pc/pc.h | 1 + src/spice2x/launcher/launcher.cpp | 3 ++ src/spice2x/launcher/options.cpp | 11 +++++ src/spice2x/launcher/options.h | 1 + 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/spice2x/games/pc/bi2x_hook.cpp b/src/spice2x/games/pc/bi2x_hook.cpp index 50b1e53..852e083 100644 --- a/src/spice2x/games/pc/bi2x_hook.cpp +++ b/src/spice2x/games/pc/bi2x_hook.cpp @@ -7,8 +7,68 @@ #include "games/io.h" #include "io.h" #include "util/tapeled.h" +#include "pc.h" +#include "util/utils.h" namespace games::pc { + bool PC_KNOB_MODE = false; + + constexpr float RELATIVE_DECAY = 0.02f; + struct FADER_RELATIVE { + // ranges from 0.f to 1.f + float previous_raw = 0.5f; + + // ranges from -1.f to +1.f + float effective_value = 0.f; + }; + + FADER_RELATIVE fader_l_relative; + FADER_RELATIVE fader_r_relative; + + float calculate_analog_raw_delta(float old_value, float new_value) { + // assumed that values are between 0.f and 1.f + + float delta = 0.f; + if ((old_value < 0.25f) && (0.75f < new_value)) { + // wrapped around: going left + delta = -((1.f - new_value) + old_value); + + } else if ((new_value < 0.25f) && (0.75f < old_value)) { + // wrapped around: going right + delta = (1.f - old_value) + new_value; + + } else { + delta = new_value - old_value; + } + return delta; + } + + float apply_decay(float val) { + // return to center over time + if (RELATIVE_DECAY < val) { + return val - RELATIVE_DECAY; + } else if (val < -RELATIVE_DECAY) { + return val + RELATIVE_DECAY; + } else { + return 0.f; + } + } + + float get_fader_knob_mode_value(float raw_analog, FADER_RELATIVE &fader) { + // add new value... + const float delta = calculate_analog_raw_delta(fader.previous_raw, raw_analog); + fader.effective_value += (delta * 20.f); + fader.previous_raw = raw_analog; + + // capture the result after adding new input value + fader.effective_value = CLAMP(fader.effective_value, -1.f, 1.f); + const float result = fader.effective_value; + + // post-process for next iteration: apply decay (return to center over time) + fader.effective_value = apply_decay(fader.effective_value); + + return result; + } /* * class definitions @@ -270,7 +330,13 @@ namespace games::pc { // FADER-L float val = 0.f; if (analogs[Analogs::FaderL].isSet()) { - val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]) - 0.5f) * 2; + if (PC_KNOB_MODE) { + val = get_fader_knob_mode_value( + GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]), + fader_l_relative); + } else { + val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderL]) - 0.5f) * 2; + } } if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Left]) && GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderL_Right])) { @@ -289,7 +355,13 @@ namespace games::pc { // FADER-R val = 0.f; if (analogs[Analogs::FaderR].isSet()) { - val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]) - 0.5f) * 2; + if (PC_KNOB_MODE) { + val = get_fader_knob_mode_value( + GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]), + fader_r_relative); + } else { + val = (GameAPI::Analogs::getState(RI_MGR, analogs[Analogs::FaderR]) - 0.5f) * 2; + } } if (GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Left]) && GameAPI::Buttons::getState(RI_MGR, buttons[Buttons::FaderR_Right])) { diff --git a/src/spice2x/games/pc/pc.h b/src/spice2x/games/pc/pc.h index e93ad70..0eca4c3 100644 --- a/src/spice2x/games/pc/pc.h +++ b/src/spice2x/games/pc/pc.h @@ -5,6 +5,7 @@ namespace games::pc { extern std::string PC_INJECT_ARGS; extern bool PC_NO_IO; + extern bool PC_KNOB_MODE; class PCGame : public games::Game { public: diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index 391c7f6..1733b5f 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -1112,6 +1112,9 @@ int main_implementation(int argc, char *argv[]) { if (options[launcher::Options::PCNoIO].is_active()) { games::pc::PC_NO_IO = options[launcher::Options::PCNoIO].value_bool(); } + if (options[launcher::Options::PCKnobMode].value_bool()) { + games::pc::PC_KNOB_MODE = true; + } if (options[launcher::Options::spice2x_EnableSMXStage].value_bool()) { rawinput::ENABLE_SMX_STAGE = true; } diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index f83410e..338b955 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -1806,6 +1806,7 @@ static const std::vector OPTION_DEFINITIONS = { .category = "Game Options (Advanced)" }, { + // PCArgs .title = "PC Arguments Override", .name = "pcargs", .desc = "Command line arguments passed to the game.", @@ -1815,6 +1816,7 @@ static const std::vector OPTION_DEFINITIONS = { .category = "Game Options (Advanced)", }, { + // PCNoIO .title = "PC Disable IO Emulation", .name = "pcnoio", .desc = "Disables BI2X hooks for PC", @@ -1823,6 +1825,15 @@ static const std::vector OPTION_DEFINITIONS = { .game_name = "Polaris Chord", .category = "Game Options (Advanced)" }, + { + // PCKnobMode + .title = "PC Fader Knobs Mode", + .name = "pcknobs", + .desc = "Allows SDVX knobs and IIDX turntables to be bound as Polaris Chord faders", + .type = OptionType::Bool, + .game_name = "Polaris Chord", + .category = "Game Options", + }, { // spice2x_LightsOverallBrightness .title = "Lights Brightness Adjustment", diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index 306b13a..7892563 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -205,6 +205,7 @@ namespace launcher { MFGNoIO, PCArgs, PCNoIO, + PCKnobMode, spice2x_LightsOverallBrightness, spice2x_WindowBorder, spice2x_WindowSize,