Added mapping. /JL

This commit is contained in:
2025-04-20 09:11:54 +02:00
parent 971e18e633
commit 354fa7364d
10 changed files with 388 additions and 267 deletions

View File

@@ -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})")

View File

@@ -18,6 +18,27 @@ class DualSenseController(ControlsBase):
self.powerlevel = self.device.battery.Level
self.batterystate = BATTERY_STATE[str(self.device.battery.State)]
self.set_player_id(PlayerID.PLAYER_1)
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"Power level: {self.powerlevel}")
print(f"Battery state: {self.batterystate}")

View File

@@ -1,199 +1,87 @@
import time
import threading
import numpy as np
import sounddevice as sd
import alsaaudio
import pulsectl
from pygameControls.controlsbase import ControlsBase
from pydualsense import *
class DualSenseEdgeController:
def __init__(self):
# DualSense input/output interface
self.ds = pydualsense()
self.ds.init()
self._listening = False
self._bindings = {}
# Audio detection
self.alsa_devices = self._get_alsa_devices()
self.pulse_devices = self._get_pulseaudio_devices()
self.dualsense_audio_device = self._detect_dualsense_audio()
print("DualSense initialized.")
# ---------------------- Device Controls ----------------------
def set_rumble(self, small_motor: int, big_motor: int):
self.ds.setRumble(small_motor, big_motor)
def stop_rumble(self):
self.set_rumble(0, 0)
def set_led_color(self, r: int, g: int, b: int):
self.ds.setLightBarColor(r, g, b)
def set_trigger_effects(self, left_mode='Off', right_mode='Off', force=0):
left = getattr(TriggerModes, left_mode.upper(), TriggerModes.Off)
right = getattr(TriggerModes, right_mode.upper(), TriggerModes.Off)
self.ds.triggerL.setMode(left)
self.ds.triggerR.setMode(right)
if force > 0:
self.ds.triggerL.setForce(force)
self.ds.triggerR.setForce(force)
# ---------------------- Predefined Rumble Patterns ----------------------
def rumble_pattern(self, pattern: str, duration: float = 1.0):
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__":
class DualSenseEdgeController(ControlsBase):
def __init__(self, joy):
self.device = pydualsense()
self.device.init()
self.name = self.device.device.get_product_string()
self.powerlevel = self.device.battery.Level
self.batterystate = BATTERY_STATE[str(self.device.battery.State)]
self.set_player_id(PlayerID.PLAYER_1)
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"Power level: {self.powerlevel}")
print(f"Battery state: {self.batterystate}")
controller = DualSenseController()
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
controller.bind("cross", lambda: controller.rumble_pattern("heartbeat", 1.5))
controller.bind("circle", lambda: controller.rumble_pattern("buzz", 0.5))
controller.bind("triangle", lambda: controller.rumble_pattern("pulse", 2))
controller.bind("square", lambda: controller.set_led_color(255, 0, 0))
# Start listening
controller.start_input_listener()
def set_player_id(self, playerid: PlayerID):
self.device.light.setPlayerID(playerid)
def left(self):
pass
def right(self):
pass
def up(self):
pass
def down(self):
pass
def pause(self):
pass
def rumble(self):
pass
@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

View File

@@ -13,6 +13,28 @@ class GenericController(ControlsBase):
self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)]
self.numbuttons: int = self.device.get_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 handle_input(self, event):
pass

View File

@@ -34,7 +34,29 @@ class LogitechDualActionController(ControlsBase):
self.numbuttons: int = self.device.get_numbuttons()
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
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 handle_input(self, event):
pass

View File

@@ -34,6 +34,28 @@ class LogitechF310Controller(ControlsBase):
self.numbuttons: int = self.device.get_numbuttons()
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
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 handle_input(self, event):
pass

View File

@@ -36,7 +36,28 @@ class LogitechF510Controller(ControlsBase):
self.numbuttons: int = self.device.get_numbuttons()
self.buttons: list = []
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 handle_input(self, event):
pass

View File

@@ -36,7 +36,28 @@ class LogitechF710Controller(ControlsBase):
self.numbuttons: int = self.device.get_numbuttons()
self.buttons: list = []
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 handle_input(self, event):
pass

View File

@@ -13,6 +13,29 @@ class XboxSeriesXController:
self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)]
self.numbuttons: int = self.device.get_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 handle_input(self, event):
pass