December 09, 2019, 08:48:30 AM

Author Topic: [bmx] Rumble effects for Xbox 360 controller by BlitzSupport [ 1+ years ago ]  (Read 4062 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
Title : Rumble effects for Xbox 360 controller
Author : BlitzSupport
Posted : 1+ years ago

Description : I didn't realise I hadn't posted <a href="../Community/posts44f5.html?topic=83365" target="_blank">this code[/url] in the Code Archives.

The code below demonstrates basic rumble support for the Xbox 360 controller. Just copy and paste everything above the demo code into your own program, and call the functions listed at the top. (I haven't made this Strict-friendly, so you'll have to tweak it if your own code is Strict/SuperStrict.)

Try changing the controller port number if it doesn't work for you.

There's also a demo of vibration based on the analog sticks' vertical positions in my first post.


Code :
Code: BlitzMax
  1. ' -----------------------------------------------------------------------------
  2. ' Xbox 360 controller vibration - demo at bottom of code!
  3. ' -----------------------------------------------------------------------------
  4.  
  5. ' Public domain!
  6.  
  7. ' -----------------------------------------------------------------------------
  8. ' Available functions...
  9. ' -----------------------------------------------------------------------------
  10.  
  11. ' InitXInput ()
  12.  
  13. '       You must call this before any of the other functions! You should also
  14. '       check the returned result. If 0, the other functions should not be called
  15. '       or your app will die. This usually means the DLL isn't installed and you
  16. '       should reinstall the Xbox 360 controller drivers.
  17.  
  18. ' Rumble (port, rumble_left, rumble_right)
  19.  
  20. '       Pass port number (0-3) and vibration values from 0-65535. The left motor
  21. '       handles low-frequency vibration and the right motor handles high-
  22. '       frequency vibration.
  23.  
  24. ' DisableRumble ()
  25.  
  26. '       Call this before exiting your program, to make sure all vibration is
  27. '       disabled, or when your app is suspended.
  28.  
  29. '       On XInput version 1.0, this just stops all vibrations. On newer versions,
  30. '       the controller outputs no vibration in response to Vibration () calls, and
  31. '       also returns neutral values for buttons, sticks, etc.
  32.  
  33. ' EnableRumble (state)
  34.  
  35. '       This can be used to re-enable controller input/output, including vibrations.
  36. '       Call this when your program is reactivated after having been suspended.
  37. '       Vibration is enabled by default.
  38.  
  39. '       NOTE: This is not available in the first release of XInput, so will simply be
  40. '       ignored if called against version 1.0.
  41.  
  42. ' -----------------------------------------------------------------------------
  43. ' Support functions. Most of this just checks carefully for the right DLL!
  44. ' -----------------------------------------------------------------------------
  45.  
  46. Function GetSystemFolder$ ()
  47.  
  48. ?Win32 ' Windows only!
  49.  
  50. '       Function ArrayFromString:Byte [] (source$)
  51. '               Local newarray:Byte [Len (source$)]
  52. '               MemCopy (newarray, source.ToCString (), Len (source$))
  53. '               Return newarray
  54. '       End Function
  55.  
  56.         Function StringFromArray$ (source:Byte [])
  57.                 Return String.FromCString (source)
  58.         End Function
  59.  
  60.         Local GetSystemDirectory_ (location:Byte Ptr, pathsize) "win32"
  61.         kernel32 = LoadLibraryA ("kernel32.dll")
  62.  
  63.         Local patharray:Byte [260]
  64.  
  65.         If kernel32
  66.                 GetSystemDirectory_ = GetProcAddress (kernel32, "GetSystemDirectoryA")
  67.                 GetSystemDirectory_ (patharray, 260)
  68.         EndIf
  69.  
  70.         Return StringFromArray (patharray)
  71.  
  72. ?
  73.  
  74. End Function
  75.  
  76. ' -----------------------------------------------------------------------------
  77. ' Find most recent XInput DLL...
  78. ' -----------------------------------------------------------------------------
  79.  
  80. Function GetXInputDLL$ ()
  81.  
  82.         x$ = "xinput9_1_0.dll"
  83.  
  84.         If FileType (GetSystemFolder () + "" + x$) = 0
  85.                 x$ = "" ' Old DLL Not found...
  86.         EndIf
  87.  
  88.         dir = ReadDir (GetSystemFolder ())
  89.  
  90.         If dir
  91.  
  92.                 Repeat
  93.  
  94.                         f$ = Lower (NextFile (dir))
  95.  
  96.                         If f$.StartsWith ("xinput") And f$.EndsWith (".dll")
  97.                                
  98.                                 ' Should probably check Mid values are 0-9 here...
  99.                                
  100.                                 version = Int (Mid (f$, 7, 1) + Mid (f$, 9, 1))
  101.                                
  102.                                 ' Result is 10 for 1.0, 11 for 1.1, 12 for 1.2, etc...
  103.                                
  104.                                 If version = 91 ' Likely to be 1.0 -- see below!
  105.                                        
  106.                                         ' Non-public versions might return 9, 8, etc for 0.9, 0.8, etc,
  107.                                         ' but 91 is the earliest public version! Exercise for the reader...
  108.                                        
  109.                                         If Mid (f$, 11, 1) = "0"
  110.                                                 version = 10 ' Ha! Stupid old "xinput9_1_0.dll" found -- really version 1.0
  111.                                         EndIf
  112.                                 EndIf
  113.        
  114.                                 If version > hiversion
  115.                                         hiversion = version
  116.                                         xinput$ = f$
  117.                                 EndIf
  118.  
  119.                         EndIf
  120.  
  121.                 Until f$ = ""
  122.  
  123.                 CloseDir dir
  124.  
  125.         EndIf
  126.  
  127.         Return xinput$
  128.        
  129. End Function
  130.  
  131. ' -----------------------------------------------------------------------------
  132. ' XInput function pointers...
  133. ' -----------------------------------------------------------------------------
  134.  
  135. ' For more information:
  136.  
  137. ' <a href="http://msdn.microsoft.com/en-us/library/bb173048(VS.85).aspx" target="_blank">http://msdn.microsoft.com/en-us/library/bb173048(VS.85).aspx</a>
  138.  
  139. Global XInput_SetState (port, hilo:Byte Ptr) "win32"
  140. Global XInput_Enable (enable) "win32"
  141.  
  142. ' -----------------------------------------------------------------------------
  143. ' Wrapper functions...
  144. ' -----------------------------------------------------------------------------
  145.  
  146. Global XInput10 = 0
  147.  
  148. Function InitXInput ()
  149.  
  150.         x$ = GetXInputDLL ()
  151.         xinput = LoadLibraryA (x$)
  152.        
  153.         If xinput
  154.  
  155.                 XInput_SetState = GetProcAddress (xinput, "XInputSetState")
  156.                 XInput_Enable = GetProcAddress (xinput, "XInputEnable")
  157.  
  158.                 ok = True
  159.                
  160.                 If XInput_Enable = Null
  161.                
  162.                         If x$ = "xinput9_1_0.dll"
  163.                        
  164.                                 ' Not in 1.0. Can continue but EnableController won't do anything...
  165.                                 XInput10 = True ' Can check this from other parts of the program if necessary...
  166.                                
  167.                                 Print ""
  168.                                 Print "XInputEnable not available in XInput 1.0 -- update Xbox controller driver!~nContinuing, but EnableController will have no effect..."
  169.                                
  170.                         Else
  171.                                 ok = False ' Not found at all!
  172.                         EndIf
  173.                        
  174.                 EndIf
  175.                
  176.                 If XInput_SetState = Null
  177.                         ok = False
  178.                 EndIf
  179.        
  180.         Else
  181.                 ok = False
  182.         EndIf
  183.  
  184.         If ok Return xinput
  185.                
  186. End Function
  187.  
  188. ' Vibration continues until next Rumble call!
  189.  
  190. Function Rumble (port, rumble_left:Short, rumble_right:Short)
  191.  
  192.         ' From <a href="http://msdn.microsoft.com/en-us/library/bb174835(VS.85).aspx" target="_blank">http://msdn.microsoft.com/en-us/library/bb174835(VS.85).aspx</a> :
  193.        
  194.         ' "The left motor is the low-frequency rumble motor. The right motor is the high-frequency rumble motor."
  195.         ' "The two motors are not the same, and they create different vibration effects."
  196.  
  197.         ' Combine two shorts into an integer...
  198.  
  199.         freqs = (rumble_right Shl 16) | rumble_left
  200.  
  201.         XInput_SetState (port, Varptr freqs)
  202.        
  203. End Function
  204.  
  205. ' Call DisableRumble before exiting program, or when app is suspended, to stop all vibrations.
  206.  
  207. ' NOTE! This also affects return results for controller inputs! Makes everything return
  208. ' as 'neutral', ie. no buttons pressed, no stick movement, etc, regardless of what
  209. ' the player may be doing with the controller. (Doesn't apply to XInput 1.0.)
  210.  
  211. Function DisableRumble ()
  212.  
  213.         ' The For loop below is intended to stop the vibrations for XInput 1.0, but although
  214.         ' XInput_Enable (0) stops vibration for later versions under normal usage, it appears to
  215.         ' have no effect if the controller is still vibrating after a program has terminated
  216.         ' abnormally, hence I'm calling this regardless...
  217.        
  218.         For port = 0 To 3
  219.                 Rumble (port, 0, 0)
  220.         Next
  221.  
  222.         ' For later versions, to disable ALL controller input/output...
  223.        
  224.         If Not XInput10
  225.                 XInput_Enable (0)
  226.         EndIf
  227.        
  228. End Function
  229.  
  230. ' Call this to re-enable vibration effects, input values, etc, if you have called
  231. ' DisableRumble while your app is suspended.
  232.  
  233. ' NOTE! This also affects return results for controller inputs! Makes everything return
  234. ' as 'neutral', ie. no buttons pressed, no stick movement, etc, regardless of what
  235. ' the player may be doing with the controller. (Doesn't apply to XInput 1.0.)
  236.  
  237. ' M$ recommend using this when the program is suspended (eg. Alt-Tab, etc)...
  238.  
  239. Function EnableRumble ()
  240.         If Not XInput10 ' Only available if XInput version > 1.0!
  241.                 XInput_Enable (1)
  242.         EndIf
  243. End Function
  244.  
  245. ' -----------------------------------------------------------------------------
  246. ' D E M O . . .
  247. ' -----------------------------------------------------------------------------
  248.  
  249. ' Change port to 1, 2 or 3 if this doesn't work for you!
  250.  
  251. port = 0
  252.  
  253. ' Always check result of InitXInput! Exit if 0 -- usually means DLL not found.
  254.  
  255. x = InitXInput ()
  256.  
  257. ' Uncomment next line if rumble continues when program is terminated early! Always
  258. ' call StopControllers before ending program in normal usage!
  259.  
  260. 'DisableRumble (); End
  261.  
  262. If x = 0
  263.         Notify "Failed to initiate XInput!~n~nCheck XInput DLL is installed and up to date."
  264.         End
  265. EndIf
  266.  
  267. ' Uncomment next line for Rumble to have no effect...
  268.  
  269. Print ""
  270. Print "Xbox 360 controller rumble test..."
  271. Print ""
  272.  
  273. Print "Low frequency motor (left)!"
  274.  
  275. Rumble (port, 25000, 0)
  276. Delay (2000)
  277.  
  278. Print "High frequency motor (right)!"
  279.  
  280. Rumble (port, 0, 25000)
  281. Delay (2000)
  282.  
  283. Print "Both motors!"
  284.  
  285. Rumble (port, 25000, 25000)
  286. Delay (2000)
  287.  
  288. Print "Low frequency upwards, high frequency downwards!"
  289.  
  290. For v = 0 To 65535 Step 100
  291.         Rumble (port, v, 65535 - v)
  292.         Delay 1
  293. Next
  294.  
  295. DisableRumble () ' Stop input/output, just to make sure...
  296.  
  297. End


