Pourquoi réinventer la roue ?

Depuis mon adolescence j’ai toujours eu une certaine passion pour l’écoute musicale. Le fait que mon père soit professeur de musique n’est sans doute pas étranger à ceci. C’est donc naturellement que j’ai souhaité au fil du temps améliorer la qualité de mon écoute musicale.

Dans un premier temps, j’ai écouté ma musique à partir de fichiers musicaux extraits directement de CD. Cependant, avec l’avènement des services de streaming musicaux, la tentation de tester une plus grande facilité d’écoute était trop forte. J’ai donc basculé mon écoute musicale sur Spotify. Possédant déjà le plus grand catalogue parmi ses concurrents, la solution était attractive.

J’ai très vite été conquis par la possibilité d’écouter n’importe quel titre de n’importe quel artiste n’importe quand. Cependant, selon moi, les recommandations “personnalisées” par un algorithme ne me faisaient pas découvrir suffisamment de genre nouveau. De plus, ayant d’abord écouté mes morceaux préférés à partir de sources “lossless”, la qualité proposée par Spotify n’était pas au rendez-vous.

J’ai donc décidé après 1an et demi d’utilisation de Spotify de passer à Deezer. La plateforme était sur le point d’offrir un service “Hi-Fi” avec une musique de qualité CD. Si l’interface et le service proposé me convenaient, pourquoi ne pas rester. Néanmoins, l’expérience était certes plus enthousiasmante que Spotify, mais les recommandations proposées ne me satisfaisaient toujours pas. Deux possibilités s’offraient alors à moi : Qobuz ou Tidal.

Comparer les services de musique en ligne : Deezer, Qobuz, Spotify et Tidal et choisir son équipement audio

Les deux services donnent accès à une qualité d’écoute suffisante. Cependant, les recommandations Tidal diffèrent fortement de mes goûts musicaux et utilise trop fréquemment des recommandations automatisées. A l’opposé, Qobuz propose des recommandations faites par des curateurs professionnels, sur des genres variés : passant par le Jazz, le classique, les musiques du monde, le rock, pop, électronique et toujours de manière surprenante. Ceci oblige à faire son propre tri. Mais comme dans la vie de tous les jours, en multipliant les expériences on finit toujours par trouver de réelles pépites et cela forge un caractère musical.

Naturellement, j’ai immédiatement acccroché au contenu proposé par Qobuz. Cependant un problème persistait : l’interface utilisateur. Je n’étais absolument pas satisfait par l’expérience proposée par Qobuz. Elle est peu intuitive, avec de nombreux bugs et ne laissant pas la place à une personnalisation suffisante. De plus, d’un point de vue audiophile, l’implémentation est difficilement justifiable. Codé en html5/JavaScript avec le moteur de rendu proton, l’interface demande beaucoup trop de ressources, ne profite pas correctement des accélérations matérielles et a une mauvaise gestion de la mémoire. Trop d’artifices sont également utilisés pour coller aux interfaces natives de chaque plateforme.

Prenez d’autres applications dédiées à l’écoute musicale telles que Audirvana ou Roon : vous verrez qu’elles sont codées en C++, optimisent leur mémoire et abaissent au maximum leurs utilisations CPU pour réduire le bruit transmis au DAC externe et améliorer au maximum la qualité d’écoute. Cependant, elles aussi ont leurs problèmes respectifs. Que ce soit financier, matériel ou l’UI, aucune solution ne me paraissait pertinente.

Pulse media player

Lorsque ces considerations ont émergé, l’été suivant mon année de L1 arrivait. J’ai donc décidé de tenter d’écrire mon propre lecteur musical. Pulse était né. Aussi bien Qobuz que Tidal disposent de leur propre API REST permettant de récupérer les fichiers musicaux et de les transférer dans sa propre chaine d’écoute. Ainsi, les buts premiers de Pulse étaient d’agréger différents services sous une interface commune, indépendante, flexible, multiplateforme, personnalisable et surtout garantissant une qualité d’écoute optimale. Pour cela, l’objectif a été de préserver le fichier original sans aucune déformation. Aussi bien lors du décodage à partir du fichier source que dans la chaine audio.

