mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 14:20:42 -07:00
rawinput: reimplement analog relative mode and delay (#674)
## Link to GitHub Issue or related Pull Request, if one exists #181 ## Description of change Implement analog relative mode and delay option using a new millisecond-based algorithm. ## Testing WIP
This commit is contained in:
@@ -39,8 +39,8 @@
|
||||
<button name="VEFX" vkey="10" analogtype="0" devid="Player 1" invert="false" debounce_up="0" debounce_down="0" velocity_threshold="0"/>
|
||||
</buttons>
|
||||
<analogs>
|
||||
<analog name="Turntable P1" devid="Player 1" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Turntable P2" devid="Player 2" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Turntable P1" devid="Player 1" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Turntable P2" devid="Player 2" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
</analogs>
|
||||
<lights>
|
||||
<light name="P1 1" devid="Player 1" index="0"/>
|
||||
@@ -117,8 +117,8 @@
|
||||
<keypad_buttons/>
|
||||
<!-- Assumes analog mode. Need to invert direction since this FW is designed for INF. -->
|
||||
<analogs>
|
||||
<analog name="Turntable P1" devid="arcin (1p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1"/>
|
||||
<analog name="Turntable P2" devid="arcin (2p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1"/>
|
||||
<analog name="Turntable P1" devid="arcin (1p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Turntable P2" devid="arcin (2p)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="true" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
</analogs>
|
||||
<lights>
|
||||
<light name="P1 1" devid="arcin (1p)" index="0"/>
|
||||
@@ -144,34 +144,34 @@
|
||||
<buttons/>
|
||||
<keypad_buttons/>
|
||||
<analogs>
|
||||
<analog name="Key 1" devid="Analogs key 01-28" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 2" devid="Analogs key 01-28" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 3" devid="Analogs key 01-28" index="6" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 4" devid="Analogs key 01-28" index="7" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 5" devid="Analogs key 01-28" index="8" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 6" devid="Analogs key 01-28" index="9" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 7" devid="Analogs key 01-28" index="10" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 8" devid="Analogs key 01-28" index="11" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 9" devid="Analogs key 01-28" index="12" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 10" devid="Analogs key 01-28" index="13" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 11" devid="Analogs key 01-28" index="14" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 12" devid="Analogs key 01-28" index="15" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 13" devid="Analogs key 01-28" index="16" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 14" devid="Analogs key 01-28" index="17" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 15" devid="Analogs key 01-28" index="18" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 16" devid="Analogs key 01-28" index="19" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 17" devid="Analogs key 01-28" index="20" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 18" devid="Analogs key 01-28" index="21" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 19" devid="Analogs key 01-28" index="22" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 20" devid="Analogs key 01-28" index="23" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 21" devid="Analogs key 01-28" index="24" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 22" devid="Analogs key 01-28" index="25" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 23" devid="Analogs key 01-28" index="26" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 24" devid="Analogs key 01-28" index="27" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 25" devid="Analogs key 01-28" index="28" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 26" devid="Analogs key 01-28" index="29" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 27" devid="Analogs key 01-28" index="30" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 28" devid="Analogs key 01-28" index="31" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Key 1" devid="Analogs key 01-28" index="4" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 2" devid="Analogs key 01-28" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 3" devid="Analogs key 01-28" index="6" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 4" devid="Analogs key 01-28" index="7" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 5" devid="Analogs key 01-28" index="8" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 6" devid="Analogs key 01-28" index="9" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 7" devid="Analogs key 01-28" index="10" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 8" devid="Analogs key 01-28" index="11" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 9" devid="Analogs key 01-28" index="12" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 10" devid="Analogs key 01-28" index="13" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 11" devid="Analogs key 01-28" index="14" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 12" devid="Analogs key 01-28" index="15" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 13" devid="Analogs key 01-28" index="16" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 14" devid="Analogs key 01-28" index="17" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 15" devid="Analogs key 01-28" index="18" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 16" devid="Analogs key 01-28" index="19" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 17" devid="Analogs key 01-28" index="20" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 18" devid="Analogs key 01-28" index="21" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 19" devid="Analogs key 01-28" index="22" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 20" devid="Analogs key 01-28" index="23" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 21" devid="Analogs key 01-28" index="24" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 22" devid="Analogs key 01-28" index="25" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 23" devid="Analogs key 01-28" index="26" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 24" devid="Analogs key 01-28" index="27" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 25" devid="Analogs key 01-28" index="28" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 26" devid="Analogs key 01-28" index="29" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 27" devid="Analogs key 01-28" index="30" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Key 28" devid="Analogs key 01-28" index="31" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
</analogs>
|
||||
<lights>
|
||||
<light name="Key 1 R" devid="Lights 01-14" index="0"/>
|
||||
@@ -277,9 +277,9 @@
|
||||
</buttons>
|
||||
<keypad_buttons/>
|
||||
<analogs>
|
||||
<analog name="Guitar P1 Wail X" devid="Gitaller P1" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Guitar P1 Wail Y" devid="Gitaller P1" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Guitar P1 Wail Z" devid="Gitaller P1" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="Guitar P1 Wail X" devid="Gitaller P1" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Guitar P1 Wail Y" devid="Gitaller P1" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="Guitar P1 Wail Z" devid="Gitaller P1" index="5" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
</analogs>
|
||||
<lights/>
|
||||
<!-- Gen 1 FAUCETWO in HID mode; should also work for later gens -->
|
||||
@@ -296,8 +296,8 @@
|
||||
</buttons>
|
||||
<keypad_buttons/>
|
||||
<analogs>
|
||||
<analog name="VOL-L" devid="Buttons (MI_01)" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="VOL-R" devid="Buttons (MI_01)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1"/>
|
||||
<analog name="VOL-L" devid="Buttons (MI_01)" index="1" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
<analog name="VOL-R" devid="Buttons (MI_01)" index="0" sensivity="1" deadzone="0" deadzone_mirror="false" invert="false" smoothing="false" multiplier="1" relative="false" delay_ms="0"/>
|
||||
</analogs>
|
||||
<lights>
|
||||
<light name="BT-A" devid="Lights (MI_02)" index="24"/>
|
||||
|
||||
@@ -237,7 +237,8 @@ float Analog::applyDeadzone(float raw_value) {
|
||||
deadzone = -deadzone;
|
||||
}
|
||||
|
||||
if (getType() != GameAPI::Analogs::AnalogType::LinearPositive) {
|
||||
// relative mode assumes that user is using a stick, so center is neutral regardless of analog type
|
||||
if (getType() != GameAPI::Analogs::AnalogType::LinearPositive || isRelativeMode()) {
|
||||
|
||||
// calculate values
|
||||
const auto delta = value - 0.5f;
|
||||
@@ -290,4 +291,75 @@ float Analog::applyDeadzone(float raw_value) {
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
float Analog::getRelativeModeValue(float raw_value) {
|
||||
const auto now = get_performance_seconds();
|
||||
auto delta_time = now - this->rel_mode_last_read_time_s;
|
||||
|
||||
if (this->rel_mode_last_read_time_s != 0.f) {
|
||||
// some heuristics to prevent huge jumps:
|
||||
// * if we went for more than 250ms without polls, discard it (e.g., during loading screens)
|
||||
// * cap the delta at 100ms to prevent huge jumps in case of very infrequent polling
|
||||
if (delta_time < 0.f || 0.25f < delta_time) {
|
||||
delta_time = 0.f;
|
||||
} else if (delta_time > 0.1f) {
|
||||
delta_time = 0.1f;
|
||||
}
|
||||
|
||||
// scale [0, 1] to [-1, 1] to simplify calculations
|
||||
const auto delta_raw_value = (raw_value - 0.5f) * 2.f;
|
||||
|
||||
// target is one revolution per second at max speed at 1.0 sensitivity
|
||||
auto adjusted_delta_value = delta_raw_value * delta_time;
|
||||
|
||||
// multiplier / divisor
|
||||
if (this->getMultiplier() > 1) {
|
||||
adjusted_delta_value *= this->getMultiplier();
|
||||
} else if (this->getMultiplier() < -1) {
|
||||
adjusted_delta_value /= -this->getMultiplier();
|
||||
}
|
||||
|
||||
// sensitivity
|
||||
if (this->isSensitivitySet()) {
|
||||
adjusted_delta_value *= this->getSensitivity();
|
||||
}
|
||||
|
||||
// calculate the new absolute value
|
||||
this->rel_mode_absolute_value += adjusted_delta_value;
|
||||
}
|
||||
|
||||
// update for next poll
|
||||
this->rel_mode_last_read_time_s = now;
|
||||
this->rel_mode_absolute_value = normalizeAnalogValue(this->rel_mode_absolute_value);
|
||||
return this->rel_mode_absolute_value;
|
||||
}
|
||||
|
||||
float Analog::getDelayedValue(float raw_value) {
|
||||
const double delay_ms = static_cast<double>(this->getDelayMs());
|
||||
if (delay_ms == 0.0) {
|
||||
return raw_value;
|
||||
}
|
||||
|
||||
// always push a new value
|
||||
const auto now = get_performance_milliseconds();
|
||||
this->delayed_inputs.emplace(now, raw_value);
|
||||
|
||||
// drain the queue down to reasonable length to prevent unconstrained growth
|
||||
// this would accommodate 1 second at ~1000Hz which is overkill
|
||||
// (UI only allows for 250ms of delay)
|
||||
while (this->delayed_inputs.size() > 1024) {
|
||||
this->delayed_inputs.pop();
|
||||
}
|
||||
|
||||
// pop until we find the oldest value still inside the delay window
|
||||
while (this->delayed_inputs.size() > 1) {
|
||||
const auto delta_t = now - this->delayed_inputs.front().time_in_ms;
|
||||
if (delta_t <= delay_ms) {
|
||||
break;
|
||||
}
|
||||
this->delayed_inputs.pop();
|
||||
}
|
||||
|
||||
return this->delayed_inputs.front().value;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
@@ -34,6 +35,11 @@ struct AnalogMovingAverage {
|
||||
float cosine;
|
||||
};
|
||||
|
||||
struct AnalogDelayEntry {
|
||||
double time_in_ms;
|
||||
float value;
|
||||
};
|
||||
|
||||
class Analog {
|
||||
private:
|
||||
std::string name;
|
||||
@@ -63,6 +69,15 @@ private:
|
||||
float divisor_previous_value = 0.5f;
|
||||
unsigned short divisor_region = 0;
|
||||
|
||||
// relative input mode
|
||||
float rel_mode_absolute_value = 0.5f;
|
||||
float rel_mode_last_read_time_s = 0.f;
|
||||
bool relative_mode = false;
|
||||
|
||||
// delay
|
||||
uint32_t delay_ms = 0;
|
||||
std::queue<AnalogDelayEntry> delayed_inputs;
|
||||
|
||||
float calculateAngularDifference(float old_rads, float new_rads);
|
||||
float normalizeAngle(float rads);
|
||||
float normalizeAnalogValue(float value);
|
||||
@@ -83,6 +98,8 @@ public:
|
||||
float applyAngularSensitivity(float raw_rads);
|
||||
float applyMultiplier(float raw_value);
|
||||
float applyDeadzone(float raw_value);
|
||||
float getRelativeModeValue(float raw_value);
|
||||
float getDelayedValue(float raw_value);
|
||||
|
||||
inline bool isSet() {
|
||||
if (this->override_enabled) {
|
||||
@@ -98,7 +115,9 @@ public:
|
||||
smoothing = false;
|
||||
deadzone_mirror = false;
|
||||
setMultiplier(1);
|
||||
setRelativeMode(false);
|
||||
setLastState(0.5f);
|
||||
setDelayMs(0);
|
||||
}
|
||||
|
||||
inline void clearBindings() {
|
||||
@@ -195,6 +214,19 @@ public:
|
||||
this->last_state = last_state;
|
||||
}
|
||||
|
||||
inline bool isRelativeMode() const {
|
||||
return this->relative_mode;
|
||||
}
|
||||
|
||||
inline void setRelativeMode(bool relative_mode) {
|
||||
if (relative_mode) {
|
||||
this->smoothing = false;
|
||||
}
|
||||
this->relative_mode = relative_mode;
|
||||
this->rel_mode_absolute_value = 0.5f;
|
||||
this->rel_mode_last_read_time_s = 0.f;
|
||||
}
|
||||
|
||||
inline GameAPI::Analogs::AnalogType getType() const {
|
||||
return this->type;
|
||||
}
|
||||
@@ -202,4 +234,12 @@ public:
|
||||
inline void setType(GameAPI::Analogs::AnalogType type) {
|
||||
this->type = type;
|
||||
}
|
||||
|
||||
inline uint32_t getDelayMs() const {
|
||||
return this->delay_ms;
|
||||
}
|
||||
|
||||
inline void setDelayMs(uint32_t delay_ms) {
|
||||
this->delay_ms = delay_ms;
|
||||
}
|
||||
};
|
||||
|
||||
+34
-21
@@ -679,38 +679,45 @@ float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, rawinput::D
|
||||
}
|
||||
|
||||
// deadzone
|
||||
// do not apply deadzone to circular analogs since it doesn't make sense
|
||||
if (analog.isDeadzoneSet() && analog.getType() != AnalogType::Circular) {
|
||||
// do not apply deadzone to circular analogs since it doesn't make sense (except in relative mode)
|
||||
if (analog.isDeadzoneSet() &&
|
||||
(analog.getType() != AnalogType::Circular || analog.isRelativeMode())) {
|
||||
value = analog.applyDeadzone(value);
|
||||
}
|
||||
|
||||
if (analog.getType() == AnalogType::Circular) {
|
||||
// integer multiplier
|
||||
value = analog.applyMultiplier(value);
|
||||
|
||||
// smoothing/sensitivity
|
||||
if (analog.getSmoothing() || analog.isSensitivitySet()) {
|
||||
float rads = value * (float) M_TAU;
|
||||
if (analog.isRelativeMode()) {
|
||||
value = analog.getRelativeModeValue(value);
|
||||
|
||||
// smoothing
|
||||
if (analog.getSmoothing()) {
|
||||
} else {
|
||||
// integer multiplier
|
||||
value = analog.applyMultiplier(value);
|
||||
|
||||
// preserve direction
|
||||
if (rads >= M_TAU) {
|
||||
rads -= 0.0001f;
|
||||
// smoothing/sensitivity
|
||||
if (analog.getSmoothing() || analog.isSensitivitySet()) {
|
||||
float rads = value * (float) M_TAU;
|
||||
|
||||
// smoothing
|
||||
if (analog.getSmoothing()) {
|
||||
|
||||
// preserve direction
|
||||
if (rads >= M_TAU) {
|
||||
rads -= 0.0001f;
|
||||
}
|
||||
|
||||
// calculate angle
|
||||
rads = analog.getSmoothedValue(rads);
|
||||
}
|
||||
|
||||
// calculate angle
|
||||
rads = analog.getSmoothedValue(rads);
|
||||
}
|
||||
// sensitivity
|
||||
if (analog.isSensitivitySet()) {
|
||||
rads = analog.applyAngularSensitivity(rads);
|
||||
}
|
||||
|
||||
// sensitivity
|
||||
if (analog.isSensitivitySet()) {
|
||||
rads = analog.applyAngularSensitivity(rads);
|
||||
// apply to value
|
||||
value = rads * (float) M_1_TAU;
|
||||
}
|
||||
|
||||
// apply to value
|
||||
value = rads * (float) M_1_TAU;
|
||||
}
|
||||
} else {
|
||||
// sensitivity
|
||||
@@ -760,6 +767,12 @@ float GameAPI::Analogs::getState(rawinput::RawInputManager *manager, rawinput::D
|
||||
value = std::clamp(value, 0.f, 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
// delay
|
||||
if (analog.getDelayMs() > 0) {
|
||||
value = analog.getDelayedValue(value);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case rawinput::MIDI: {
|
||||
|
||||
@@ -252,6 +252,8 @@ bool Config::addGame(Game &game) {
|
||||
bool invert = false;
|
||||
bool smoothing = false;
|
||||
int multiplier = 1;
|
||||
bool relative_mode = false;
|
||||
uint32_t delay = 0;
|
||||
tinyxml2::XMLError err1 = gameAnalogNode->QueryIntAttribute("index", &index);
|
||||
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
|
||||
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
|
||||
@@ -259,6 +261,8 @@ bool Config::addGame(Game &game) {
|
||||
gameAnalogNode->QueryBoolAttribute("invert", &invert);
|
||||
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
|
||||
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
|
||||
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
|
||||
gameAnalogNode->QueryUnsignedAttribute("delay_ms", &delay);
|
||||
const char *devid = gameAnalogNode->Attribute("devid");
|
||||
|
||||
if (err1 != tinyxml2::XMLError::XML_SUCCESS || !devid) {
|
||||
@@ -273,6 +277,8 @@ bool Config::addGame(Game &game) {
|
||||
gameAnalogNode->SetAttribute("invert", it.getInvert());
|
||||
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
||||
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
||||
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
||||
gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
|
||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||
} else {
|
||||
it.setIndex(static_cast<unsigned short int>(index));
|
||||
@@ -283,6 +289,8 @@ bool Config::addGame(Game &game) {
|
||||
it.setInvert(invert);
|
||||
it.setSmoothing(smoothing);
|
||||
it.setMultiplier(multiplier);
|
||||
it.setRelativeMode(relative_mode);
|
||||
it.setDelayMs(delay);
|
||||
}
|
||||
} else {
|
||||
gameAnalogNode = this->configFile.NewElement("analog");
|
||||
@@ -294,6 +302,8 @@ bool Config::addGame(Game &game) {
|
||||
gameAnalogNode->SetAttribute("invert", it.getInvert());
|
||||
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
||||
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
||||
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
||||
gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
|
||||
gameAnalogNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
|
||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||
}
|
||||
@@ -447,6 +457,8 @@ bool Config::addGame(Game &game) {
|
||||
gameAnalogNode->SetAttribute("invert", it.getInvert());
|
||||
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
|
||||
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
|
||||
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
|
||||
gameAnalogNode->SetAttribute("delay_ms", it.getDelayMs());
|
||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||
}
|
||||
|
||||
@@ -679,6 +691,8 @@ bool Config::updateBinding(const Game &game, const Analog &analog) {
|
||||
gameAnalogNode->SetAttribute("invert", analog.getInvert());
|
||||
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
|
||||
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
|
||||
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
|
||||
gameAnalogNode->SetAttribute("delay_ms", analog.getDelayMs());
|
||||
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
|
||||
} else {
|
||||
gameAnalogNode = this->configFile.NewElement("analog");
|
||||
@@ -689,6 +703,8 @@ bool Config::updateBinding(const Game &game, const Analog &analog) {
|
||||
gameAnalogNode->SetAttribute("invert", analog.getInvert());
|
||||
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
|
||||
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
|
||||
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
|
||||
gameAnalogNode->SetAttribute("delay_ms", analog.getDelayMs());
|
||||
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
|
||||
gameAnalogsNode->InsertEndChild(gameAnalogNode);
|
||||
}
|
||||
@@ -1125,6 +1141,8 @@ std::vector<Analog> Config::getAnalogs(const std::string &gameName) {
|
||||
bool invert = false;
|
||||
bool smoothing = false;
|
||||
int multiplier = 1;
|
||||
bool relative_mode = false;
|
||||
uint32_t delay = 0;
|
||||
gameAnalogNode->QueryIntAttribute("index", &index);
|
||||
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
|
||||
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
|
||||
@@ -1132,6 +1150,8 @@ std::vector<Analog> Config::getAnalogs(const std::string &gameName) {
|
||||
gameAnalogNode->QueryBoolAttribute("invert", &invert);
|
||||
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
|
||||
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
|
||||
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
|
||||
gameAnalogNode->QueryUnsignedAttribute("delay_ms", &delay);
|
||||
const char *devid = gameAnalogNode->Attribute("devid");
|
||||
|
||||
// create analog and add to list
|
||||
@@ -1143,6 +1163,8 @@ std::vector<Analog> Config::getAnalogs(const std::string &gameName) {
|
||||
analog.setInvert(invert);
|
||||
analog.setSmoothing(smoothing);
|
||||
analog.setMultiplier(multiplier);
|
||||
analog.setRelativeMode(relative_mode);
|
||||
analog.setDelayMs(delay);
|
||||
if (devid) {
|
||||
analog.setDeviceIdentifier(devid);
|
||||
}
|
||||
|
||||
@@ -69,6 +69,8 @@ namespace overlay::windows {
|
||||
el->SetAttribute("invert", analog.invert);
|
||||
el->SetAttribute("smoothing", analog.smoothing);
|
||||
el->SetAttribute("multiplier", analog.multiplier);
|
||||
el->SetAttribute("relative", analog.relative_mode);
|
||||
el->SetAttribute("delay_ms", analog.delay_ms);
|
||||
parent->InsertEndChild(el);
|
||||
}
|
||||
|
||||
@@ -90,6 +92,8 @@ namespace overlay::windows {
|
||||
el->QueryBoolAttribute("invert", &a.invert);
|
||||
el->QueryBoolAttribute("smoothing", &a.smoothing);
|
||||
el->QueryIntAttribute("multiplier", &a.multiplier);
|
||||
el->QueryBoolAttribute("relative", &a.relative_mode);
|
||||
el->QueryUnsignedAttribute("delay_ms", &a.delay_ms);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
@@ -783,42 +783,7 @@ namespace games::iidx {
|
||||
}
|
||||
|
||||
// return higher 8 bit
|
||||
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<std::pair<double, uint8_t>> delay_queue[2];
|
||||
auto &queue = delay_queue[player];
|
||||
|
||||
const auto max_delta_ms =
|
||||
static_cast<double>((player == 0) ? TT_DELAY_P1 : TT_DELAY_P2);
|
||||
|
||||
// always push a new value
|
||||
const auto now = get_performance_milliseconds();
|
||||
queue.push(std::make_pair(now, result));
|
||||
|
||||
// drain the queue down to reasonable length to prevent unconstrained growth
|
||||
// this would accommodate 1 second at ~1000Hz
|
||||
// (in reality all three iidx I/O emulation runs well under 500Hz)
|
||||
while (queue.size() > 1024) {
|
||||
queue.pop();
|
||||
}
|
||||
|
||||
// pop until we find one that falls just under the time threshold
|
||||
while (!queue.empty()) {
|
||||
const auto delta_t = now - queue.front().first;
|
||||
if (delta_t <= max_delta_ms) {
|
||||
break;
|
||||
}
|
||||
queue.pop();
|
||||
}
|
||||
|
||||
result = queue.front().second;
|
||||
}
|
||||
|
||||
return result;
|
||||
return (uint8_t)(ret_value >> 2);
|
||||
}
|
||||
|
||||
unsigned char get_slider(uint8_t slider) {
|
||||
|
||||
@@ -31,8 +31,6 @@ namespace games::iidx {
|
||||
extern bool NATIVE_TOUCH;
|
||||
extern std::optional<std::string> SOUND_OUTPUT_DEVICE;
|
||||
extern std::optional<std::string> ASIO_DRIVER;
|
||||
extern uint32_t TT_DELAY_P1;
|
||||
extern uint32_t TT_DELAY_P2;
|
||||
extern uint8_t DIGITAL_TT_SENS;
|
||||
extern std::optional<std::string> SUBSCREEN_OVERLAY_SIZE;
|
||||
extern std::optional<std::string> SCREEN_MODE;
|
||||
|
||||
@@ -518,12 +518,6 @@ 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();
|
||||
|
||||
@@ -655,30 +655,6 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.game_name = "Beatmania IIDX",
|
||||
.category = "Game Options",
|
||||
},
|
||||
{
|
||||
// IIDXTTDelayP1
|
||||
.title = "IIDX TT Delay ms (Player 1)",
|
||||
.name = "iidxttdelayp1",
|
||||
.desc = "Delays turntable by number of milliseconds. "
|
||||
"Delay will be *at most* the specified period; maximum error is dependent on the game's poll rate. "
|
||||
"As usual, changing any option requires a restart. Default: 0 (no delay).",
|
||||
.type = OptionType::Integer,
|
||||
.setting_name = "(0-500)",
|
||||
.game_name = "Beatmania IIDX",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
// IIDXTTDelayP2
|
||||
.title = "IIDX TT Delay ms (Player 2)",
|
||||
.name = "iidxttdelayp2",
|
||||
.desc = "Delays turntable by number of milliseconds. "
|
||||
"Delay will be *at most* the specified period; maximum error is dependent on the game's poll rate. "
|
||||
"As usual, changing any option requires a restart. Default: 0 (no delay).",
|
||||
.type = OptionType::Integer,
|
||||
.setting_name = "(0-500)",
|
||||
.game_name = "Beatmania IIDX",
|
||||
.category = "Game Options (Advanced)",
|
||||
},
|
||||
{
|
||||
// spice2x_IIDXDigitalTTSensitivity
|
||||
.title = "IIDX Digital TT Sensitivity",
|
||||
|
||||
@@ -69,8 +69,6 @@ namespace launcher {
|
||||
IIDXAsioDriver,
|
||||
IIDXBIO2FW,
|
||||
IIDXTDJMode,
|
||||
IIDXTTDelayP1,
|
||||
IIDXTTDelayP2,
|
||||
spice2x_IIDXDigitalTTSensitivity,
|
||||
IIDXDigitalTTSocd,
|
||||
spice2x_IIDXLDJForce720p,
|
||||
|
||||
@@ -2539,9 +2539,9 @@ namespace overlay::windows {
|
||||
}
|
||||
}
|
||||
|
||||
// hide deadzone for circular analog since it doesn't make any sense
|
||||
// hide deadzone for circular analog since it doesn't make any sense (unless in relative mode)
|
||||
if ((device->type == rawinput::HID || device->type == rawinput::MIDI) &&
|
||||
analog.getType() != GameAPI::Analogs::AnalogType::Circular) {
|
||||
((analog.getType() != GameAPI::Analogs::AnalogType::Circular) || analog.isRelativeMode())) {
|
||||
auto deadzone = analog.getDeadzone();
|
||||
|
||||
// for back compat (before each analog had a type)
|
||||
@@ -2585,16 +2585,46 @@ namespace overlay::windows {
|
||||
if (analog.getType() == GameAPI::Analogs::AnalogType::Circular) {
|
||||
// smoothing
|
||||
bool smoothing = analog.getSmoothing();
|
||||
ImGui::BeginDisabled(analog.isRelativeMode());
|
||||
ImGui::Checkbox("Smooth Axis (adds latency)", &smoothing);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"Apply a moving average algorithm; intended for angular input (knobs, turntables). "
|
||||
"Adds a slight bit of latency to input as the algorithm averages out recent input. "
|
||||
"Only use in dire situations where the input is too jittery for the game.");
|
||||
ImGui::EndDisabled();
|
||||
if (smoothing != analog.getSmoothing()) {
|
||||
analog.setSmoothing(smoothing);
|
||||
}
|
||||
|
||||
// relative input mode
|
||||
bool relative_analog = analog.isRelativeMode();
|
||||
ImGui::Checkbox("Relative Axis", &relative_analog);
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"Use relative directional input instead of positional values. "
|
||||
"Can be used to translate analog sticks to knob input.\n\n"
|
||||
"At default settings, max speed is one revolution per second.\n\n"
|
||||
"Adjust sensitivity to increase/decrease max speed, and deadzone to prevent jitter.");
|
||||
if (relative_analog != analog.isRelativeMode()) {
|
||||
analog.setRelativeMode(relative_analog);
|
||||
}
|
||||
}
|
||||
|
||||
// delay
|
||||
int delay = analog.getDelayMs();
|
||||
if (ImGui::InputInt("Delay (ms)", &delay, 1, 1)) {
|
||||
delay = std::clamp(delay, 0, 250);
|
||||
analog.setDelayMs(delay);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::HelpMarker(
|
||||
"Adds an artificial delay, in milliseconds.\n\n"
|
||||
"Value read by the game engine will be delayed AT MOST this amount of time (coould be slightly less).\n\n"
|
||||
"To minimize quantization error, delay value should be a multiple of the game's poll rate, "
|
||||
"add 0.5ms of buffer, and then round up to the nearest millisecond.\n\n"
|
||||
"If you aren't sure, assume the game polls for input at the same rate as the display FPS."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5406,6 +5436,8 @@ namespace overlay::windows {
|
||||
a.setInvert(ta.invert);
|
||||
a.setSmoothing(ta.smoothing);
|
||||
a.setMultiplier(ta.multiplier);
|
||||
a.setRelativeMode(ta.relative_mode);
|
||||
a.setDelayMs(ta.delay_ms);
|
||||
::Config::getInstance().updateBinding(game, a);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,8 @@ namespace overlay::windows {
|
||||
bool invert = false;
|
||||
bool smoothing = false;
|
||||
int multiplier = 1;
|
||||
bool relative_mode = false;
|
||||
uint32_t delay_ms = 0;
|
||||
|
||||
bool is_device() const { return !device_identifier.empty(); }
|
||||
bool is_unbound() const { return device_identifier.empty() && index == 0xFF; }
|
||||
@@ -87,6 +89,8 @@ namespace overlay::windows {
|
||||
invert = a.getInvert();
|
||||
smoothing = a.getSmoothing();
|
||||
multiplier = a.getMultiplier();
|
||||
relative_mode = a.isRelativeMode();
|
||||
delay_ms = a.getDelayMs();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user