Comments :


BlitzSupport(Posted 1+ years ago)

 This is Simon Armstrong's FreeJoy demo with rumble added to the analog sticks. Try holding both sticks all the way back and then move one or the other up and down to feel the low and high frequency motors in action.
Code: [Select]

' testjoy.bmx [hacked]

Import Pub.FreeJoy


' -----------------------------------------------------------------------------
' Support functions. Most of this just checks carefully for the right DLL!
' -----------------------------------------------------------------------------

Function GetSystemFolder$ ()

?Win32 ' Windows only!

' Function ArrayFromString:Byte [] (source$)
' Local newarray:Byte [Len (source$)]
' MemCopy (newarray, source.ToCString (), Len (source$))
' Return newarray
' End Function

Function StringFromArray$ (source:Byte [])
Return String.FromCString (source)
End Function

Local GetSystemDirectory_ (location:Byte Ptr, pathsize) "win32"
kernel32 = LoadLibraryA ("kernel32.dll")

Local patharray:Byte [260]

If kernel32
GetSystemDirectory_ = GetProcAddress (kernel32, "GetSystemDirectoryA")
GetSystemDirectory_ (patharray, 260)
EndIf

Return StringFromArray (patharray)

?

End Function

' -----------------------------------------------------------------------------
' Find most recent XInput DLL...
' -----------------------------------------------------------------------------

