URL: https://linuxfr.org/news/un-serveur-musical-pour-mon-salon Title: Un serveur musical pour mon salon Authors: raphj palm123, cli345, BenoĂźt Sibaud, devnewton đŸș, Nils Ratusznik et BAud Date: 2025ćčŽ04月27æ—„T20:06:46+02:00 License: CC By-SA Tags: musique, mpd, pipewire, samba, bluetooth, audio et audiovisuel Score: 69 Aujourd’hui, on va mettre en place un serveur musical pilotable Ă  distance en utilisant MPD. Il sera notamment capable de jouer de la musique stockĂ©e dessus ou des radios Internet. Il sera aussi capable de se comporter comme une enceinte Bluetooth. On va parler de rĂ©cup de vieux matos, de Debian, MPD, PipeWire, Samba, d’agent Bluetooth, de systemd (`-analyze`, `-logind`), de Powertop et de vbetool. ![Serveur musical - les clients MPD se connectent Ă  MPD, les clients Bluetooth peuvent jouer de la musique, les clients SMB peuvent envoyer des fichier, et le serveur est reliĂ© Ă  des enceintes en Jack](https://raphael.jakse.fr/static/serveur-musical.svg?v=4) Cet article au ton trĂšs « administration systĂšme » s’adresse Ă  : - des gens qui voudraient mettre en place un systĂšme plus ou moins similaire, mĂȘme pour faire autre chose dans le mĂȘme esprit (en mode tutoriel) ; - des gens qui aiment les dĂ©tails techniques et voir les trucs cools qu’on peut faire avec les logiciels libres ; - toute autre personne curieuse pour d’autres raisons. Il est probablement trop technique pour quelqu’un qui ne manipule pas la ligne de commande, qui pourra peut-ĂȘtre malgrĂ© tout, avec suffisamment de motivation, se laisser porter par la dĂ©marche. ---- [Debian](https://www.debian.org/) [Using a Raspberry Pi as a Bluetooth speaker with PipeWire](https://www.collabora.com/news-and-blog/blog/2022/09/02/using-a-raspberry-pi-as-a-bluetooth-speaker-with-pipewire-wireplumber/) [Music Player Daemon](https://mpd.orghttps//www.musicpd.org/) [MPD Clients](https://www.musicpd.org/clients/) [M.A.L.P sur F-Droid](https://f-droid.org/packages/org.gateshipone.malp/) [Cantata](https://github.com/CDrummond/cantata) [Samba](https://www.samba.org/) [Command-Line Methods for Turning Monitor on or off in Linux](https://www.baeldung.com/linux/cli-monitor-turn-on-off) [How do I run a single command at startup using systemd?](https://askubuntu.com/questions/919054/how-do-i-run-a-single-command-at-startup-using-systemd) [Writing a systemd service to be executed at resume](https://unix.stackexchange.com/questions/124212/writing-a-systemd-service-to-be-executed-at-resume) [How to manage wireless connections using iwd on Linux](https://linuxconfig.org/how-to-manage-wireless-connections-using-iwd-on-linux) [How do I make Powertop changes permanent?](https://askubuntu.com/questions/112705/how-do-i-make-powertop-changes-permanent) [rsgain](https://github.com/complexlogic/rsgain) [ Using MPD for ReplayGain](https://funloop.org/post/2021-01-05-mpd-and-replaygain.html) [Guide: Getting Wireplumber to work without D-Bus](https://forum.artixlinux.org/index.php/topic,4230.0.html) ---- # Introduction Note de lecture : cette dĂ©pĂȘche est trĂšs dĂ©taillĂ©e, je vous conseille de passer les sections qui vous intĂ©ressent moins. ## Motivation Dans mon salon, j’ai des petites enceintes toutes bĂȘtes qui sonnent plutĂŽt bien. Mettre de la musique implique de s’embĂȘter Ă  brancher un ordinateur, sur lequel je suis le seul avoir le contrĂŽle. Ce serait bien d’avoir un systĂšme prĂȘt Ă  l’emploi et que tout le monde peut contrĂŽler. ## Objectifs - Pas d’achat : on fait avec de la rĂ©cup - Peu gourmand en Ă©nergie - Silencieux (Ă  part la musique, bien sĂ»r) - Facile Ă  utiliser pour une personne non technique - Pouvoir mettre de la musique sans que ça soit pĂ©nible, en utilisant ma bibliothĂšque musicale locale, ou des radios internet - Pouvoir laisser n’importe qui se connecter en Bluetooth et lancer sa propre musique Nous allons, ensemble, remplir ces objectifs. On va rentrer dans les dĂ©tails, qui peuvent ĂȘtre utiles dans d’autres applications, et parce que je sais que certaines personnes ici aiment ça, bande de geeks :-) ## MatĂ©riel Ă  disposition - des enceintes parfaitement fonctionnelles mais sans fonctionnalitĂ© Bluetooth - un appareil style netbook du dĂ©but des annĂ©es 2010 (dans mon cas, c’est une vieille tablette Airis Kira Slimpad plus vraiment adaptĂ©e au web moderne, dotĂ©e d’un processeur Intel Atom un peu lent, d’un peu de stockage assez lent, d’un Wifi plutĂŽt lent, du Bluetooth, d’1 Giga de mĂ©moire vive) **Note sur les interfĂ©rences Wifi et Bluetooth.** Le Wifi de cette tablette est en 2,4 GHz, pareil que le Bluetooth. Tout Ă©change wifi cause des perturbations sur le Bluetooth et tout transfert intensif rend le Bluetooth inutilisable. Du grand classique. Un Wifi 5, 6 ou 7 aurait Ă©tĂ© apprĂ©ciable. Il serait possible d’utiliser une carte Wifi USB, mais je n’en ai pas donc on fera sans. ## Ce qu’on va faire dans les grandes lignes - Installer une Debian minimale - La configurer pour qu’elle soit accessible par le rĂ©seau, la plus rapide et lĂ©gĂšre possible en utilisation mĂ©moire, en temps de dĂ©marrage et en consommation Ă©nergĂ©tique - Installer et configurer MPD - Installer et configurer Samba - Configurer en mode « enceinte Bluetooth » # Installation standard minimaliste de Debian Par souci de concision, on ne va pas dĂ©tailler l’installation de Debian, il existe d’autres ressources au besoin. En rĂ©sumĂ© : - Debian classique en 32 bits (ça consomme moins de mĂ©moire que du 64 bits) - j’ai laissĂ© l’installateur faire le partitionnement (une partition principale en ext4, et une partition swap de 1G) - j’ai ajoutĂ© l’option *noatime* sur la partition principale pour Ă©viter d’écrire inutilement lors des accĂšs, ce qui use le SSD et ralentit le systĂšme (d’autant que le SSD est lent) - lors de l’étape Tasksel, choisir *console*, *serveur ssh* et *utilitaires standard*, et en particulier pas d’environnement de bureau - on installe sudo et on ajoute l’utilisateur au groupe sudo, ou alors on se donne accĂšs Ă  root en ssh avec une clĂ© SSH - on installe iwd (le remplaçant moderne de `wpa_supplicant`, supposĂ© plus performant et plus stable permettant Ă©galement de se passer de NetworkManager assez facilement) et on [connecte l’appareil en wifi avec](https://linuxconfig.org/how-to-manage-wireless-connections-using-iwd-on-linux) - on identifie et dĂ©sactive ou on dĂ©sinstalle le superflu avec `systemd-analyze critical-chain` et `systemd-analyze blame` (typiquement, si vous avez installĂ© NetworkManager, ModemManager a peut-ĂȘtre Ă©tĂ© installĂ© alors que vous n’avez pas de modem Ă  gĂ©rer) - on peut configurer le menu de Grub pour moins attendre au dĂ©marrage Note : sur cette tablette, l’installateur Debian n’arrive pas Ă  se connecter en Wifi, j’ai donc utilisĂ© la version DVD (le premier suffit). # Gains Ă©nergĂ©tiques potentiels ## Éteindre l’écran L’écran est potentiellement une des plus grosses sources de consommation Ă©lectrique. On n’en a pas besoin, donc on va l’éteindre au dĂ©marrage et Ă  la sortie de veille. Pour cela, on va installer vbetool (sources : [outils pour Ă©teindre l’écran](https://www.baeldung.com/linux/cli-monitor-turn-on-off), [lancer une commande au dĂ©marrage](https://askubuntu.com/questions/919054/how-do-i-run-a-single-command-at-startup-using-systemd), [lancer une commande aprĂšs la veille](https://unix.stackexchange.com/questions/124212/writing-a-systemd-service-to-be-executed-at-resume)): ```bash sudo apt install vbetool cat << EOF | sudo tee /etc/systemd/system/screenoff.service [Unit] Description=Screen off After=suspend.target [Service] ExecStart=vbetool dpms off [Install] WantedBy=multi-user.target suspend.target EOF ``` Attention : ça peut compliquer grandement l’usage de l’appareil, on peut vouloir appliquer un dĂ©lai avant extinction pour se faciliter la vie. ## Powertop pour amĂ©liorer la consommation Ă©lectrique Powertop permet de voir ce qui utilise le CPU et les diverses ressources, et d’ajuster un peu les paramĂštres de mise en veille de diffĂ©rents pĂ©riphĂ©riques. On va l’installer : ```bash sudo apt install powertop ``` Ensuite, ça peut ĂȘtre cool de lancer l’outil pour constater un peu ce qui tourne et consomme des ressources, de se dĂ©placer dans les onglets, et de tenter des trucs dans l’onglet « Tunables » : ```bash sudo powertop ``` Si passer tout Ă  `Good` ne cause pas de problĂšme d’instabilitĂ© Ă©vidente, alors on peut appliquer la configuration de Powertop Ă  chaque dĂ©marrage ([source](https://askubuntu.com/questions/112705/how-do-i-make-powertop-changes-permanent)) : ```bash cat << EOF | sudo tee /etc/systemd/system/powertop.service [Unit] Description=PowerTOP auto tune [Service] Type=oneshot Environment="TERM=dumb" RemainAfterExit=true ExecStart=/usr/sbin/powertop --auto-tune [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable powertop.service ``` Sinon, il y a des solutions mentionnĂ©es dans la source pour dĂ©sactiver certains changements (si vous observez des dysfonctionnements avec certains pĂ©riphĂ©riques par exemple, et notamment si vous avez des problĂšmes de Wifi ou Bluetooth) Perso, je sais que sur cette tablette, passer tout Ă  `Good` fait (faisait il y a 10 ans en tout cas) qu’aprĂšs un dĂ©lai la premiĂšre frappe sur le clavier ou le premier clic de la souris Ă©tait ignorĂ©, et aussi Ă©tait nĂ©cessaire pour rĂ©veiller l’USB – clairement je m’en fiche ici, mais si votre wifi ou votre Bluetooth est en USB et que les paramĂštres causent une extinction aprĂšs un dĂ©lai, clairement ce n’est pas bon). ## Bonus : Configurer le bouton power pour mettre en veille Sur ma tablette, un appui court sur le bouton power Ă©teint la tablette (et ensuite on la rallume en appuyant 3 longues secondes). Si on souhaite qu’un appui court mette en veille l’appareil et un appui long l’éteigne, comme ça on fait un compromis Ă©nergĂ©tique supposĂ©ment raisonnable pour rendre l’ensemble un poil plus pratique, c’est facile avec systemd. Ajoutez ces deux lignes au fichier `/etc/systemd/logind.conf` : ```bash HandlePowerKey=suspend HandlePowerKeyLongPress=poweroff ``` Rechargez les paramĂštres : ```bash sudo systemctl restart systemd-logind ``` # MPD : Music Player Deamon Ok, passons au cƓur du sujet : mpd. ## KĂ©sako Simplement, c’est un lecteur de musique pilotable Ă  distance qui est capable de : - lire de la musique que vous mettez dans son dossier de travail ; - lire des playlists que vous mettez dans son dossier de travail ; - lire des flux radio, qui sont par exemple dĂ©finis dans des playlists. Entre autres. Certains clients MPD, comme Cantata (une application Qt5 plus ou moins abandonnĂ©e mais encore dans les dĂ©pĂŽts), sont mĂȘme capables de lire de la musique sur votre serveur MPD que vous avez localement sur votre ordinateur, ou de gĂ©rer les playlists. Ça rend d’ailleurs la constitution de playlists vaguement confortable. Vous n’avez pas besoin d’écrire des playlists M3U Ă  la main, quoi. Les avantages sont multiples : - c’est mĂ©ga lĂ©ger, une machine Ă©puisĂ©e peut faire tourner MPD Ă  l’aise - si vous lisez la musique stockĂ©e sur le serveur, le rĂ©seau n’est pas engorgĂ© - on peut ĂȘtre plusieurs Ă  contrĂŽler la musique, ce n’est pas une personne qui contrĂŽle tout, et on peut le faire depuis le canapĂ© - il existe toute une flopĂ©e de clients, il y en a pour tous les goĂ»ts pourvu que vous aimiez les logiciels abandonnĂ©s ou en ligne de commande / en ncurses (ouais, c’est quand mĂȘme un problĂšme que j’identifie et qui a largement retardĂ© mon adoption de MPD) - les gens non techniques apprĂ©cieront les applications mobiles telles que M.A.L.P pour gĂ©rer la musique et le volume sonore. ## C’est parti pour l’installation ```bash sudo apt install mpd ``` On va modifier sa configuration : ```bash sudo nano /etc/mpd.conf ``` On peut laisser les paramĂštres par dĂ©faut suivants : ``` music_directory "/var/lib/mpd/music" playlist_directory "/var/lib/mpd/playlists" ``` Vous l’aurez compris, c’est lĂ  oĂč on stocke les musiques et les playlists. Dans la section suivante, on verra comment rendre le dĂ©pĂŽt de morceaux simple et convivial. On va laisser la plupart des autres paramĂštres par dĂ©faut. On va changer `bind_to_address`, qui est par dĂ©faut Ă  `localhost`, mais on veut que n’importe quel appareil sur le rĂ©seau soit capable de s'y connecter. On va aussi explicitement mettre le port Ă  la valeur par dĂ©faut (ce n’est probablement pas nĂ©cessaire, mais c’est ce que j’ai fait) : ``` bind_to_address "0.0.0.0" port "6600" ``` On veut aussi que quand des fichiers sont changĂ©s dans les dossiers `music` et `playlists`, mpd se mette Ă  jour tout seul pour ne pas avoir Ă  le baby-sitter : ``` auto_update "yes" ``` J’ai tentĂ© d’activer zeroconf pour que les clients MPD puissent le trouver tout seul : ``` zeroconf_enabled "yes" zeroconf_name "Music Player @ %h" ``` Mais en vrai, je n’ai pas rĂ©ussi Ă  faire fonctionner ça. En tout cas, un prĂ©requis est d’avoir installĂ© et activĂ© `avahi-daemon`, on verra ça plus tard dans la partie Samba du coup. Vous aurez peut-ĂȘtre envie de mettre un mot de passe voire de changer les permissions par dĂ©faut en dĂ©commentant et adaptant les paramĂštres suivants, mais c’est optionnel : ``` #password "password@read,add,control,admin" #default_permissions "read,add,control,admin" ``` Ensuite, la partie critique, la sortie audio. Pour l’instant, on va dire Ă  `mpd` d’utiliser [Alsa](https://fr.wikipedia.org/wiki/Advanced_Linux_Sound_Architecture) directement. C’est le plus direct et le plus lĂ©ger (on passera Ă  [PipeWire](https://fr.wikipedia.org/wiki/PipeWire) plus tard, pour gĂ©rer l’aspect rĂ©cepteur Bluetooth) ``` audio_output { type "alsa" name "My ALSA Device" device "hw:0,0" # optional mixer_type "hardware" # optional # mixer_device "default" # optional mixer_control "Master" # optional mixer_index "0" # optional } ``` Pour une de mes installations, j’ai commentĂ© `mixer_device` parce que ce n’est manifestement pas la bonne valeur chez moi, et que ça marche bien sans. Vous pouvez vous passer des autres valeurs optionnelles, mais vous n’aurez pas le contrĂŽle du volume sonore depuis les clients MPD si vous faites ça. Vous allez donc devoir trouver les bonnes valeurs pour les paramĂštres `mixer_*`, et pour `device`. ainsi que `mixer_control` et `mixer_index`. Quelques indices : - `hw:0,0` est probablement la bonne valeur pour `device`, et `0` pour `mixer_index` aussi. Vous pouvez lister vos cartes son avec `aplay -L`. Vous aurez peut-ĂȘtre besoin d’installer le paquet `alsa-utils`. - la valeur de `mixer_control` est le nom du contrĂŽle que vous utiliserez pour changer le volume dans `alsamixer`, du paquet `alsamixergui` que vous aurez probablement besoin d’installer. Si vous galĂ©rez trop avec les valeurs de `mixer-*`, vous pouvez simplement utiliser `mixer_type "software"`, c’est moins propre mais ça devrait faire le taf. Et sinon, vous pouvez toujours sortir l’artillerie lourde et passer directement Ă  PipeWire. Pour appliquer vos modifications : ```bash systemctl enable --now mpd # À partir de Debian Trixie, mpd n’est plus activĂ© par dĂ©faut au niveau du systĂšme systemctl restart mpd # Si MPD tournait dĂ©jĂ  ``` Vous pouvez dĂ©boguer vos changements avec la commande suivante, qui suit les logs en temps rĂ©el : ```bash journalctl -fu mpd ``` Vous avez plusieurs options pour essayer de lire des choses avec mpd : - installer l’application M.A.L.P sur votre tĂ©lĂ©phone Android, ou une autre application cliente MPD, et ajouter un profil avec la bonne adresse, le bon port et le bon mot de passe ; - installer un client comme Cantata sur votre ordinateur, avec la bonne adresse, le bon port et le bon mot de passe ; - installer `mpc` directement sur le serveur. Normalement `mpc play` permet de lancer la lecture. Moi, j’ai testĂ© avec une webradio dans une playlist (`/var/lib/mpd/playlists/radio-paradise-main-mix.m3u` avec le contenu `http://stream.radioparadise.com/ogg-192m`), mais on peut aussi Ă©videmment placer un morceau dans `/var/lib/mpd/music/`. ## ReplayGain Le niveau sonore de mes morceaux n’est pas homogĂšne, donc il faut sans cesse adapter le volume d’un morceau Ă  l’autre. C’est pĂ©nible, voire inutilisable en l’état. Une solution pour ça est replay gain : on analyse et on enregistre le niveau sonore d’une piste dans ses mĂ©tadonnĂ©es. Il existe plein d’outils pour faire ça, dont https://github.com/complexlogic/rsgain On peut le faire avant d’envoyer les fichiers sur l’appareil. Pour ma part, je l’ai fait sur la tablette, et il n’existe pas de paquet Debian 32 bits, donc je l’ai compilĂ© : ```bash sudo apt install cmake build-essential pkd-config git libavcodec-dev libavformat-dev libtag1-dev libebur128 libinih-dev libfmt-dev git clone --depth=1 https://github.com/complexlogic/rsgain cd rsgain mkdir build cd build cmake .. make -j2 sudo make install ``` Il faudra s'assurer que les morceaux au format Opus sont Ă©tiquetĂ©s avec le tag `R128_TRACK_GAIN` et pas `REPLAYGAIN_TRACK_GAIN`, parce que c'est ce que MPD [s’attend Ă  avoir](https://github.com/MusicPlayerDaemon/MPD/issues/2055#issuecomment-2211164742). Pour ça, on va convaincre rsgain de suivre les standards (que certains lecteurs de musiques ne comprennent pas) en crĂ©ant un preset qui contient : ``` [Opus] OpusMode=s ``` Mes morceaux ne sont pas organisĂ©s par albums, donc je dĂ©sactive l’analyse par album. Je vais donc partir du [preset no_album](https://github.com/complexlogic/rsgain/blob/master/config/presets/no_album.ini) : ``` mkdir -p ~/.config/rsgain/presets; cat << EOF> ~/.config/rsgain/presets/no_album_standard_opus.ini [Global] TagMode=i Album=false TargetLoudness=-18 ClipMode=p MaxPeakLevel=0.0 TruePeak=false Lowercase=false ID3v2Version=keep PreserveMtimes=false DualMono=false OpusMode=s EOF ``` Ensuite, on peut le rsgain sur le dossier de musiques avec ce preset. Mes morceaux ne sont pas organisĂ©s par albums, donc je dĂ©sactive l’analyse par album. ```bash rsgain easy -p no_album_standard_opus -m MAX /var/lib/mpd/music ``` Note : l'option `--skip-existing` permet d'ignorer les fichiers dĂ©jĂ  taggĂ©s : ```bash rsgain easy --skip-existing -p no_album_standard_opus -m MAX /var/lib/mpd/music ``` Avec cette option, on peut exĂ©cuter cette tĂąche rĂ©guliĂšrement, par exemple dans un cron, pour calculer le ReplayGain pour les nouveaux fichiers. Pour la premiĂšre exĂ©cution, il vaut certainement mieux ne pas l’utiliser, sinon, si vous aviez dĂ©jĂ  des fichiers qui avaient l'information, il se peut que le rĂ©sultat ne soit pas uniforme. Il faut dire Ă  MPD d’utiliser le ReplayGain dans `/etc/mpd.conf` : ``` replaygain "track" ``` Vous aurez peut-ĂȘtre besoin de jouer avec les autres paramĂštres liĂ©s au volume et au ReplayGain. Voici les miens : ``` # Ce paramĂštre dĂ©finit la prĂ©-amplification Ă  appliquer pour les morceaux qui ont l'information du ReplayGain replaygain_preamp "0" # Ce paramĂštre dĂ©finit la prĂ©-amplification Ă  appliquer pour les morceaux qui ne l'ont pas replaygain_missing_preamp "0" # Faut-il interdire Ă  MPD de dĂ©passer le niveau original d'amplification en appliquant le ReplayGain? replaygain_limit "no" # Faut-il permettre Ă  MPD d'ajuster le volume pendant la lecture pour normaliser ? volume_normalization "no" ``` Un autre paramĂštre qu’on peut rĂ©gler, c'est la maniĂšre dont MPD rĂšgle le volume dynamiquement pour ReplayGain. Dans votre bloc `audio_output`, vous pouvez ajouter `replay_gain_handler` et la valeur `"software"` (c'est la valeur par dĂ©faut) ou `"mixer"`. En thĂ©orique, les traitements software dĂ©gradent le son, mais en pratique, avec `"mixer"`, je tombe sur [ce bug qui met le volume Ă  100% aprĂšs chaque changement de piste](https://github.com/MusicPlayerDaemon/MPD/issues/1778). Note : je ne suis pas encore convaincu d’avoir rĂ©ussi Ă  trouver les rĂ©glages parfaits, n’hĂ©sitez pas Ă  expĂ©rimenter. ## Les clients MPD À ce stade, vous devriez avoir un serveur MPD fonctionnel et configurĂ©. Si applicable, vous pouvez commencer Ă  suggĂ©rer aux gens de votre foyer d’installer l’application M.A.L.P sur leur appareil Android ; elle est libre et disponible sur F-Droid et sur le Play Store. Avec un peu de chance, votre enthousiasme Ă©tait communicatif et c’est eux qui vous demanderont :-) Pour les autres types d’appareils, vous allez devoir faire vos recherches vous-mĂȘme je n’ai pas Ă©tudiĂ© les options sous Windows, Mac ou iPhone, mais il y en a. Pour Linux, j’ai essayĂ© Cantata. Il me convient, si ce n’est qu’il a l’air un peu abandonnĂ©, et il a une interface certes conviviale, mais quand mĂȘme un peu brute. Il existe des widgets qui s’intĂšgrent aux diffĂ©rents environnements de bureaux pour les diffĂ©rents systĂšmes d’exploitation, je n’ai pas explorĂ©. Le site de MPD propose une [liste de clients](https://www.musicpd.org/clients/), et le wiki de Arch [aussi](https://wiki.archlinux.org/title/Music_Player_Daemon#Clients). ![M.A.L.P, un client mobile pour MPD](https://f-droid.org/repo/org.gateshipone.malp/en-US/phoneScreenshots/Playlist.png) # Samba pour dĂ©poser les morceaux (et les playlists) DĂ©poser des morceaux, vous allez probablement le faire depuis un ordinateur, et Ă  peu prĂšs n’importe quel systĂšme d’exploitation est capable d’aller chercher un dossier SMB en rĂ©seau, alors je vous propose de configurer un serveur Samba. Ça a le bon goĂ»t d’ĂȘtre trĂšs lĂ©ger, trĂšs simple Ă  faire et de fonctionner depuis n’importe quel OS. Allons-y, et tant qu’à faire, on va aussi installer Avahi, qui permettra aux ordinateurs sous Linux et Mac de dĂ©couvrir les dossiers partagĂ©s tous seuls : ```bash sudo apt install samba avahi-daemon ``` On va partager nos dossiers `music` et `playlists` au monde entier en lecture-Ă©criture (YOLO). On Ă©dite `/etc/samba/smb.conf`: ``` [Musique] path=/var/lib/mpd/music read only=no writable=yes comment=Fichiers musique MPD guest ok = yes force group = audio force user = mpd browsable = yes public = yes create mask = 0644 directory mask = 0755 [Playlists] path=/var/lib/mpd/playlists read only=no writable=yes comment=Listes de lecture MPD guest ok = yes force group = audio force user = mpd browsable = yes public = yes create mask = 0644 directory mask = 0755 ``` Je ne maitrise pas particuliĂšrement Samba et il y a peut-ĂȘtre des options superflues, mais globalement l’esprit c’est : - n’importe qui doit pouvoir accĂ©der Ă  ces deux en lecture et en Ă©criture depuis le rĂ©seau. En particulier, la crĂ©ation de dossiers doit marcher - MPD doit pouvoir lire ce qu’on a Ă©crit dans ces dossiers - les fichiers et dossiers doivent avoir des permissions sensĂ©es Bien sĂ»r, on peut vouloir restreindre l’accĂšs Ă  certains utilisateurs et/ou avec un mot de passe. Je vous laisse creuser. AprĂšs un redĂ©marrage de Samba : ```bash sudo systemctl restart samba ``` Avec un peu de chance, dans l’onglet « RĂ©seau » de votre gestionnaire de fichier, dans la section « Partages SMB », votre appareil apparait. Sinon, vous devriez pouvoir y accĂ©der avec `smb://HOST/` avec Dolphin et probablement Nautilus, probablement `\\HOST` sous Windows. ## Alternatives possibles Ă  Samba - Si on a un NAS, monter un dossier sur le serveur MPD, voire installer MPD sur le serveur de stockage, ou avoir une tĂąche chron qui fait un rsync bien placĂ© - Mettre en place une synchronisation avec Nextcloud ou Syncthing, et faire pointer MPD vers le bon dossier, ou ajouter le dossier de MPD comme dossier de stockage externe Ă  Nextcloud par exemple - SFTP - NFS - FTP (mais les autres options sont probablement meilleures) # RĂ©cepteur Bluetooth Ce n’est bien sĂ»r pas nĂ©cessaire si vous ĂȘtes parfaitement satisfait·e avec MPD, mais si vous voulez que votre appareil soit en plus capable de se comporter comme une enceinte Bluetooth, vous ĂȘtes au bon endroit. Les difficultĂ©s qu’on va rĂ©soudre sont les suivantes : - pour l’instant, MPD accĂšde au son directement avec ALSA, et en gĂ©nĂ©ral on ne peut pas ĂȘtre plusieurs sur ALSA. En tout cas, et mĂȘme s’il a l’air possible de faire fonctionner Bluetooth et ALSA ensemble, ça n’a pas l’air d’ĂȘtre terriblement simple ou mĂȘme stable. Donc on va utiliser PipeWire. On aurait pu utiliser PulseAudio, mais PipeWire le remplace, et fonctionne gĂ©nĂ©ralement mieux. - PipeWire, c’est pensĂ© pour ĂȘtre lancĂ© dans une session graphique d’un utilisateur, mais nous, on a un serveur *headless*. Il va falloir faire en sorte de lancer une session utilisateur au dĂ©marrage sans interaction, et que cette session ne soit pas tuĂ©e. - mpd tourne avec son utilisateur, PipeWire avec son utilisateur, et aprĂšs s’ĂȘtre rendu compte qu’il faut que ça soit les mĂȘmes, faut aussi savoir comment, et le faire. Lors de l’installation de Debian, on a dĂ©fini un utilisateur. On peut utiliser cet utilisateur. Sinon, on peut aussi en crĂ©er un pour ça, pensez bien Ă  l’ajouter aux groupes `audio` et `bluetooth`. ## Garder une session utilisateur active On va dĂ©marrer une session utilisateur au boot : ```bash sudo loginctl enable-linger user # remplacer user par le nom d’utilisateur ``` On va s’assurer que les processus de cette session ne sont pas tuĂ©s au moment oĂč on quitte une session (par exemple quand on quitte une session ssh) : dans le fichier `/etc/systemd/logind.conf`, dĂ©commentez la ligne `KillExcludeUsers` et ajouter le nom d’utilisateur aprĂšs le `=`. Vous deviez avoir ``` KillExcludeUsers=user ``` oĂč `user` est le nom d’utilisateur. On peut maintenant recharger ces paramĂštres : ```bash sudo systemctl restart systemd-logind ``` ## Installer PipeWire et les choses nĂ©cessaires À ce stade, MPD bloque probablement l’utilisation du son parce qu’il s’y connecte via ALSA. On va le stopper. ```bash sudo systemctl stop mpd ``` PipeWire et WirePlumber vont dorĂ©navant gĂ©rer le son, et `libspa-0.2-bluetooth` permet au dĂ©mon qui gĂšre le Bluetooth (Bluez) de s’inter-connecter Ă  PipeWire pour le Bluetooth Audio. ```bash sudo apt install wireplumber pipewire libspa-0.2-bluetooth ``` En tant que votre utilisateur (nommĂ© `user` dans les commandes prĂ©cĂ©dentes) (c’est important), activez PipeWire au dĂ©marrage et lancez-le : ```bash systemctl --user enable --now pipewire wireplumber ``` Notez que `pipewire-pulse` n’est pas nĂ©cessaire, d’ailleurs vous pouvez le supprimer ou le dĂ©sactiver en toute sĂ©curitĂ© s’il a Ă©tĂ© installĂ©. ## Installer un agent Bluetooth qui accepte toutes les connexions audio sans vĂ©rifications avec code Normalement, accepter les connexions Bluetooth se fait Ă  l’aide d’un agent Bluetooth : - qui tourne dans votre session graphique : c’est gĂ©rĂ© par votre environnement de bureau, ou une application comme `bluetooth-applet` (est-ce que ça existe encore ?) que vous lancez. LĂ , Ă©videmment, on n’a pas de session graphique, et pour l’instant on n’a pas d’agent Bluetooth qui tourne. - En ligne de commande, avec un outil comme `bluetoothctl`. Je vous invite Ă  essayer. Vous pouvez lancer des commandes comme `pairable on`, `discoverable on`, `scan on`, et essayer de vous connecter avec un autre appareil. AprĂšs vos tests, vous pouvez tout recommencer en faisant oublier les appareils des deux cĂŽtĂ©s. Évidemment, on ne va pas se connecter en ssh pour lancer `bluetoothctl` Ă  chaque fois qu’on veut se connecter en Bluetooth. On va mettre en place un agent qui dĂ©marre automatiquement et qui a un comportement similaire Ă  un casque ou des enceintes Bluetooth : qui accepte toutes les connexions Bluetooth audio. Pour ça, on va utiliser un script Python [partagĂ© par Collabora](https://www.collabora.com/news-and-blog/blog/2022/09/02/using-a-raspberry-pi-as-a-bluetooth-speaker-with-pipewire-wireplumber/) sous Licence LGPL 2.1+ qui fait ça trĂšs bien et qu’on va lancer au dĂ©marrage. **Bien sĂ»r, ça veut dire que vos voisins peuvent s’amuser Ă  jouer des trucs chez vous**, ou mĂȘme se connecter fortuitement en choisissant la mauvaise entrĂ©e. Ce script a une dĂ©pendance, qu’on va installer : ```bash sudo apt install python3-dbus ``` On va placer ce script dans `speaker-agent.py`: ```python #!/usr/bin/python3 # SPDX-License-Identifier: LGPL-2.1-or-later import dbus import dbus.service import dbus.mainloop.glib from gi.repository import GLib BUS_NAME = 'org.bluez' AGENT_INTERFACE = 'org.bluez.Agent1' AGENT_PATH = "/speaker/agent" A2DP = '0000110d-0000-1000-8000-00805f9b34fb' AVRCP = '0000110e-0000-1000-8000-00805f9b34fb' bus = None class Rejected(dbus.DBusException): _dbus_error_name = "org.bluez.Error.Rejected" class Agent(dbus.service.Object): exit_on_release = True def set_exit_on_release(self, exit_on_release): self.exit_on_release = exit_on_release @dbus.service.method(AGENT_INTERFACE, in_signature="", out_signature="") def Release(self): print("Release") if self.exit_on_release: mainloop.quit() @dbus.service.method(AGENT_INTERFACE, in_signature="os", out_signature="") def AuthorizeService(self, device, uuid): # Always authorize A2DP and AVRCP connection if uuid in [A2DP, AVRCP]: print("AuthorizeService (%s, %s)" % (device, uuid)) return else: print("Service rejected (%s, %s)" % (device, uuid)) raise Rejected("Connection rejected by user") @dbus.service.method(AGENT_INTERFACE, in_signature="", out_signature="") def Cancel(self): print("Cancel") if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() agent = Agent(bus, AGENT_PATH) mainloop = GLib.MainLoop() # By default Bluetooth adapter is not discoverable and there's # a 3 min timeout # Set it as always discoverable adapter = dbus.Interface(bus.get_object(BUS_NAME, "/org/bluez/hci0"), "org.freedesktop.DBus.Properties") adapter.Set("org.bluez.Adapter1", "DiscoverableTimeout", dbus.UInt32(0)) adapter.Set("org.bluez.Adapter1", "Discoverable", True) print("RPi speaker discoverable") # As the RPi speaker will not have any interface, create a pairing # agent with NoInputNoOutput capability obj = bus.get_object(BUS_NAME, "/org/bluez") manager = dbus.Interface(obj, "org.bluez.AgentManager1") manager.RegisterAgent(AGENT_PATH, "NoInputNoOutput") print("Agent registered") manager.RequestDefaultAgent(AGENT_PATH) mainloop.run() ``` Le script mentionne le Raspberry Pi, mais il n’y a absolument rien de spĂ©cifique au Raspberry dedans, il est suffisamment gĂ©nĂ©rique. On va lancer ce script au dĂ©marrage en crĂ©ant le fichier `~/.config/systemd/user/speaker-agent.service` ``` [Unit] Description=Bluetooth speaker agent [Service] ExecStart=python3 speaker-agent.py [Install] WantedBy=default.target ``` Et en l’activant (`--now` le lance tout de suite) : ```bash systemctl --user enable --now speaker-agent.service ``` Il faudra aussi mettre `JustWorksRepairing = always` dans `/etc/bluetooth/main.conf` pour permettre le re-appairage sans interaction. Bon lĂ  j’avoue, je paraphrase largement ma source :-) Ensuite, on va autoriser la connexion Bluetooth mĂȘme sans session active (en SSH par exemple) ([source](https://forum.artixlinux.org/index.php/topic,4230.0.html)). Si on ne fait pas ça, la connexion Bluetooth n’est pas possible si l’utilisateur n’a pas une session active (les symptĂŽmes : on arrive Ă  se connecter en Bluetooth que quand on est logguĂ© en SSH ou autre, et la connexion Bluetooth casse dĂšs qu’on quitte la session). ```bash mkdir -p ~/.config/wireplumber/bluetooth.lua.d cat> ~/.config/wireplumber/bluetooth.lua.d/80-disable-logind.lua << EOF -- Disable arbitration of user allowance of bluetooth via D-Bus user session bluez_monitor.properties["with-logind"] = false EOF systemctl --user restart wireplumber ``` ## Adapter MPD (et Samba) pour utiliser PipeWire Pour que MPD utilise PipeWire, il faut adapter : 1. sa configuration pour qu’il tourne avec le mĂȘme utilisateur 2. sa configuration `audio_output` 3. les permissions dans `/var/lib/mpd` Dans `/etc/mpd.conf`, changer la ligne user : ``` user "mpd" ``` Elle doit maintenant utiliser votre utilisateur : ``` user "user" ``` Commentez votre bloc `audio_output`, on va maintenant utiliser PipeWire (je suppose qu’on pourrait garder les deux et les clients MPD peuvent probablement permettre de choisir la sortie son, mais ça me parait complexifier l’utilisation pour un intĂ©rĂȘt pas clair, ce qui va contre nos objectifs) : ``` audio_output { type "pipewire" name "PipeWire Sound Server" } ``` Maintenant, il est temps d’adapter les permissions dans `/var/lib/mpd`. On va stopper Samba juste avant, et adapter sa configuration. ```bash sudo systemctl stop mpd samba # si mpd tournait encore sudo chown -rv user /var/lib/mpd sudo systemctl start mpd ``` Note : MPD peut aussi ĂȘtre dĂ©marrĂ© dans une session utilisateur et Ă  ce stade, c’est ce qu’il serait probablement le plus logique de faire, en bougeant `/etc/mpd.conf` et le contenu de `/var/lib/mpd` dans le dossier de notre utilisateur. [C’est d’ailleurs la maniĂšre privilĂ©giĂ©e de dĂ©marrer MPD Ă  partir de Debian Trixie](https://wiki.debian.org/mpd#Headless_setup). Par simplicitĂ© et cohĂ©rence, et parce que cette section « RĂ©cepteur Bluetooth » est optionnelle mais que les manipulations pour lancer une session utilisateur au dĂ©marrage dĂ©crites dans cette section seraient nĂ©cessaires pour lancer MPD en tant que service utilisateur au dĂ©marrage dans tous les cas et que ça apporte une rĂ©elle complexitĂ©, on fait le choix de garder MPD en tant que service systĂšme. Modifiez `/etc/samba/smb.conf`. Dans les deux blocs de partages qu’on a ajoutĂ© prĂ©cĂ©demment, changez la ligne `force user = mpd` en: ``` force user = user ``` Puis on peut redĂ©marrer Samba : ```bash sudo systemctl start samba ``` ## Permettre Ă  PipeWire de configurer sa prioritĂ© Si vous voyez cela dans les logs de PipeWire : ``` user@tablette:~$ journalctl --user -fu pipewire avril 29 13:41:01 tablette systemd[514]: Started pipewire.service - PipeWire Multimedia Service. avril 29 13:41:01 tablette pipewire[531]: mod.rt: Can't find org.freedesktop.portal.Desktop. Is xdg-desktop-portal running? avril 29 13:41:01 tablette pipewire[531]: mod.rt: found session bus but no portal avril 29 13:41:02 tablette pipewire[531]: mod.rt: RTKit error: org.freedesktop.DBus.Error.AccessDenied avril 29 13:41:02 tablette pipewire[531]: mod.rt: could not set nice-level to -11: Permission non accordĂ©e avril 29 13:41:02 tablette pipewire[531]: mod.rt: RTKit error: org.freedesktop.DBus.Error.AccessDenied avril 29 13:41:02 tablette pipewire[531]: mod.rt: could not make thread 547 realtime using RTKit: Permission non accordĂ©e ``` Ça veut grosso modo dire que PipeWire cherche Ă  se rendre plus prioritaire via un mĂ©canisme fourni par les environnements de bureau (xdg-desktop-portal), n’y arrive pas parce qu’évidemment, aucun environnement de bureau ne tourne, alors il essaie de demander au service systĂšme rtkit, et se fait jeter. Ce n’est pas trĂšs grave et on pourrait vivre sans, mais ça pourrait aider Ă  limiter les saccades sonores, donc on va rĂ©parer ça (et je pense avoir vu une bonne amĂ©lioration grĂące Ă  ça). Le fichier `/usr/share/polkit-1/actions/org.freedesktop.RealtimeKit1.policy` dicte qui a le droit ou non de configurer sa prioritĂ© (dĂ©couvert [ici](https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/1069#note_1050399), mais le conseil de modifier ce fichier systĂšme n’est pas bon, au moins parce qu’une mise Ă  jour future risque d’écraser les modifications) : ```xml Lennart Poettering Grant high priority scheduling to a user process Bir sĂŒrece yĂŒksek öncelikli çalıßabilme yetkisi ver Authentication is required to grant an application high priority scheduling SĂŒrecin yĂŒksek öncelikli çalıßtırılabilmesi için yetki gerekiyor no yes yes Grant realtime scheduling to a user process Bir sĂŒrece gerçek zamanlı çalıßabilme yetkisi ver Authentication is required to grant an application realtime scheduling SĂŒrecin gerçek zamanlı çalıßtırılabilmesi için yetki gerekiyor no yes yes ``` Dans un systĂšme Unix, les paramĂštres systĂšmes sont dans `/etc`. Pour Polkit, il existe un mĂ©canisme pour Ă©crire des rĂšgles, qu’on va utiliser. On va crĂ©er une rĂšgle qui permet Ă  n’importe quel utilisateur du groupe `audio` de modifier la prioritĂ© de ses processus. C’est probablement trop large, mais je ne connais pas bien Polkit et ça fera le taf pour notre application dĂ©diĂ©e Ă  l’audio. Si vous avez des meilleures idĂ©es, n’hĂ©sitez pas Ă  partager en commentaire. ```bash sudo cat> /etc/polkit-1/rules.d/rt.rules << EOF polkit.addRule(function(action, subject) { if (subject.isInGroup("audio") && ( action.id == "org.freedesktop.RealtimeKit1.acquire-high-priority" || action.id == "org.freedesktop.RealtimeKit1.acquire-real-time" )) { return polkit.Result.YES; } }) EOF sudo systemctl restart polkit.service systemctl --user restart pipewire ``` On pourra constater l’absence des Ă©checs dans les journaux de PipeWire. Bon, on sent bien que toute cette utilisation audio sans session utilisateur standard n’est pas un cas d’utilisation hyper bien prĂ©vu et on se retrouve Ă  toucher des coins un peu sombres du systĂšme... ## Évitez les flux Wifi 2,4 GHz Si vous avez un Wifi en 2,4 GHz, ça peut causer des soucis avec le Bluetooth, et le son peut saccader. Si vous observez cela, il faudra alors limiter au maximum les services et autres tĂąches de fond qui font des communications rĂ©seau. Évidemment, si vous pouvez utiliser un cĂąble Ethernet, c’est encore mieux. Sur ce plan, tous les codecs audio Bluetooth ne semblent pas se valoir. Pour tester ça, j’ai lancĂ© un test iperf3 entre la tablette et mon ordinateur portable pour saturer le Wifi. Ça devenait immĂ©diatement catastrophique avec le codec SBC-XQ, alors qu’avec le codec Opus 05, il y a initialement des saccades, puis ça s’amĂ©liore vite. J’imagine que le codec Opus dĂ©grade trĂšs efficacement la qualitĂ© pour compenser. Bon, malheureusement, tous les systĂšmes ne permettent pas de choisir son codec donc ce n’est qu’une solution partielle au problĂšme. # Note sur l’utilisation des ressources C'est lĂ©ger : ``` load average: 0,12, 0,10, 0,05 ``` ``` $ free -mh total utilisĂ© libre partagĂ© tamp/cache disponible Mem: 986Mi 253Mi 324Mi 6,1Mi 550Mi 733Mi Échange: 974Mi 0B 974Mi ``` Globalement, le CPU s’ennuie en pleine lecture, et Ă  peine un tiers du Giga de mĂ©moire vive est utilisĂ©, la partition d’échange s’ennuie, donc il y a encore largement la place de faire tourner d’autres trucs sur cet appareil si jamais. On peut aussi constater qu’ajouter MPD et tout ce bazar Ă  une installation existante ne la surchargerait pas plus que ça. On a aussi un temps de dĂ©marrage autour des 20 secondes, ce qui est franchement pas mal. # Conclusion et amĂ©liorations possibles On est pas mal rentrĂ©s dans les dĂ©tails, c’était l’occasion d’explorer plein de choses mine de rien. J’ai Ă  la fois appris des choses, prĂ©cisĂ© des connaissances, et mis plein de choses que je savais ensemble pour obtenir un rĂ©sultat trĂšs satisfaisant. On se retrouve Ă  manipuler de la gestion de services, des configurations systemd un peu poussĂ©es, du bluetooth, du son avec ALSA et PipeWire, de la gestion de session utilisateur sur un systĂšme headless, et plein d’autres trucs et aller dans les dĂ©tails comme le boot pour avoir quelque chose de rapide, comme l’écran Ă©teint au bon moment, ou la personnalisation du comportement du bouton power (honnĂȘtement, je n’étais pas trĂšs sĂ»r que c’était possible, j’avais lancĂ© la recherche au cas oĂč !). J’espĂšre que l’aventure vous a plu aussi. Bien sĂ»r l’ensemble est perfectible, alors je vous laisse avec des idĂ©es, n’hĂ©sitez pas Ă  partager les vĂŽtres en commentaires : - **Jouer un son au dĂ©marrage / Ă  l’appairage Bluetooth**. – pour l’instant, la tablette s’allume et puis plus rien. En gĂ©nĂ©ral, les enceintes Bluetooth jouent un petit son quand elles sont prĂȘtes ou qu’elles viennent d’ĂȘtre appairĂ©es et ça peut ĂȘtre pratique - **Commande vocale**. Il y a clairement des maniĂšres d’utiliser le micro de la tablette pour demander le morceau suivant, prĂ©cĂ©dent ou rĂ©gler le volume. Ça peut ĂȘtre pratique quand on n’a pas le tĂ©lĂ©phone sous la main et ça peut avoir son petit effet en soirĂ©e la premiĂšre fois, tant que les gens ne sont pas encore complĂštement blasĂ©s par le concept parce que tout le monde n’a pas un Google Nest ou un Alexa chez soi, surtout dans ma bulle sociale. Mais c’est probablement finalement trĂšs gadget et je me vois mal interrompre une conversation en criant un ordre pour gĂ©rer la musique... - **Appairage Bluetooth plus sĂ©curisĂ©**. En gĂ©nĂ©ral, les enceintes Bluetooth acceptent les nouveaux appareils dans un mode spĂ©cial. En appuyant sur le bouton Bluetooth, ou quelque chose comme ça. Ça peut Ă©viter que les voisin·e·s ne te rickrollent au moment le plus inopportun. Ça vaudrait le coup de travailler sur quelque chose comme ça. Avec l’écran tactile, il est probablement possible de dessiner une forme particuliĂšre reconnue (ça serait un peu badasse, ou plus probablement, n’accepter (une seule) nouvelle connexion que dans les X minutes aprĂšs le dĂ©marrage ou le retour de veille. Comme ça, demander l’appairage consiste Ă  appuyer deux fois sur le bouton power, ce qui est plutĂŽt acceptable. Si vous avez des idĂ©es, n’hĂ©sitez pas Ă  partager... - **RĂ©veil Ă  distance avec du Wake-on-LAN**. Ça ne s’applique probablement pas Ă  mon matĂ©riel, mais il est possible d’utiliser astucieusement le WoL pour rĂ©veiller l'appareil Ă  distance, avec Ă©ventuellement la complicitĂ© d’un routeur ou d’un serveur toujours allumĂ© chez vous. - **DĂ©sactiver le Wifi quand le Bluetooth est utilisĂ©**. Pour Ă©viter les interfĂ©rences, on pourrait imaginer que quand un appareil se connecte en Bluetooth, on Ă©teint le Wifi (avec rfkill par exemple), on met MPD en pause (ou on le stoppe s’il est en train de jouer un flux) parce qu’on ne peut plus le contrĂŽler, puisque le Wifi n’est plus actif, et on rĂ©active le Wifi quand l’appareil Bluetooth est dĂ©connectĂ©. On pourrait mĂȘme ĂȘtre plus fin et dĂ©tecter quand du son est jouĂ©. - **Automatiquement mettre MPD en pause lors d’une connexion Bluetooth**. (un peu doublon avec le prĂ©cĂ©dent point) Pour l’instant, il faut manuellement mettre en pause mpd, sinon les deux flux audios se jouent en mĂȘme temps. -- **Changer la classe Bluetooth de l’appareil**. Ça permettrait Ă  l’appareil de se dĂ©clarer comme appareil audio, pour que ça affiche le bon icĂŽne sur les autres appareils. - **Mises Ă  jour automatiques**. Il ne faut pas que ça casse des choses en pleine lecture, ni que ça cause des interfĂ©rences avec le Bluetooth Ă  cause des tĂ©lĂ©chargements. - **Ne pas persister les logs**. Pour l’instant, les logs sont Ă©crits dans `/var/log` sur le SSD, entrainant une usure et un ralentissement cependant probablement tous deux nĂ©gligeables. On pourrait vouloir ne pas les garder, mais c’est aussi risquer de perdre des informations de dĂ©bogage le jour oĂč il y a un pĂ©pin. Je vais probablement trouver d’autres choses Ă  amĂ©liorer aprĂšs publication de l’article. Je partagerai peut-ĂȘtre les choses intĂ©ressantes en commentaires ou dans des journaux, et je ferai peut-ĂȘtre vivre [l’article sur mon site](https://raphael.jakse.fr/blog/1-mpd.xml).

AltStyle ă«ă‚ˆăŁăŠć€‰æ›ă•ă‚ŒăŸăƒšăƒŒă‚ž (->ă‚ȘăƒȘă‚žăƒŠăƒ«) /