Dans le cadre de mon HTPC, je me suis heurté à la qualité audio délivrée par PulseAudio dans sa configuration initiale.
En effet, la configuration par défaut délivre un son adapté à des enceintes bas de gamme d'ordinateur, ce qui permet de ne pas exiger des performances trop gourmandes au processeur alors que dans la majeure partie des cas ce n'est pas nécessaire.

Un HTPC n'est toutefois pas un cas d'usage standard, et je souhaite obtenir une qualité optimale en accord avec les sources utilisées.

Bien que la réponse la plus courante soit de se débarrasser de PulseAudio pour utiliser seulement ALSA, je n'ai jamais trouvé d'explication rationnelle à cela. Excepté une latence supplémentaire ajoutée par PulseAudio, ce qui est évident étant donné qu'il se comporte comme une couche supplémentaire.
En revanche utiliser ALSA directement est une source de complication dont je préfère me passer...

L'essentiel de la configuration de PulseAudio se fait via le fichier /etc/pulse/daemon.conf

On peut afficher la configuration actuelle de PulseAudio avec l'argument --dump-conf

pulseaudio --dump-conf

Pour modifier cette configuration, nous allons ajouter notre propre configuration à la fin du fichier /etc/pulse/daemon.conf

# Audiophile setting
default-sample-format = s24le
default-sample-rate = 48000
resample-method = soxr-vhq
avoid-resampling = true
remixing-use-all-sink-channels = no
remixing-produce-lfe = no
remixing-consume-lfe = no
high-priority = yes
nice-level = -11
realtime-scheduling = yes
realtime-priority = 5

Après toute modification de la configuration de PulseAudio, il est nécessaire de le redémarrer:

pulseaudio -k; sleep 2; pulseaudio --start

default-sample-format

Considérant que PulseAudio va se charger de décoder le signal audio, qui est, le plus souvent, encodé dans un format audio, que ce soit mp3, wma ou de préférence flac, Dolby ou DTS, PulseAudio doit définir la taille allouée au codage de chaque échantillon au format PCM qui sera produit.

Le principe est simple, plus la taille allouée est importante, plus le codage de la quantification est fin et précis. Mais plus la taille allouée est importante plus il impactera la mémoire et le processeur.

Il est ici question de la quantification de l'échantillon, soit la précision sur l'axe vertical de la sinusoïde du signal.

En terme purement audio, il s'agit de la plage dynamique.

PulseAudio supporte un certain nombre de formats pour les échantillons, qui sont indiqués sur cette page.
Dans le cas d'un processeur performant, et considérant que nous recherchons un son optimal, nous ferons le choix du format le plus précis, accepté par la carte son, le s24.

Toutefois, PulseAudio supporte plusieurs formats s24, différenciés par le byte order supporté par le processeur.
Pour choisir, il faut connaître le byte order utilisé par le processeur installé.
Cette commande indiquera le byte order à choisir:

lscpu | grep 'Byte Order'

Enfin, pour connaître les formats accepté par la carte son, nous disposons de cette commande, qui va afficher les capacités des cartes sons installées.

cat /proc/asound/card?/codec* | grep -E "Codec|bits"

Ainsi que celle-ci pour connaître les formats accepté sur les port HDMI utilisés, le cas échéant.

cat /proc/asound/card?/eld* | grep -E "bits"

Il est inutile d'utiliser un format plus grand que celui supporté par la carte son utilisée, il se verrait réduit, entraînant une surcharge de travail superflue.

Par défaut, PulseAudio utilise un format 16 bits signé (s16le), la qualité CD.

default-sample-rate

Le sample rate est le pendant du sample format, alors que le second concerne la taille verticale de l'échantillon, le premier concerne le nombre d'échantillons par seconde, donc la taille horizontale (la durée) de l'échantillon.

À l'inverse de sample format, le sample rate a tout intérêt à être le plus petit possible.
En effet, une seule valeur étant possible pour chaque échantillon, pour obtenir d'avantage de finesse il faut multiplier les échantillons.

Ce paramètre concerne toutefois l’échantillonnage par défaut, celui sous lequel PulseAudio ne descendra pas.

