-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathsubwoofer.py
283 lines (221 loc) · 7.74 KB
/
subwoofer.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#!/usr/bin/env python
import subprocess
import atexit
import sys
import os
import time
from subprocess import call
############
# Settings #
############
# For when you don't have headphones in and you want custom volume balance
speakerBalance = 0
# For when YOU DO have headphones in and you want custom volume balance
# (broken debalanced headphones and whatnot)
headphonesBalance = 0
# This helps the subwoofer get the correct left/right stereo in so it sounds like
subwooferBalance = 0 # Default: -25
# This is extra volume for the subwoofer, independent of what stereo balance it gets as input
extraVolume = 0 # Default: -11
pulseaudio_detect_intervals = 5 # Default: 5. No. of seconds between pulseaudio detects.
# The default of this worked on Ubuntu 16.10. It has been reported the script does not work anymore as of January 2021, and so this has been set to 0 to disable the script.
# This number is very important and as of January 2021, it is uncertain what number should be here.
subwoofer_cutoff_volume = 0 # Default: 87 (again, for Ubuntu 16.10 ONLY and not for >newer< Ubuntus)
# These are needed so the detection of volume change / headphones plug in or out not be done
# for all of the enabled audio devices. Change to the alternatives if subwoofer doesn't enable
# or disable on such events.
devId = 1 # alternative would be 0
sinkNo = "1" # alternative would be "" for all
dev="/dev/snd/hwC1D0"
#############
# Functions #
#############
# Global variables
running = True
headphones_in = False
speakers_set = False
headphones_set = False
curr_volume = 0
pactl = None
# Subwoofer part
################
def enable_subwoofer():
# As of January 2021, these have to be checked as they are most likely not correct anymore.
#call(["sudo", "hda-verb", dev, "0x17", "SET_POWER", "0x0"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
#call(["sudo", "hda-verb", dev, "0x1a", "SET_POWER", "0x0"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# As of January 2021, these have to be checked as they are most likely not correct anymore.
#call(["sudo", "hda-verb", dev, "0x17", "0x300", "0xb000"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
#call(["sudo", "hda-verb", dev, "0x17", "0x707", "0x40"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
#call(["sudo", "hda-verb", dev, "0x1a", "0x707", "0x25"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
pass
def disable_subwoofer():
call(["sudo", "hda-verb", dev, "0x1a", "0x707", "0x20"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def set_subwoofer_volume(volumes):
valL = 0xa000 + volumes[0]
valR = 0x9000 + volumes[1]
# As of January 2021, these have to be checked as they are most likely not correct anymore.
#call(["sudo", "hda-verb", dev, "0x03", "0x300", hex(valL)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
#call(["sudo", "hda-verb", dev, "0x03", "0x300", hex(valR)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print "Subwoofer volumes set. Left: " + str(volumes[0]) + ". Right: " + str(volumes[1]) + "."
def calculate_subwoofer_volume(spk_vol, balance):
balL = 100
balR = 100
if balance < 0:
balL = 100
balR = balR + balance
else:
balL = balL - balance
balR = 100
valL = subwoofer_cutoff_volume * spk_vol * balL / 100 / 100 + extraVolume
valR = subwoofer_cutoff_volume * spk_vol * balR / 100 / 100 + extraVolume
vals = calibrate_cutoff_volume([valL, valR])
return vals
def set_subwoofer():
vol = get_biggest_volume()
subVols = calculate_subwoofer_volume(vol, subwooferBalance)
set_subwoofer_volume(subVols)
# Speaker part
##############
def calculate_speaker_balance(spk_vol, balance):
vol = get_biggest_volume()
balL = 100
balR = 100
if balance < 0:
balL = 100
balR = balR + balance
else:
balL = balL - balance
balR = 100
valL = spk_vol * balL / 100
valR = spk_vol * balR / 100
return [valL, valR]
def set_speaker_volumes(volumes):
volumes = calibrate100(volumes)
call(["amixer", "-D", "pulse", "set", "Master", str(volumes[0]) + "%," + str(volumes[1]) + "%"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print "Speaker volumes set. Left: " + str(volumes[0]) + ". Right: " + str(volumes[1])
def set_speakers():
global speakers_set
global headphones_set
if speakers_set:
return
else:
speakers_set = True
headphones_set = False
vol = get_biggest_volume()
spkVols = calculate_speaker_balance(vol, speakerBalance)
set_speaker_volumes( spkVols)
def get_biggest_volume():
volumes = get_volumes()
if len(volumes) == 1:
return volumes[0]
if volumes[0] > volumes[1]:
return volumes[0]
else:
return volumes[1]
def get_volumes():
amixer = subprocess.Popen(["amixer", "-D", "pulse", "get", "Master"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = []
for line in iter(amixer.stdout.readline, ''):
if '%' in line:
vol = line.split('[')[1].split('%]')[0]
output.append(int(vol))
return output
# Headphones part
#################
def set_headphones():
global headphones_set
global speakers_set
if headphones_set:
return
else:
headphones_set = True
speakers_set = False
vol = get_biggest_volume()
spkVols = calculate_speaker_balance(vol, headphonesBalance)
set_speaker_volumes(spkVols)
def headphones_in_query():
global headphones_in
amixer = subprocess.Popen(["amixer", "-c", str(devId), "cget", "numid=22"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
i = 0
count = False
for line in iter(amixer.stdout.readline, ''):
if "numid=22" in line:
count = True
if count:
i = i + 1
if i == 3:
if "values=off" in line:
headphones_in = False
elif "values=on" in line:
headphones_in = True
break
amixer.terminate()
def check_headphones():
headphones_in_query()
if headphones_in:
#print "Headphones in!"
disable_subwoofer()
set_headphones()
else:
#print "Headphones out!"
enable_subwoofer()
set_speakers()
# Additional functions
######################
def calibrate(volumes, limit):
if volumes[0] > limit:
volumes[0] = limit
elif volumes[0] < 0:
volumes[0] = 0
if volumes[1] > limit:
volumes[1] = limit
elif volumes[1] < 0:
volumes[1] = 0
return [volumes[0], volumes[1]]
def calibrate100(volumes):
return calibrate(volumes, 100)
def calibrate_cutoff_volume(volumes):
return calibrate(volumes, subwoofer_cutoff_volume)
def check_volume():
global curr_volume
new_volume = get_biggest_volume()
if curr_volume != new_volume:
curr_volume = new_volume
print "Volume change detected: ", curr_volume
if headphones_in == False:
set_subwoofer()
def on_exit():
global pactl
if pactl is not None:
pactl.terminate()
disable_subwoofer()
########
# Main #
########
if __name__ == "__main__":
if ("exit" in sys.argv) or (("pre" in sys.argv) and
(("suspend" in sys.argv) or ("hibernate" in sys.argv) or ("hybrid-sleep" in sys.argv))):
on_exit()
sys.exit(0)
atexit.register(on_exit)
pulseaudio_detected = False
while pulseaudio_detected is False:
pgrep = subprocess.Popen(["pgrep","-u",str(os.getuid()),"pulseaudio"], stdout=subprocess.PIPE)
for line in iter(pgrep.stdout.readline, ''):
if line.strip():
print "Pulseaudio detected"
pulseaudio_detected = True
else:
time.sleep(pulseaudio_detect_intervals)
if pgrep is not None:
pgrep.terminate()
headphones_in_query()
if headphones_in == False:
enable_subwoofer()
set_subwoofer()
set_speakers()
pactl = subprocess.Popen(["pactl", "subscribe"], stdout=subprocess.PIPE)
for line in iter(pactl.stdout.readline, ''):
if "Event 'change' on sink #" + sinkNo in line:
check_headphones()
check_volume()