41

I'm running audio over HDMI to my receiver, which has a full 7.1 setup connected to it. However, most of the stuff that I listen to has far fewer channels—some is mono, most is stereo, a bit is 5.1. When the receiver is fed with the right number of channels, it has various DSP effects which work fairly well (e.g. in splitting out a center channel, in sending base to the subwoofer, auto-detecting Dolby matrix surround, etc.). This ceases working if you send it a full 7.1 signal with a bunch of silent channels.

The number of output channels in PulseAudio is configured, per-card/sink, by the profile assigned to the card. You can change the assigned profile with pacmd, pactl, and various GUI programs.

How can I set it up to automatically change the profile based on the channel usage in the input stream(s)? E.g., if the input streams are only using front-left and front-right, switch to the stereo profile. If they're using back-left and back-right as well, switch to quadrophonic, etc.

derobert
  • 107,579
  • 20
  • 231
  • 279
  • 3
    Maybe I'm wrong, but I don't think it is feasible at all. Pulseaudio manages multiple streams at once: which profile would be correct if different streams with different channel numbers are playing together? – Alessio Gaeta Aug 26 '15 at 10:14
  • @meden The one with the highest number of channels, I guess. – derobert Aug 26 '15 at 15:14
  • Yes, of course, but still there will be cases in which you could not achieve your goal. BTW, I do not think there is a way in Pulseaudio to conditionally switch a profile. Maybe the Kodi guide to Pulseaudio [http://kodi.wiki/view/PulseAudio] could be useful to have some hint (i.e. using passthrough and let your receiver do all the work, provided it supports all formats you use). – Alessio Gaeta Aug 27 '15 at 09:25
  • This is an interesting question. I don't have the audio setup available to test this, but as a work around, if you were to create profiles for each instance you envision, setup a script to analyze the streams in advance (perhaps by grepping the output of `mediainfo` or `ffmpeg` or `avconv`) and then use `pacmd` to select the appropriate profile prior to launching your player, From a logical standpoint I would expect it to work provided you were playing one audio at a time. – Elder Geek Dec 15 '16 at 22:42
  • 4
    It might be worth submitting this question as a feature request at https://launchpad.net/bugs/bugtrackers/pulseaudio-bugs if it has not already been submitted there. It would be an interesting feature and pulse audio could probably solve this better internally than by using an external script. – Alexander Jan 13 '17 at 14:07
  • @A.B That'd be changing based on the output sinks, not the input streams. I'm interested in when a program (client) connects with a 5.1/7.1 source, switch the output to 5.1/7.1 — otherwise, leave it at stereo. Output is HDMI, which can do all of those (and PA can already switch, just not automatically) – derobert Nov 03 '17 at 18:09
  • How about if you do it by the "application-name" attribute? do you usually play 5.1 stuff in a specific program? – Tanami Nov 28 '17 at 05:20
  • @Tanami not really, both stereo and 5.1 from the same program (e.g., mpv). There might be a way to do it for mpv specifically with its built-in scripting of course. – derobert Nov 28 '17 at 05:46
  • Well, in your position I would just make wrapper scripts, i.e. mpv-2, mpv-51, mpv-71, etc.. – Tanami Nov 28 '17 at 05:53
  • @Tanami There are plenty of kluges to work around the issue... Wrapper scripts are what I currently do for programs which are always 7.1 for example. And I'm pretty sure an mpv script could do it based on the actual videos being played. – derobert Nov 28 '17 at 05:58

2 Answers2

3

This is just a concept and no complete solution:

You may be able to do your own handling of events with patcl subscribe if you can assign configurations to pulsaudio clients (e.g., if the movie player connects, you switch to 7.1 configuration, otherwise you use the stereo configuratioin). You will get a constant stream of events from patcl subscribe, which you can handle by observing client connections. Then you can just call pactl/pacmd to change the profile.

Example output of pactl subscribe:

$ pactl subscribe 
Event 'change' on sink #0
Event 'remove' on sink-input #93
Event 'change' on sink #0
Event 'change' on source #0
Event 'change' on sink #0
Event 'change' on source #0
Event 'new' on sink-input #98
Event 'change' on sink-input #98

Please note, that you still have the problem to handle cases in which you have multiple clients with a different number of channels.

Furthermore, the above approach is rather limited. The per application settings are not sufficient to detect the real number of used channels of the application (e.g. vlc might play a 5.1 or 7.1 video). I have not found a way to get the number actually used channels of an pulseaudio client over pactl, but I might have missed some options here. Alternatively, you might be also able to hook into the video player application itself and get the missing bits form there.

Till Schäfer
  • 406
  • 5
  • 11
  • With `pactl list | grep format.channels` you should be able to find out the max. # of channels used (and more information as well). Using that information in a script triggered by subscribed events could lead to a solution. – ridgy Jan 07 '18 at 17:43
  • This works for me! I posted it as a separate answer below because comments are limited in characters. – Gregory McIntyre Sep 04 '21 at 21:11
0

I worked the pactl subscribe idea into a solution - the first solution that worked for me, finally!

In .xsessionrc I have

pkill --signal 9 -f auto-switch.rb
~/.auto-switch.rb &

And in ~/.auto-switch.rb

#!/usr/bin/env ruby

require 'open3'

RE = /^Event 'new' on sink #(\d+)$/

Open3.popen2('pactl', 'subscribe') do |_stdin, stdout, _status_thread|
  stdout.each_line do |line|
    match = RE.match(line)
    system('pactl', 'set-default-sink', match.captures.first) if match
  end
end

(And chmod +x ~/.auto-switch.rb)

This listens to pactl subscribe and runs pactl set... commands as you like whenever pactl subscribe reports some change in your setup. You could modify this with extra logic for your situation if you pleased. You could write it in some other language besides Ruby too, if you preferred.

I just wanted to come here to report that this has been working perfectly for me for weeks now and is a vastly simpler solution to all the others I could find on StackOverflow related websites. Thank you!