Obtenir un son numérique multicanal depuis son ordinateur

De quoi s'agit-il?

J'ai récemment eu l'opportunité de me pencher sur la possibilité d'écouter dans de bonnes conditions des enregistrements sonores qui disposent de plus de deux canaux. Des films en 5.1 (6 canaux) quoi. Et j'ai découvert un univers complexe et contre intuitif, qui plus est, mais ce n'est qu'une impression non vérifié, peu utilisé donc moins testé. J'ai donc eu envie d'écrire ici mon retour d'expérience avec l'espoir qu'il permettra à d'autres d'y voir clair plus vite que moi.

Il n'est ici pas question de juger de la qualité sonore des différentes technologies mise en œuvre, ni de construire un référentiel historique rigoureux. L'objet est de poser l'architecture et les clés qui permettent d'avoir une idée de pourquoi ça ne marche pas, de manière très pratique.

Les matériels

Les matériels que l'on veut mettre en place sont, selon ce qui est disponible sur le marché, les suivants :

On va s'intéresser à la liaison entre l'ordinateur et le réception AV.

Liaison numérique multicanal

Il semble que les moyens de transmettre un son multicanal (plus que stéréo) entre deux appareils grand public soient les suivants :

En HDMI, il y a plusieurs façons de transmettre un son numérique multicanal dont au moins les suivantes :

Y'a pas le son des dialogues

Par exemple, sur le récepteur AV que j'ai choisi pour son faible encombrement, mais qui vu son faible prix, est plutôt bas de gamme, les capacités affichées sont explicites :

$ cat /proc/asound/card0/eld#2.3
monitor_present         1
eld_valid               1
monitor_name            LT-1080
connection_type         HDMI
eld_version             [0x2] CEA-861D or below
edid_version            [0x3] CEA-861-B, C or D
manufacture_id          0x8d32
product_id              0x2c02
port_id                 0x0
support_hdcp            0
support_ai              0
audio_sync_delay        0
speakers                [0x1] FL/FR
sad_count               3
sad0_coding_type        [0x1] LPCM
sad0_channels           2
sad0_rates              [0x1ee0] 32000 44100 48000 88200 96000 176400 192000
sad0_bits               [0xe0000] 16 20 24
sad1_coding_type        [0x2] AC-3
sad1_channels           8
sad1_rates              [0xe0] 32000 44100 48000
sad1_max_bitrate        448000
sad2_coding_type        [0x7] DTS
sad2_channels           8
sad2_rates              [0xe0] 32000 44100 48000
sad2_max_bitrate        1536000

Ou y voit :

Ce qu'il faut comprendre, c'est qu'on ne pourra pas envoyer à ce récepteur AV plus que du stéréo en LPCM. Le LPCM, c'est pourtant ce que fait pulseaudio par défaut. Par conséquent, selon le profil de sortie sélectionné dans pulseaudio :

Le profil se change dans pavucontrol ou en ligne de commande, par exemple :

$ pacmd set-card-profile 0 output:hdmi-surround

Il va donc falloir envoyer du DTS ou de l'AC3 dans une sortie stéréo pour que ça marche. C'est la solution bien connue de ceux qui sont contraints de passer par un câble TOSLINK.

Les solutions logicielles pour sortir en AC3

Les composants logiciels

Vraiment rapidement, les composants logiciels suivants sont impliqués :

passthrough

L'une des solutions les plus mises en avant est celle de la transmission brute (passthrough). Elle vise à limiter les encodages avec pertes et décodages du son en visant de ne laisser entre le média source et les enceintes qu'une seule étape de décodage faite par le récepteur AV. Ceci ne vaut bien sûr que si le média source est déjà dans un CODEC supporté.

Pour le mettre en œuvre, si on utilise pulseaudio et qu'on veut autoriser le signal AC3 ou DTS à passer à travers, et bien paramétrer le profil de sortie en stéréo (oui ce n'est pas logique, mais il faut se rappeler que les 6 voies sont encodées dans un signal stéréo), par exemple sur la carte 0 :

$ pacmd set-card-profile 0 output:hdmi-stereo
$ pactl set-sink-formats 0 "pcm; ac3-iec61937; dts-iec61937"

