Multiroom parfait

Enceinte multiroom spotity connect + bluetooth + mpd avec snapcast

Cela fait un moment que je cherche une solution multiroom complète, depuis un moment, je tournais sur LMS (logitech media server) depuis quelques années, mais il me manquait quelques fonctionnalités. Et surtout il y en avait en trop, je n'utilise pas l'écoute local, mes deux sources sont spotify, et de temps en temps, youtube, jamendo ou autre depuis mon smartphone.

Après de nombreuse recherche, j'étais tombé sur snapcast, mais après quelques tests, je trouvais la qualité pas top, ça se désynchronisait assez souvant, sans revenir en synchro, donc pas forcément top top. En fin de compte, deux mois après mon essaie, une nouvelle version est sortie, mais je ne l'avais pas vu, je teste, et y'a du mieux, du beaucoup mieux. Toujours quelques désynchro minime, mais ça se re-synchronise très très vite, en moins d'une seconde généralement.

J'ai donc commencé à chercher la solution AllInOne parfaite, j'ai donc testé mopidy, basé sur mpd, facile de configuration avec snapcast, mais j'ai eu pas mal de petit bug, comme la perte de mon spotify connect par exemple, c'est embêtant, sachant que c'est ma principale utilisation.

J'ai ensuite testé volumio, qui propose des plugins pour snapcast, spotify et youtube. Bon le plugin youtube, je n'ai pas réussi à la faire fonctionner. Ensuite le plugin spotify fonctionnait parfaitement, jusqu'à l'activation du plugin snapcast, qui à fait perdre mon spotify connect, et impossible de le récupérer, sauf en supprimant le plugin snapcast, et en redémarrant la bête. Sans compter les bugs avec l'interface, qui lors de l'installation d'un plugin, se perds dans les noms, par exemple snapcast noté comme activé, alors que je viens de l'installer, et spotify désactivé, alors qu'il était activé avant, et en cliquant sur les paramètres de spotify, j'arrivais dans les paramètres de youtube. Pas très pratique.

Ma solution

La solution trouvé est relativement simple, et repose sur snapcast donc, avec librespot, mpd et la gestion du bluetooth. Pour la gestion du multiroom, j'utilise l'application android snapcast, qui permets de gérer les groupes, elle n'est pas top top pour le moment, j'ai donc installer une petite interface web permettant cette gestion.

Snapcast

Snapcast est le coeur de notre solution, via un système client/serveur, il permets la diffusion d'un flux et une synchronisation de celui-ci sur plusieurs appareils. J'admets que je ne connais pas les détails techniques de son fonctionnement.

Librespot

Librespot permets la création d'un point spotify connect. Spotify connect n'est pas comme le bluetooth, si vous connecter votre smartphone dessus, c'est librespot qui va chercher le flux, le smartphone n'est donc qu'un controlleur.

Bluetooth

La partie la plus compliqué à mettre en place, j'ai beaucoup galéré, avec l'utilisation de multiple tuto sans succès (souvent obsolète). Pour finir que suis partie sur un script tout prêt nommé bt_speaker, il fonctionne parfaitement, il y aura par contre quelques modifications à effectué au niveau de sa configuration.

mpd

MPD (Music Player Daemon) est un serveur de musique.

Nous avons besoin

