Turning Mouse/Spinner Into Steering Analog Input for MAME, Arcade and Other Games

It is possible to turn your mouse or spinner such as the Ultimarc SpinTrak into a virtual analog device detected by various emulator (arcade) as well as PC games.

Only two softwares are needed:
  • vJoy - Create virtual joystick on your PC. You'll need a feeder application to provide input to the virtual joystick.
  • FreePIE - Emulate a virtual joystick and send the key buttons to vJoy.


Download vJoy here.

During installing it will prompt you to install a driver. Click YES and you'll see a new vJoy device in the Device Manager.

A new vJoy Device found in Device Manager upon successful installing of vJoy driver.

vJoy program folder.

Download FreePIE here.

FreePIE main UI window.

Launch FreePIE and copy and paste the following Python script. Note that this is not my work and you'll find the original post written by a guy called Skagen here.

if starting:    
    system.setThreadTiming(TimingTypes.HighresSystemTimer)
    system.threadExecutionInterval = 5
    
    def set_button(button, key):
        if keyboard.getKeyDown(key):
            v.setButton(button, True)
        else:
            v.setButton(button, False)
    
    def calculate_rate(max, time):
        if time > 0:
            return max / (time / system.threadExecutionInterval)
        else:
            return max

    int32_max = (2 ** 14) - 1
    int32_min = (( 2** 14) * -1) + 1
    
    v = vJoy[0]
    v.x, v.y, v.z, v.rx, v.ry, v.rz, v.slider, v.dial = (int32_min,) * 8

    # =============================================================================================
    # Axis inversion settings (multiplier): normal = 1; inverted = -1
    # =============================================================================================
    global throttle_inversion, braking_inversion, clutch_inversion
    throttle_inversion = 1
    braking_inversion = 1
    clutch_inversion = 1
    
    # =============================================================================================
    # Mouse settings
    # =============================================================================================
    global mouse_sensitivity, sensitivity_center_reduction
    mouse_sensitivity = 25.0
    sensitivity_center_reduction = 5.0
    
    # =============================================================================================
    # Ignition cut settings
    # =============================================================================================
    global ignition_cut_time, ignition_cut_elapsed_time
    ignition_cut_enabled = True
    ignition_cut_time = 100
    ignition_cut_elapsed_time = 0
    
    global ignition_cut, ignition_cut_released
    # Init values, do not change
    ignition_cut = False
    ignition_cut_released = True
    
    # =============================================================================================
    # Steering settings
    # =============================================================================================
    global steering, steering_max, steering_min, steering_center_reduction    
    # Init values, do not change
    steering = 0.0
    steering_max = float(int32_max)
    steering_min = float(int32_min)
    steering_center_reduction = 1.0
    
    # =============================================================================================
    # Throttle settings
    # =============================================================================================
    global throttle_blip_enabled
    throttle_blip_enabled = True
    
    # In milliseconds
    throttle_increase_time = 100
    throttle_increase_time_after_ignition_cut = 0
    throttle_increase_time_blip = 50
    throttle_decrease_time = 100
    
    global throttle, throttle_max, throttle_min
    # Init values, do not change
    throttle_max = int32_max * throttle_inversion
    throttle_min = int32_min * throttle_inversion
    throttle = throttle_min
    
    global throttle_increase_rate, throttle_decrease_rate
    # Set throttle behaviour with the increase and decrease time,
    # the actual increase and decrease rates are calculated automatically
    throttle_increase_rate = calculate_rate(throttle_max, throttle_increase_time)
    throttle_increase_rate_after_ignition_cut = calculate_rate(throttle_max, throttle_increase_time_after_ignition_cut) 
    throttle_increase_rate_blip = calculate_rate(throttle_max, throttle_increase_time_blip)
    throttle_decrease_rate = calculate_rate(throttle_max, throttle_decrease_time) * -1
    
    # =============================================================================================
    # Braking settings
    # =============================================================================================
    # In milliseconds
    braking_increase_time = 100
    braking_decrease_time = 100
    
    global braking, braking_max, braking_min
    # Init values, do not change
    braking_max = int32_max * braking_inversion
    braking_min = int32_min * braking_inversion
    braking = braking_min
    
    global braking_increase_rate, braking_decrease_rate
    # Set braking behaviour with the increase and decrease time,
    # the actual increase and decrease rates are calculated automatically
    braking_increase_rate = calculate_rate(braking_max, braking_increase_time)
    braking_decrease_rate = calculate_rate(braking_max, braking_decrease_time) * -1
    
    # =============================================================================================
    # Clutch settings
    # =============================================================================================   
    # In milliseconds
    clutch_increase_time = 0
    clutch_decrease_time = 50
    
    global clutch, clutch_max, clutch_min
    # Init values, do not change
    clutch_max = int32_max * clutch_inversion
    clutch_min = int32_min * clutch_inversion
    clutch = clutch_min
    
    global clutch_increase_rate, clutch_decrease_rate
    # Set clutch behaviour with the increase and decrease time,
    # the actual increase and decrease rates are calculated automatically
    clutch_increase_rate = calculate_rate(clutch_max, clutch_increase_time)
    clutch_decrease_rate = calculate_rate(clutch_max, clutch_decrease_time) * -1
    
    # =============================================================================================
    # Button and key assignments
    # =============================================================================================
    global clutch_key
    clutch_key = Key.C
    
    global shift_up_key, shift_up_button
    shift_up_key = Key.W 
    shift_up_button = 1
    
    global shift_down_key, shift_down_button
    shift_down_key = Key.S
    shift_down_button = 2

    global look_left_key, look_left_button
    look_left_key = Key.A
    look_left_button = 3

    global look_right_key, look_right_button
    look_right_key = Key.D
    look_right_button = 4

    global look_back_key, look_back_button
    look_back_key = Key.X
    look_back_button = 5

    global change_view_key, change_view_button
    change_view_key = Key.V 
    change_view_button = 6

    global indicator_left_key, indicator_left_button
    indicator_left_key = Key.Q
    indicator_left_button = 7

    global indicator_right_key, indicator_right_button
    indicator_right_key = Key.E
    indicator_right_button = 8

