Ad majorem lulzis gloriam
Vomito ergo sum


L’incroyable aventure de Pulseaudio et la carte Nvidia

Posted on

« Et le magicien changea alors la configuration d’Alsa pour le rendre compatible avec cette sortie HDMI de merde ! »

Bon, c’est quoi encore ton problème ?

Depuis un certain temps, ma carte graphique Nvidia a décidé de ne plus fonctionner correctement en sortie audio : de temps en temps, à la sortie de veille, plus de son ; à d’autre moment, les sortie stéréo HDMI s’inverse (il y a deux écrans branchés dessus et de temps en temps le son part sur l’un, de temps en temps sur l’autre).

En fait, j’ai découvert que cette conne de carte avait un comportement assez bizarre :

Donc, sous Alsa, au lieu de voir 4 sorties avec des dénominations bien précises, j’en vois un paquet qui ont des numéros et bien évidemment rien n’est vraiment consistant :

$ aplay -l | grep -i carte
carte 0: PCH [HDA Intel PCH], périphérique 0: VT1708S Analog [VT1708S Analog]
carte 0: PCH [HDA Intel PCH], périphérique 2: VT1708S Alt Analog [VT1708S Alt Analog]
carte 0: PCH [HDA Intel PCH], périphérique 3: VT1708S Digital [VT1708S Digital]
carte 1: NVidia [HDA NVidia], périphérique 3: HDMI 0 [HDMI 0]
carte 1: NVidia [HDA NVidia], périphérique 7: HDMI 1 [HDMI 1]
carte 1: NVidia [HDA NVidia], périphérique 8: HDMI 2 [HDMI 2]
carte 1: NVidia [HDA NVidia], périphérique 9: HDMI 3 [HDMI 3]
carte 1: NVidia [HDA NVidia], périphérique 10: HDMI 4 [HDMI 4]
carte 1: NVidia [HDA NVidia], périphérique 11: HDMI 5 [HDMI 5]
carte 1: NVidia [HDA NVidia], périphérique 12: HDMI 6 [HDMI 6]
carte 3: Mic [Samson Meteor Mic], périphérique 0: USB Audio [USB Audio]

La carte 0 est une carte son intégrée à la carte mère que je ne souhaite pas forcément utiliser. Mon idée est de tout faire passer en HDMI pour pouvoir branche les enceintes/le casque sur l’écran et donc brancher éventuellement d’autres choses dessus (ma console, un autre PC, etc…) sans avoir à tout recâbler à chaque fois. La carte 2 (qui ne se voit pas ici) est une Webcam : c’est donc seulement une entrée, pas une sortie, d’où son absence ici.

La carte 3 est un micro USB qui se trouve aussi avoir une sortie audio.

Le problème vient donc de la carte 1. Pour une raison que j’ignore complètement, elle indique 7 sorties (comme marqué plus haut) alors qu’elle n’en a physiquement que 4. Le plus étrange, c’est que xrandr aussi m’indique 6 sorties :

$ xrandr | grep connected
DVI-I-0 disconnected (normal left inverted right x axis y axis)
DVI-I-1 disconnected (normal left inverted right x axis y axis)
HDMI-0 connected 1080x1920+0+0 left (normal left inverted right x axis y axis) 344mm x 194mm
DP-0 disconnected (normal left inverted right x axis y axis)
DP-1 disconnected (normal left inverted right x axis y axis)
DVI-D-0 connected primary 1920x1080+1080+0 (normal left inverted right x axis y axis) 509mm x 286mm

Il y a donc des sorties factices ou des sorties internes (?) qui sont captés par Alsa et derrière par Pulseaudio comme étant des sorties réelles mais qui ne font en réalité rien du tout. Les deux écrans que j’ai (HDMI-0 et DVI-D-0) ont la capacité de faire sortir du son (via des petits hauts-parleurs et une sortie jack).

Voilà, le problème est posé : comment faire pour que cette putain de carte de merde sorte du son seulement sur l’écran en DVI et y compris lors de la sortie de veille du PC et de manière consistante si possible ?

L’approche par Pulseaudio

