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:
+209
-104
@@ -20,12 +20,16 @@
|
|||||||
|
|
||||||
#include "vigemstub/helper.h"
|
#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)
|
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;
|
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;
|
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)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
log_to_writer(log_writer_stdout, NULL);
|
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");
|
log_info("vigem init succeeded, beginning poll loop");
|
||||||
|
|
||||||
// Initial output state, turn all lights off
|
// Create clean output state
|
||||||
iidx_io_ep1_set_deck_lights(0);
|
_all_lights_off();
|
||||||
iidx_io_ep1_set_panel_lights(0);
|
|
||||||
iidx_io_ep1_set_top_lamps(0);
|
|
||||||
iidx_io_ep1_set_top_neons(false);
|
|
||||||
|
|
||||||
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') {
|
if (config.cab_light.text_16seg[0] != '\0') {
|
||||||
vigem_iidxio_cab_16seg_sequencer_init(config.text_16seg, config.text_scroll_cycle_time_ms);
|
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) {
|
while (loop) {
|
||||||
|
for (uint8_t i = 0; i < JOYSTICKS_NUM; i++) {
|
||||||
|
memset(&state[i], 0, sizeof(state[i]));
|
||||||
|
}
|
||||||
|
|
||||||
if (!iidx_io_ep2_recv()) {
|
if (!iidx_io_ep2_recv()) {
|
||||||
log_warning("iidxio receiving failed");
|
log_warning("iidxio receiving failed");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t i = 0; i < JOYSTICKS_NUM; i++) {
|
// Keys/buttons
|
||||||
memset(&state[i], 0, sizeof(state[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 14 keys
|
|
||||||
uint16_t keys = iidx_io_ep2_get_keys();
|
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();
|
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();
|
uint8_t system = iidx_io_ep2_get_sys();
|
||||||
|
|
||||||
state[2].wButtons |= _check_assign_key(
|
_handle_buttons_14keys(keys, state);
|
||||||
system, IIDX_IO_SYS_TEST, XUSB_GAMEPAD_X);
|
_handle_buttons_panel(panel, state);
|
||||||
state[2].wButtons |= _check_assign_key(
|
_handle_buttons_system(system, state);
|
||||||
system, IIDX_IO_SYS_SERVICE, XUSB_GAMEPAD_Y);
|
|
||||||
state[2].wButtons |= _check_assign_key(
|
|
||||||
system, IIDX_IO_SYS_COIN, XUSB_GAMEPAD_START);
|
|
||||||
|
|
||||||
// Turntable
|
// Turntable
|
||||||
uint8_t turntable[2];
|
|
||||||
|
|
||||||
turntable[0] = iidx_io_ep2_get_turntable(0);
|
uint8_t turntable[TURNTABLE_NUM];
|
||||||
turntable[1] = iidx_io_ep2_get_turntable(1);
|
|
||||||
|
|
||||||
if (config.relative_analog) {
|
for (uint8_t i = 0; i < TURNTABLE_NUM; i++) {
|
||||||
turntable_buffered[0] = _convert_relative_analog(
|
turntable[i] = iidx_io_ep2_get_turntable(i);
|
||||||
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);
|
|
||||||
|
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vigem_target_x360_update(client, pad[0], state[0]);
|
_handle_turntable(&config, turntable, state);
|
||||||
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
|
// Light related outputs
|
||||||
|
|
||||||
if (config.enable_keylight) {
|
if (config.cab_light.enable_keylight) {
|
||||||
iidx_io_ep1_set_deck_lights(keys);
|
iidx_io_ep1_set_deck_lights(keys);
|
||||||
iidx_io_ep1_set_panel_lights(panel);
|
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;
|
bool neon;
|
||||||
uint8_t spots;
|
uint8_t spots;
|
||||||
|
|
||||||
@@ -274,10 +388,12 @@ int main(int argc, char **argv)
|
|||||||
iidx_io_ep1_set_top_lamps(spots);
|
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);
|
vigem_iidxio_cab_16seg_sequencer_update(buffer_16seg);
|
||||||
iidx_io_ep3_write_16seg(buffer_16seg);
|
iidx_io_ep3_write_16seg(buffer_16seg);
|
||||||
|
}
|
||||||
|
|
||||||
if (!iidx_io_ep1_send()) {
|
if (!iidx_io_ep1_send()) {
|
||||||
log_warning("iidxio sending failed");
|
log_warning("iidxio sending failed");
|
||||||
@@ -290,22 +406,11 @@ int main(int argc, char **argv)
|
|||||||
loop = false;
|
loop = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// avoid banging
|
// Avoid CPU banging
|
||||||
Sleep(1);
|
Sleep(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup, turn all lights off
|
_all_lights_off_shutdown();
|
||||||
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);
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < JOYSTICKS_NUM; i++) {
|
for (uint8_t i = 0; i < JOYSTICKS_NUM; i++) {
|
||||||
vigem_target_remove(client, pad[i]);
|
vigem_target_remove(client, pad[i]);
|
||||||
|
|||||||
Reference in New Issue
Block a user