Learn only extremes, not the middle

Callers (i.e., OEHIDEvent) now pass an expected middle value to the
scaledValue function, so scaledValue doesn't have to learn the middle.  We
assume the middle is in fact the middle of the IOKit range for the HID
class.

This fixes the drift problem where the character would sometimes walk
slowly if the user had only fully trained the calibration in one direction
on an axis.

The only remaining limitation at this point is: you must run before you
can walk.  If you only ever push the joystick to 25%, the scaler will
assume 25% is the max and will scale it to +1.0 instead of +0.25.  I think
it's rare users will even notice (most games push the analog stick to its
extremes most of the time), and if they do, pushing the stick to its
extreme just once solves the problem.
This commit is contained in:
Patrick Reynolds
2019-10-11 02:37:41 -04:00
parent 356aa8d89b
commit fbb49c7a04
4 changed files with 17 additions and 24 deletions
+1 -1
View File
@@ -85,7 +85,7 @@ extern NSString *const OEDeviceHandlerPlaceholderOriginalDeviceDidBecomeAvailabl
- (CGFloat)deadZoneForControlDescription:(OEControlDescription *)controlDesc;
- (void)setDeadZone:(CGFloat)deadZone forControlDescription:(OEControlDescription *)controlDesc;
- (CGFloat)scaledValue:(CGFloat)rawValue forAxis:(OEHIDEventAxis)axis controlCookie:(NSUInteger)cookie;
- (CGFloat)scaledValue:(CGFloat)rawValue forAxis:(OEHIDEventAxis)axis controlCookie:(NSUInteger)cookie withMiddle:(CGFloat)middle;
@end
+8 -17
View File
@@ -40,15 +40,13 @@ NS_ASSUME_NONNULL_BEGIN
#define NO __objc_no
#endif
#define LEARN_COUNT 8
NSString *const OEDeviceHandlerDidReceiveLowBatteryWarningNotification = @"OEDeviceHandlerDidReceiveLowBatteryWarningNotification";
NSString *const OEDeviceHandlerPlaceholderOriginalDeviceDidBecomeAvailableNotification = @"OEDeviceHandlerPlaceholderOriginalDeviceDidBecomeAvailableNotification";
static NSString *const OEDeviceHandlerUniqueIdentifierKey = @"OEDeviceHandlerUniqueIdentifier";
typedef struct {
int min, max, deccount, inccount;
int min, max;
} OEAutoCalibration;
@interface OEDeviceHandler ()
@@ -193,8 +191,6 @@ typedef struct {
OEAutoCalibration newCal;
newCal.min = 100000;
newCal.max = -100000;
newCal.inccount = 0;
newCal.deccount = 0;
cal = [NSValue valueWithBytes:&newCal objCType:@encode(OEAutoCalibration)];
[_calibrations setObject:cal forKey:@(controlCookie)];
return newCal;
@@ -220,7 +216,7 @@ typedef struct {
_deadZones[@([[controlDesc genericEvent] cookie])] = @(deadZone);
}
- (CGFloat)scaledValue:(CGFloat)rawValue forAxis:(OEHIDEventAxis)axis controlCookie:(NSUInteger)cookie
- (CGFloat)scaledValue:(CGFloat)rawValue forAxis:(OEHIDEventAxis)axis controlCookie:(NSUInteger)cookie withMiddle:(CGFloat)middle
{
FIXME("move all scaling logic here from OEHIDEvent in a *clean* way");
OEAutoCalibration cal = [self calibrationForControlCookie:cookie];
@@ -228,34 +224,29 @@ typedef struct {
if (rawValue < cal.min)
{
cal.min = rawValue;
cal.deccount++;
changed = YES;
}
if (rawValue > cal.max)
{
cal.max = rawValue;
cal.inccount++;
changed = YES;
}
if (changed)
{
NSLog(@"AutoCal: cookie=%lu rawValue=%f min=%d count=%d max=%d count=%d",
cookie, rawValue, cal.min, cal.deccount, cal.max, cal.inccount);
NSLog(@"AutoCal: cookie=%lu rawValue=%f min=%d max=%d",
cookie, rawValue, cal.min, cal.max);
NSValue *val = [NSValue valueWithBytes:&cal objCType:@encode(OEAutoCalibration)];
[_calibrations setObject:val forKey:@(cookie)];
}
NSInteger middleValue = (cal.max + cal.min) / 2 + 1;
if(cal.min >= 0)
{
cal.min -= middleValue;
rawValue -= middleValue;
cal.max -= middleValue;
cal.min -= middle;
rawValue -= middle;
cal.max -= middle;
}
if ((rawValue < 0 && cal.deccount < LEARN_COUNT)
|| (rawValue > 0 && cal.inccount < LEARN_COUNT))
if (cal.min == cal.max)
return -100; // not enough samples
if(rawValue < 0) return -rawValue / (CGFloat)cal.min;
+5 -3
View File
@@ -741,13 +741,15 @@ static CGEventSourceRef _keyboardEventSource;
{
case OEHIDEventTypeAxis :
{
NSInteger min = IOHIDElementGetLogicalMin(elem);
NSInteger max = IOHIDElementGetLogicalMax(elem);
CGFloat deadZone = [aDeviceHandler deadZoneForControlCookie:_cookie];
CGFloat scaledValue = [aDeviceHandler scaledValue:value forAxis:_data.axis.axis controlCookie:_cookie];
CGFloat scaledValue = [aDeviceHandler scaledValue:value forAxis:_data.axis.axis controlCookie:_cookie withMiddle:(min+max)/2+1];
if (scaledValue < -1.001 || scaledValue > 1.001) {
/* device handler does not handle scaling */
scaledValue = _OEScaledValueForAxis(IOHIDElementGetLogicalMin(elem),
scaledValue = _OEScaledValueForAxis(min,
value,
IOHIDElementGetLogicalMax(elem));
max);
}
if(-deadZone <= scaledValue && scaledValue <= deadZone)
@@ -462,7 +462,7 @@ static OEHACProControllerStickCalibration OEHACConvertCalibration(
}
- (CGFloat)scaledValue:(CGFloat)rawValue forAxis:(OEHIDEventAxis)axis controlCookie:(NSUInteger)cookie
- (CGFloat)scaledValue:(CGFloat)rawValue forAxis:(OEHIDEventAxis)axis controlCookie:(NSUInteger)cookie withMiddle:(CGFloat)middle
{
OEHACProControllerAxisCalibration *selectedCalibration;
switch (axis) {
@@ -587,14 +587,14 @@ static OEHACProControllerStickCalibration OEHACConvertCalibration(
OEHIDEvent *event;
cookie = [OESwitchProControllerHIDDeviceParser _cookieFromUsage:xaxis];
value = [self scaledValue:stickData.x forAxis:xaxis controlCookie:cookie];
value = [self scaledValue:stickData.x forAxis:xaxis controlCookie:cookie withMiddle:0];
if (fabs(value) < [self deadZoneForControlCookie:cookie])
value = 0;
event = [OEHIDEvent axisEventWithDeviceHandler:self timestamp:now axis:xaxis value:value cookie:cookie];
[self dispatchEvent:event];
cookie = [OESwitchProControllerHIDDeviceParser _cookieFromUsage:yaxis];
value = [self scaledValue:stickData.y forAxis:yaxis controlCookie:cookie];
value = [self scaledValue:stickData.y forAxis:yaxis controlCookie:cookie withMiddle:0];
if (fabs(value) < [self deadZoneForControlCookie:cookie])
value = 0;
event = [OEHIDEvent axisEventWithDeviceHandler:self timestamp:now axis:yaxis value:value cookie:cookie];