Je passe les détails sur l'installation d'un raspberry-pi, et sa configuration, je pars du principe que vous avec un RPI3, avec debian 9 dessus, à jour, et correctement configuré.
Donc ce qu'il faut :

  • Un RPI3 pour le serveur + client
  • Autant de RPI que voulu pour les clients
  • Des SD ou clés USB avec debian 9 (j'ai pris la version lite perso)
  • Des enceintes
  • Et du temps (2 bonnes heures)

Pour une meilleur qualité sonore, n'oubliez pas d'ajouter audio_pwm_mode=2 dans le config.txt de la partition boot.

Installation du serveur

Installation de snapcast

J'ai décidé d'utiliser mon serveur également en client, donc j'installe snapserver et snapclient dessus. (Bon ce n'est pas vrai, mais c'est possible ^^)

$ wget https://github.com/badaix/snapcast/releases/download/v0.15.0/snapclient_0.15.0_armhf.deb -O /tmp/snapclient.deb
$ wget https://github.com/badaix/snapcast/releases/download/v0.15.0/snapserver_0.15.0_armhf.deb -O /tmp/snapserver.deb
$ dpkg -i /tmp/snapclient.deb /tmp/snapserver.deb
$ apt install -f 

Installation de librespot

La compilation de librespot plante avec la version de rust disponible sur raspbian, il faut donc compiler sa propre version :

$ curl https://sh.rustup.rs -sSf | sh

Puis on installe librespot, comptez une bonne heure pour la compilation.

$ source $HOME/.cargo/env
$ git clone https://github.com/librespot-org/librespot /tmp/librespot
$ apt install build-essential libasound2-dev pkg-config portaudio19-dev
$ cd /tmp/librespot
$ cargo build --release
$ cp /tmp/librespot/target/release/librespot /usr/bin/librespot

Installation du bluetooth

Pour le bluetooth, j'ai vraiment galéré, mais j'ai trouvé un petit script qui gère tout ça bien

$ wget https://raw.githubusercontent.com/lukasjapan/bt-speaker/master/install.sh -O /tmp/bluetooth.sh
$ chmod u+x /tmp/bluetooth.sh
$ /tmp/bluetooth.sh

J'ai eu un petit soucis de permission en lançant le service, pour le corriger :

$ chown -R btspeaker:btspeaker /home/btspeaker
$ chmod -R 775 /home/btspeaker

Lors de mes tests, je n'ai pas essayé d'affiner ces permissions, mais je pense qu'un 760 devrait fonctionner.

Installation de mpd

L'installation de mpd est très simple :

$ apt install mpd

Configuration

Configuration d'alsa

Nous allons commencer par configurer alsa pour y créer une interface virtuelle, qui va tout rediriger vers un fichier, ce fichier sera utiliser par snapcast pour le bluetooth.

Il suffit de créer le fichier /etc/asound.conf :

pcm.bluetooth {
        type plug
        slave.pcm ratebluetooth 
}

pcm.ratebluetooth {
        type rate
        slave {
                pcm bluewrite
                format S16_LE
                rate 44100
        }
}

pcm.bluewrite {
        type file
        slave.pcm null
        file "/tmp/bluetooth.snap"
        format "raw"
}

pcm.mpd {
        type plug
        slave.pcm ratempd 
}

pcm.ratempd {
        type rate
        slave {
                pcm mpdwrite
                format S16_LE
                rate 48000
        }
}

pcm.mpdwrite {
        type file
        slave.pcm null
        file "/tmp/mpd.snap"
        format "raw"
}

Configuration de snapcast

Nous allons modifier le fichier default de snapserver qui se trouve dans /etc/default/snapserver, afin d'y modifier SNAPSERVER_OPTS :

SNAPSERVER_OPTS="--sampleformat 48000:16:2 -c flac -s pipe:///tmp/bluetooth.snap?name=bluetooth -s spotify:///usr/bin/librespot?name=spotify&username=XXXXXXXXXX&password=XXXXXXXXXX&devicename=snapspot -s pipe:///tmp/mpd.snap?name=mpd"

Nous pourrions également ajouter airplay, mais n'ayant pas d'appareil apple, je ne peux tester, je vous renvoie au github de snapcast pour la configuration.

Puis on relance snapcast :

$ systemctl restart snapserver
$ systemctl restart snapclient

Puis nous allons modifier le client, pour lui donner un petit nom, pour ceci il suffit de modifier le fichier /etc/hostname, donc je le nomme simplement bureau, puisque se RPI est dans mon bureau. Pour prendre en compte ce changement, il suffit de reboot le RPI.

Configuration du bluetooth

Pour commencer, nous allons modifier le fichier /etc/bluetooth/main.conf, et y modifier la valeur de class (qui est normalement commenté) à 0x200414. Ceci aura pour effet de rendre notre périphérique seulement en écoute, il sera donc détecté en tant qu'enceinte/casque.

Ensuite nous allons donner un petit nom à notre périphérique bluetooth, pour ceci créez le fichier /etc/machine-info et y mettre :

PRETTY_HOSTNAME=snaptooth

Nous avons dernier fichier à modifier, il s'agit du fichier /etc/bt_speaker/config.ini, ou nous modifions la valeur de play_command par :

play_command = aplay --device bluetooth -f cd -

Cette commande permets l'écoute de tout son arrivant du bluetooth, et de le retransmettre sur l'interface virtuelle nommé bluetooth, qui écrit dans le fichier bluetooth.snap, qui est lu par snapcast.

Je n'avais pas un bon son en 48Mhz, je suis donc passé en 44.1Mhz (option -f cd). Si jamais vous voulez tester avec une fréquence de 48Mhz, vous pouvez tester avec -f dat, sans oublier de modifier la configuration de asound.conf et modifier pcm.ratebluetooth.

Puis on relance le service btspeaker :

$ systemctl restart bt_speaker

Configuration de mpd

Je passe la configuration de base de mpd (webradio, dossier de media etc ...), ici nous ne verrons que la connexion à snapcast.

Dans le fichier /etc/mpd.conf, commentez la partie audio_output présente, et ajoutez ceci :

audio_output {
    type            "fifo"
    name            "my pipe"
    path            "/tmp/mpd.snap"
    format          "48000:16:2"
    mixer_type      "software"
}

Installation et configuration des clients

Pour les clients, c'est vraiment très simple :

$ wget https://github.com/badaix/snapcast/releases/download/v0.15.0/snapclient_0.15.0_armhf.deb -O /tmp/snapclient.deb
$ dpkg -i /tmp/snapclient.deb
$ apt install -f 

Nous modifions également le /etc/hostname, par exemple, là je l'appelle donc salon.

Et on modifie le fichier /etc/default/snapclient pour y ajouter l'IP du serveur snapcast :

SNAPCLIENT_OPTS="-h 192.168.1.201"

et on reboot.

Utilisation

Pour l'instant j'utilise seulement depuis une vieille tablette, avec seulement 3 applications dessus, MALP pour la gestion des musiques MPD, Spotify pour gérer spotify, et snapcast pour gérer les clients. Mais je suis actuellement en train de travailler sur une interface qui permettra une gestion plus fine de snapcast, et pourquoi par la suite la gestion de mpd et spotify, mais là c'est vraiment pas pour demain.

Snapcast fonctionne par stream (avec ce tutoriel, nous en avons 3, spotify, bluetooth et mpd), avec l'application snapcast, nous pouvons donc associé tel ou tel client à un stream. L'application ne permets pas une gestion fine, notamment la création de groupe (qui est gérer par snapcast via JSON-RPC), nous pourrions par exemple mettre 3 clients dans le salon, et en créer un groupe pour les liés ensembles tout le temps.

Troubleshooting

Volume trop bas

Nous pouvons régler le volume global du RPI :

$ alsamixer

Mais au prochain reboot, on perd tout, pour sauvegarder :

$ alsactl store

Annexe

Autre que les applications utilisées, j'ai également suivi quelques tutoriels, qui m'ont permis de comprendre un peu mieux ce que je faisais :

Les documentations officiel :