Controller Input for Emulator Development
#1
I've made another attempt to document the controller input system. It came out more verbose and abstract than I wanted, but maybe it'll be useful for someone.

Introduction

Input can come from many types of peripherals such as controllers, arcade cabinets, keyboards and remotes. However, the emulator needs to emulate the peripherals of its game platform. For example, NES emulators can emulate controllers, light guns and flight simulator joysticks. This system translates input from hardware peripherals to emulated ones.

Table of contents

  1. Controller profiles
  2. Joystick drivers
  3. Button maps
  4. Joystick driver fuckery
Reply
#2
1. Controller profiles

Image

Each emulated peripheral has a profile describing its input. Controller is a generic word for these peripherals, and this is the name shown in the GUI.

Each controller has parts that generate input, like buttons, keys, triggers, analog sticks and accelerometers. These parts are called features of the controller.

Features generate input in several different ways. For example, a button is either 1 or 0. Analog sticks, on the other hand, have two degrees of freedom. They contain two values that can range from -1.0 (fully down/left) to 1.0 (fully up/right).

Features can also receive input. Rumble motors and other haptic parts accept digital or analog input.

Features are grouped by the type of input they generate/accept. For example:

Scalar features
  • Regular buttons generate a single number (either 0 or 1), so these are called scalar features.
  • Likewise, triggers and pressure-sensitive buttons generate a single number (analog value between 0.0 and 1.0, inclusive). These are also called scalar features.
  • For simplicity, keys are considered buttons
  • D-pads, also called hats, are treated as four buttons, so they result in four scalar features.

Vector features
  • Analog sticks generate two analog values, so these are called vector features.
  • Likewise, accelerometers have an X, Y and Z axis and are also called vector features.

Haptic features
  • Motors are technically scalar features, but because they accept input instead of generating it, they're usually processed in a different part of the code. For clarity, they are just called haptic features.
Reply
#3
2. Joystick drivers

Unfortunately, none of the input provided by any joystick driver has information about the kind of features it belongs to. Drivers simply provide a long list of values. Generally, these are bools or floats, but some interfaces use enums, like the directions on a d-pad.

To connect this information to the features of a controller profile, it is split into elements that better translate to controller features. These elements are called driver primitives.

2.1. Types of driver primitives

Buttons

Consider a bool reported by the driver. This probably belongs to something that can be pressed, so it's called a button. A bool can't belong to multiple features, so it is a driver primitive.

Hat directions

Some drivers use enums or bit flags to report hat presses. In Kodi, hats are treated like four separate buttons for simplicity. Hat enums can belong to four features, so they contain four driver primitives, one for each direction.

Semiaxis directions

A float is a little trickier. The immediate assumption is an axis of an analog stick or accelerometer. However, in DirectInput, triggers are combined into a single float. Therefore, a float can map to two features, so each half of the axis (called a semiaxis) is a driver primitive.

This has interesting implications. An analog stick has two axes, which is four driver primitives. Each semiaxis can map to a different feature, so the analog stick is able to emulate the four buttons of a d-pad, or the N64's C buttons.
Reply
#4
3. Button maps

Now we return to controller profiles. Each emulator requires its own set of controllers, each with their list of features. These are mapped to the underlying driver primitives provided by the joystick driver using a button map.

In the code, the button map is abstracted away behind a button map interface. This additional abstraction adds some code, but it allows button maps to be managed by an add-on.

The current add-on for button maps, peripheral.joystick, can only recite button maps it has been given. However, it could be made smarter by using existing button map data. For example, if the add-on knows the most popular way to map a 360 controller to a SNES controller, it can generate a SNES button map knowing only the 360 one.
Reply
#5
4. Joystick driver fuckery

Of course, joystick drivers have many quirks that greatly complicate things. So much so that they deserve their own chapter. Here's a list of some of the quirks I've encountered:

Combined triggers

DirectInput combines left and right triggers into a single axis. They are combined using the strategy in Chapter 4: Dimension Reduction.

Kodi solves this by splitting the axis into two semiaxes, as explained in Chapter 2: Joystick drivers. Each semiaxis is mapped to its own trigger.

Anomalous triggers

Not all triggers start at 0.0 and travel to 1.0 (or -1.0 in DirectInput). Some triggers start at 1.0 or -1.0, and travel to 0.0 or to the opposite unit. These are called anomalous triggers. These triggers have two properties:
  • Center - The theory here is that initial perturbations are minimal. This means that the center is determined by rounding the first value to the closest int.
  • Range - The range can be half range (assumed) or full range (detected when a value has the opposite sign)

Anomalous triggers are detected by the FSM in AnomalousTriggerFilter.cpp.

Discrete D-pads

Instead of four buttons or a hat enum, D-pads can sometimes be reported as floats that use the discrete values -1.0, 0.0 and 1.0. Fortunately, because analog sticks can emulate D-pads, we can simply treat the discrete D-pad as an analog stick.

Repeated input

Some buttons generate two input events. For example, some hats operate as four digital buttons AND as a discrete D-pad. This is solved via a "cooldown" while mapping, which ignores any input for around 50ms after a button is mapped.

Hat enums

I consider hat enums a quirk because it just makes so much more sense to represent them using four buttons. It doesn't even guarantee mutual exclusion between opposite directions, as this can be violated by a flag with the improper bits set.

Pressure-sensitive buttons

Pressure-sensitive buttons can be reported as an analog axis instead of a digital value.

Incomplete information

Pertinent info (name, USB VID and PID, etc) might be missing, making it hard to identify the correct button map.
Reply

Logout Mark Read Team Forum Stats Members Help
Controller Input for Emulator Development1