La vision côté Alsa, tu peux la voir plus haut. La vision côté Pulseaudio n’est pas beaucoup plus complexe en réalité :

$ pacmd list-cards
4 card(s) available.
    index: 0
	name: <alsa_card.pci-0000_01_00.1>
	driver: <module-alsa-card.c>
	owner module: 5
	properties:
	[…]
	profiles:
		output:hdmi-stereo: Sortie Digital Stereo (HDMI) (priority 5900, available: unknown)
		output:hdmi-stereo-extra1: Sortie Digital Stereo (HDMI 2) (priority 5700, available: unknown)
		output:hdmi-stereo-extra2: Sortie Digital Stereo (HDMI 3) (priority 5700, available: no)
		output:hdmi-surround-extra2: Sortie Digital Surround 5.1 (HDMI 3) (priority 600, available: no)
		output:hdmi-surround71-extra2: Sortie Digital Surround 7.1 (HDMI 3) (priority 600, available: no)
		output:hdmi-stereo-extra3: Sortie Digital Stereo (HDMI 4) (priority 5700, available: no)
		output:hdmi-surround-extra3: Sortie Digital Surround 5.1 (HDMI 4) (priority 600, available: no)
		output:hdmi-surround71-extra3: Sortie Digital Surround 7.1 (HDMI 4) (priority 600, available: no)
		output:hdmi-stereo-extra4: Sortie Digital Stereo (HDMI 5) (priority 5700, available: no)
		output:hdmi-surround-extra4: Sortie Digital Surround 5.1 (HDMI 5) (priority 600, available: no)
		output:hdmi-surround71-extra4: Sortie Digital Surround 7.1 (HDMI 5) (priority 600, available: no)
		output:hdmi-stereo-extra5: Sortie Digital Stereo (HDMI 6) (priority 5700, available: no)
		output:hdmi-surround-extra5: Sortie Digital Surround 5.1 (HDMI 6) (priority 600, available: no)
		output:hdmi-surround71-extra5: Sortie Digital Surround 7.1 (HDMI 6) (priority 600, available: no)
		output:hdmi-stereo-extra6: Sortie Digital Stereo (HDMI 7) (priority 5700, available: no)
		output:hdmi-surround-extra6: Sortie Digital Surround 5.1 (HDMI 7) (priority 600, available: no)
		output:hdmi-surround71-extra6: Sortie Digital Surround 7.1 (HDMI 7) (priority 600, available: no)
		off: Éteint (priority 0, available: unknown)
	active profile: <alsa_output.pci-0000_01_00.1.hdmi-stereo>
	sinks:
		alsa_output.pci-0000_01_00.1.hdmi-stereo/#0: GM204 High Definition Audio Controller Digital Stereo (HDMI)
		alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1/#3: GM204 High Definition Audio Controller Digital Stereo (HDMI 2)
		alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2/#4: GM204 High Definition Audio Controller Digital Stereo (HDMI 3)
	[…]

Il y a donc pas mal de profils disponibles : pour chaque sortie physique, un profil Surround, un profil Surround 7.1 et un profil Stéréo. Il y a également un profil off qui permet d’éteindre complètement la carte et d’empêcher toute sortie son par là.

L’approche naïve consiste donc à se dire : il suffit d’utiliser un petit logiciel comme pavucontrol pour régler l’ensemble des sorties et entrées de la carte, d’éteindre les sorties qui ne nous intéressent pas, et le tour est joué. Et en effet, dans un premier temps, ça a marché : j’ai choisi le profil qui va bien pour sortir le son sur l’écran principal et le tour était joué.

Ça n’a pas réglé mon souci de son absent en sortie de veille (on y reviendra), mais effectivement ça « marche ». Sauf qu’au premier redémarrage, sans rien toucher physiquement évidemment, Pulseaudio s’emmêle les saucisses et la sortie HDMI3 devient la sortie HDMI2, la sortie HDMI4 devient la 1, etc…

Impossible d’avoir un résultat probant avec cette méthode !!

Forcer le hardware

