-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
123 lines (108 loc) · 3.87 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import subprocess
import os
os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
import argparse
import time
from pathlib import Path
from threading import Thread
import pygame
from pydub import AudioSegment, playback
from rich.console import Console
from rich.progress import track
class DualSub:
def __init__(self):
self.c = Console()
pygame.init()
pygame.joystick.init()
self.joysticks = [
pygame.joystick.Joystick(x) for x in range(pygame.joystick.get_count())
]
self.bass_chunks = []
def chunk_bass_audio(self, audio: AudioSegment, chunk_size: int):
for i in track(
list(range(0, len(audio), chunk_size)),
description="[b green]Chunking audio[/]",
):
chunk = audio[i : i + chunk_size] # type: AudioSegment
low_hz = chunk.low_pass_filter(80) # type: AudioSegment
if low_hz.max > 12500:
self.bass_chunks.append(2)
elif low_hz.max > 10000:
self.bass_chunks.append(1)
else:
self.bass_chunks.append(0)
def run(self, args):
if os.name != "posix":
self.c.print("[--led] `{}` is not supported yet".format(os.name))
args.led = False
if not self.joysticks:
if args.led:
self.c.print(
"[red]No joystick found. Please connect a joystick and try again. Program still work with LED arg[/red]"
)
else:
return self.c.print(
"[red]No joysticks found. Please connect one and try again.[/]"
)
for j in self.joysticks:
j.stop_rumble()
audio_file = args.input # type: str
if not Path(audio_file).exists():
self.c.print(f"File {audio_file} does not exist", style="bold red")
return
audio = AudioSegment.from_file(audio_file) # type: AudioSegment
chunk_size = 100 # ms
self.chunk_bass_audio(audio, chunk_size)
play_thread = Thread(target=playback.play, args=(audio,))
if self.joysticks:
vibro_thread = Thread(
target=self.thread_rumble,
args=(
self.bass_chunks,
chunk_size,
),
)
vibro_thread.start()
if args.led:
led_thread = Thread(
target=self.thread_led,
args=(self.bass_chunks,),
)
led_thread.start()
play_thread.start()
@staticmethod
def thread_led(chunks):
for i in chunks:
if i == 2:
subprocess.call(["xset", "led", "named", "Scroll Lock"])
else:
subprocess.call(["xset", "-led", "named", "Scroll Lock"])
time.sleep(0.1)
def thread_rumble(self, chunks, chunk_size: int):
for i in track(
chunks,
total=len(chunks),
description="[b green]Playing audio[/]",
):
if i == 2:
self.joysticks[0].rumble(1, 1, chunk_size // 10)
elif i == 1:
self.joysticks[0].rumble(0.5, 0.5, chunk_size // 100)
else:
self.joysticks[0].stop_rumble()
time.sleep(0.1)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='Turn your DualShock4 into "subwoofer"'
)
parser.add_argument("-i", "--input", help="Input audio file", required=True)
parser.add_argument(
"-l",
"--led",
help="Turn keyboard blinking (If ur kbd use ScrollLock for LED)",
default=False,
action="store_true",
)
args = parser.parse_args()
ex = DualSub()
ex.run(args)