Add support for sources (i.e. microphones)

The widget will to its best to detect the active source.

- Updated client code to detect source changes and add object path information to
  the volume and mute updated signals.
- Added new functions to toggle mute and change volume on the microphone. The
  functions name have `_mic` appended.
- Refactored widget code to reduce duplication.
- Updated README with examples on how to add controls for the microphone to `rc.lua`.
This commit is contained in:
Stefano Mazzucco 2017-06-02 12:12:42 +01:00
parent a80a9a39b6
commit 8a357e481f
3 changed files with 97 additions and 20 deletions

View File

@ -92,9 +92,14 @@ Finally add some keyboard shortcuts to control the volume:
``` lua
awful.util.table.join(
-- Audio
awful.key({ }, "XF86AudioRaiseVolume", pulse.volume_up),
awful.key({ }, "XF86AudioLowerVolume", pulse.volume_down),
awful.key({ }, "XF86AudioMute", pulse.toggle_muted)
awful.key({ }, "XF86AudioMute", pulse.toggle_muted),
-- Microphone
awful.key({"Shift"}, "XF86AudioRaiseVolume", pulse.volume_up_mic),
awful.key({"Shift"}, "XF86AudioLowerVolume", pulse.volume_down_mic),
awful.key({ }, "XF86MicMute", pulse.toggle_muted_mic),
)
```

View File

@ -78,6 +78,18 @@ function widget:update_sink(object_path)
self.sink = pulse.get_device(self.connection, object_path)
end
function widget:update_source(sources)
for _, source_path in ipairs(sources) do
local s = pulse.get_device(self.connection, source_path)
if s.Name and not s.Name:match("%.monitor$") then
self.source = s
break
else
self.source = nil
end
end
end
function widget.volume_up()
if not widget.sink:is_muted() then
widget.sink:volume_up()
@ -94,6 +106,24 @@ function widget.toggle_muted()
widget.sink:toggle_muted()
end
function widget.volume_up_mic()
if widget.source and not widget.source:is_muted() then
widget.source:volume_up()
end
end
function widget.volume_down_mic()
if widget.source and not widget.source:is_muted() then
widget.source:volume_down()
end
end
function widget.toggle_muted_mic()
if widget.source then
widget.source:toggle_muted()
end
end
function widget:kill_client()
if type(self.server_pid) == "number" then
awful.spawn("kill -TERM " .. self.server_pid)
@ -102,30 +132,41 @@ end
function widget:run_client()
local function update_after_signal(line, regex, sub, sep)
sep = sep or " "
local v, found = line:gsub(regex, sub)
if found ~= 0 then
local idx = v:find(sep)
local vol = v:sub(1, idx - 1)
local path = v:sub(idx + 1)
if path:find("/sink%d+$") then
self:update_appearance(vol)
self.notify(vol)
end
end
end
local pid = awful.spawn.with_line_callback(
[[lua -e 'require("pulseaudio_widget_client")']],
{
stdout = function (line)
local v, found, _
v, found = line:gsub("^(VolumeUpdated:%s+)(%d)", "%2")
if found ~= 0 then
self:update_appearance(v)
widget.notify(v)
end
update_after_signal(line, "^(VolumeUpdated:%s+)(%d+)(|)([%w/]+)", "%2 %4")
v, found = line:gsub("^(MuteUpdated:%s+)(%w)", "%2")
if found ~= 0 then
self:update_appearance(v)
widget.notify(v)
end
update_after_signal(line, "^(MuteUpdated:%s+)(%w+)(|)([%w/]+)", "%2 %4")
local v, found
v, found = line:gsub("^(NewSink:%s+)(/.*%w)", "%2")
if found ~=0 then
self:update_sink(v)
local volume = self.sink:is_muted() and "Muted" or self.sink:get_volume_percent()[1]
self:update_appearance(volume)
widget.notify(volume)
self.notify(volume)
end
v, found = line:gsub("^(NewSource:%s+)(/.*%w)", "%2")
if found ~=0 then
self:update_source({v})
end
end
})
@ -154,8 +195,10 @@ function widget:init()
self.connection = pulse.get_connection(address)
self.core = pulse.get_core(self.connection)
local sink_path = assert(self.core:get_sinks()[1], "No sinks found")
self:update_source(self.core:get_sources())
local sink_path = assert(self.core:get_sinks()[1], "No sinks found")
self:update_sink(sink_path)
local volume = self.sink:is_muted() and "Muted" or self.sink:get_volume_percent()[1]
self:update_appearance(volume)

View File

@ -37,17 +37,31 @@ local address = pulse.get_address()
local connection = pulse.get_connection(address)
local core = pulse.get_core(connection)
local sink = pulse.get_device(connection, core.Sinks[1])
local source
local function get_source(conn, sources)
for _, source_path in ipairs(sources) do
local s = pulse.get_device(conn, source_path)
if s.Name and not s.Name:match("%.monitor$") then
return s
end
end
end
source = get_source(connection, core.Sources)
-- listen on ALL objects as sinks may change
core:ListenForSignal("org.PulseAudio.Core1.Device.VolumeUpdated", {})
core:ListenForSignal("org.PulseAudio.Core1.Device.MuteUpdated", {})
local function connect_sink(s)
local function connect_device(s)
if s.signals.VolumeUpdated then
s:connect_signal(
function (self, vols)
local v = math.ceil(tonumber(vols[1][1]) / self.BaseVolume * 100)
print(string.format("VolumeUpdated: %s", v))
-- FIXME: BaseVolume for sources (i.e. microphones) won't give the correct percentage
print(string.format("VolumeUpdated: %s|%s", v, self.object_path))
end,
"VolumeUpdated"
)
@ -55,27 +69,42 @@ local function connect_sink(s)
if s.signals.MuteUpdated then
s:connect_signal(
function (_, is_mute)
function (self, is_mute)
local m = is_mute[1] and "Muted" or "Unmuted"
print(string.format("MuteUpdated: %s", m))
print(string.format("MuteUpdated: %s|%s", m, self.object_path))
end,
"MuteUpdated"
)
end
end
connect_sink(sink)
connect_device(sink)
if source then
connect_device(source)
end
core:ListenForSignal("org.PulseAudio.Core1.NewSink", {core.object_path})
core:connect_signal(
function (_, newsinks)
print(string.format("NewSink: %s", newsinks[1]))
sink = pulse.get_device(connection, newsinks[1])
connect_sink(sink)
connect_device(sink)
end,
"NewSink"
)
core:ListenForSignal("org.PulseAudio.Core1.NewSource", {core.object_path})
core:connect_signal(
function (_, newsources)
print(string.format("NewSource: %s", newsources[1]))
source = get_source(connection, newsources)
if source then
connect_device(source)
end
end,
"NewSource"
)
-- Start the client. Send SIGTERM to stop it.
print("Starting Awesome PulseAudio Widget Client")
GLib.MainLoop():run()