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
|
from collections import namedtuple
import pacmd
class SinkSource:
def __init__(self, pitem, kind_of_thing):
assert type(pitem) == pacmd.Item
self._i = pitem
self._kind_of_thing = kind_of_thing
def kind(self):
return self._kind_of_thing
def name(self):
return self._i.ch["name"].value
def description(self):
return self._i.ch["properties"].ch["device.description"].value
def default(self):
return self._i.default
def index(self):
return self._i.index
def state(self):
return self._i.ch["state"].value
def muted(self):
return _parse_muted(self._i)
def volume(self):
return _parse_volume(self._i)
def set_volume(self, vol):
assert type(vol) == float or type(vol) == int
vol = max(0.0, min(1.0, vol))
vol = round(vol * _get_maxvol(self._i))
self.set_raw_volume(vol)
def set_raw_volume(self, vol):
assert type(vol) == float or type(vol) == int
pacmd.pacmd("set-{}-volume".format(self._kind_of_thing),
str(self.index()), str(vol))
def set_muted(self, yes):
pacmd.pacmd("set-{}-mute".format(self._kind_of_thing),
str(self.index()), "true" if yes else "false")
def set_default(self):
pacmd.pacmd("set-default-{}".format(self._kind_of_thing),
str(self.index()))
class Sink(SinkSource):
def __init__(self, pitem):
super().__init__(pitem, "sink")
class Source(SinkSource):
def __init__(self, pitem):
super().__init__(pitem, "source")
class InputOutput:
def __init__(self, pitem, kind_of_thing, linked_kind):
assert type(pitem) == pacmd.Item
self._i = pitem
self._kind_of_thing = kind_of_thing
self._linked_kind = linked_kind
# sink-input or source-output
def kind(self):
return self._kind_of_thing
# sink or source
def linked_kind(self):
return self._linked_kind
def properties(self):
return self._i
def name(self):
try:
name = self._i.ch["properties"].ch["media.name"].value
# Append the first that is present, if any
for key in ["application.name", "application.process.binary"]:
if key in self._i.ch["properties"].ch:
name += " (" + self._i.ch["properties"].ch[key].value + ")"
break
except Exception as e:
return "???"
return name
def linked_index(self):
return int(self._i.ch[self._linked_kind].value.split()[0])
# Don't know if this is ever true; it wouldn't make much sense to have a
# 'default' input or output.
def default(self):
return self._i.default
def index(self):
return self._i.index
def driver(self):
return self._i.ch["driver"].value
def muted(self):
return _parse_muted(self._i)
def volume(self):
return _parse_volume(self._i)
def set_volume(self, vol):
assert type(vol) == float or type(vol) == int
vol = max(0.0, min(1.0, vol))
vol = round(vol * _get_maxvol(self._i))
self.set_raw_volume(vol)
def set_raw_volume(self, vol):
assert type(vol) == float or type(vol) == int
pacmd.pacmd("set-{}-volume".format(self._kind_of_thing),
str(self.index()), str(vol))
def set_muted(self, yes):
pacmd.pacmd("set-{}-mute".format(self._kind_of_thing),
str(self.index()), "true" if yes else "false")
def move_to(self, idx):
assert type(idx) == int
pacmd.pacmd("move-{}".format(self._kind_of_thing),
str(self.index()), str(idx))
class SinkInput(InputOutput):
def __init__(self, pitem):
super().__init__(pitem, "sink-input", "sink")
class SourceOutput(InputOutput):
def __init__(self, pitem):
super().__init__(pitem, "source-output", "source")
# TODO: How should one really do this? This is just a collection of hacks that
# accidentally works for all my devices, but will probably fail the moment you
# try another.
def _get_maxvol(item):
if "volume steps" in item.ch:
return int(item.ch["volume steps"].value.strip()) - 1
elif "base volume" in item.ch:
num, perc = item.ch["base volume"].value.split("/")[0:2]
num = int(num.strip())
perc = int(num.replace("%", "").strip())
return num * (100 / perc)
else:
return 65536
def _parse_volume(item):
vol = [int(x.split(":")[1].split("/")[0].strip())
for x in item.ch["volume"].value[0].split(",")]
maxvol = _get_maxvol(item)
return [v / maxvol for v in vol]
def _parse_muted(item):
if item.ch["muted"].value == "yes": return True
elif item.ch["muted"].value == "no": return False
else: assert False
def _list_things(pacmd_obj, section_name, klass):
assert len(pacmd_obj.sections) == 1
assert pacmd_obj.sections[0].name == section_name
return [klass(item) for item in pacmd_obj.sections[0].items]
def list_sinks():
return _list_things(pacmd.list_sinks(), "sink", Sink)
def list_sink_inputs():
return _list_things(pacmd.list_sink_inputs(), "input", SinkInput)
def list_sources():
return _list_things(pacmd.list_sources(), "source", Source)
def list_source_outputs():
return _list_things(pacmd.list_source_outputs(), "output", SourceOutput)
|