From 12c52bb0d8d0c53372af12adf8075d5519a0ff40 Mon Sep 17 00:00:00 2001 From: icex2 Date: Thu, 31 Dec 2020 15:58:18 +0100 Subject: [PATCH] vigem-iidxio: Major refactoring incorporating features from Grim's branch - 3 turntable modes: analog (raw), relative and button mapped - re-structure main to make it easier to maintain and less repetitive code --- src/main/vigem-iidxio/main.c | 315 +++++++++++++++++++++++------------ 1 file changed, 210 insertions(+), 105 deletions(-) diff --git a/src/main/vigem-iidxio/main.c b/src/main/vigem-iidxio/main.c index ab25e80..8ab527a 100644 --- a/src/main/vigem-iidxio/main.c +++ b/src/main/vigem-iidxio/main.c @@ -20,12 +20,16 @@ #include "vigemstub/helper.h" -#define ANALOG_FIXED_SENSITIVITY 256 +#define JOYSTICKS_NUM 3 +#define TURNTABLE_NUM 2 -static const uint8_t JOYSTICKS_NUM = 3; +static uint8_t _tt_last_raw[TURNTABLE_NUM]; +static int32_t _tt_state_for_analog[TURNTABLE_NUM]; +static int16_t _tt_state_for_btn[TURNTABLE_NUM]; static int16_t _convert_analog_to_s16(uint8_t val) { + // TT analog value is 8 bit, upscale to 16 bit return (int64_t) val * 256; } @@ -88,6 +92,176 @@ static uint16_t _check_assign_key(uint16_t input, size_t idx_in, size_t bit_out) return 0; } +static int32_t _handle_turntable_analog( + const struct vigem_iidxio_config* config, + uint8_t tt_cur, + uint8_t tt_last, + int32_t tt_state, + XUSB_REPORT *pad_state) +{ + int32_t state = tt_state; + + if (config->tt.analog.relative) { + state = _convert_relative_analog( + tt_cur, tt_last, state, config->tt.analog.relative_sensitivity); + + pad_state->sThumbLX = _filter_floor(state, config->tt.analog.relative_sensitivity / 2); + } else { + pad_state->sThumbLX = _convert_analog_to_s16(tt_cur); + } + + return state; +} + +static int16_t _handle_turntable_as_button( + const struct vigem_iidxio_config* config, + uint8_t tt_cur, + uint8_t tt_last, + int16_t tt_state, + XUSB_REPORT *pad_state) +{ + const uint8_t btn_inc = config->tt.button.debounce; + const uint16_t btn_threshhold = btn_inc * config->tt.button.threshold; + const uint16_t btn_max = btn_threshhold * 2; + + int8_t delta = tt_cur - tt_last; + int16_t state = tt_state; + + if (config->tt.debug_output) { + printf("delta_button %d, ", delta); + } + + // Update state according to delta, debounce and max values + if (delta > 0 && state < btn_max) { + state += btn_inc; + } else if (delta < 0 && abs(state) < btn_max) { + state -= btn_inc; + } + + // With each update, automatically counter the player's TT movement to turn the state + // back to 0, e.g. player moves TT -> state += 20. with no additional movement, neutral state + // (0) is back in 20 ticks + if (state > 0) { + state--; + } else if (state < 0) { + state++; + } + + if (state > btn_threshhold) { + pad_state->wButtons |= XUSB_GAMEPAD_LEFT_THUMB; + } else if (state < (int16_t) btn_threshhold * -1) { + pad_state->wButtons |= XUSB_GAMEPAD_RIGHT_THUMB; + } + + return state; +} + +static void _handle_turntable( + const struct vigem_iidxio_config* config, + uint8_t* tt, + XUSB_REPORT *state) +{ + for (uint8_t i = 0; i < TURNTABLE_NUM; i++) { + if (config->tt.debug_output) { + printf("TT (%d): last_raw %d, raw %d, ", i, _tt_last_raw[i], tt[i]); + } + + _tt_state_for_btn[i] = _handle_turntable_as_button( + config, tt[i], _tt_last_raw[i], _tt_state_for_btn[i], &state[i]); + + _tt_state_for_analog[i] = _handle_turntable_analog( + config, tt[i], _tt_last_raw[i], _tt_state_for_analog[i], &state[i]); + + _tt_last_raw[i] = tt[i]; + + if (config->tt.debug_output) { + printf("state_btn %d, state_rel_analog %d\n", + _tt_state_for_btn[i], _tt_state_for_analog[i]); + } + } +} + +static void _handle_buttons_14keys(uint16_t keys, XUSB_REPORT *state) +{ + state[0].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P1_1, XUSB_GAMEPAD_A); + state[0].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P1_2, XUSB_GAMEPAD_B); + state[0].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P1_3, XUSB_GAMEPAD_X); + state[0].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P1_4, XUSB_GAMEPAD_Y); + state[0].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P1_5, XUSB_GAMEPAD_LEFT_SHOULDER); + state[0].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P1_6, XUSB_GAMEPAD_RIGHT_SHOULDER); + state[0].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P1_7, XUSB_GAMEPAD_BACK); + + state[1].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P2_1, XUSB_GAMEPAD_A); + state[1].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P2_2, XUSB_GAMEPAD_B); + state[1].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P2_3, XUSB_GAMEPAD_X); + state[1].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P2_4, XUSB_GAMEPAD_Y); + state[1].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P2_5, XUSB_GAMEPAD_LEFT_SHOULDER); + state[1].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P2_6, XUSB_GAMEPAD_RIGHT_SHOULDER); + state[1].wButtons |= _check_assign_key( + keys, IIDX_IO_KEY_P2_7, XUSB_GAMEPAD_BACK); +} + +static void _handle_buttons_panel(uint8_t panel, XUSB_REPORT *state) +{ + state[0].wButtons |= _check_assign_key( + panel, IIDX_IO_PANEL_LIGHT_P1_START, XUSB_GAMEPAD_START); + state[1].wButtons |= _check_assign_key( + panel, IIDX_IO_PANEL_LIGHT_P2_START, XUSB_GAMEPAD_START); + + state[2].wButtons |= _check_assign_key( + panel, IIDX_IO_PANEL_LIGHT_VEFX, XUSB_GAMEPAD_B); + state[2].wButtons |= _check_assign_key( + panel, IIDX_IO_PANEL_LIGHT_EFFECT, XUSB_GAMEPAD_A); +} + +static void _handle_buttons_system(uint8_t system, XUSB_REPORT *state) +{ + state[2].wButtons |= _check_assign_key( + system, IIDX_IO_SYS_TEST, XUSB_GAMEPAD_X); + state[2].wButtons |= _check_assign_key( + system, IIDX_IO_SYS_SERVICE, XUSB_GAMEPAD_Y); + state[2].wButtons |= _check_assign_key( + system, IIDX_IO_SYS_COIN, XUSB_GAMEPAD_START); +} + +static void _all_lights_off() +{ + iidx_io_ep1_set_deck_lights(0); + iidx_io_ep1_set_panel_lights(0); + iidx_io_ep1_set_top_lamps(0); + iidx_io_ep1_set_top_neons(false); +} + +static void _all_lights_off_shutdown() +{ + // Cleanup, turn all lights off + _all_lights_off(); + + // Depending on the IO, pushing the state to the actual outputs, e.g. lights on/off + // can be a bit finicky. Do a few times to "enforce" the state + for (uint8_t i = 0; i < 3; i++) { + iidx_io_ep1_send(); + // Required to handle iidxio-ezusb specific quirks with flushing 16seg text + iidx_io_ep2_recv(); + iidx_io_ep3_write_16seg(" "); + + Sleep(10); + } +} + int main(int argc, char **argv) { log_to_writer(log_writer_stdout, NULL); @@ -141,122 +315,62 @@ int main(int argc, char **argv) log_info("vigem init succeeded, beginning poll loop"); - // Initial output state, turn all lights off - iidx_io_ep1_set_deck_lights(0); - iidx_io_ep1_set_panel_lights(0); - iidx_io_ep1_set_top_lamps(0); - iidx_io_ep1_set_top_neons(false); + // Create clean output state + _all_lights_off(); - vigem_iidxio_cab_light_sequencer_init(config.cab_light_mode); + vigem_iidxio_cab_light_sequencer_init(config.cab_light.light_mode); - if (config.text_16seg[0] != '\0') { - vigem_iidxio_cab_16seg_sequencer_init(config.text_16seg, config.text_scroll_cycle_time_ms); + if (config.cab_light.text_16seg[0] != '\0') { + vigem_iidxio_cab_16seg_sequencer_init( + config.cab_light.text_16seg, config.cab_light.text_scroll_cycle_time_ms); } - uint8_t turntable_buffered[2] = {0, 0}; - uint8_t turntable_last[2] = {0, 0}; - while (loop) { + for (uint8_t i = 0; i < JOYSTICKS_NUM; i++) { + memset(&state[i], 0, sizeof(state[i])); + } + if (!iidx_io_ep2_recv()) { log_warning("iidxio receiving failed"); break; } - for (uint8_t i = 0; i < JOYSTICKS_NUM; i++) { - memset(&state[i], 0, sizeof(state[i])); - } - - // 14 keys + // Keys/buttons + uint16_t keys = iidx_io_ep2_get_keys(); - - state[0].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P1_1, XUSB_GAMEPAD_A); - state[0].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P1_2, XUSB_GAMEPAD_B); - state[0].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P1_3, XUSB_GAMEPAD_X); - state[0].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P1_4, XUSB_GAMEPAD_Y); - state[0].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P1_5, XUSB_GAMEPAD_LEFT_SHOULDER); - state[0].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P1_6, XUSB_GAMEPAD_RIGHT_SHOULDER); - state[0].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P1_7, XUSB_GAMEPAD_BACK); - - state[1].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P2_1, XUSB_GAMEPAD_A); - state[1].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P2_2, XUSB_GAMEPAD_B); - state[1].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P2_3, XUSB_GAMEPAD_X); - state[1].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P2_4, XUSB_GAMEPAD_Y); - state[1].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P2_5, XUSB_GAMEPAD_LEFT_SHOULDER); - state[1].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P2_6, XUSB_GAMEPAD_RIGHT_SHOULDER); - state[1].wButtons |= _check_assign_key( - keys, IIDX_IO_KEY_P2_7, XUSB_GAMEPAD_BACK); - - // Panel buttons uint8_t panel = iidx_io_ep2_get_panel(); - - state[0].wButtons |= _check_assign_key( - panel, IIDX_IO_PANEL_LIGHT_P1_START, XUSB_GAMEPAD_START); - state[1].wButtons |= _check_assign_key( - panel, IIDX_IO_PANEL_LIGHT_P2_START, XUSB_GAMEPAD_START); - - state[2].wButtons |= _check_assign_key( - panel, IIDX_IO_PANEL_LIGHT_VEFX, XUSB_GAMEPAD_B); - state[2].wButtons |= _check_assign_key( - panel, IIDX_IO_PANEL_LIGHT_EFFECT, XUSB_GAMEPAD_A); - - // System buttons uint8_t system = iidx_io_ep2_get_sys(); - state[2].wButtons |= _check_assign_key( - system, IIDX_IO_SYS_TEST, XUSB_GAMEPAD_X); - state[2].wButtons |= _check_assign_key( - system, IIDX_IO_SYS_SERVICE, XUSB_GAMEPAD_Y); - state[2].wButtons |= _check_assign_key( - system, IIDX_IO_SYS_COIN, XUSB_GAMEPAD_START); + _handle_buttons_14keys(keys, state); + _handle_buttons_panel(panel, state); + _handle_buttons_system(system, state); // Turntable - uint8_t turntable[2]; - - turntable[0] = iidx_io_ep2_get_turntable(0); - turntable[1] = iidx_io_ep2_get_turntable(1); - if (config.relative_analog) { - turntable_buffered[0] = _convert_relative_analog( - turntable[0], turntable_last[0], turntable_buffered[0], ANALOG_FIXED_SENSITIVITY); - turntable_buffered[1] = _convert_relative_analog( - turntable[1], turntable_last[1], turntable_buffered[1], ANALOG_FIXED_SENSITIVITY); + uint8_t turntable[TURNTABLE_NUM]; - state[0].sThumbLX = _filter_floor(turntable_buffered[0], ANALOG_FIXED_SENSITIVITY / 2); - state[1].sThumbLX = _filter_floor(turntable_buffered[1], ANALOG_FIXED_SENSITIVITY / 2); - - turntable_last[0] = turntable[0]; - turntable_last[1] = turntable[1]; - } else { - state[0].sThumbLX = _convert_analog_to_s16(turntable[0]); - state[1].sThumbLX = _convert_analog_to_s16(turntable[1]); + for (uint8_t i = 0; i < TURNTABLE_NUM; i++) { + turntable[i] = iidx_io_ep2_get_turntable(i); } + + _handle_turntable(&config, turntable, state); - vigem_target_x360_update(client, pad[0], state[0]); - vigem_target_x360_update(client, pad[1], state[1]); + // Pad update + + for (uint8_t i = 0; i < JOYSTICKS_NUM; i++) { + vigem_target_x360_update(client, pad[i], state[i]); + } // ----------------------------------------------------------------------------- // Light related outputs - if (config.enable_keylight) { + if (config.cab_light.enable_keylight) { iidx_io_ep1_set_deck_lights(keys); iidx_io_ep1_set_panel_lights(panel); } - if (config.cab_light_mode != LIGHT_SEQ_MODE_OFF) { + if (config.cab_light.light_mode != LIGHT_SEQ_MODE_OFF) { bool neon; uint8_t spots; @@ -274,10 +388,12 @@ int main(int argc, char **argv) iidx_io_ep1_set_top_lamps(spots); } - char buffer_16seg[9]; + if (config.cab_light.text_16seg[0] != '\0') { + char buffer_16seg[9]; - vigem_iidxio_cab_16seg_sequencer_update(buffer_16seg); - iidx_io_ep3_write_16seg(buffer_16seg); + vigem_iidxio_cab_16seg_sequencer_update(buffer_16seg); + iidx_io_ep3_write_16seg(buffer_16seg); + } if (!iidx_io_ep1_send()) { log_warning("iidxio sending failed"); @@ -290,22 +406,11 @@ int main(int argc, char **argv) loop = false; } - // avoid banging + // Avoid CPU banging Sleep(1); } - // Cleanup, turn all lights off - iidx_io_ep1_set_deck_lights(0); - iidx_io_ep1_set_panel_lights(0); - iidx_io_ep1_set_top_lamps(0); - iidx_io_ep1_set_top_neons(false); - - iidx_io_ep1_send(); - // Required to handle iidxio-ezusb specific quirks with flushing 16seg text - iidx_io_ep2_recv(); - iidx_io_ep3_write_16seg(" "); - - Sleep(10); + _all_lights_off_shutdown(); for (uint8_t i = 0; i < JOYSTICKS_NUM; i++) { vigem_target_remove(client, pad[i]);