Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
5be0dace93 | |||
d6f72a8e74 | |||
5a5acdf583 | |||
9c66fe8930 | |||
3980bd92ca | |||
75fb3f577b | |||
b9a9d60e52 | |||
e0609fd526 | |||
e9a7764459 | |||
8cba42f903 | |||
58320211cd | |||
4adc27aec5 | |||
758f5b7b3d | |||
cccaf0c8cb | |||
19fee503fa | |||
fd7a3ea0b5 | |||
6248124a20 | |||
ce694b3625 | |||
1d65c88a7b | |||
867bf09b90 | |||
436d77109a | |||
354fa7364d |
151
joystick.py
Normal file
151
joystick.py
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import pygame
|
||||||
|
|
||||||
|
pygame.init()
|
||||||
|
|
||||||
|
|
||||||
|
# This is a simple class that will help us print to the screen.
|
||||||
|
# It has nothing to do with the joysticks, just outputting the
|
||||||
|
# information.
|
||||||
|
class TextPrint:
|
||||||
|
def __init__(self):
|
||||||
|
self.reset()
|
||||||
|
self.font = pygame.font.Font(None, 25)
|
||||||
|
|
||||||
|
def tprint(self, screen, text):
|
||||||
|
text_bitmap = self.font.render(text, True, (0, 0, 0))
|
||||||
|
screen.blit(text_bitmap, (self.x, self.y))
|
||||||
|
self.y += self.line_height
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.x = 10
|
||||||
|
self.y = 10
|
||||||
|
self.line_height = 15
|
||||||
|
|
||||||
|
def indent(self):
|
||||||
|
self.x += 10
|
||||||
|
|
||||||
|
def unindent(self):
|
||||||
|
self.x -= 10
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Set the width and height of the screen (width, height), and name the window.
|
||||||
|
screen = pygame.display.set_mode((500, 700))
|
||||||
|
pygame.display.set_caption("Joystick example")
|
||||||
|
|
||||||
|
# Used to manage how fast the screen updates.
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
# Get ready to print.
|
||||||
|
text_print = TextPrint()
|
||||||
|
|
||||||
|
# This dict can be left as-is, since pygame will generate a
|
||||||
|
# pygame.JOYDEVICEADDED event for every joystick connected
|
||||||
|
# at the start of the program.
|
||||||
|
joysticks = {}
|
||||||
|
|
||||||
|
done = False
|
||||||
|
while not done:
|
||||||
|
# Event processing step.
|
||||||
|
# Possible joystick events: JOYAXISMOTION, JOYBALLMOTION, JOYBUTTONDOWN,
|
||||||
|
# JOYBUTTONUP, JOYHATMOTION, JOYDEVICEADDED, JOYDEVICEREMOVED
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
done = True # Flag that we are done so we exit this loop.
|
||||||
|
|
||||||
|
if event.type == pygame.JOYBUTTONDOWN:
|
||||||
|
print("Joystick button pressed.")
|
||||||
|
if event.button == 0:
|
||||||
|
joystick = joysticks[event.instance_id]
|
||||||
|
if joystick.rumble(0.7, 1.0, 1000):
|
||||||
|
print(f"Rumble effect played on joystick {event.instance_id}")
|
||||||
|
|
||||||
|
if event.type == pygame.JOYBUTTONUP:
|
||||||
|
print("Joystick button released.")
|
||||||
|
|
||||||
|
# Handle hotplugging
|
||||||
|
if event.type == pygame.JOYDEVICEADDED:
|
||||||
|
# This event will be generated when the program starts for every
|
||||||
|
# joystick, filling up the list without needing to create them manually.
|
||||||
|
joy = pygame.joystick.Joystick(event.device_index)
|
||||||
|
joysticks[joy.get_instance_id()] = joy
|
||||||
|
print(f"Joystick {joy.get_instance_id()} connencted")
|
||||||
|
|
||||||
|
if event.type == pygame.JOYDEVICEREMOVED:
|
||||||
|
del joysticks[event.instance_id]
|
||||||
|
print(f"Joystick {event.instance_id} disconnected")
|
||||||
|
|
||||||
|
# Drawing step
|
||||||
|
# First, clear the screen to white. Don't put other drawing commands
|
||||||
|
# above this, or they will be erased with this command.
|
||||||
|
screen.fill((255, 255, 255))
|
||||||
|
text_print.reset()
|
||||||
|
|
||||||
|
# Get count of joysticks.
|
||||||
|
joystick_count = pygame.joystick.get_count()
|
||||||
|
|
||||||
|
text_print.tprint(screen, f"Number of joysticks: {joystick_count}")
|
||||||
|
text_print.indent()
|
||||||
|
|
||||||
|
# For each joystick:
|
||||||
|
for joystick in joysticks.values():
|
||||||
|
jid = joystick.get_instance_id()
|
||||||
|
|
||||||
|
text_print.tprint(screen, f"Joystick {jid}")
|
||||||
|
text_print.indent()
|
||||||
|
|
||||||
|
# Get the name from the OS for the controller/joystick.
|
||||||
|
name = joystick.get_name()
|
||||||
|
text_print.tprint(screen, f"Joystick name: {name}")
|
||||||
|
|
||||||
|
guid = joystick.get_guid()
|
||||||
|
text_print.tprint(screen, f"GUID: {guid}")
|
||||||
|
|
||||||
|
power_level = joystick.get_power_level()
|
||||||
|
text_print.tprint(screen, f"Joystick's power level: {power_level}")
|
||||||
|
|
||||||
|
# Usually axis run in pairs, up/down for one, and left/right for
|
||||||
|
# the other. Triggers count as axes.
|
||||||
|
axes = joystick.get_numaxes()
|
||||||
|
text_print.tprint(screen, f"Number of axes: {axes}")
|
||||||
|
text_print.indent()
|
||||||
|
|
||||||
|
for i in range(axes):
|
||||||
|
axis = joystick.get_axis(i)
|
||||||
|
text_print.tprint(screen, f"Axis {i} value: {axis:>6.3f}")
|
||||||
|
text_print.unindent()
|
||||||
|
|
||||||
|
buttons = joystick.get_numbuttons()
|
||||||
|
text_print.tprint(screen, f"Number of buttons: {buttons}")
|
||||||
|
text_print.indent()
|
||||||
|
|
||||||
|
for i in range(buttons):
|
||||||
|
button = joystick.get_button(i)
|
||||||
|
text_print.tprint(screen, f"Button {i:>2} value: {button}")
|
||||||
|
text_print.unindent()
|
||||||
|
|
||||||
|
hats = joystick.get_numhats()
|
||||||
|
text_print.tprint(screen, f"Number of hats: {hats}")
|
||||||
|
text_print.indent()
|
||||||
|
|
||||||
|
# Hat position. All or nothing for direction, not a float like
|
||||||
|
# get_axis(). Position is a tuple of int values (x, y).
|
||||||
|
for i in range(hats):
|
||||||
|
hat = joystick.get_hat(i)
|
||||||
|
text_print.tprint(screen, f"Hat {i} value: {str(hat)}")
|
||||||
|
text_print.unindent()
|
||||||
|
|
||||||
|
text_print.unindent()
|
||||||
|
|
||||||
|
# Go ahead and update the screen with what we've drawn.
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
# Limit to 30 frames per second.
|
||||||
|
clock.tick(30)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
# If you forget this line, the program will 'hang'
|
||||||
|
# on exit if running from IDLE.
|
||||||
|
pygame.quit()
|
@@ -9,7 +9,7 @@ from .xbox_series_x_controller import XboxSeriesXController
|
|||||||
from .generic_controller import GenericController
|
from .generic_controller import GenericController
|
||||||
from .logitech_dual_action_controller import LogitechDualActionController
|
from .logitech_dual_action_controller import LogitechDualActionController
|
||||||
|
|
||||||
__version__ = "0.0.4"
|
__version__ = "0.1.7"
|
||||||
|
|
||||||
CONTROLLERS = {
|
CONTROLLERS = {
|
||||||
"DualSense Wireless Controller": DualSenseController,
|
"DualSense Wireless Controller": DualSenseController,
|
||||||
|
@@ -30,4 +30,12 @@ class ControlsBase(ABC):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def rumble(self):
|
def rumble(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def stop_rumble(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def close(self):
|
||||||
pass
|
pass
|
@@ -1,70 +0,0 @@
|
|||||||
import os
|
|
||||||
import time
|
|
||||||
import numpy as np
|
|
||||||
import sounddevice as sd
|
|
||||||
import alsaaudio
|
|
||||||
import pulsectl
|
|
||||||
|
|
||||||
class DualSenseAudio:
|
|
||||||
def __init__(self):
|
|
||||||
self.alsa_devices = self._get_alsa_devices()
|
|
||||||
self.pulse_devices = self._get_pulseaudio_devices()
|
|
||||||
self.dualsense_device = self._detect_dualsense()
|
|
||||||
|
|
||||||
def _get_alsa_devices(self):
|
|
||||||
try:
|
|
||||||
cards = alsaaudio.cards()
|
|
||||||
return cards
|
|
||||||
except Exception as e:
|
|
||||||
print("ALSA detection failed:", e)
|
|
||||||
return []
|
|
||||||
|
|
||||||
def _get_pulseaudio_devices(self):
|
|
||||||
try:
|
|
||||||
pulse = pulsectl.Pulse("dualsense-audio")
|
|
||||||
sinks = pulse.sink_list()
|
|
||||||
return sinks
|
|
||||||
except Exception as e:
|
|
||||||
print("PulseAudio detection failed:", e)
|
|
||||||
return []
|
|
||||||
|
|
||||||
def _detect_dualsense(self):
|
|
||||||
# Check ALSA names
|
|
||||||
for card in self.alsa_devices:
|
|
||||||
if "DualSense" in card:
|
|
||||||
return {'type': 'alsa', 'name': card}
|
|
||||||
|
|
||||||
# Check PulseAudio sinks
|
|
||||||
for sink in self.pulse_devices:
|
|
||||||
if "dualsense" in sink.description.lower():
|
|
||||||
return {'type': 'pulse', 'name': sink.name}
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def play_tone(self, frequency=440.0, duration=2.0, volume=0.5):
|
|
||||||
if not self.dualsense_device:
|
|
||||||
print("DualSense speaker not found.")
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f"Playing tone on DualSense ({self.dualsense_device['type']})...")
|
|
||||||
|
|
||||||
fs = 48000 # Sample rate
|
|
||||||
t = np.linspace(0, duration, int(fs * duration), False)
|
|
||||||
tone = np.sin(frequency * 2 * np.pi * t) * volume
|
|
||||||
audio = tone.astype(np.float32)
|
|
||||||
|
|
||||||
if self.dualsense_device['type'] == 'pulse':
|
|
||||||
sd.play(audio, samplerate=fs, device=self.dualsense_device['name'])
|
|
||||||
elif self.dualsense_device['type'] == 'alsa':
|
|
||||||
device_index = self.alsa_devices.index(self.dualsense_device['name'])
|
|
||||||
sd.play(audio, samplerate=fs, device=device_index)
|
|
||||||
sd.wait()
|
|
||||||
|
|
||||||
def list_devices(self):
|
|
||||||
print("ALSA Devices:")
|
|
||||||
for card in self.alsa_devices:
|
|
||||||
print(f" - {card}")
|
|
||||||
print("\nPulseAudio Devices:")
|
|
||||||
for sink in self.pulse_devices:
|
|
||||||
print(f" - {sink.name} ({sink.description})")
|
|
||||||
|
|
@@ -18,10 +18,40 @@ class DualSenseController(ControlsBase):
|
|||||||
self.powerlevel = self.device.battery.Level
|
self.powerlevel = self.device.battery.Level
|
||||||
self.batterystate = BATTERY_STATE[str(self.device.battery.State)]
|
self.batterystate = BATTERY_STATE[str(self.device.battery.State)]
|
||||||
self.set_player_id(PlayerID.PLAYER_1)
|
self.set_player_id(PlayerID.PLAYER_1)
|
||||||
|
self.numaxis: int = joy.get_numaxes()
|
||||||
|
self.axis: list = [joy.get_axis(a) for a in range(self.numaxis)]
|
||||||
|
self.numhats: int = joy.get_numhats()
|
||||||
|
self.hats: list = [joy.get_hat(h) for h in range(self.numhats)]
|
||||||
|
self.numbuttons: int = joy.get_numbuttons()
|
||||||
|
self.buttons: list = [joy.get_button(b) for b in range(self.numbuttons)]
|
||||||
|
self.mapping = {
|
||||||
|
"left stick x": self.axis[0],
|
||||||
|
"left stick y": self.axis[1],
|
||||||
|
"right stick x": self.axis[3],
|
||||||
|
"right stick y": self.axis[4],
|
||||||
|
"right trigger": self.buttons[5],
|
||||||
|
"left trigger": self.buttons[2],
|
||||||
|
"dhat x": self.hats[0][0],
|
||||||
|
"dhat y": self.hats[0][1],
|
||||||
|
"left button": self.buttons[4],
|
||||||
|
"right button": self.buttons[5],
|
||||||
|
"cross button": self.buttons[0],
|
||||||
|
"triangle button": self.buttons[2],
|
||||||
|
"circle button": self.buttons[1],
|
||||||
|
"square button": self.buttons[3],
|
||||||
|
"left stick button": self.buttons[11],
|
||||||
|
"right stick button": self.buttons[12],
|
||||||
|
"connect button": self.buttons[8],
|
||||||
|
"list button": self.buttons[9],
|
||||||
|
"logo button": self.buttons[10]
|
||||||
|
}
|
||||||
print(f"{self.name} connected")
|
print(f"{self.name} connected")
|
||||||
print(f"Power level: {self.powerlevel}")
|
print(f"Power level: {self.powerlevel}")
|
||||||
print(f"Battery state: {self.batterystate}")
|
print(f"Battery state: {self.batterystate}")
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.device.close()
|
||||||
|
|
||||||
def handle_input(self, event):
|
def handle_input(self, event):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -46,9 +76,16 @@ class DualSenseController(ControlsBase):
|
|||||||
def pause(self):
|
def pause(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def rumble(self):
|
def rumble(self, left, right):
|
||||||
pass
|
if not left in range(256) or not right in range(256):
|
||||||
|
raise ValueError("left and right values must be in the range 0 - 255")
|
||||||
|
self.device.setLeftMotor(left)
|
||||||
|
self.device.setRightMotor(right)
|
||||||
|
|
||||||
|
def stop_rumble(self):
|
||||||
|
self.device.setLeftMotor(0)
|
||||||
|
self.device.setRightMotor(0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self._name
|
return self._name
|
||||||
|
@@ -1,199 +1,103 @@
|
|||||||
import time
|
from pygameControls.controlsbase import ControlsBase
|
||||||
import threading
|
|
||||||
import numpy as np
|
|
||||||
import sounddevice as sd
|
|
||||||
import alsaaudio
|
|
||||||
import pulsectl
|
|
||||||
from pydualsense import *
|
from pydualsense import *
|
||||||
|
|
||||||
|
|
||||||
class DualSenseEdgeController:
|
class DualSenseEdgeController(ControlsBase):
|
||||||
def __init__(self):
|
def __init__(self, joy):
|
||||||
# DualSense input/output interface
|
self.device = pydualsense()
|
||||||
self.ds = pydualsense()
|
self.device.init()
|
||||||
self.ds.init()
|
self.name = self.device.device.get_product_string()
|
||||||
self._listening = False
|
self.powerlevel = self.device.battery.Level
|
||||||
self._bindings = {}
|
self.batterystate = BATTERY_STATE[str(self.device.battery.State)]
|
||||||
|
self.set_player_id(PlayerID.PLAYER_1)
|
||||||
# Audio detection
|
self.numaxis: int = joy.get_numaxes()
|
||||||
self.alsa_devices = self._get_alsa_devices()
|
self.axis: list = [joy.get_axis(a) for a in range(self.numaxis)]
|
||||||
self.pulse_devices = self._get_pulseaudio_devices()
|
self.numhats: int = joy.get_numhats()
|
||||||
self.dualsense_audio_device = self._detect_dualsense_audio()
|
self.hats: list = [joy.get_hat(h) for h in range(self.numhats)]
|
||||||
|
self.numbuttons: int = joy.get_numbuttons()
|
||||||
print("DualSense initialized.")
|
self.buttons: list = [joy.get_button(b) for b in range(self.numbuttons)]
|
||||||
|
self.mapping = {
|
||||||
# ---------------------- Device Controls ----------------------
|
"left stick x": self.axis[0],
|
||||||
|
"left stick y": self.axis[1],
|
||||||
def set_rumble(self, small_motor: int, big_motor: int):
|
"right stick x": self.axis[3],
|
||||||
self.ds.setRumble(small_motor, big_motor)
|
"right stick y": self.axis[4],
|
||||||
|
"right trigger": self.buttons[5],
|
||||||
def stop_rumble(self):
|
"left trigger": self.buttons[2],
|
||||||
self.set_rumble(0, 0)
|
"dhat x": self.hats[0][0],
|
||||||
|
"dhat y": self.hats[0][1],
|
||||||
def set_led_color(self, r: int, g: int, b: int):
|
"left button": self.buttons[4],
|
||||||
self.ds.setLightBarColor(r, g, b)
|
"right button": self.buttons[5],
|
||||||
|
"cross button": self.buttons[0],
|
||||||
def set_trigger_effects(self, left_mode='Off', right_mode='Off', force=0):
|
"triangle button": self.buttons[2],
|
||||||
left = getattr(TriggerModes, left_mode.upper(), TriggerModes.Off)
|
"circle button": self.buttons[1],
|
||||||
right = getattr(TriggerModes, right_mode.upper(), TriggerModes.Off)
|
"square button": self.buttons[3],
|
||||||
self.ds.triggerL.setMode(left)
|
"left stick button": self.buttons[11],
|
||||||
self.ds.triggerR.setMode(right)
|
"right stick button": self.buttons[12],
|
||||||
if force > 0:
|
"connect button": self.buttons[8],
|
||||||
self.ds.triggerL.setForce(force)
|
"list button": self.buttons[9],
|
||||||
self.ds.triggerR.setForce(force)
|
"logo button": self.buttons[10]
|
||||||
|
}
|
||||||
# ---------------------- Predefined Rumble Patterns ----------------------
|
print(f"{self.name} connected")
|
||||||
|
print(f"Power level: {self.powerlevel}")
|
||||||
def rumble_pattern(self, pattern: str, duration: float = 1.0):
|
print(f"Battery state: {self.batterystate}")
|
||||||
patterns = {
|
|
||||||
"pulse": self._pulse_rumble,
|
|
||||||
"heartbeat": self._heartbeat_rumble,
|
|
||||||
"buzz": self._buzz_rumble,
|
|
||||||
"wave": self._wave_rumble,
|
|
||||||
"alarm": self._alarm_rumble,
|
|
||||||
}
|
|
||||||
if pattern in patterns:
|
|
||||||
threading.Thread(target=patterns[pattern], args=(duration,), daemon=True).start()
|
|
||||||
else:
|
|
||||||
print(f"Unknown rumble pattern: {pattern}")
|
|
||||||
|
|
||||||
def _pulse_rumble(self, duration):
|
|
||||||
end = time.time() + duration
|
|
||||||
while time.time() < end:
|
|
||||||
self.set_rumble(50, 150)
|
|
||||||
time.sleep(0.2)
|
|
||||||
self.stop_rumble()
|
|
||||||
time.sleep(0.2)
|
|
||||||
|
|
||||||
def _heartbeat_rumble(self, duration):
|
|
||||||
end = time.time() + duration
|
|
||||||
while time.time() < end:
|
|
||||||
self.set_rumble(200, 200)
|
|
||||||
time.sleep(0.1)
|
|
||||||
self.stop_rumble()
|
|
||||||
time.sleep(0.1)
|
|
||||||
self.set_rumble(100, 100)
|
|
||||||
time.sleep(0.1)
|
|
||||||
self.stop_rumble()
|
|
||||||
time.sleep(0.4)
|
|
||||||
|
|
||||||
def _buzz_rumble(self, duration):
|
|
||||||
self.set_rumble(80, 255)
|
|
||||||
time.sleep(duration)
|
|
||||||
self.stop_rumble()
|
|
||||||
|
|
||||||
def _wave_rumble(self, duration):
|
|
||||||
start = time.time()
|
|
||||||
while time.time() - start < duration:
|
|
||||||
for i in range(0, 256, 25):
|
|
||||||
self.set_rumble(i, 255 - i)
|
|
||||||
time.sleep(0.05)
|
|
||||||
for i in reversed(range(0, 256, 25)):
|
|
||||||
self.set_rumble(i, 255 - i)
|
|
||||||
time.sleep(0.05)
|
|
||||||
self.stop_rumble()
|
|
||||||
|
|
||||||
def _alarm_rumble(self, duration):
|
|
||||||
end = time.time() + duration
|
|
||||||
while time.time() < end:
|
|
||||||
self.set_rumble(255, 0)
|
|
||||||
time.sleep(0.1)
|
|
||||||
self.set_rumble(0, 255)
|
|
||||||
time.sleep(0.1)
|
|
||||||
self.stop_rumble()
|
|
||||||
|
|
||||||
# ---------------------- Input Listener + Bindings ----------------------
|
|
||||||
|
|
||||||
def bind(self, button: str, action: callable):
|
|
||||||
"""Bind a button to a callable. Ex: controller.bind('cross', lambda: rumble_pattern('buzz'))"""
|
|
||||||
self._bindings[button] = action
|
|
||||||
|
|
||||||
def start_input_listener(self):
|
|
||||||
def listen():
|
|
||||||
while self._listening:
|
|
||||||
#self.ds.update()
|
|
||||||
for button, action in self._bindings.items():
|
|
||||||
if getattr(self.ds, button, False):
|
|
||||||
action()
|
|
||||||
self._listening = True
|
|
||||||
thread = threading.Thread(target=listen, daemon=True)
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
def stop_input_listener(self):
|
|
||||||
self._listening = False
|
|
||||||
|
|
||||||
# ---------------------- Audio Output ----------------------
|
|
||||||
|
|
||||||
def _get_alsa_devices(self):
|
|
||||||
try:
|
|
||||||
return alsaaudio.cards()
|
|
||||||
except Exception:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def _get_pulseaudio_devices(self):
|
|
||||||
try:
|
|
||||||
pulse = pulsectl.Pulse("dualsense-audio")
|
|
||||||
return pulse.sink_list()
|
|
||||||
except Exception:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def _detect_dualsense_audio(self):
|
|
||||||
# Check ALSA names
|
|
||||||
for card in self.alsa_devices:
|
|
||||||
if "DualSense" in card:
|
|
||||||
return {'type': 'alsa', 'name': card}
|
|
||||||
|
|
||||||
# Check PulseAudio sinks
|
|
||||||
for sink in self.pulse_devices:
|
|
||||||
if "dualsense" in sink.description.lower():
|
|
||||||
return {'type': 'pulse', 'name': sink.name}
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def play_tone(self, frequency=440.0, duration=2.0, volume=0.5):
|
|
||||||
if not self.dualsense_audio_device:
|
|
||||||
print("DualSense speaker not detected.")
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f"Playing tone on DualSense ({self.dualsense_audio_device['type']})...")
|
|
||||||
|
|
||||||
fs = 48000 # Sample rate
|
|
||||||
t = np.linspace(0, duration, int(fs * duration), False)
|
|
||||||
tone = np.sin(frequency * 2 * np.pi * t) * volume
|
|
||||||
audio = tone.astype(np.float32)
|
|
||||||
|
|
||||||
try:
|
|
||||||
if self.dualsense_audio_device['type'] == 'pulse':
|
|
||||||
sd.play(audio, samplerate=fs, device=self.dualsense_audio_device['name'])
|
|
||||||
elif self.dualsense_audio_device['type'] == 'alsa':
|
|
||||||
device_index = self.alsa_devices.index(self.dualsense_audio_device['name'])
|
|
||||||
sd.play(audio, samplerate=fs, device=device_index)
|
|
||||||
sd.wait()
|
|
||||||
except Exception as e:
|
|
||||||
print("Failed to play tone:", e)
|
|
||||||
|
|
||||||
def list_audio_devices(self):
|
|
||||||
print("ALSA Devices:")
|
|
||||||
for card in self.alsa_devices:
|
|
||||||
print(f" - {card}")
|
|
||||||
print("\nPulseAudio Devices:")
|
|
||||||
for sink in self.pulse_devices:
|
|
||||||
print(f" - {sink.name} ({sink.description})")
|
|
||||||
|
|
||||||
# ---------------------- Cleanup ----------------------
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.ds.close()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
|
|
||||||
controller = DualSenseController()
|
def close(self):
|
||||||
|
self.device.close()
|
||||||
|
|
||||||
|
def handle_input(self, event):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_led(self, red: int, green: int, blue: int):
|
||||||
|
self.device.light.setColorI(red, green, blue)
|
||||||
|
|
||||||
# Bind buttons to patterns
|
def set_player_id(self, playerid: PlayerID):
|
||||||
controller.bind("cross", lambda: controller.rumble_pattern("heartbeat", 1.5))
|
self.device.light.setPlayerID(playerid)
|
||||||
controller.bind("circle", lambda: controller.rumble_pattern("buzz", 0.5))
|
|
||||||
controller.bind("triangle", lambda: controller.rumble_pattern("pulse", 2))
|
def left(self):
|
||||||
controller.bind("square", lambda: controller.set_led_color(255, 0, 0))
|
pass
|
||||||
|
|
||||||
# Start listening
|
def right(self):
|
||||||
controller.start_input_listener()
|
pass
|
||||||
|
|
||||||
|
def up(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def down(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def rumble(self, left, right):
|
||||||
|
if not left in range(256) or not right in range(256):
|
||||||
|
raise ValueError("left and right values must be in the range 0 - 255")
|
||||||
|
self.device.setLeftMotor(left)
|
||||||
|
self.device.setRightMotor(right)
|
||||||
|
|
||||||
|
def stop_rumble(self):
|
||||||
|
self.device.setLeftMotor(0)
|
||||||
|
self.device.setRightMotor(0)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@name.setter
|
||||||
|
def name(self, name: str) -> None:
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def powerlevel(self) -> str:
|
||||||
|
return self._powerlevel
|
||||||
|
|
||||||
|
@powerlevel.setter
|
||||||
|
def powerlevel(self, lvl: str) -> None:
|
||||||
|
self._powerlevel = lvl
|
||||||
|
|
||||||
|
@property
|
||||||
|
def batterystate(self) -> int:
|
||||||
|
return self._batterystate
|
||||||
|
|
||||||
|
@batterystate.setter
|
||||||
|
def batterystate(self, state) -> None:
|
||||||
|
self._batterystate = state
|
@@ -13,6 +13,31 @@ class GenericController(ControlsBase):
|
|||||||
self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)]
|
self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)]
|
||||||
self.numbuttons: int = self.device.get_numbuttons()
|
self.numbuttons: int = self.device.get_numbuttons()
|
||||||
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
||||||
|
self.mapping = {
|
||||||
|
"left stick x": self.axis[0],
|
||||||
|
"left stick y": self.axis[1],
|
||||||
|
"right stick x": self.axis[2],
|
||||||
|
"right stick y": self.axis[3],
|
||||||
|
"right trigger": self.buttons[7],
|
||||||
|
"left trigger": self.buttons[6],
|
||||||
|
"dhat x": self.hats[0][0],
|
||||||
|
"dhat y": self.hats[0][1],
|
||||||
|
"left button": self.buttons[4],
|
||||||
|
"right button": self.buttons[5],
|
||||||
|
"X button": self.buttons[0],
|
||||||
|
"Y button": self.buttons[3],
|
||||||
|
"A button": self.buttons[1],
|
||||||
|
"B button": self.buttons[2],
|
||||||
|
"left stick button": self.buttons[10],
|
||||||
|
"right stick button": self.buttons[11],
|
||||||
|
"back button": self.buttons[8],
|
||||||
|
"start button": self.buttons[9],
|
||||||
|
"logo button": None
|
||||||
|
}
|
||||||
|
print(f"{self.name} connected.")
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def handle_input(self, event):
|
def handle_input(self, event):
|
||||||
pass
|
pass
|
||||||
@@ -35,6 +60,9 @@ class GenericController(ControlsBase):
|
|||||||
def rumble(self):
|
def rumble(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def stop_rumble(self):
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self._name
|
return self._name
|
||||||
|
@@ -34,6 +34,31 @@ class LogitechDualActionController(ControlsBase):
|
|||||||
self.numbuttons: int = self.device.get_numbuttons()
|
self.numbuttons: int = self.device.get_numbuttons()
|
||||||
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
||||||
self.input_mode = InputMode.DirectInput
|
self.input_mode = InputMode.DirectInput
|
||||||
|
self.mapping = {
|
||||||
|
"left stick x": self.axis[0],
|
||||||
|
"left stick y": self.axis[1],
|
||||||
|
"right stick x": self.axis[2],
|
||||||
|
"right stick y": self.axis[3],
|
||||||
|
"right trigger": self.buttons[7],
|
||||||
|
"left trigger": self.buttons[6],
|
||||||
|
"dhat x": self.hats[0][0],
|
||||||
|
"dhat y": self.hats[0][1],
|
||||||
|
"left button": self.buttons[4],
|
||||||
|
"right button": self.buttons[5],
|
||||||
|
"X button": self.buttons[0],
|
||||||
|
"Y button": self.buttons[3],
|
||||||
|
"A button": self.buttons[1],
|
||||||
|
"B button": self.buttons[2],
|
||||||
|
"left stick button": self.buttons[10],
|
||||||
|
"right stick button": self.buttons[11],
|
||||||
|
"back button": self.buttons[8],
|
||||||
|
"start button": self.buttons[9],
|
||||||
|
"logo button": None
|
||||||
|
}
|
||||||
|
print(f"{self.name} connected.")
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def handle_input(self, event):
|
def handle_input(self, event):
|
||||||
pass
|
pass
|
||||||
@@ -56,6 +81,9 @@ class LogitechDualActionController(ControlsBase):
|
|||||||
def rumble(self):
|
def rumble(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def stop_rumble(self):
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self._name
|
return self._name
|
||||||
|
@@ -34,6 +34,31 @@ class LogitechF310Controller(ControlsBase):
|
|||||||
self.numbuttons: int = self.device.get_numbuttons()
|
self.numbuttons: int = self.device.get_numbuttons()
|
||||||
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
||||||
self.input_mode = InputMode.XInput
|
self.input_mode = InputMode.XInput
|
||||||
|
self.mapping = {
|
||||||
|
"left stick x": self.axis[0],
|
||||||
|
"left stick y": self.axis[1],
|
||||||
|
"right stick x": self.axis[3],
|
||||||
|
"right stick y": self.axis[4],
|
||||||
|
"right trigger": self.axis[2],
|
||||||
|
"left trigger": self.axis[5],
|
||||||
|
"dhat x": self.hats[0][0],
|
||||||
|
"dhat y": self.hats[0][1],
|
||||||
|
"left button": self.buttons[4],
|
||||||
|
"right button": self.buttons[5],
|
||||||
|
"X button": self.buttons[2],
|
||||||
|
"Y button": self.buttons[3],
|
||||||
|
"A button": self.buttons[0],
|
||||||
|
"B button": self.buttons[1],
|
||||||
|
"left stick button": self.buttons[9],
|
||||||
|
"right stick button": self.buttons[10],
|
||||||
|
"back button": self.buttons[6],
|
||||||
|
"start button": self.buttons[7],
|
||||||
|
"logo button": self.buttons[8]
|
||||||
|
}
|
||||||
|
print(f"{self.name} connected.")
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def handle_input(self, event):
|
def handle_input(self, event):
|
||||||
pass
|
pass
|
||||||
@@ -56,6 +81,9 @@ class LogitechF310Controller(ControlsBase):
|
|||||||
def rumble(self):
|
def rumble(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def stop_rumble(self):
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self._name
|
return self._name
|
||||||
|
@@ -36,7 +36,31 @@ class LogitechF510Controller(ControlsBase):
|
|||||||
self.numbuttons: int = self.device.get_numbuttons()
|
self.numbuttons: int = self.device.get_numbuttons()
|
||||||
self.buttons: list = []
|
self.buttons: list = []
|
||||||
self.input_mode: InputMode.DirectInput
|
self.input_mode: InputMode.DirectInput
|
||||||
self.input_connection: ConnectionType.WIRED
|
self.mapping = {
|
||||||
|
"left stick x": self.axis[0],
|
||||||
|
"left stick y": self.axis[1],
|
||||||
|
"right stick x": self.axis[3],
|
||||||
|
"right stick y": self.axis[4],
|
||||||
|
"right trigger": self.axis[2],
|
||||||
|
"left trigger": self.axis[5],
|
||||||
|
"dhat x": self.hats[0][0],
|
||||||
|
"dhat y": self.hats[0][1],
|
||||||
|
"left button": self.buttons[4],
|
||||||
|
"right button": self.buttons[5],
|
||||||
|
"X button": self.buttons[2],
|
||||||
|
"Y button": self.buttons[3],
|
||||||
|
"A button": self.buttons[0],
|
||||||
|
"B button": self.buttons[1],
|
||||||
|
"left stick button": self.buttons[9],
|
||||||
|
"right stick button": self.buttons[10],
|
||||||
|
"back button": self.buttons[6],
|
||||||
|
"start button": self.buttons[7],
|
||||||
|
"logo button": self.buttons[8]
|
||||||
|
}
|
||||||
|
print(f"{self.name} connected.")
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def handle_input(self, event):
|
def handle_input(self, event):
|
||||||
pass
|
pass
|
||||||
@@ -59,6 +83,9 @@ class LogitechF510Controller(ControlsBase):
|
|||||||
def rumble(self):
|
def rumble(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def stop_rumble(self):
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self._name
|
return self._name
|
||||||
|
@@ -36,7 +36,31 @@ class LogitechF710Controller(ControlsBase):
|
|||||||
self.numbuttons: int = self.device.get_numbuttons()
|
self.numbuttons: int = self.device.get_numbuttons()
|
||||||
self.buttons: list = []
|
self.buttons: list = []
|
||||||
self.input_mode: InputMode.DirectInput
|
self.input_mode: InputMode.DirectInput
|
||||||
self.input_connection: ConnectionType.WIRED
|
self.mapping = {
|
||||||
|
"left stick x": self.axis[0],
|
||||||
|
"left stick y": self.axis[1],
|
||||||
|
"right stick x": self.axis[3],
|
||||||
|
"right stick y": self.axis[4],
|
||||||
|
"right trigger": self.axis[2],
|
||||||
|
"left trigger": self.axis[5],
|
||||||
|
"dhat x": self.hats[0][0],
|
||||||
|
"dhat y": self.hats[0][1],
|
||||||
|
"left button": self.buttons[4],
|
||||||
|
"right button": self.buttons[5],
|
||||||
|
"X button": self.buttons[2],
|
||||||
|
"Y button": self.buttons[3],
|
||||||
|
"A button": self.buttons[0],
|
||||||
|
"B button": self.buttons[1],
|
||||||
|
"left stick button": self.buttons[9],
|
||||||
|
"right stick button": self.buttons[10],
|
||||||
|
"back button": self.buttons[6],
|
||||||
|
"start button": self.buttons[7],
|
||||||
|
"logo button": self.buttons[8]
|
||||||
|
}
|
||||||
|
print(f"{self.name} connected.")
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def handle_input(self, event):
|
def handle_input(self, event):
|
||||||
pass
|
pass
|
||||||
@@ -59,6 +83,9 @@ class LogitechF710Controller(ControlsBase):
|
|||||||
def rumble(self):
|
def rumble(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def stop_rumble(self):
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self._name
|
return self._name
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import time
|
from pygameControls.controlsbase import ControlsBase
|
||||||
import threading
|
|
||||||
|
|
||||||
class XboxSeriesXController:
|
class XboxSeriesXController(ControlsBase):
|
||||||
def __init__(self, joy):
|
def __init__(self, joy):
|
||||||
self.device = joy
|
self.device = joy
|
||||||
self.instance_id: int = self.device.get_instance_id()
|
self.instance_id: int = self.device.get_instance_id()
|
||||||
@@ -13,6 +12,32 @@ class XboxSeriesXController:
|
|||||||
self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)]
|
self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)]
|
||||||
self.numbuttons: int = self.device.get_numbuttons()
|
self.numbuttons: int = self.device.get_numbuttons()
|
||||||
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
||||||
|
self.mapping = {
|
||||||
|
"left stick x": self.axis[0],
|
||||||
|
"left stick y": self.axis[1],
|
||||||
|
"right stick x": self.axis[2],
|
||||||
|
"right stick y": self.axis[3],
|
||||||
|
"right trigger": self.axis[4],
|
||||||
|
"left trigger": self.axis[5],
|
||||||
|
"dhat x": self.hats[0][0],
|
||||||
|
"dhat y": self.hats[0][1],
|
||||||
|
"left button": self.buttons[6],
|
||||||
|
"right button": self.buttons[7],
|
||||||
|
"X button": self.buttons[3],
|
||||||
|
"Y button": self.buttons[4],
|
||||||
|
"A button": self.buttons[0],
|
||||||
|
"B button": self.buttons[1],
|
||||||
|
"left stick button": self.buttons[13],
|
||||||
|
"right stick button": self.buttons[14],
|
||||||
|
"logo button": self.buttons[12],
|
||||||
|
"share button": self.buttons[15],
|
||||||
|
"list button": self.buttons[11],
|
||||||
|
"copy button": self.buttons[10]
|
||||||
|
}
|
||||||
|
print(f"{self.name} connected.")
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def handle_input(self, event):
|
def handle_input(self, event):
|
||||||
pass
|
pass
|
||||||
@@ -32,9 +57,14 @@ class XboxSeriesXController:
|
|||||||
def pause(self):
|
def pause(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def rumble(self):
|
def rumble(self, left, right):
|
||||||
pass
|
if not left in range(256) or not right in range(256):
|
||||||
|
raise ValueError("left and right values must be in the range 0 - 255")
|
||||||
|
self.joy.rumble(left, right)
|
||||||
|
|
||||||
|
def stop_rumble(self):
|
||||||
|
self.joy.rumble(0, 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self._name
|
return self._name
|
||||||
|
Reference in New Issue
Block a user