Il faut considérer que l’échantillonnage le plus courant est 44.1kHz, soit 44100 échantillons par seconde.
Je choisis personnellement d'utiliser 48kHz par défaut, ce qui conduit à une légère extrapolation des sources à 44.1kHz sans représenter une charge de travail trop importante.

Il est à noter qu'il serait plus juste de choisir un échantillonnage par défaut de 44.1kHz, puisque étant le plus répandu.
Mais, à mon oreille, l'échantillonnage extrapolé à 48kHz produit un son de meilleur qualité.

Par défaut, PulseAudio utilise un échantillonnage de 44.1kHz par défaut et 48kHz en choix alternatif.

Toutefois, nous verrons par la suite que PulseAudio, par défaut, n'autorise en réalité que ces échantillonnages, soit 44.1 soit 48 et resample tout ce qui ne correspond pas.

Il est important également de ne pas choisir un échantillonnage plus important que les capacités de la carte son utilisée.
Cette information peut être obtenue avec les commandes suivantes

cat /proc/asound/card?/codec* | grep -E "Codec|rate"
cat /proc/asound/card?/eld* | grep -E "coding|rates"

resample-method

Ce paramètre permet de choisir l'algorithme à utiliser pour le ré-échantillonnage lorsque c'est nécessaire.

La commande suivante permet de connaître les différents algorithmes disponible:

pulseaudio --dump-resample-methods

Encore une fois, l'objectif étant la qualité audio, nous choisirons l'algorithme le plus performant en terme de qualité audio, le soxr-vhq.
Il est aussi le plus exigeant sur le processeur, mais c'est tout à fait négligeable sur un processeur moderne.

Par défaut, PulseAudio utilise l'algorithme speex-float-1, qui est rapide mais peu précis.

avoid-resampling

Un paramètre essentiel, qui fonctionne de paire avec default-sample-rate.
Configurer avoid-resampling à yes permet à PulseAudio de ne pas ré-échantillonner la source si ce n'est pas nécessaire.

Par pas nécessaire, il faut comprendre qu'avec ce paramètre activé, PulseAudio ne ré-échantillonnera pas les sources dès lors qu'elles sont supérieures ou égales à la valeur de default-sample-rate.

Ce paramètre permet donc de jouer les sons échantillonnés à 96, 192 voir 384kHz sans les détériorer. C'est un paramètre essentiel !

Par défaut, PulseAudio n'utilise pas ce paramètre, donc toutes les sources sont réduite à 48kHz au maximum par défaut.

remixing-use-all-sink-channels

Ce paramètre indique à PulseAudio si il doit remixer les sources pour les faire correspondre au mapping actuel de la sortie audio.
En plus concret, ce paramètre permet de upmixer les sources stéréo en surround, dans le cas d'une sortie 5.1, 7.1 ou plus. Ou au contraire de downmixer une source surround en stéréo le cas échéant.

Dans le cas d'une installation surround, cela revient à détruire l'image stéréo originale de la musique et produire le son sur toutes les enceintes.

La question du mapping des canaux est bien mieux gérée par un ampli Hi-Fi.

Par défaut, PulseAudio a ce paramètre réglé sur yes.

Un cas d'usage de ce paramètre est toutefois le cas de sons d'ambiance stéréo simulés en surround par des algorithmes de type Dolby Surround, DTS Neural:X and DTS Virtual:X.
Cela reste toutefois un cas d'usage très spécifique !

remixing-produce-lfe et remixing-consume-lfe

remixing-produce-lfe est utilisé pour créer un canal LFE (Low Frequency Effects) sur une source qui n'en dispose pas.
En somme, ce paramètre permet de créer un canal pour le caisson de basse à partir d'une source qui n'en disposerais pas.

Il y a peu de cas d'usage de ce paramètre, le caisson de basse étant en général pris en charge par l'ampli Hi-Fi ou disposant de son propre filtre passe-bas.

Le paramètre remixing-consume-lfe effectue l'inverse en remixant le canal LFE dans les autres canaux.
Ce paramètre peut être utile dans le cas d'un défaut de transmission du canal LFE ou l'absence de caisson de basse, tel que décrit dans Downmix 5.1 vers 5.0 avec PulseAudio.