Ma première idée était donc de forcer une corrélation stricte entre le hardware (les sorties audio telle qu’elles sont vues par Alsa) et la partie Pulseaudio. Pour ce faire, on peut passer par le fichier /etc/pulse/default.pa. Première chose, repérer quelle sortie correspond à l’écran. Pour cela, on utilise la commande aplay avec les bonnes options :

$ aplay -D plughw:1,3 /usr/share/sounds/alsa/Front_Right.wav

Voilà, tu fais ça avec chaque sortie (voire la sortie de la commande aplay -l plus haut) jusqu’à ce que tu trouves la sortie de ton écran. À partir de là, tu peux ajouter la commande suivante dans le fichier /etc/pulse/default.pa :

load-module module-alsa-sink device=hw:1,7

Ce fichier est le fichier de configuration par défaut de Pulseaudio, qui prend uniquement des commandes pacmd, et qui est lu par Pulseaudio à chaque fois qu’il crée un profil utilisateur. Ça, ça veut dire que c’est juste une configuration par défaut et pas autre chose. Pour la prendre en charge, il faut donc arrêter pulseaudio, supprimer la configuration utilisateur et redémarrer pulseaudio. Ça se fait comme ça :

$ pulseaudio --kill; sleep 1; rm -rf .config/pulse; pulseaudio --start

Bien évidemment toute configuration personnalisée via pavucontrol ou autre sera perdue :niais:.

Je te coupe tout de suite le suspense : ça ne résiste pas à un redémarrage. C’est assez étonnant d’ailleurs parce que les indices hardware ne semblent pas particulièrement changer : Alsa détecte toujours la sortie 7 comme étant la sortie 7.

Mais bien sûr, côté Pulseaudio, il y a une sorte de renumérotation qui fait que alsa_output.pci-0000_01_00.1.hdmi-stereo va changer de sortie vis-à-vis d’Alsa (et pareil pour alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1 et ainsi de suite).

Un simple problème de persmission

Visiblement, il traîne dans les archives du code de Pulseaudio un certain nombre de bêtises et notamment le fait qu’un groupe serait codé en dur dedans : si l’utilisateur n’est pas dans le groupe audio, y’a plein de trucs qui ne marcheraient pas.

Bon, on ne va pas se cacher : ça ne marche pas forcément mieux.

Premières conclusions

À partir de ce moment, j’en ai conclu qu’il n’était pas possible de fixer véritablement la sortie audio : Pulseaudio ne détecte pas les sorties dans le bon ordre et même en le forçant, visiblement il fait un peu ce qu’il veut (soit au démarrage, soit en sortie de veille). J’aurais tendance à penser que le processus de détection n’est malheureusement pas déterministe.

Clairement, le souci vient de Pulseaudio cependant, Alsa semble vraiment détecter les sorties audio toujours dans le même ordre. Il est aussi possible que le problème vienne des pilotes Nvidia qui ferait n’importe quoi avec la détection des sorties. Ou il est encore possible que ce soit une combinaison des deux. Quoiqu’il arrive, il est clair que je n’arriverai pas à solutionner le problème avec juste de la configuration.

Et si on sortait le son partout sans réfléchir ?

À partir de ce moment, je pense avoir essayé toutes les possibilités « rationnelles » sur ce problème. Et quand on a essayé toutes les possibilités rationnelles, il faut commencer à penser autrement. Je me suis donc dit : pourquoi ne pas sortir le son sur toutes les sorties HDMI en même temps ? Finalement, ce serait le plus simple : quel que soit la configuration de Pulseaudio, la détection, l’ordre des sorties, etc… tout sera de toutes manières capables de sortir du son et il n’y aura plus qu’à rendre les sorties inutiles muettes manuellement.

Sauf que par défaut, Pulseaudio n’a pas de profil pour sortir sur toutes les sorties HDMI en même temps.

Profil Pulseaudio

Du coup, il faut aller dans le fichier /usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf, à la toute fin, il y a un exemple impliquant un profil multi-sorties, c’est donc de celui-ci qu’on va se servir :

[Profile forced-hdmi-stereo]
description = Foobar
output-mappings = hdmi-stereo hdmi-stereo-extra1 hdmi-stereo-extra2
input-mappings =