(ou cocher les cases correspondantes dans pavucontrol)

Et dans tous les cas il faudra indiquer à son logiciel de lecture qu'il faut envoyer le signal brute à la sortie sonore, par exemple pour mpv :

audio-spdif=ac3,dts              # enable passthrough
audio-channels=5.1,stereo
af=lavcac3enc                    # encode audio output into AC3

Il est intéressant de se plonger dans la documentation du filtre de sortie audio lavcac3encpour constater qu'il a été bien pensé : il ne fait rien s'il ne reçoit que du stéréo ou s'il reçoit déjà du AC3, ce qui évite de dégrader en théorie la qualité par un encodage/décodage de plus. Par contre, si la source est autre chose, on évite pas le décodage puis l'encodage à nouveau.

Les limites de cette solution sont les suivantes :

Toutes les voies décodées

Si on laisse le décodage au logiciel, le HDMI permet de sortir les 6 voies décodées (LPCM à 6 voies). Le profil pulseaudio à mettre en œuvre est le suivant:

$ pacmd set-card-profile 0 output:hdmi-surround

Si le récepteur AV le supporte, pas de problème, c'est une bonne solution, peut être la solution qui a ma préférence.

La limite de cette solution est que beaucoup de récepteurs AV ne supportent le LPCM qu'en stéréo. On peut donc se retrouver avec un son qui ne sera pas à 6 voies comme attendu :

Encodage à la volée par ALSA (via pulseaudio)

Comme d'être encodée en AC3 est une contrainte de notre sortie sonore, autant le gérer de manière globale pour toutes les applications. Ainsi, pour un jeu ou n'importe quel lecteur de média, le son sera encodé à la volée.

Pour le mettre en œuvre, si on utilise pulseaudio, le profil correspond n'existe pas. J'ai ajouté manuellement ce qui allait bien dans /usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf :

[Mapping hdmi-ac3-surround-extra1]
description = Digital Surround 5.1 (HDMI 2/AC3)
device-strings = plug:{SLAVE="a52:%f,'hw:%f,7'"}
paths-output = hdmi-output-1
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
priority = 6
direction = output

Le profil se change dans pavucontrol ou en ligne de commande, par exemple :

$ pacmd set-card-profile 0 output:hdmi-ac3-surround-extra1

Les limites de cette solution sont les suivantes :

Il existe aussi un plugin dcaenc pour ALSA pour encoder à la volée en DTS mais j'ai constaté qu'il prenait 15-20% d'un cœur CPU en lecture contre seulement 3-4% pour l'encodage en AC3 par a52. De plus il ne semble plus maintenu et n'est pas disponible dans ma distribution de prédilection même si j'ai pu assez rapidement créer un paquet.

Encodage à la volée par ALSA (sans pulseaudio)

Comme la sortie sonore que j'utilise est dédiée à cet usage, et pour m'affranchir des petits bémols liés à pulseaudio, j'ai retiré ce dernier de l'équation.

On crée d'abord une sortie ALSA à utiliser (ac3hdmi) qui adapte le format et transmet à l'encodeur AC3 (a52hdmi) :

$ cat /etc/asound.conf
pcm.a52hdmi {
    type a52
    slavepcm "hdmi:CARD=PCH,DEV=1"   # CARD and DEV to adapt from aplay -L
    bitrate 640
}
pcm.ac3hdmi {
    type plug
    slave.pcm "a52hdmi"
}

Ensuite, on utilise cette nouvelle sortie dans notre lecteur, par exemple avec mpv:

audio-channels=5.1,stereo
video-sync=display-resample     # may improve A/V sync
ao=alsa
audio-device="alsa/ac3hdmi"

Il est probable qu'on puisse s'affranchir de la déclaration dans /etc/asound.conf avec pour mpv :

audio-device="alsa/plug:{SLAVE=\"a52:0,'hw:0,7'\"}" # to adapt from aplay -L

La limite de cette solution sont les suivantes :

Quelles leçons?

On peut en tirer les leçons suivantes :

Et voici un petit schéma pour résumer tout cela : Architecture physique son numérique multicanal Linux.