pygameControls 0.0.4__tar.gz → 0.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/PKG-INFO +1 -1
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls/dualsense_controller.py +21 -0
- pygamecontrols-0.0.5/pygameControls/dualsense_edge_controller.py +87 -0
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls/generic_controller.py +22 -0
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls/logitech_dual_action_controller.py +23 -1
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls/logitech_f310_controller.py +22 -0
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls/logitech_f510_controller.py +22 -1
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls/logitech_f710_controller.py +22 -1
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls/xbox_series_x_controller.py +23 -0
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls.egg-info/PKG-INFO +1 -1
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls.egg-info/SOURCES.txt +0 -1
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/setup.py +1 -1
- pygamecontrols-0.0.4/pygameControls/dualsense_audio.py +0 -70
- pygamecontrols-0.0.4/pygameControls/dualsense_edge_controller.py +0 -199
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/LICENSE +0 -0
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/README.md +0 -0
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls/__init__.py +0 -0
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls/controller.py +0 -0
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls/controlsbase.py +0 -0
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls.egg-info/dependency_links.txt +0 -0
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls.egg-info/top_level.txt +0 -0
- {pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/setup.cfg +0 -0
@@ -18,6 +18,27 @@ class DualSenseController(ControlsBase):
|
|
18
18
|
self.powerlevel = self.device.battery.Level
|
19
19
|
self.batterystate = BATTERY_STATE[str(self.device.battery.State)]
|
20
20
|
self.set_player_id(PlayerID.PLAYER_1)
|
21
|
+
self.mapping = {
|
22
|
+
"left stick x": self.axis[0],
|
23
|
+
"left stick y": self.axis[1],
|
24
|
+
"right stick x": self.axis[3],
|
25
|
+
"right stick y": self.axis[4],
|
26
|
+
"right trigger": self.buttons[5],
|
27
|
+
"left trigger": self.buttons[2],
|
28
|
+
"dhat x": self.hats[0][0],
|
29
|
+
"dhat y": self.hats[0][1],
|
30
|
+
"left button": self.buttons[4],
|
31
|
+
"right button": self.buttons[5],
|
32
|
+
"cross button": self.buttons[0],
|
33
|
+
"triangle button": self.buttons[2],
|
34
|
+
"circle button": self.buttons[1],
|
35
|
+
"square button": self.buttons[3],
|
36
|
+
"left stick button": self.buttons[11],
|
37
|
+
"right stick button": self.buttons[12],
|
38
|
+
"connect button": self.buttons[8],
|
39
|
+
"list button": self.buttons[9],
|
40
|
+
"logo button": self.buttons[10]
|
41
|
+
}
|
21
42
|
print(f"{self.name} connected")
|
22
43
|
print(f"Power level: {self.powerlevel}")
|
23
44
|
print(f"Battery state: {self.batterystate}")
|
@@ -0,0 +1,87 @@
|
|
1
|
+
from pygameControls.controlsbase import ControlsBase
|
2
|
+
from pydualsense import *
|
3
|
+
|
4
|
+
|
5
|
+
class DualSenseEdgeController(ControlsBase):
|
6
|
+
def __init__(self, joy):
|
7
|
+
self.device = pydualsense()
|
8
|
+
self.device.init()
|
9
|
+
self.name = self.device.device.get_product_string()
|
10
|
+
self.powerlevel = self.device.battery.Level
|
11
|
+
self.batterystate = BATTERY_STATE[str(self.device.battery.State)]
|
12
|
+
self.set_player_id(PlayerID.PLAYER_1)
|
13
|
+
self.mapping = {
|
14
|
+
"left stick x": self.axis[0],
|
15
|
+
"left stick y": self.axis[1],
|
16
|
+
"right stick x": self.axis[3],
|
17
|
+
"right stick y": self.axis[4],
|
18
|
+
"right trigger": self.buttons[5],
|
19
|
+
"left trigger": self.buttons[2],
|
20
|
+
"dhat x": self.hats[0][0],
|
21
|
+
"dhat y": self.hats[0][1],
|
22
|
+
"left button": self.buttons[4],
|
23
|
+
"right button": self.buttons[5],
|
24
|
+
"cross button": self.buttons[0],
|
25
|
+
"triangle button": self.buttons[2],
|
26
|
+
"circle button": self.buttons[1],
|
27
|
+
"square button": self.buttons[3],
|
28
|
+
"left stick button": self.buttons[11],
|
29
|
+
"right stick button": self.buttons[12],
|
30
|
+
"connect button": self.buttons[8],
|
31
|
+
"list button": self.buttons[9],
|
32
|
+
"logo button": self.buttons[10]
|
33
|
+
}
|
34
|
+
print(f"{self.name} connected")
|
35
|
+
print(f"Power level: {self.powerlevel}")
|
36
|
+
print(f"Battery state: {self.batterystate}")
|
37
|
+
|
38
|
+
def handle_input(self, event):
|
39
|
+
pass
|
40
|
+
|
41
|
+
def set_led(self, red: int, green: int, blue: int):
|
42
|
+
self.device.light.setColorI(red, green, blue)
|
43
|
+
|
44
|
+
def set_player_id(self, playerid: PlayerID):
|
45
|
+
self.device.light.setPlayerID(playerid)
|
46
|
+
|
47
|
+
def left(self):
|
48
|
+
pass
|
49
|
+
|
50
|
+
def right(self):
|
51
|
+
pass
|
52
|
+
|
53
|
+
def up(self):
|
54
|
+
pass
|
55
|
+
|
56
|
+
def down(self):
|
57
|
+
pass
|
58
|
+
|
59
|
+
def pause(self):
|
60
|
+
pass
|
61
|
+
|
62
|
+
def rumble(self):
|
63
|
+
pass
|
64
|
+
|
65
|
+
@property
|
66
|
+
def name(self) -> str:
|
67
|
+
return self._name
|
68
|
+
|
69
|
+
@name.setter
|
70
|
+
def name(self, name: str) -> None:
|
71
|
+
self._name = name
|
72
|
+
|
73
|
+
@property
|
74
|
+
def powerlevel(self) -> str:
|
75
|
+
return self._powerlevel
|
76
|
+
|
77
|
+
@powerlevel.setter
|
78
|
+
def powerlevel(self, lvl: str) -> None:
|
79
|
+
self._powerlevel = lvl
|
80
|
+
|
81
|
+
@property
|
82
|
+
def batterystate(self) -> int:
|
83
|
+
return self._batterystate
|
84
|
+
|
85
|
+
@batterystate.setter
|
86
|
+
def batterystate(self, state) -> None:
|
87
|
+
self._batterystate = state
|
@@ -13,6 +13,28 @@ class GenericController(ControlsBase):
|
|
13
13
|
self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)]
|
14
14
|
self.numbuttons: int = self.device.get_numbuttons()
|
15
15
|
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
16
|
+
self.mapping = {
|
17
|
+
"left stick x": self.axis[0],
|
18
|
+
"left stick y": self.axis[1],
|
19
|
+
"right stick x": self.axis[2],
|
20
|
+
"right stick y": self.axis[3],
|
21
|
+
"right trigger": self.buttons[7],
|
22
|
+
"left trigger": self.buttons[6],
|
23
|
+
"dhat x": self.hats[0][0],
|
24
|
+
"dhat y": self.hats[0][1],
|
25
|
+
"left button": self.buttons[4],
|
26
|
+
"right button": self.buttons[5],
|
27
|
+
"X button": self.buttons[0],
|
28
|
+
"Y button": self.buttons[3],
|
29
|
+
"A button": self.buttons[1],
|
30
|
+
"B button": self.buttons[2],
|
31
|
+
"left stick button": self.buttons[10],
|
32
|
+
"right stick button": self.buttons[11],
|
33
|
+
"back button": self.buttons[8],
|
34
|
+
"start button": self.buttons[9],
|
35
|
+
"logo button": None
|
36
|
+
}
|
37
|
+
print(f"{self.name} connected.")
|
16
38
|
|
17
39
|
def handle_input(self, event):
|
18
40
|
pass
|
{pygamecontrols-0.0.4 → pygamecontrols-0.0.5}/pygameControls/logitech_dual_action_controller.py
RENAMED
@@ -34,7 +34,29 @@ class LogitechDualActionController(ControlsBase):
|
|
34
34
|
self.numbuttons: int = self.device.get_numbuttons()
|
35
35
|
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
36
36
|
self.input_mode = InputMode.DirectInput
|
37
|
-
|
37
|
+
self.mapping = {
|
38
|
+
"left stick x": self.axis[0],
|
39
|
+
"left stick y": self.axis[1],
|
40
|
+
"right stick x": self.axis[2],
|
41
|
+
"right stick y": self.axis[3],
|
42
|
+
"right trigger": self.buttons[7],
|
43
|
+
"left trigger": self.buttons[6],
|
44
|
+
"dhat x": self.hats[0][0],
|
45
|
+
"dhat y": self.hats[0][1],
|
46
|
+
"left button": self.buttons[4],
|
47
|
+
"right button": self.buttons[5],
|
48
|
+
"X button": self.buttons[0],
|
49
|
+
"Y button": self.buttons[3],
|
50
|
+
"A button": self.buttons[1],
|
51
|
+
"B button": self.buttons[2],
|
52
|
+
"left stick button": self.buttons[10],
|
53
|
+
"right stick button": self.buttons[11],
|
54
|
+
"back button": self.buttons[8],
|
55
|
+
"start button": self.buttons[9],
|
56
|
+
"logo button": None
|
57
|
+
}
|
58
|
+
print(f"{self.name} connected.")
|
59
|
+
|
38
60
|
def handle_input(self, event):
|
39
61
|
pass
|
40
62
|
|
@@ -34,6 +34,28 @@ class LogitechF310Controller(ControlsBase):
|
|
34
34
|
self.numbuttons: int = self.device.get_numbuttons()
|
35
35
|
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
36
36
|
self.input_mode = InputMode.XInput
|
37
|
+
self.mapping = {
|
38
|
+
"left stick x": self.axis[0],
|
39
|
+
"left stick y": self.axis[1],
|
40
|
+
"right stick x": self.axis[3],
|
41
|
+
"right stick y": self.axis[4],
|
42
|
+
"right trigger": self.axis[2],
|
43
|
+
"left trigger": self.axis[5],
|
44
|
+
"dhat x": self.hats[0][0],
|
45
|
+
"dhat y": self.hats[0][1],
|
46
|
+
"left button": self.buttons[4],
|
47
|
+
"right button": self.buttons[5],
|
48
|
+
"X button": self.buttons[2],
|
49
|
+
"Y button": self.buttons[3],
|
50
|
+
"A button": self.buttons[0],
|
51
|
+
"B button": self.buttons[1],
|
52
|
+
"left stick button": self.buttons[9],
|
53
|
+
"right stick button": self.buttons[10],
|
54
|
+
"back button": self.buttons[6],
|
55
|
+
"start button": self.buttons[7],
|
56
|
+
"logo button": self.buttons[8]
|
57
|
+
}
|
58
|
+
print(f"{self.name} connected.")
|
37
59
|
|
38
60
|
def handle_input(self, event):
|
39
61
|
pass
|
@@ -36,7 +36,28 @@ class LogitechF510Controller(ControlsBase):
|
|
36
36
|
self.numbuttons: int = self.device.get_numbuttons()
|
37
37
|
self.buttons: list = []
|
38
38
|
self.input_mode: InputMode.DirectInput
|
39
|
-
self.
|
39
|
+
self.mapping = {
|
40
|
+
"left stick x": self.axis[0],
|
41
|
+
"left stick y": self.axis[1],
|
42
|
+
"right stick x": self.axis[3],
|
43
|
+
"right stick y": self.axis[4],
|
44
|
+
"right trigger": self.axis[2],
|
45
|
+
"left trigger": self.axis[5],
|
46
|
+
"dhat x": self.hats[0][0],
|
47
|
+
"dhat y": self.hats[0][1],
|
48
|
+
"left button": self.buttons[4],
|
49
|
+
"right button": self.buttons[5],
|
50
|
+
"X button": self.buttons[2],
|
51
|
+
"Y button": self.buttons[3],
|
52
|
+
"A button": self.buttons[0],
|
53
|
+
"B button": self.buttons[1],
|
54
|
+
"left stick button": self.buttons[9],
|
55
|
+
"right stick button": self.buttons[10],
|
56
|
+
"back button": self.buttons[6],
|
57
|
+
"start button": self.buttons[7],
|
58
|
+
"logo button": self.buttons[8]
|
59
|
+
}
|
60
|
+
print(f"{self.name} connected.")
|
40
61
|
|
41
62
|
def handle_input(self, event):
|
42
63
|
pass
|
@@ -36,7 +36,28 @@ class LogitechF710Controller(ControlsBase):
|
|
36
36
|
self.numbuttons: int = self.device.get_numbuttons()
|
37
37
|
self.buttons: list = []
|
38
38
|
self.input_mode: InputMode.DirectInput
|
39
|
-
self.
|
39
|
+
self.mapping = {
|
40
|
+
"left stick x": self.axis[0],
|
41
|
+
"left stick y": self.axis[1],
|
42
|
+
"right stick x": self.axis[3],
|
43
|
+
"right stick y": self.axis[4],
|
44
|
+
"right trigger": self.axis[2],
|
45
|
+
"left trigger": self.axis[5],
|
46
|
+
"dhat x": self.hats[0][0],
|
47
|
+
"dhat y": self.hats[0][1],
|
48
|
+
"left button": self.buttons[4],
|
49
|
+
"right button": self.buttons[5],
|
50
|
+
"X button": self.buttons[2],
|
51
|
+
"Y button": self.buttons[3],
|
52
|
+
"A button": self.buttons[0],
|
53
|
+
"B button": self.buttons[1],
|
54
|
+
"left stick button": self.buttons[9],
|
55
|
+
"right stick button": self.buttons[10],
|
56
|
+
"back button": self.buttons[6],
|
57
|
+
"start button": self.buttons[7],
|
58
|
+
"logo button": self.buttons[8]
|
59
|
+
}
|
60
|
+
print(f"{self.name} connected.")
|
40
61
|
|
41
62
|
def handle_input(self, event):
|
42
63
|
pass
|
@@ -13,6 +13,29 @@ class XboxSeriesXController:
|
|
13
13
|
self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)]
|
14
14
|
self.numbuttons: int = self.device.get_numbuttons()
|
15
15
|
self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)]
|
16
|
+
self.mapping = {
|
17
|
+
"left stick x": self.axis[0],
|
18
|
+
"left stick y": self.axis[1],
|
19
|
+
"right stick x": self.axis[2],
|
20
|
+
"right stick y": self.axis[3],
|
21
|
+
"right trigger": self.axis[4],
|
22
|
+
"left trigger": self.axis[5],
|
23
|
+
"dhat x": self.hats[0][0],
|
24
|
+
"dhat y": self.hats[0][1],
|
25
|
+
"left button": self.buttons[6],
|
26
|
+
"right button": self.buttons[7],
|
27
|
+
"X button": self.buttons[3],
|
28
|
+
"Y button": self.buttons[4],
|
29
|
+
"A button": self.buttons[0],
|
30
|
+
"B button": self.buttons[1],
|
31
|
+
"left stick button": self.buttons[13],
|
32
|
+
"right stick button": self.buttons[14],
|
33
|
+
"logo button": self.buttons[12],
|
34
|
+
"share button": self.buttons[15],
|
35
|
+
"list button": self.buttons[11],
|
36
|
+
"copy button": self.buttons[10]
|
37
|
+
}
|
38
|
+
print(f"{self.name} connected.")
|
16
39
|
|
17
40
|
def handle_input(self, event):
|
18
41
|
pass
|
@@ -1,70 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import time
|
3
|
-
import numpy as np
|
4
|
-
import sounddevice as sd
|
5
|
-
import alsaaudio
|
6
|
-
import pulsectl
|
7
|
-
|
8
|
-
class DualSenseAudio:
|
9
|
-
def __init__(self):
|
10
|
-
self.alsa_devices = self._get_alsa_devices()
|
11
|
-
self.pulse_devices = self._get_pulseaudio_devices()
|
12
|
-
self.dualsense_device = self._detect_dualsense()
|
13
|
-
|
14
|
-
def _get_alsa_devices(self):
|
15
|
-
try:
|
16
|
-
cards = alsaaudio.cards()
|
17
|
-
return cards
|
18
|
-
except Exception as e:
|
19
|
-
print("ALSA detection failed:", e)
|
20
|
-
return []
|
21
|
-
|
22
|
-
def _get_pulseaudio_devices(self):
|
23
|
-
try:
|
24
|
-
pulse = pulsectl.Pulse("dualsense-audio")
|
25
|
-
sinks = pulse.sink_list()
|
26
|
-
return sinks
|
27
|
-
except Exception as e:
|
28
|
-
print("PulseAudio detection failed:", e)
|
29
|
-
return []
|
30
|
-
|
31
|
-
def _detect_dualsense(self):
|
32
|
-
# Check ALSA names
|
33
|
-
for card in self.alsa_devices:
|
34
|
-
if "DualSense" in card:
|
35
|
-
return {'type': 'alsa', 'name': card}
|
36
|
-
|
37
|
-
# Check PulseAudio sinks
|
38
|
-
for sink in self.pulse_devices:
|
39
|
-
if "dualsense" in sink.description.lower():
|
40
|
-
return {'type': 'pulse', 'name': sink.name}
|
41
|
-
|
42
|
-
return None
|
43
|
-
|
44
|
-
def play_tone(self, frequency=440.0, duration=2.0, volume=0.5):
|
45
|
-
if not self.dualsense_device:
|
46
|
-
print("DualSense speaker not found.")
|
47
|
-
return
|
48
|
-
|
49
|
-
print(f"Playing tone on DualSense ({self.dualsense_device['type']})...")
|
50
|
-
|
51
|
-
fs = 48000 # Sample rate
|
52
|
-
t = np.linspace(0, duration, int(fs * duration), False)
|
53
|
-
tone = np.sin(frequency * 2 * np.pi * t) * volume
|
54
|
-
audio = tone.astype(np.float32)
|
55
|
-
|
56
|
-
if self.dualsense_device['type'] == 'pulse':
|
57
|
-
sd.play(audio, samplerate=fs, device=self.dualsense_device['name'])
|
58
|
-
elif self.dualsense_device['type'] == 'alsa':
|
59
|
-
device_index = self.alsa_devices.index(self.dualsense_device['name'])
|
60
|
-
sd.play(audio, samplerate=fs, device=device_index)
|
61
|
-
sd.wait()
|
62
|
-
|
63
|
-
def list_devices(self):
|
64
|
-
print("ALSA Devices:")
|
65
|
-
for card in self.alsa_devices:
|
66
|
-
print(f" - {card}")
|
67
|
-
print("\nPulseAudio Devices:")
|
68
|
-
for sink in self.pulse_devices:
|
69
|
-
print(f" - {sink.name} ({sink.description})")
|
70
|
-
|
@@ -1,199 +0,0 @@
|
|
1
|
-
import time
|
2
|
-
import threading
|
3
|
-
import numpy as np
|
4
|
-
import sounddevice as sd
|
5
|
-
import alsaaudio
|
6
|
-
import pulsectl
|
7
|
-
from pydualsense import *
|
8
|
-
|
9
|
-
|
10
|
-
class DualSenseEdgeController:
|
11
|
-
def __init__(self):
|
12
|
-
# DualSense input/output interface
|
13
|
-
self.ds = pydualsense()
|
14
|
-
self.ds.init()
|
15
|
-
self._listening = False
|
16
|
-
self._bindings = {}
|
17
|
-
|
18
|
-
# Audio detection
|
19
|
-
self.alsa_devices = self._get_alsa_devices()
|
20
|
-
self.pulse_devices = self._get_pulseaudio_devices()
|
21
|
-
self.dualsense_audio_device = self._detect_dualsense_audio()
|
22
|
-
|
23
|
-
print("DualSense initialized.")
|
24
|
-
|
25
|
-
# ---------------------- Device Controls ----------------------
|
26
|
-
|
27
|
-
def set_rumble(self, small_motor: int, big_motor: int):
|
28
|
-
self.ds.setRumble(small_motor, big_motor)
|
29
|
-
|
30
|
-
def stop_rumble(self):
|
31
|
-
self.set_rumble(0, 0)
|
32
|
-
|
33
|
-
def set_led_color(self, r: int, g: int, b: int):
|
34
|
-
self.ds.setLightBarColor(r, g, b)
|
35
|
-
|
36
|
-
def set_trigger_effects(self, left_mode='Off', right_mode='Off', force=0):
|
37
|
-
left = getattr(TriggerModes, left_mode.upper(), TriggerModes.Off)
|
38
|
-
right = getattr(TriggerModes, right_mode.upper(), TriggerModes.Off)
|
39
|
-
self.ds.triggerL.setMode(left)
|
40
|
-
self.ds.triggerR.setMode(right)
|
41
|
-
if force > 0:
|
42
|
-
self.ds.triggerL.setForce(force)
|
43
|
-
self.ds.triggerR.setForce(force)
|
44
|
-
|
45
|
-
# ---------------------- Predefined Rumble Patterns ----------------------
|
46
|
-
|
47
|
-
def rumble_pattern(self, pattern: str, duration: float = 1.0):
|
48
|
-
patterns = {
|
49
|
-
"pulse": self._pulse_rumble,
|
50
|
-
"heartbeat": self._heartbeat_rumble,
|
51
|
-
"buzz": self._buzz_rumble,
|
52
|
-
"wave": self._wave_rumble,
|
53
|
-
"alarm": self._alarm_rumble,
|
54
|
-
}
|
55
|
-
if pattern in patterns:
|
56
|
-
threading.Thread(target=patterns[pattern], args=(duration,), daemon=True).start()
|
57
|
-
else:
|
58
|
-
print(f"Unknown rumble pattern: {pattern}")
|
59
|
-
|
60
|
-
def _pulse_rumble(self, duration):
|
61
|
-
end = time.time() + duration
|
62
|
-
while time.time() < end:
|
63
|
-
self.set_rumble(50, 150)
|
64
|
-
time.sleep(0.2)
|
65
|
-
self.stop_rumble()
|
66
|
-
time.sleep(0.2)
|
67
|
-
|
68
|
-
def _heartbeat_rumble(self, duration):
|
69
|
-
end = time.time() + duration
|
70
|
-
while time.time() < end:
|
71
|
-
self.set_rumble(200, 200)
|
72
|
-
time.sleep(0.1)
|
73
|
-
self.stop_rumble()
|
74
|
-
time.sleep(0.1)
|
75
|
-
self.set_rumble(100, 100)
|
76
|
-
time.sleep(0.1)
|
77
|
-
self.stop_rumble()
|
78
|
-
time.sleep(0.4)
|
79
|
-
|
80
|
-
def _buzz_rumble(self, duration):
|
81
|
-
self.set_rumble(80, 255)
|
82
|
-
time.sleep(duration)
|
83
|
-
self.stop_rumble()
|
84
|
-
|
85
|
-
def _wave_rumble(self, duration):
|
86
|
-
start = time.time()
|
87
|
-
while time.time() - start < duration:
|
88
|
-
for i in range(0, 256, 25):
|
89
|
-
self.set_rumble(i, 255 - i)
|
90
|
-
time.sleep(0.05)
|
91
|
-
for i in reversed(range(0, 256, 25)):
|
92
|
-
self.set_rumble(i, 255 - i)
|
93
|
-
time.sleep(0.05)
|
94
|
-
self.stop_rumble()
|
95
|
-
|
96
|
-
def _alarm_rumble(self, duration):
|
97
|
-
end = time.time() + duration
|
98
|
-
while time.time() < end:
|
99
|
-
self.set_rumble(255, 0)
|
100
|
-
time.sleep(0.1)
|
101
|
-
self.set_rumble(0, 255)
|
102
|
-
time.sleep(0.1)
|
103
|
-
self.stop_rumble()
|
104
|
-
|
105
|
-
# ---------------------- Input Listener + Bindings ----------------------
|
106
|
-
|
107
|
-
def bind(self, button: str, action: callable):
|
108
|
-
"""Bind a button to a callable. Ex: controller.bind('cross', lambda: rumble_pattern('buzz'))"""
|
109
|
-
self._bindings[button] = action
|
110
|
-
|
111
|
-
def start_input_listener(self):
|
112
|
-
def listen():
|
113
|
-
while self._listening:
|
114
|
-
#self.ds.update()
|
115
|
-
for button, action in self._bindings.items():
|
116
|
-
if getattr(self.ds, button, False):
|
117
|
-
action()
|
118
|
-
self._listening = True
|
119
|
-
thread = threading.Thread(target=listen, daemon=True)
|
120
|
-
thread.start()
|
121
|
-
|
122
|
-
def stop_input_listener(self):
|
123
|
-
self._listening = False
|
124
|
-
|
125
|
-
# ---------------------- Audio Output ----------------------
|
126
|
-
|
127
|
-
def _get_alsa_devices(self):
|
128
|
-
try:
|
129
|
-
return alsaaudio.cards()
|
130
|
-
except Exception:
|
131
|
-
return []
|
132
|
-
|
133
|
-
def _get_pulseaudio_devices(self):
|
134
|
-
try:
|
135
|
-
pulse = pulsectl.Pulse("dualsense-audio")
|
136
|
-
return pulse.sink_list()
|
137
|
-
except Exception:
|
138
|
-
return []
|
139
|
-
|
140
|
-
def _detect_dualsense_audio(self):
|
141
|
-
# Check ALSA names
|
142
|
-
for card in self.alsa_devices:
|
143
|
-
if "DualSense" in card:
|
144
|
-
return {'type': 'alsa', 'name': card}
|
145
|
-
|
146
|
-
# Check PulseAudio sinks
|
147
|
-
for sink in self.pulse_devices:
|
148
|
-
if "dualsense" in sink.description.lower():
|
149
|
-
return {'type': 'pulse', 'name': sink.name}
|
150
|
-
|
151
|
-
return None
|
152
|
-
|
153
|
-
def play_tone(self, frequency=440.0, duration=2.0, volume=0.5):
|
154
|
-
if not self.dualsense_audio_device:
|
155
|
-
print("DualSense speaker not detected.")
|
156
|
-
return
|
157
|
-
|
158
|
-
print(f"Playing tone on DualSense ({self.dualsense_audio_device['type']})...")
|
159
|
-
|
160
|
-
fs = 48000 # Sample rate
|
161
|
-
t = np.linspace(0, duration, int(fs * duration), False)
|
162
|
-
tone = np.sin(frequency * 2 * np.pi * t) * volume
|
163
|
-
audio = tone.astype(np.float32)
|
164
|
-
|
165
|
-
try:
|
166
|
-
if self.dualsense_audio_device['type'] == 'pulse':
|
167
|
-
sd.play(audio, samplerate=fs, device=self.dualsense_audio_device['name'])
|
168
|
-
elif self.dualsense_audio_device['type'] == 'alsa':
|
169
|
-
device_index = self.alsa_devices.index(self.dualsense_audio_device['name'])
|
170
|
-
sd.play(audio, samplerate=fs, device=device_index)
|
171
|
-
sd.wait()
|
172
|
-
except Exception as e:
|
173
|
-
print("Failed to play tone:", e)
|
174
|
-
|
175
|
-
def list_audio_devices(self):
|
176
|
-
print("ALSA Devices:")
|
177
|
-
for card in self.alsa_devices:
|
178
|
-
print(f" - {card}")
|
179
|
-
print("\nPulseAudio Devices:")
|
180
|
-
for sink in self.pulse_devices:
|
181
|
-
print(f" - {sink.name} ({sink.description})")
|
182
|
-
|
183
|
-
# ---------------------- Cleanup ----------------------
|
184
|
-
|
185
|
-
def close(self):
|
186
|
-
self.ds.close()
|
187
|
-
|
188
|
-
if __name__ == "__main__":
|
189
|
-
|
190
|
-
controller = DualSenseController()
|
191
|
-
|
192
|
-
# Bind buttons to patterns
|
193
|
-
controller.bind("cross", lambda: controller.rumble_pattern("heartbeat", 1.5))
|
194
|
-
controller.bind("circle", lambda: controller.rumble_pattern("buzz", 0.5))
|
195
|
-
controller.bind("triangle", lambda: controller.rumble_pattern("pulse", 2))
|
196
|
-
controller.bind("square", lambda: controller.set_led_color(255, 0, 0))
|
197
|
-
|
198
|
-
# Start listening
|
199
|
-
controller.start_input_listener()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|