L’architecture

La principale difficulté dans l’élaboration de Pulse est de lui donner accès à un canal audio exclusif sur demande. Aucune autre application ne peut alors utiliser la sortie audio sélectionnée. Mais, Pulse peut ainsi respecter l’échantillonnage et la quantification du fichier original, minimisant ainsi tout bruit introduit par un rééchantillonnage du mixer interne au système d’exploitation. De plus, les mixers système sont bien trop souvent paramétrés à 16bit/48Khz : bien trop bas pour supporter la plus grande dynamique offerte par une quantification 24bits et la largeur de scène donnée par une fréquence d’échantillonnage plus grande.

De plus, j’ai dû penser Pulse de la manière la plus flexible dès le départ. L’interface se doit d’être découplée du moteur audio pour être consistante entre les différents systèmes d’exploitation. Néanmoins, elle ne doit pas interférer avec des API et services différents. Pour cela, j’ai opté pour l’utilisation du framework Qt. Il permet de créer une interface en QML avec un rendu rapide et peu gourmand grâce à l’utilisation de technologie de rendu 3D modernes tels que Vulkan, OpenGL ou metal. L’interface est alors indépendante du système d’exploitation. Seule la logique interne la restreint. De plus, Qt offre la possibilité de coder la logique de Pulse en C++, language parfaitement adapté pour maximiser l’efficacité du traitement de données audio tout en étant compatible avec toutes les plateformes modernes.

En effet, d’une part Pulse se doit de fonctionner avec des plateformes mobiles dont les API sont codées en Kotlin/Java pour Android et Swift/objective-C pour iOS. Mais aussi d’autre part de fonctionner avec des interfaces plus classiques sur Windows/macOS/Linux, généralement avec des librairies d’accès en C++. Qt propose des API internes (wrapper des APIs de chaque plateforme) mais celle-ci ne sont pas très adaptées pour une utilisation audiophile. L’architecture de Pulse ne prévoit donc leur utilisation qu’en cas de fallback si aucune autre possibilité ne s’offre. Pour les plateformes de bureau et après avoir expérimenté avec de nombreuses options et librairies différentes, mon choix s’est porté sur libsoundio. C’est une librairie flexible, multiplateforme, permettant un contrôle exclusif de l’interface audio et gérant les particularités de chaque plateforme pour le développeur. L’interface principale est en C++, donc parfaitement adapté à Qt.

Mais, au-delà de ces considérations, de nombreux problèmes sont venus s’ajouter. Les plus embêtants viennent la pluspart du temps des systèmes d’exploitations mobiles. En effet, ils ont la désagréable habitude de forcer une application à quitter dès que l’utilisateur n’est plus en interaction avec elle. Ce qui d’un point de vue de la consommation de l’appareil est souhaitable … sauf dans le cas de la lecture en arrière-plan de musique ! Découpler le moteur audio totalement sous un autre process système est donc nécessaire pour faire tourner celui-ci indépendamment de l’UI.

D’autres subtilités techniques telles que la compilation et l’encapsulation des librairies externes ont été particulièrement chronophages. Prenons par exemple le cas de FFMPEG. Le son dans les fichiers originaux (Flac/mp3/AAC/Vorbis) est disponible sous forme de données compressées. Cependant, chaque plateforme n’est pas forcément nativement capable de les décompresser/décoder ! En effet, elles ne disposent pas toutes des librairies de décodage nécessaires ou des APIs requises. FFMPEG arrive donc à la rescousse grâce à ses algorithmes (parfois avec accélérations matérielles) pour décompresser les fichiers audio et fournir les données audio décompressées sous forme PCM que des interfaces audio peuvent ensuite lire. FFMPEG propose de plus des équaliseurs, chose très appréciée par certains audiophiles, mais également une multitude de filtres et transcodeurs. Le packager dans Pulse était donc une nécessité ! Mais cela n’a pas été chose facile de le compiler pour chaque plateforme et l’intégrer ensuite au processus de Qt.

