-
-
Save uriel1998/1791270 to your computer and use it in GitHub Desktop.
#!/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 |
0x200 rulez for asus xonar u1
I will report pulseaudio-utils maintainer to include this (or something similar) as pulse-mixer
Please make sure to credit jaspervdj; he did most of the hard work. I just tweaked it a bit. :)
And yeah, I do like the keyboard shortcuts to be pretty bit volume jumps. I usually use them for "OH CRAP THE PHONE IS RINGING" sorts of things. :)
Great script. But:
There is a little bug, that swaps the volumes between different sinks when they do not have the same levels at program start. But as my ruby knowledge is miserable, I can not fix it ):
CmdLine Output that shows the prob of alternating the devices:
hermann@saab:~$ ./pa_helper.ruby up
##Current status##########################################
ID Sink Name Mute Vol Default
0. pci-0000_00_1b.0 no 51% *
1. pci-0000_01_00.1 no 28%
##########################################################
hermann@saab:~$ ./pa_helper.ruby up
##Current status##########################################
ID Sink Name Mute Vol Default
0. pci-0000_00_1b.0 no 28% *
1. pci-0000_01_00.1 no 52%
##########################################################
hermann@saab:~$ ./pa_helper.ruby up
##Current status##########################################
ID Sink Name Mute Vol Default
0. pci-0000_00_1b.0 no 53% *
1. pci-0000_01_00.1 no 29%
##########################################################
hermann@saab:~$ ./pa_helper.ruby up
##Current status##########################################
ID Sink Name Mute Vol Default
0. pci-0000_00_1b.0 no 30% *
1. pci-0000_01_00.1 no 54%
##########################################################
hermann@saab:~$ ./pa_helper.ruby up
##Current status##########################################
ID Sink Name Mute Vol Default
0. pci-0000_00_1b.0 no 54% *
1. pci-0000_01_00.1 no 31%
##########################################################
Thanks for the script. Since (K)ubuntu upgrade to 13.04 it isn't working properly anymore for me. I found that the reason for this is that the pacmd dump gives the wrong device index number out. pacmd list-sinks gives the right sink index number though. I don't know how to do the enumration with pacmd list-sinks, because the syntax in pacmd list-sinks is different. It's not like in pacmd dump device_id="1", but it is: index: 1
Also the name of the device is not name="....", but name: ..... in pacmd list-sinks.
My workaround is to delete the if condition from 151 to 173, not the content, but only the "if" (151), "else" (171) and "end" (173).
Then the script works for me again, since I can give the script the right index of my sink, else the script would tell me that that sink number doesn't exist (and that I'd be a silly person ^^).
thanks. your script is epic. but use volume_set_relative +-0x300 it will change volume more softly