mirror of
https://github.com/OpenEmu/OpenEmu-SDK.git
synced 2025-11-01 10:15:12 +00:00
Automatically learn analog-control calibration
The default OEHIDEvent class assumes that controller devices will always use the full range specified by IOKit. For example, if IOKit maps a control to the range 0..255, OEHIDEvent assumes that every attached device will return 0 and 255 when pushed to its physical limits. Many N64 controller adapters apparently do not behave this way. They return less than the full range of values IOKit expects, leading to characters and vehicles being unable to use their full range of motion. Link can only tiptoe in Zelda games, for example. This patch teaches the OEDeviceHandler class to automatically learn the bounds of each analog axis, individually. So if the actual physical range of the X axis of an N64 controller is 49..192, all logical values will be scaled based on that range instead of 0..255. This, I think, is tidier than adding a calibration interface. Why ask the user to configure what the software can learn for itself? One limitation is that OEDeviceHandler must relearn its range each time you start the the emulator. That means until you've pushed the joystick through its full range of motion, the scaling will behave in unexpected ways. For example, if you push an X axis to the right, the learned range might be 128..192. That rightward push will correctly be scaled to +1.0, but when you return the stick to the center, 128 will now scale to -1.0. Pushing the stick to the left fixes this by teaching the program that the minimum is (e.g.) 49 and not 128. Put simply, the user must push all analog axes through their full range of motion at least once when the emulator starts. There's room for improvement on this "relearn" issue, which I'll address in a follow-up commit.
This commit is contained in:
@@ -49,6 +49,8 @@ static NSString *const OEDeviceHandlerUniqueIdentifierKey = @"OEDeviceHandlerUni
|
||||
{
|
||||
OEDeviceDescription *_deviceDescription;
|
||||
NSMutableDictionary *_deadZones;
|
||||
NSMutableDictionary *_autoCalMins;
|
||||
NSMutableDictionary *_autoCalMaxs;
|
||||
}
|
||||
|
||||
@property(readwrite) NSUInteger deviceNumber;
|
||||
@@ -71,6 +73,8 @@ static NSString *const OEDeviceHandlerUniqueIdentifierKey = @"OEDeviceHandlerUni
|
||||
FIXME("Save default dead zones in user defaults based on device description.");
|
||||
_defaultDeadZone = 0.125;
|
||||
_deadZones = [[NSMutableDictionary alloc] init];
|
||||
_autoCalMins = [[NSMutableDictionary alloc] init];
|
||||
_autoCalMaxs = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -177,6 +181,20 @@ static NSString *const OEDeviceHandlerUniqueIdentifierKey = @"OEDeviceHandlerUni
|
||||
return deadZone != nil ? [deadZone doubleValue] : _defaultDeadZone;
|
||||
}
|
||||
|
||||
- (CGFloat)autoCalMinForControlCookie:(NSUInteger)controlCookie;
|
||||
{
|
||||
NSNumber *autoCalMin = _autoCalMins[@(controlCookie)];
|
||||
|
||||
return autoCalMin != nil ? [autoCalMin doubleValue] : 100000;
|
||||
}
|
||||
|
||||
- (CGFloat)autoCalMaxForControlCookie:(NSUInteger)controlCookie;
|
||||
{
|
||||
NSNumber *autoCalMax = _autoCalMaxs[@(controlCookie)];
|
||||
|
||||
return autoCalMax != nil ? [autoCalMax doubleValue] : -100000;
|
||||
}
|
||||
|
||||
- (CGFloat)deadZoneForControlDescription:(OEControlDescription *)controlDesc;
|
||||
{
|
||||
return [self deadZoneForControlCookie:[[controlDesc genericEvent] cookie]];
|
||||
@@ -193,7 +211,37 @@ static NSString *const OEDeviceHandlerUniqueIdentifierKey = @"OEDeviceHandlerUni
|
||||
- (CGFloat)scaledValue:(CGFloat)rawValue forAxis:(OEHIDEventAxis)axis controlCookie:(NSUInteger)cookie
|
||||
{
|
||||
FIXME("move all scaling logic here from OEHIDEvent in a *clean* way");
|
||||
return -100;
|
||||
NSInteger min = [self autoCalMinForControlCookie:cookie];
|
||||
NSInteger max = [self autoCalMaxForControlCookie:cookie];
|
||||
BOOL log = NO;
|
||||
if (rawValue < min)
|
||||
{
|
||||
_autoCalMins[@(cookie)] = @(rawValue);
|
||||
log = YES;
|
||||
}
|
||||
if (rawValue > max)
|
||||
{
|
||||
_autoCalMaxs[@(cookie)] = @(rawValue);
|
||||
log = YES;
|
||||
}
|
||||
if (log)
|
||||
NSLog(@"AutoCal: cookie=%lu rawValue=%f min=%ld max=%ld", cookie, rawValue, min, max);
|
||||
if (min == max)
|
||||
return -100; // first sample
|
||||
|
||||
NSInteger middleValue = (max + min) / 2 + 1;
|
||||
|
||||
if(min >= 0)
|
||||
{
|
||||
min -= middleValue;
|
||||
rawValue -= middleValue;
|
||||
max -= middleValue;
|
||||
}
|
||||
|
||||
if(rawValue < 0) return -rawValue / (CGFloat)min;
|
||||
else if(rawValue > 0) return rawValue / (CGFloat)max;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user