Par défaut, ces 2 paramètres sont désactivés.

high-priority, nice-level, realtime-scheduling et realtime-priority

Ces paramètres, que j'ai personnellement choisi de laisser dans leur valeur par défaut, concerne la priorité d'exécution de PulseAudio.

Utiliser high-priority à yes permet de changer la priorité de PulseAudio en appliquant le niveau de niceness de l'option nice-level.
Cela permet à PulseAudio d'avoir un accès prioritaire sur le processeur par rapport à d'autres processus moins importants.

Quand à realtime-scheduling, il permet à PulseAudio d'utiliser l'ordonnanceur (scheduler) CPU temps réel SCHED_FIFO. Cela permet à PulseAudio de disposer d'un traitement temps réel de ses requêtes par le processeur en outrepassant la pile de requêtes envoyées par les autres processus.
realtime-priority est la priorité des requêtes de PulseAudio dans l'ordonnanceur SCHED_FIFO par rapport aux autres requêtes présentes dans cet ordonnanceur.

Par défaut, PulseAudio est paramétré pour travailler en temps réel avec des valeurs de priorités qui permettent de ne pas empiéter sur les processus vitaux du système.

tsched=0

Cet argument, tsched=0, est omniprésent sur internet dès lors qu'on se plaint de craquements ou de défauts dans le son produit par PulseAudio.
Pourtant le problème peut être plutôt à chercher du côté de l'économie d'énergie, comme expliqué dans Lorsque le son n’est pas utilisé, le HTPC produit d’affreux bruits.

tsched signifie timer-based scheduling, c'est la méthode de buffering utilisé par PulseAudio pour transmettre les échantillons audio à la carte son.
Comme expliqué par Lennart Poettering, le développeur à l'origine de cet algorithme, cette méthode de buffering permet de travailler sur des échantillons de taille variable, en fonction du comportement de la carte son, de la latence observée et du comportement de la source.

Le timer-based scheduling remplace la méthode par interruptions (IRQ), beaucoup moins flexible car basée sur des tailles d'échantillons fixes.

Ajouter tsched=0 sur un module de PulseAudio revient à lui demander de ne pas travailler avec la méthode timer-based scheduling mais d'utiliser à la place la méthode par interruptions.
Raison pour laquelle je déconseille l'usage de cet argument.

Par défaut, PulseAudio utilise le bufferring de type timer-based scheduling.

default-fragments et default-fragment-size-msec

Ces 2 paramètres ne sont utilisés qu'avec la méthode de buffering par interruptions.

default-fragments correspond au nombre de fragments à envoyer à la carte son. default-fragment-size-msec correspond à la taille, en millisecondes, de chaque fragment.

Il est difficile de paramétrer correctement la taille de buffer, car celle-ci se fait en millisecondes, or le même son en 16b/44kHz ne fait pas la même taille que son homologue en 24b/192kHz pour la même durée.
Dès lors, si la valeur par défaut de 100ms fonctionne parfaitement pour une source 44kHz, il produit des craquements pour une source 192kHz.

Réduire le durée du buffer permet d'améliorer le rendu des sources 192kHz, mais produira des défauts pour les sources 44kHz.
Réduire la durée du buffer augmente également le nombre d'interruptions et donc la charge processeur.

Globalement, il est préférable de ne pas utiliser ce mode de buffering et de garder le mode timer-based scheduling utilisé par défaut.
Si toutefois vous souhaitez utiliser ce mode de buffering, il faudra chercher la valeur la plus adéquat pour votre matériel et les sources utilisées.

Attention, contrairement à ce qu'on peut parfois lire, device.buffering.buffer_size et device.buffering.fragment_size ne donne pas la taille de buffer acceptée par la carte son ! Seulement la taille actuelle de buffer utilisée par PulseAudio.

Par défaut, PulseAudio n'utilise pas ces paramètres.
Toutefois, ils sont présent et par défaut réglés sur 4 fragments de 25ms. Soit 100ms.

Comments

  1. Un media center (HTPC) dans mon salon : L'odyssée de Servus on 07.09.2021

    […] Son haute fidélité sur Linux avec PulseAudio […]

Leave a Reply