# =================================================================================================
# LOOP START
# =================================================================================================

# =================================================================================================
# Steering logic
# =================================================================================================
if steering > 0:
    steering_center_reduction = sensitivity_center_reduction ** (1 - (steering / steering_max))
elif steering < 0:
    steering_center_reduction = sensitivity_center_reduction ** (1 - (steering / steering_min))

steering = steering + ((float(mouse.deltaX) * mouse_sensitivity) / steering_center_reduction)

if steering > steering_max:
    steering = steering_max
elif steering < steering_min:
    steering = steering_min

v.x = int(round(steering))

# =================================================================================================
# Clutch logic
# =================================================================================================
if (throttle_blip_enabled and keyboard.getKeyDown(shift_down_key)) or (ignition_cut_enabled and ignition_cut_released and keyboard.getKeyDown(shift_up_key)) or keyboard.getKeyDown(clutch_key):
    clutch = clutch_max
else:
    clutch = clutch + clutch_decrease_rate

if clutch > clutch_max * clutch_inversion:
    clutch = clutch_max * clutch_inversion
elif clutch < clutch_min * clutch_inversion:
    clutch = clutch_min * clutch_inversion

v.slider = clutch

# =================================================================================================
# Throttle logic
# =================================================================================================
if ignition_cut_enabled and ignition_cut and ignition_cut_elapsed_time < ignition_cut_time:
    ignition_cut_elapsed_time = ignition_cut_elapsed_time + system.threadExecutionInterval

if ignition_cut_enabled and not ignition_cut_released and keyboard.getKeyUp(shift_up_key):
    ignition_cut_released = True

if throttle_blip_enabled and ((ignition_cut_enabled and not ignition_cut) or (not ignition_cut_enabled)) and keyboard.getKeyDown(shift_down_key):
    # Throttle blip
    throttle = throttle + throttle_increase_rate_blip
elif ignition_cut_enabled and ignition_cut_released and keyboard.getKeyDown(shift_up_key):
    # Ignition cut
    throttle = throttle_min
    ignition_cut = True
    ignition_cut_released = False
    ignition_cut_elapsed_time = 0
elif mouse.leftButton:
    if ignition_cut_enabled and ignition_cut and ignition_cut_elapsed_time >= ignition_cut_time:
        throttle = throttle_max
    else:
        throttle = throttle + throttle_increase_rate
else:
    throttle = throttle + throttle_decrease_rate

if ignition_cut_enabled and ignition_cut and ignition_cut_elapsed_time >= ignition_cut_time:
    ignition_cut = False
    ignition_cut_elapsed_time = 0

if throttle > throttle_max * throttle_inversion:
    throttle = throttle_max * throttle_inversion
elif throttle < throttle_min * throttle_inversion:
    throttle = throttle_min * throttle_inversion

v.y = throttle

# =================================================================================================
# Braking logic
# =================================================================================================
if mouse.rightButton:
    braking = braking + braking_increase_rate
else:
    braking = braking + braking_decrease_rate

if braking > braking_max * braking_inversion:
    braking = braking_max * braking_inversion
elif braking < braking_min * braking_inversion:
    braking = braking_min * braking_inversion

v.rz = braking

# =================================================================================================
# Buttons post-throttle logic
# =================================================================================================
set_button(look_left_button, look_left_key)
set_button(look_right_button, look_right_key)
set_button(look_back_button, look_back_key)
set_button(change_view_button, change_view_key)
set_button(indicator_left_button, indicator_left_key)
set_button(indicator_right_button, indicator_right_key)

# =================================================================================================
# PIE diagnostics logic
# =================================================================================================
diagnostics.watch(v.x)
diagnostics.watch(v.y)
diagnostics.watch(v.rz)
diagnostics.watch(v.slider)
diagnostics.watch(steering_center_reduction)
diagnostics.watch(throttle_blip_enabled)
diagnostics.watch(ignition_cut_enabled)

Save the file as mousesteering.py

Press F5 or Script > Run Script.

You can verify that the input is working by seeing the Watch tab at the bottom or launching vJoy Monitor

A script running on FreePIE.

vJoy Monitor that monitors our feeder application, i.e. the FreePIE script

The mousesteering.py Script

The script accepts the following analog inputs (i.e. the value will increasing more the longer you hold down the analog key). There are also various key inputs for Z, Rx, Ry, sl0, and sl1. But I find that the X-Axis is enough to simulate the steering wheels. You might use other analog keys for analog pedal, brake, etc.
  • X- Axis - Move mouse device/spinner to the left
  • X+ Axis - Move mouse device/spinner to the right
  • Y - Left mouse button
  • Rz - Right mouse button

Example - Sega Model 2 Emulator
Here is an example of setting the analog control for Daytona USA for Model 2 Arcade emulator.
Configuring vJoy X-Axis as the steering control in Daytona 2 USA emulated via Model 2 Emulator.



Enjoy using your mouse or spinner in various system!

Post a Comment

If you found this article or post helpful to you, feel free to enter your comments below ;)