Function GetXInputDLL$ ()

x$ = "xinput9_1_0.dll"

If FileType (GetSystemFolder () + "" + x$) = 0
x$ = "" ' Old DLL Not found...
EndIf

dir = ReadDir (GetSystemFolder ())

If dir

Repeat

f$ = Lower (NextFile (dir))

If f$.StartsWith ("xinput") And f$.EndsWith (".dll")

' Should probably check Mid values are 0-9 here...

version = Int (Mid (f$, 7, 1) + Mid (f$, 9, 1))

' Result is 10 for 1.0, 11 for 1.1, 12 for 1.2, etc...

If version = 91 ' Likely to be 1.0 -- see below!

' Non-public versions might return 9, 8, etc for 0.9, 0.8, etc,
' but 91 is the earliest public version! Exercise for the reader...

If Mid (f$, 11, 1) = "0"
version = 10 ' Ha! Stupid old "xinput9_1_0.dll" found -- really version 1.0
EndIf
EndIf

If version > hiversion
hiversion = version
xinput$ = f$
EndIf

EndIf

Until f$ = ""

CloseDir dir

EndIf

Return xinput$

End Function

' -----------------------------------------------------------------------------
' XInput function pointers...
' -----------------------------------------------------------------------------

' For more information:

' <a href="http://msdn.microsoft.com/en-us/library/bb173048(VS.85).aspx" target="_blank">http://msdn.microsoft.com/en-us/library/bb173048(VS.85).aspx</a>

