-
Notifications
You must be signed in to change notification settings - Fork 0
/
kde-volume
executable file
·144 lines (137 loc) · 4.45 KB
/
kde-volume
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
#!/usr/bin/python3
#+
# DBussy example -- examine and change master audio volume settings under KDE.
# Invoke this script as follows:
#
# kde-volume
#
# to show the current volume and mute settings, or
#
# kde-volume «volume»
#
# to set the volume to «volume», or
#
# kde-volume --mute «mute»
#
# to set the mute state to «mute»
# «volume» can be specified as a percentage (ending with “%”) or as a fraction
# in [0 .. 1], while «mute» is 1 for muted or 0 for not muted. For example,
#
# kde-volume 40%
#
# or
#
# kde-volume 0.4
#
# either of which will set the volume to 40%, or
#
# kde-volume --mute 0
#
# to ensure muting is off.
#
# Copyright 2017 by Lawrence D'Oliveiro <ldo@geek-central.gen.nz>. This
# script is licensed CC0
# <https://creativecommons.org/publicdomain/zero/1.0/>; do with it
# what you will.
#-
import sys
import dbussy
import getopt
import ravel
kmix_dest = "org.kde.kmix"
mixers_base = ["Mixers"]
mixset_iface = "org.kde.KMix.MixSet"
mixer_iface = "org.kde.KMix.Mixer"
mixcontrol_iface = "org.kde.KMix.Control"
#+
# Mainline
#-
loop = dbussy.get_event_loop()
verbose = False
new_vol = None
new_mute = None
set_mute = False
opts, args = getopt.getopt \
(
sys.argv[1:],
"",
["mute", "verbose"]
)
for keyword, value in opts :
if keyword == "--mute" :
set_mute = True
elif keyword == "--verbose" :
verbose = True
#end if
#end for
if len(args) not in (0, 1) :
raise getopt.GetoptError("expecting at most one arg")
#end if
if len(args) > 0 :
if set_mute :
new_mute = bool(int(args[0]))
else :
new_vol = args[0]
if new_vol.endswith("%") :
new_vol = float(new_vol[:-1]) / 100
else :
new_vol = float(new_vol)
#end if
#end if
#end if
async def mainline() :
conn = await ravel.session_bus_async(loop)
kmix = conn[kmix_dest]
mixers = await kmix[mixers_base].get_async_interface(mixset_iface)
current_master_mixer_id = await mixers.currentMasterMixer
current_master_control_id = await mixers.currentMasterControl
if verbose :
mixer_paths = await mixers.mixers
sys.stdout.write \
(
"current master mixer = %s, current master control = %s, mixers = %s\n"
%
(repr(current_master_mixer_id), repr(current_master_control_id), repr(mixer_paths))
)
#end if
# Unfortunately, current_master_mixer_id is not an object path. So I need to introspect
# all mixers for devices and streams (both capture and playback -- note I can’t tell
# which is which!) to get their “id” properties to find which one matches.
mixer_names = list(node.name for node in (await kmix.introspect_async(mixers_base)).nodes)
mixers_by_name = {}
for mixer_name in mixer_names :
mixer = await kmix[mixers_base + [mixer_name]].get_async_interface(mixer_iface)
mixers_by_name[mixer_name] = \
{
# "name" : mixer_name, # not needed
"id" : await mixer.id,
"masterControl" : await mixer.masterControl,
# luckily this is a full path
}
# cannot do this directly in a dict comprehension -- get “not supported” error
#end for
mixers_by_id = dict((mixer["id"], mixer) for mixer in mixers_by_name.values())
if verbose :
sys.stdout.write("mixers = %s\n" % repr(mixers_by_id))
#end if
master_control_path = mixers_by_id[current_master_mixer_id]["masterControl"]
master_control = await kmix[master_control_path].get_async_interface(mixcontrol_iface)
abs_volume_min = await master_control.absoluteVolumeMin
abs_volume_max = await master_control.absoluteVolumeMax
if new_vol != None :
master_control.absoluteVolume = round(new_vol * abs_volume_max)
elif new_mute != None :
master_control.mute = new_mute
#end if
await ravel.set_prop_flush(master_control)
# not really necessary because will be flushed by following further calls
abs_volume = await master_control.absoluteVolume
volume = await master_control.volume
muted = await master_control.mute
sys.stdout.write("control %s vol " % current_master_control_id)
if verbose :
sys.stdout.write("%d [%d .. %d] = " % (abs_volume, abs_volume_min, abs_volume_max))
#end if
sys.stdout.write("%.3f = %d%%, muted = %s\n" % (abs_volume / abs_volume_max, volume, muted))
#end mainline
loop.run_until_complete(mainline())