[pulseaudio-discuss] execute commands on suspend/resume?

Sean Greenslade sean at seangreenslade.com
Sun Sep 5 03:25:28 UTC 2021


[Re-adding the mailing list and reformatting]

On Sun, Sep 05, 2021 at 10:23:14AM +1000, Bernd Wechner wrote:
> On 5/9/21 06:13, Sean Greenslade wrote:
> > On Tue, Jun 08, 2021 at 08:17:00PM -0700, Peter Meilstrup wrote:
> > > Hello pulseaudio list,
> > > I am wondering if it is possible to have a command be executed when a
> > > sink is suspended or resumed (via module-suspend-on-idle).
> > > Specifically I want to switch my amplifier on and off automatically
> > > via home automation. Is this supported in pulseaudio?
> > 
> > I realize this is a reply to a pretty old message, but in case you or
> > others are still wondering about this:
> > 
> > It's pretty straightforward to do this with a custom pulseaudio client
> > application. I took a quick stab at it with the "pulsectl" python
> > library. Example script attached, just modify the sink.name line to
> > match your target device.
> 
> This looks awesome, been wondering for a while if there was a way I could
> watch for pulse events and enforce a default device. I have endless trouble
> on a HTPC that seems when the projector is switched on or off seeing the
> surround sound device disappear and reappear and with that cycle a new
> device inadvertently selected. I would like to somehow say, no matter what
> the F happens, unless it's user specified  select the surround sound system
> and configure it for 5.1 (it also flips to stereo a lot on such power
> cycles).

This part of your particular issue was solved in the most recent pulse
release (15.0). From the release notes:

>> Card profiles can be set to sticky
>> 
>> Card profiles can be set to sticky using the command "pactl send-message
>> /card/<card name> set-profile-sticky 'true|false'". The current status
>> can be queried using pactl send-message /card/<card name>
>> get-profile-sticky. If a card profile is sticky, it will always be
>> restored, even if the profile is unavailable. Also port availability
>> changes on the card will not impact the profile choice. This is for
>> example useful to set the card profile permanently to "off" for HDMI
>> devices. Setting profiles to sticky is already implemented in
>> pavucontrol and will be provided with the next pavucontrol release.

The various module-*-restore modules should also migrate wayward streams
back to the originally selected sound card once it reappears, though
it's possible that applications might be trying to outsmart pulse and do
their own output selection / migration.

> I see some scope with pulse.event_listen() but looking at the scant docs I
> see also event_callback_set() which seems more appropriate (setting a
> callback and parking) but I find the docs are very poor here as to what
> either of these actually do, and I'm a bit concerned running a script like
> yours with a "while True" loop as a daemon monitoring changes.
> 
> Thoughts? Perhaps theres another approach to this altogether I'm no aware
> of?

I agree the docs are a little sparse for pulsectl. I did have to do some
source reading to figure it out. Maybe I should have put some more
comments in that script.

The event_mask_set and event_callback_set lines outside the while loop
establish which types of events will be received and establish the
callback function as the handler of those events. Once inside the loop,
the event_listen() function when called with no arguments will block
indefinitely until an event is received. It's not busy-waiting; the
script will sleep and use no CPU until a matching event happens. At that
point, the script is woken up and the callback is fired. Since this
callback is called from within the pulse mainloop, we can't call other
pulse functions (for reasons related to the way the pulseaudio C APIs
are structured). So we use the callback to store the sink index that
triggered it and then break out of event_listen function. Once that
happens, our while loop goes on to process the event. We can now call
other pulseaudio functions (like sink_list() to get the state of the
sink that triggered our callback). Once we're done processing, the while
loop re-fires the event_listen(), and thus goes back to sleep until the
next event.

There may be a small race condition in this code, as I'm not certain if
events that happen while we're not in event_listen() get queued up. In
this example it's probably fine, as the default auto-suspend takes 5
seconds to transition state. If you were really concerned about not
losing any events, you could rearchitect the script to spawn a separate
processing thread and just flag processing events from the main thread
using a thread-safe shared object (e.g. queue.Queue) without exiting the
event_listen function.

--Sean



More information about the pulseaudio-discuss mailing list