Global XInput_SetState (port, hilo:Byte Ptr) "win32"
Global XInput_Enable (enable) "win32"

' -----------------------------------------------------------------------------
' Wrapper functions...
' -----------------------------------------------------------------------------

Global XInput10 = 0

Function InitXInput ()

x$ = GetXInputDLL ()
xinput = LoadLibraryA (x$)

If xinput

XInput_SetState = GetProcAddress (xinput, "XInputSetState")
XInput_Enable = GetProcAddress (xinput, "XInputEnable")

ok = True

If XInput_Enable = Null

If x$ = "xinput9_1_0.dll"

' Not in 1.0. Can continue but EnableController won't do anything...
XInput10 = True ' Can check this from other parts of the program if necessary...

Print ""
Print "XInputEnable not available in XInput 1.0 -- update Xbox controller driver!~nContinuing, but EnableController will have no effect..."

Else
ok = False ' Not found at all!
EndIf

EndIf

If XInput_SetState = Null
ok = False
EndIf

Else
ok = False
EndIf

If ok Return xinput

End Function

' Vibration continues until next Rumble call!

Function Rumble (port, rumble_left:Short, rumble_right:Short)

' From <a href="http://msdn.microsoft.com/en-us/library/bb174835(VS.85).aspx" target="_blank">http://msdn.microsoft.com/en-us/library/bb174835(VS.85).aspx</a> :

' "The left motor is the low-frequency rumble motor. The right motor is the high-frequency rumble motor."
' "The two motors are not the same, and they create different vibration effects."

' Combine two shorts into an integer...

freqs = (rumble_right Shl 16) | rumble_left

XInput_SetState (port, Varptr freqs)

End Function

' Call DisableRumble before exiting program, or when app is suspended, to stop all vibrations.

' NOTE! This also affects return results for controller inputs! Makes everything return
' as 'neutral', ie. no buttons pressed, no stick movement, etc, regardless of what
' the player may be doing with the controller. (Doesn't apply to XInput 1.0.)

Function DisableRumble ()

' The For loop below is intended to stop the vibrations for XInput 1.0, but although
' XInput_Enable (0) stops vibration for later versions under normal usage, it appears to
' have no effect if the controller is still vibrating after a program has terminated
' abnormally, hence I'm calling this regardless...

For port = 0 To 3
Rumble (port, 0, 0)
Next

' For later versions, to disable ALL controller input/output...

If Not XInput10
XInput_Enable (0)
EndIf

End Function

' Call this to re-enable vibration effects, input values, etc, if you have called
' DisableRumble while your app is suspended.

' NOTE! This also affects return results for controller inputs! Makes everything return
' as 'neutral', ie. no buttons pressed, no stick movement, etc, regardless of what
' the player may be doing with the controller. (Doesn't apply to XInput 1.0.)

' M$ recommend using this when the program is suspended (eg. Alt-Tab, etc)...

Function EnableRumble ()
If Not XInput10 ' Only available if XInput version > 1.0!
XInput_Enable (1)
EndIf
End Function

If Not JoyCount() RuntimeError "No joystick found!"

If InitXInput () = 0 RuntimeError "Install Xbox controller driver!"

Graphics 640,480

