Change volume via script

I thought I would share something I have found useful.

For some reason, a lot of #s did not paste where they belong, but I think you know where they go. :slight_smile:

It lets you change sound volume in a script.

#!/bin/bash
# Sets sound volume to 30 %
#    
# REQUIRES THAT RUBY BE INSTALLED AND THAT VOL BE IN THE PATH !!
# See notes in vol !!
vol 30

----------------------------------

#!/usr/bin/ruby
# vol (original name was volume.rb)
#
# PUTS THIS IN A DIR IN THE PATH !!!
# LIKE /BIN and change owner to you and set execute bit
#
# 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/.
#

# Function to test if argument is number, from
#http://stackoverflow.com/questions/5...-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

Seen that script before, but I’ve got no idea what I’d use it for. I have vol keys on my laptop that work fine, along with the audio controls.