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
This commit is contained in:
icex2
2020-12-31 15:58:18 +01:00
parent 279d4de4b3
commit 12c52bb0d8

View File

@@ -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]);