-
Notifications
You must be signed in to change notification settings - Fork 0
/
pavolume.rb
executable file
·233 lines (214 loc) · 9.37 KB
/
pavolume.rb
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
#!/usr/bin/ruby
#
# Moved to: https://github.com/uriel1998/volumerb
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License. To view a copy of this license, visit
# http://creativecommons.org/licenses/by-sa/3.0/.
#
# Forked/derived from original by Jasper Van der Jeugt (jaspervdj);
# large chunks rewritten by Steven Saus (uriel1998)
# This is designed to set your pulseaudio volume settings, muting, and
# default sink from the command line. It will even switch currently
# running streams to the new sink when you switch it!
# Usage will be largely for folks who are using keybinds for volume, etc.
# Originally wrote this to work with ALSA cards - that's what I have.
# (see line 46) but the code at line 45 *should* work for anything
# that's set up as a device in PulseAudio. This is written to work
# with the version of PulseAudio I have on my system - 0.9.22.
# By perusing the online PulseAudio documentation, it looks like this
# *should* still work just fine for later versions. Please ping me one
# way or another so I can properly document/fix it...
# To do: -Set toggle (like the q toggle) so that actions only impact
# current default sink
# -Add in toggle so simple output available for notify-osd
# 20121114:uriel1998: Added introduction/description above
# 20121114:uriel1998: Standardized indentations
# 20121114:uriel1998: Rewrote if/else statements into case statements
# 20121114:uriel1998: Added usage instructions when no arguments given
# 20121114:uriel1998: Added quiet option
# 20121114:uriel1998: Added explicit setting of volume
# 20121114:uriel1998: Added explicit muting/unmuting instead of just
# toggling mute
# 20121114:uriel1998: Formatted output to be somewhat more human-
# readable, including padding & volume percentages
# 20121114:uriel1998: Added function to allow changing default sink
# 20121114:uriel1998: Added code to switch playing streams to new sink.
# 20121114:uriel1998: Rewrote array; arranged by sink id instead of name
# 20120210:uriel1998: Changed bit of code toggling mute
# 20120210:uriel1998: Added output, so it could be piped to a notify-osd
# or somesuch if desired. (superceded 20121114)
# 20120210:uriel1998: Original fork from https://gist.github.com/814634
# Function to test if argument is number, from
#http://stackoverflow.com/questions/5661466/test-if-string-is-a-number-in-ruby-on-rails
class String
def is_number?
true if Float(self) rescue false
end
end
# Pulseaudio volume control
class Pulse
attr_reader :volumes, :mutes
# Constructor
def initialize
dump = `pacmd dump`.lines
@volumes = {}
@mutes = {}
@names = {}
@id = {}
$longname = 0
dump.each do |line|
args = line.split
#We are using the id to actually enumerate this bloody array.
if !args[2].nil? and args[2].include? "device_id" then # this should work for any recognized device
#if args[1] == "module-alsa-card" then # this works if you only have ALSA cards.
s1 = args[2].sub("device_id=\"","")
s2 = s1.sub("\"","")
number = s2.to_i
@id[number] = number
crapstring = args[3].sub("name=\"","")
@names[number] = crapstring.sub("\"","")
if @names[number].length > $longname
$longname = @names[number].length
end
else
if @names.keys.length > 0 # we already have something in the array
@names.keys.each do |sink|
if !args[1].nil? and args[1].include? @names[sink] # and if it's a sink we recognize AND not nil...
result = case args[0]
when "set-default-sink" then $defaultsink = args[1].sub("alsa_output.","") # static variable
when "set-sink-volume" then @volumes[@id[sink]] = args[2].hex
when "set-sink-mute" then @mutes[@id[sink]] = args[2]
end
end
end
end
end
end
# Adjust the volume with the given increment for every sink
def volume_set_relative(increment)
@volumes.keys.each do |sink|
volume = @volumes[sink] + increment
volume = [[0, volume].max, 0x10000].min
@volumes[sink] = volume
`pacmd set-sink-volume #{sink} #{"0x%x" % volume}`
end
end
# Adjust the volume with the given increment for every sink
def volume_set_absolute(setvol)
if setvol < 100.1
@volumes.keys.each do |sink|
volume = setvol * 65536 / 100
@volumes[sink] = volume
`pacmd set-sink-volume #{sink} #{"0x%x" % volume}`
end
end
end
# Toggle the mute setting for every sink
def mute_toggle
@mutes.keys.each do |sink|
if @mutes[sink] == "yes"
`pacmd set-sink-mute #{sink} no`
else
`pacmd set-sink-mute #{sink} yes`
end
end
end
def mute(setting)
@mutes.keys.each do |sink|
`pacmd set-sink-mute #{sink} #{setting}`
end
end
# give me that sweet percentage value.
def percentage(vol)
return vol * 100 / 65536
end
def setdefault
@names.keys.each do |sink|
if $defaultsink.include? @names[sink]
puts "#{@id[sink]}. #{@names[sink]} *"
else
puts "#{@id[sink]}. #{@names[sink]}"
end
end
puts "Which sink shall be set as default (enter the number)"
scratch = STDIN.gets.chomp
newdefault = scratch.to_i
if @id.include? newdefault
`pacmd set-default-sink #{newdefault}`
# Now to move all current playing stuff to the new sink....
dump2 = `pacmd list-sink-inputs`.lines
@inputs = {}
counter = 0
dump2.each do |line|
args = line.split
if args[0] == "index:" # We need to find the item index for each playing stream
@inputs[counter] = args[1]
counter += 1
end
end
# And now to shift them all to the new sink.
count2 = 0
while count2 < counter
`pacmd move-sink-input #{@inputs[count2]} #{newdefault}`
count2 += 1
end
else
puts "That input index does not exist. You silly person."
end
end
# so we can have nice things.
def padstring(strlen)
dyncounter = 0
counter = $longname.to_i - strlen.to_i
padder = " "
while dyncounter < counter do
padder << " "
dyncounter += 1
end
return padder
end
# Report out settings for each sink.
def status
# needed to get new values
initialize
puts "##Current status##########################################"
puts "ID Sink Name#{padstring(11)} Mute Vol Default"
@id.keys.each do |sink|
# making volume into a percentage for humans
# Not sure why I have to pass to a subprocess to make it do, but...
volpercent = percentage(@volumes[sink])
if $defaultsink.include? @names[sink]
puts "#{@id[sink]}. #{@names[sink]}#{padstring(@names[sink].length)} #{@mutes[sink]} #{volpercent}% *"
else
puts "#{@id[sink]}. #{@names[sink]}#{padstring(@names[sink].length)} #{@mutes[sink]} #{volpercent}%"
end
end
puts "##########################################################"
end
end
# Control code
p = Pulse.new
unless ARGV.length > 0
puts "\nUsage: ruby volume.rb [0-100|up|down|toggle|mute|unmute|default] [q]\n[0-100] - set percentage of max volume for all sinks\nup|down - Changes volume on all sinks\ntoggle|mute|unmute - Sets mute on all sinks\ndefault - Select default sink from commandline\nq - quiet; no status output\n"
else
if ARGV.first.is_number?
absvolume = ARGV.first.to_i
p.volume_set_absolute(absvolume)
else
result = case ARGV.first
when "up" then p.volume_set_relative 0x1000
when "down" then p.volume_set_relative -0x1000
when "toggle" then p.mute_toggle
when "mute" then p.mute("yes")
when "unmute" then p.mute("no")
when "default" then p.setdefault
# status not needed; it's included
end
end
end
# Always give us the results.
if !ARGV.include? "q"
p.status
end
end