Sans entrer dans chaque point technique que la partie audio que Pulse a pu soulever, voici un diagramme succinct et incomplet de Pulse tel que pensé à ses débuts :

Pulse early diagram

Le design d’une interface utilisateur

L’autre grand volet de Pulse a bien sûr été l’UI. Première chose que tout utilisateur voit. Cette partie a été passionnante par bien des aspects. Rien que l’élaboration du squelette principal a été un défi en lui-même. L’UI se doit d’être “responsive” et convenir à toutes les tailles d’écran. De 5″ à 42″ en passant par une interaction à la souris, au doigt ou au stylet. Mentionnerais-je les multiples résolutions ? Les changements d’échelles (aka “zoom” dans Windows) que des écrans de plus en plus définis obligent ? Les couleurs variantes d’un écran à l’autre ? Les modes sombres ? Les tailles multiples ? Les polices de caractères plus nombreuses les unes que les autres ? Vous l’aurez compris, désigner une UI est un métier ! Tout autant que le développement pur et dur, designer une UI/UX qui plaira à de multiples utilisateurs sur différents systèmes et supports est un challenge.

C’est donc avec un grand plaisir que j’ai essayé de relever ce défi ! Qt de part son language multiplateformes QML, ses modules “QtQuick” et ses nombreuses facilités a permis de gagner de nombreuses heures de travail. Mais, des détails ont nécessité de nombreux arrachages de cheveux ! L’intégration de la fenêtre à macOS de façon à minimiser l’apparition de la bar des menus. La synchronisation de la barre des notifications sous iOS. La gestion de multiples icônes. Tant de sujets, que la découverte est à la fois un enfer sur terre et une joie profonde lorsque maitrisée ! Pour le design des icônes, apprendre à maitriser Affinity Designer a été très formatif. Le logiciel possède un prix unique et permet de designer ses propres icônes sous forme vectoriel et de les exporter sous format SVG. Ils seront alors automatiquement rastérisés par Qt (mis sous formes de pixels) en fonction de la plateforme où l’application tourne. Ceci réduit considérablement le travail du développeur. De plus, des ressources gratuites sont accessibles en ligne. Que ce soit sur flaticon.com ou bien par google. Le résultat parle pour lui-même. Il n’attend plus que la finalisation de la “backend” de Pulse pour fonctionner :

Pulse’s desktop UI

Bien sûr, cette interface peut paraitre élémentaire ou simple. Mais, pas du tout ! Décomposer en multiples “modules”, elle est hautement évolutive et peut s’adapter à de nombreux services web, comme en témoigne l’arborescence des fichiers de l’UI de Pulse (ci-dessous). Des éléments sont ainsi pensés spécialement pour les interfaces “desktop” tels que le mini lecteur”. D’autres sont conçus pour une interface mobile tels que les notifications médias. Mais tous sont conçus pour être le plus possible “service agnostique” et pouvoir servir la même fonction, peu importe la source musicale.

Un projet au long therme

Pulse a été initialement pensé comme un projet d’été, mais pas seulement. D’une part conçu comme un apprentissage des technologies permettant la conception d’un logiciel, d’autre part comme une opportunité d’améliorer mon expérience d’écoute musicale. Et c’est cette dernière vision que je souhaite développer à l’avenir. Je ne finirai peut-être pas Pulse tout de suite. Mais j’espère m’y remettre à de multiples occasions dans les années à venir. Certaines technologies apparaitront très certainement, me permettant par la même occasion de me faciliter la tâche et de fournir un logiciel encore plus adapté. Peut-être, entamerai-je même une réécriture des briques initialement faites dans l’UI et le moteur. Seul l’avenir apportera réponse.