C’est assez bête basiquement : ce profil dit simplement que toutes les sorties Pulseaudio peuvent être utilisées en même temps. Maintenant, ce n’est pas parce qu’elles peuvent être utilisées en même temps qu’elles le seront. En fait, si tu sélectionnes le profil Foobar dans l’onglet Configuration de pavucontrol, tu peux alors zapper entre les différentes sorties sans repasser par un profil particulier.

Sauf que pour le moment, c’est tout ce que ça permet de faire : nous avons un profil qui peut interagir avec toutes les sorties mais pas sortir simultanément le son sur toutes ces sorties.

Ajout d’une sortie multiple

Pour arriver à faire une sortie multiple, il faut utiliser un autre module Pulseaudio : combined-sink. Dans Pulseaudio un sink est une sortie. Ce que nous avons fait précédemment, c’est créer un profil disposant de plusieurs sinks. Il faut donc se débrouiller pour les combiner. Encore une fois, ça se passe dans le fichier /etc/pulse/default.pa :

set-card-profile 0 forced-hdmi-stereo

load-module module-combine-sink sink_name=combined slaves=alsa_output.pci-0000_01_00.1.hdmi-stereo,alsa_output.pci-0000_01_00.1.hdmi-stereo-extra1,alsa_output.pci-0000_01_00.1.hdmi-stereo-extra2

set-default-sink combined

La première ligne sert simplement à indiquer à Pulseaudio que la carte Nvidia (qui est détectée systématiquement comme carte 0 dans mon cas) est la carte part défaut. La seconde ligne crée un nouveau sink (une sortie donc) qui combine toutes les sorties de la carte (pour obtenir le nom des différents sorties, il suffit d’utiliser la commande pactl list-sinks). De cette manière, si les sorties sont interverties par Pulseaudio, quelque soit la condition, toutes les sorties HDMI de la machine recevront du son. Voilà, c’est à peu près aussi con que ça.

La dernière ligne sert juste à sélectionner cette sortie par défaut.

Bon OK, t’as du son mais est-ce que t’en as toujours en sortie de veille ?

Non toujours pas :/

C’était un peu prévisible : j’ai deux problèmes en fait. Le premier est maintenant réglé, pour le second voilà ce qu’il en est… Pulseaudio semble refaire une détection complète à la sortie de veille (dans le cadre d’un PC Portable, ça paraît logique en fait) et la carte Nvidia n’est donc pas détectée suffisamment vite.

En fait, si à la sortie de veille, je vais dans pavucontrol, je peux très bien la rallumer là et elle marche parfaitement bien après. Le souci, c’est que je n’ai pas du tout envie de faire ça.

De là, je me suis dit que ce n’était pas forcément un souci de bascule mais peut-être de détection d’état : à la sortie de veille, la carte vidéo met peut-être un peu de temps à redétecter les écrans (en HDMI, ça ne serait pas surprenant) et Pulseaudio part donc du principe qu’il faut qu’il bascule les sources et les sorties ailleurs pour éviter de couper le son. L’intention est louable mais en l’occurence, c’est un peu inutile pour mon cas.

J’ai donc simplement désactivé le module qui s’occupe de ça, en l’occurence module-switch-on-port-available. Pour le désactiver, il suffit de le commenter dans le fichier /etc/pulse/default.pa.

Conclusage définitif

Putain, ça fait quand même beaucoup de bordel pour arriver à faire fonctionner les choses correctement. Je comprends le principe de faire de la détection dynamique systématiquement, je pense que c’est une bonne chose dans l’absolu, mais je pense qu’il faut aussi laisser la possibilité à l’utilisateur de fixer un certain nombre de choses si ça foire ou si ça n’est pas adapté à la situation.

Ce que j’ai fait là ressemble quand même largement plus à du hack qu’à de la vraie configuration (surtout la partie multiple sorties à vrai dire), mais je suis assez satisfait du résultat : ça fonctionne à tous les coups et sans faire des pieds et des mains ou s’agiter dans la conf ou dans les menus ou commandes à chaque sortie de veille.