-- pustule
-- Copyright 2014-2020 Thomas Jost <schnouki@schnouki.net>
--
-- This file is part of pustule.
--
-- pustule is free software: you can redistribute it and/or modify it under the
-- terms of the GNU General Public License as published by the Free Software
-- Foundation, either version 3 of the License, or (at your option) any later
-- version.
--
-- pustule is distributed in the hope that it will be useful, but WITHOUT ANY
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along with
-- pustule. If not, see <http://www.gnu.org/licenses/>.
-- Welcome to the Pustule default configuration script! Here's a short
-- explanation of how this works.
--
-- Each application that plays sound using PulseAudio creates one or several
-- inputs (or "sink input" in PulseAudio). The volume of each input can be set
-- independently.
--
-- The sound is then played on one of several devices ("sinks" in PulseAudio).
-- Again, the volume of each of them can be set independently. An input can be
-- bound to a specific device, or use the global default device.
--
-- pustule sends a signal when an input or device is added to or removed from
-- the PulseAudio daemon. There are 4 signals: "input_added", "input_removed",
-- "device_added", "device_removed". Listeners can be added for any of these
-- signals using the `pustule.connect_signal()` and
-- `pustule.disconnect_signal()`.
--
-- The volume of each input and device (a number between 0 and 1) can be set
-- using the `pustule.set_input_volume()` and `pustule.set_device_volume()`
-- functions.
--
-- In the default script, there are a few helpers that provide sane defaults for
-- writing rich rules. For instance everything is logged, including all the
-- properties of each new input. Some fun examples are shown, such as setting
-- different volumes for music and video players and for web browsers.
--
-- In order for pustule to work, you must disable the flat volumes option in the
-- PulseAudio daemon configuration. To do that, add the following line to
-- /etc/pulse/daemon.conf:
-- flat-volumes = no
-- And restart PulseAudio.
local io, string = io, string
local pustule = require("pustule")
------------------------------------------------------------------------
-- Helpers
------------------------------------------------------------------------
function printf(s, ...)
return io.write(s:format(...))
end
-- Table of all the active inputs and devices
active_inputs = {}
active_devices = {}
function set_input_volume(idx, vol)
printf("[%d] Setting volume to %.2f.\n", idx, vol)
pustule.set_input_volume(idx, vol)
active_inputs[idx] = true
end
--- Check if a volume has already been set for an input
function is_input_volume_set(idx)
return active_inputs[idx] == true
end
function set_device_volume(idx)
printf("{%d} Setting volume to %.2f.\n", idx, vol)
pustule.set_device_volume(idx, vol)
end
function show_input(idx, input)
local k, v
for k, v in pairs(input) do
printf(" [%d] %s: %s\n", idx, k, v)
end
end
function show_device(idx, device)
local k, v
for k, v in pairs(device) do
printf(" {%d} %s: %s\n", idx, k, v)
end
end
------------------------------------------------------------------------
-- Application-specific rules
------------------------------------------------------------------------
--- Set volume for web browsers
function input_added__browser(idx, input)
if not is_input_volume_set(idx)
and (input["application.process.binary"] == "firefox"
or input["application.process.binary"] == "chromium")
then
set_input_volume(idx, 0.8)
end
end
pustule.connect_signal("input_added", input_added__browser)
------------------------------------------------------------------------
-- Media roles
------------------------------------------------------------------------
-- Table mapping idx to role for inputs that have a media.role
roles = {}
--- Set volume for various applications based on their media role
function input_added__media_role(idx, input)
if is_input_volume_set(idx) then return end
-- Add role to inputs that should have one but don't
if input["application.process.binary"] == "mpg123" or input["application.process.binary"] == "ogg123" then
input["media.role"] = "music"
end
local role = input["media.role"]
if not role then return end
roles[idx] = role
-- Set volume depending on the role
if role == "music" then
set_input_volume(idx, 0.5)
elseif role == "video" then
set_input_volume(idx, 0.8)
end
end
pustule.connect_signal("input_added", input_added__media_role)
--- Cleanup the roles table
function input_removed__media_role(idx)
if roles[idx] then roles[idx] = nil end
end
pustule.connect_signal("input_removed", input_removed__media_role)
------------------------------------------------------------------------
-- Default rules: inputs
------------------------------------------------------------------------
function input_added__all(idx, input)
printf("[%d] Input added.\n", idx)
show_input(idx, input)
end
pustule.connect_signal("input_added", input_added__all)
function input_removed__all(idx, input)
printf("[%d] Input removed.\n", idx)
active_inputs[idx] = nil
end
pustule.connect_signal("input_removed", input_removed__all)
--- Set a default value for the volume if it wasn't set by the previous listeners
function input_added__default_volume(idx, input)
if not is_input_volume_set(idx) then
set_input_volume(idx, 0.6)
end
end
pustule.connect_signal("input_added", input_added__default_volume)
------------------------------------------------------------------------
-- Default rules: devices
------------------------------------------------------------------------
function device_added__all(idx, device)
printf("{%d} Device added.\n", idx)
show_device(idx, device)
active_devices[idx] = device
end
pustule.connect_signal("device_added", device_added__all)
function device_removed__all(idx)
printf("{%d} Device removed.\n", idx)
active_devices[idx] = nil
end
pustule.connect_signal("device_removed", device_removed__all)