From 79a3e32d5db55a2740ed76b7c6a8944e59cb73da Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Sat, 2 May 2026 02:17:51 -0700 Subject: [PATCH] iidx: delay logic for iidx tt (#669) ## Link to GitHub Issue or related Pull Request, if one exists Re-implements fix for #181 as it was removed by #668 ## Description of change Add two options to add artificial delay to turntable input. These operate directly on the input logic (`get_tt`) which is called by I/O emulation code. Functionally, it's **identical** to the `Delay (experimental)` option we had in the analog tab, just moved to Options tab. ## Testing 64 bit tdj: ok 64 bit ldj bi2a: ok 32 bit ldj ezusb: ok Worth noting that TDJ bi2x_hook polls at 120Hz, bi2a at ~500Hz, and ezusb at ~170Hz --- src/spice2x/games/iidx/iidx.cpp | 23 ++++++++++++++++++++++- src/spice2x/games/iidx/iidx.h | 2 ++ src/spice2x/launcher/launcher.cpp | 6 ++++++ src/spice2x/launcher/options.cpp | 20 ++++++++++++++++++++ src/spice2x/launcher/options.h | 2 ++ 5 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/spice2x/games/iidx/iidx.cpp b/src/spice2x/games/iidx/iidx.cpp index 0bf0f09..a64179d 100644 --- a/src/spice2x/games/iidx/iidx.cpp +++ b/src/spice2x/games/iidx/iidx.cpp @@ -67,6 +67,8 @@ namespace games::iidx { std::optional SOUND_OUTPUT_DEVICE = std::nullopt; std::optional SOUND_OUTPUT_DEVICE_IN_EFFECT = std::nullopt; std::optional ASIO_DRIVER = std::nullopt; + uint32_t TT_DELAY_P1 = 0; + uint32_t TT_DELAY_P2 = 0; uint8_t DIGITAL_TT_SENS = 4; std::optional SUBSCREEN_OVERLAY_SIZE = std::nullopt; std::optional SCREEN_MODE = std::nullopt; @@ -781,7 +783,26 @@ namespace games::iidx { } // return higher 8 bit - return (uint8_t) (ret_value >> 2); + uint8_t result = (uint8_t) (ret_value >> 2); + + // delay + if ((player == 0 && TT_DELAY_P1 > 0) || + (player == 1 && TT_DELAY_P2 > 0)) { + + static std::queue delay_queue[2]; + auto &queue = delay_queue[player]; + + const auto max_size = + static_cast((player == 0) ? TT_DELAY_P1 : TT_DELAY_P2) + 1; + + queue.push(result); + while (queue.size() > max_size) { + queue.pop(); + } + result = queue.front(); + } + + return result; } unsigned char get_slider(uint8_t slider) { diff --git a/src/spice2x/games/iidx/iidx.h b/src/spice2x/games/iidx/iidx.h index 2178901..f5cadfe 100644 --- a/src/spice2x/games/iidx/iidx.h +++ b/src/spice2x/games/iidx/iidx.h @@ -31,6 +31,8 @@ namespace games::iidx { extern bool NATIVE_TOUCH; extern std::optional SOUND_OUTPUT_DEVICE; extern std::optional ASIO_DRIVER; + extern uint32_t TT_DELAY_P1; + extern uint32_t TT_DELAY_P2; extern uint8_t DIGITAL_TT_SENS; extern std::optional SUBSCREEN_OVERLAY_SIZE; extern std::optional SCREEN_MODE; diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index 32b032b..9be9e88 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -518,6 +518,12 @@ int main_implementation(int argc, char *argv[]) { if (options[launcher::Options::IIDXTDJMode].value_bool()) { games::iidx::TDJ_MODE = true; } + if (options[launcher::Options::IIDXTTDelayP1].is_active()) { + games::iidx::TT_DELAY_P1 = options[launcher::Options::IIDXTTDelayP1].value_uint32(); + } + if (options[launcher::Options::IIDXTTDelayP2].is_active()) { + games::iidx::TT_DELAY_P2 = options[launcher::Options::IIDXTTDelayP2].value_uint32(); + } if (options[launcher::Options::spice2x_IIDXDigitalTTSensitivity].is_active()) { games::iidx::DIGITAL_TT_SENS = (uint8_t) options[launcher::Options::spice2x_IIDXDigitalTTSensitivity].value_uint32(); diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index 42944cf..c6f3dcc 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -655,6 +655,26 @@ static const std::vector OPTION_DEFINITIONS = { .game_name = "Beatmania IIDX", .category = "Game Options", }, + { + // IIDXTTDelayP1 + .title = "IIDX TT Delay Frames (Player 1)", + .name = "iidxttdelayp1", + .desc = "Delays turntable input by number of poll frames. Heavily dependent on what I/O emulation is in use! " + "As usual, changing any option requires a restart. Default: 0 (no delay).", + .type = OptionType::Integer, + .game_name = "Beatmania IIDX", + .category = "Game Options (Advanced)", + }, + { + // IIDXTTDelayP2 + .title = "IIDX TT Delay Frames (Player 2)", + .name = "iidxttdelayp2", + .desc = "Delays turntable input by number of poll frames. Heavily dependent on what I/O emulation is in use! " + "As usual, changing any option requires a restart. Default: 0 (no delay).", + .type = OptionType::Integer, + .game_name = "Beatmania IIDX", + .category = "Game Options (Advanced)", + }, { // spice2x_IIDXDigitalTTSensitivity .title = "IIDX Digital TT Sensitivity", diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index 49974c5..2b97a9f 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -69,6 +69,8 @@ namespace launcher { IIDXAsioDriver, IIDXBIO2FW, IIDXTDJMode, + IIDXTTDelayP1, + IIDXTTDelayP2, spice2x_IIDXDigitalTTSensitivity, IIDXDigitalTTSocd, spice2x_IIDXLDJForce720p,