Function drawprop(n$,p#,y)
Local w
DrawText n$,0,y
w=Abs(p)*256
If p<0
DrawRect 320-w,y,w,16
Else
DrawRect 320,y,w,16
EndIf
End Function

Local t=0

While Not KeyHit(KEY_ESCAPE)
Cls

SetColor 255,255,255
Local n=JoyCount()
DrawText "joycount="+n,0,0
DrawText "JoyName(0)="+JoyName(0),0,20
DrawText "JoyButtonCaps(0)="+Bin$(JoyButtonCaps(0)),0,40
DrawText "JoyAxisCaps(0)="+Bin$(JoyAxisCaps(0)),0,60

For Local i=0 To 31
SetColor 255,255,255
If JoyDown(i) SetColor 255,0,0
DrawOval i*16,80,14,14
Next

SetColor 255,255,0
drawprop "JoyX=",JoyX(0),100
drawprop "JoyY:",JoyY(0),120

freqlo# = 65535.0 * JoyY (0) ' 0.0 - 2.0 (1.0 = centered)
freqlo = 65535.0 - (freqlo + 65535.0) / 2.0
DrawText freqlo, 0, 400

drawprop "JoyZ:",JoyZ(0),140
drawprop "JoyR:",JoyR(0),160

freqhi# = 65535.0 * JoyR (0) ' 0.0 - 2.0 (1.0 = centered)
freqhi = 65535.0 - (freqhi + 65535.0) / 2.0
DrawText freqhi, 0, 420

drawprop "JoyU:",JoyU(0),180
drawprop "JoyV:",JoyV(0),200
drawprop "JoyHat:",JoyHat(0),220
drawprop "JoyWheel:",JoyWheel(0),240

DrawRect 0,280,t,10
t=(t+1)&511

Rumble port, freqlo, freqhi

Flip
Wend

End
For real life use, you would probably want to create arrays that represent 'waves' (like sound waves), with attack/sustain/decay/release sections, and call Rumble () with that data. It might even be worth trying to scale a sound sample to the 0-65535 values supported by the controller, and pass each value from the scaled sample to the controller... [/i]

Offline IanMartin

  • Full Member
  • ***
  • Posts: 122
    • Retrolutionary.com
Can we comment on these again?
Platfinity (made with BlitzMax) on Steam:
http://store.steampowered.com/app/365440/Platfinity/

Offline IanMartin

  • Full Member
  • ***
  • Posts: 122
    • Retrolutionary.com
Ah good, yeah, my comments are showing up. 

Did anyone ever figure out how to correctly read the Xbox 360 controller Left and Right Triggers?  If you press them at the same time they read 0.  This is fine if you were actually using them as a rudder...but not so good if users want to remap jump or run or something to the trigger(s).  Any solution?
Platfinity (made with BlitzMax) on Steam:
http://store.steampowered.com/app/365440/Platfinity/

Offline c0Zm1c

  • Jr. Member
  • **
  • Posts: 5
Did anyone ever figure out how to correctly read the Xbox 360 controller Left and Right Triggers?  If you press them at the same time they read 0.  This is fine if you were actually using them as a rudder...but not so good if users want to remap jump or run or something to the trigger(s).  Any solution?

The code in this old thread reads left and right triggers independently. It's for Blitz3D but is surely portable to BlitzMax? (I don't know much about BlitzMax!)

Offline col

  • Sr. Member
  • ****
  • Posts: 439
I believe he got it all working via this thread...

https://www.syntaxbomb.com/index.php/topic,3439.0.html
To be is to be perceived.

https://github.com/davecamp

Offline IanMartin

  • Full Member
  • ***
  • Posts: 122
    • Retrolutionary.com
Yep, with a bunch of help from col and Henri's pre-built version I got it working!

I've got four controllers working now with vibration on all of them.  I've been meaning to post a follow up with some of my code as I'm working on a four player local co-op setup to use in future games.  Hopefully I'll get some time to work soon. 

The code doesn't seem to work with the Steam Controller, but being able to use the triggers properly and vibration more than makes up for that I think!
I'm not sure how many people have a Steam Controller or actually use it?  I'll probably get complaints about that, but not much I can do I guess.  It seems you can do some things without XInput - I have some controllers that don't support it but work with standard BlitzMAX commands - but if you want vibration, I'm pretty sure XInput has to happen.
I got stuttering/freezing/1 FPS I think from using the JoyCount() command, so I think col's XInput stuff is going to be way better!  I might try and use it in Platfinity...just afraid I'll lose Steam Controller and support for some of my weird controllers that I told my users would work with it...nothing is ever easy on PC! heheheh 

If anybody has any questions about it, I'm going to be working on four player stuff when I get a chance, so I'll help with it if I can :)

Platfinity (made with BlitzMax) on Steam:
http://store.steampowered.com/app/365440/Platfinity/