Cette page intitulée "MAO sur Raspberry-pi" présente l'implémentation d'un synthétiseur midi sur une plateforme Raspberry-pi.
Le dispositif est constitué d'un clavier-maître midi, d'un Raspberry-pi 2 modèle B et d'une carte son externe. La distribution utilisée est la Raspbian-Jessie et les logiciels principaux ALSA, Jackd et Fluidsynth.
Table des matières
- Préparer la plateforme : installation de Raspbian-jessie sur Raspberry PI 2 modèle B
- Mise en place de la carte son Behringer UCA202
- Automatiser l'activation du dispositif
- Réaliser un arrêt propre du Raspberry
- Intégration/finalisation du code
- Conclusion
- Liste des liens cités dans le texte
Préparer la plateforme : installation de Raspbian-jessie sur Raspberry PI 2 modèle B
La démarche
Pour faire cette installation, on s'est principalement appuyé sur Linux Pratique HS n°30 . On a téléchargé la distribution à partir du site raspbian-france.fr sur une carte micro SDHC de 8 Go. La carte a ensuite été installée dans le Raspi (NB : on nommera ainsi le Raspberry-pi sous forme abrégée).
Ensuite, on démarre le Raspi, configure la localisation et le clavier en « fr », et on lance la première mise à jour via un terminal en lançant sous root la commande
apt update && apt dist-upgrade
On installe ensuite les composants logiciels nécessaires à la gestion de la chaîne audio et des périphériques Midi en se basant sur Linux pratique HS 29 . On s'assure notamment de l'installation des paquets suivants :
- alsa-utils, la boîte à outil associée au serveur de son Alsa
- timidity (jouer un fichier midi)
- qjackctl (la fameuse interface graphique pour contrôler le fameux serveur de son Jackd qui gère l'interconnexion des différents composants de la chaîne audio avec son interface graphique),
- fluidsynth, qsynth, fluid-soundfont-gm (Ces 3 paquets permettent de disposer d'un synthétiseur à faible latence, attention le dernier paquet pèse près de 150 Mo)
Résolution de quelques problèmes ou manques dans la configuration de base de la Raspbian-Jessie
Augmenter le nombre de bureaux virtuels fixé à 1 à l'origine
Habitué à travailler avec plusieurs bureaux virtuels, on se trouve bien à l'étroit avec un seul. Solution trouvée dans debian-facile.org . Procédure : l'installation d'Openbox repose sur 3 paquets : openbox, obconf et obmenu. Mais sur notre Raspi, on constate que obconf et obmenu ne sont pas installés. On le fait donc et après quelques manipulations de configuration, on trouve désormais un outil dans préférences "Openbox Configuration Manager" grâce à qui, on peut ajouter des bureaux virtuels
Activation du pavé numérique à la mise en route
Solution :
sudo apt-get install numlockx
Son véhiculé par la prise HDMI du Raspi non restitué par les HP intégrés à l'écran
Solution donnée dans www.framboise314.fr.
Créer des lanceurs d'application
- Sur le bureau
On remarque que les lanceurs préexistants se trouvent sous/usr/share/applications
. Pour les faire apparaître sur le bureau, il suffit de créer un lien symbolique dans~/Desktop
. Ainsi pour l'application XXX :cd ~/Desktop ; ln -s /usr/share/applications/XXX.desktop
- Sur le tableau de bord
On trouve une méthode ici environnement LXDE mais ceci ne concerne que les applications disposant déjà d'un lanceur. On n'a pas trouvé la façon d'en créer depuis zéro et le drag&drop de permet pas d'en déposer un sur le tableau de bord :-(
Mise en place de la carte son Behringer UCA202
Pourquoi utiliser une carte son ? Pourquoi celle-ci ?
La décision d'ajouter une carte son externe au Raspi a été prise à la suite d'un certain nombre d'essais de synthèse sonore du clavier maître. Lors de ceux-ci, soit on obtenait un son correct avec une latence excessive, soit on obtenait une latence correcte mais avec un son distordu. Pour le premier cas, les essais étaient réalisés avec le couple Alsa/Timidity (Alsa pour gérer les connexions et Timidity implémentant le synthétiseur midi). Pour le second, avec le couple Jackd/Fluidsynth (rôles analogues).
Attention, il est tout à fait possible qu'on s'y soit mal pris pour réaliser ces expérimentations, mais en tout cas, c'est elles qui nous ont conduit à recourir à une carte son externe.
Pourquoi avoir choisi la carte Behringer UCA202 ? On recherchait un produit USB de coût raisonnable et ami de GNU/linux. Cette carte jouissait d'une bonne appréciation sur le site de Linux-mao : Behringer UCA202. Il nous en a coûté environ 35 €.
Installation de la carte son sur le Raspi
Au plan matériel, c'est assez trivial puisqu'il suffit de connecter la carte à un port USB du Raspi et de relier la sortie casque de la carte à un casque audio ou à des hauts-parleurs amplifiés.
On démarre ensuite le Raspi. On constate qu'il n'y a pas de nouveau périphérique USB détecté, mais en affichant le mixer Alsa (clic droit sur icône HP dans le tableau de bord), on voit 2 nouvelles entrées :
- USB Audio CODEC et
- Device Settings
Mise en place de notre configuration « synthé midi »
À la lumière de nos essais précédents qui s'étaient déroulés sans carte son externe, on a opté pour une chaîne audio basée sur Jackd (interface graphique : Qjackctl) et Fluidsynth (interface graphique : Qsynth) associé au fichier de formes Fluid-soundfont-gm. Le clavier maître est connecté au Raspi par USB, ceci assurant également son alimentation électrique. L'ensemble est représenté dans la figure ci-dessous :
D'assez nombreuses difficultés ont été rencontrées pour faire fonctionner cet ensemble. Nous ne les décrirons pas ici pour ne pas perdre le lecteur. Nous citerons néanmoins l'une d'elles que nous avons identifiée qu'assez tardivement. Elle concerne la coexistence entre la carte son interne au Raspi et la carte son externe. Notre dispositif n'a, en effet, commencé à fonctionner que lorsqu'on a inhibé la carte son interne. Ceci se fait en « blacklistant » le pilote de la carte interne dédié au serveur de son Alsa. Ceci grâce aux indications trouvées ici Use-USB-Sound-Card-in-Raspberry-Pi .
Voici la démarche que nous avons établie pour faire fonctionner notre plateforme :
- Inhiber la carte son interne du Raspi en créant le fichier
/etc/modprobe.d/alsa-blacklist.conf
avec le contenu « -+blacklist snd_bcm2835+- ». Il est sage alors de redémarrer le Raspi pour s'assurer de la prise en compte de cette disposition. - Après avoir connecté le clavier maître et la carte son, lancer QjackCtl (une interface graphique pour Jackd).
- Actionner son bouton « Réglages » pour fixer les paramètres suivants :
- Interface = hw:CODEC USB Audio Codec
Note : « hw:CODEC,0 USB Audio (hw:1,0) » est également proposé et semble donner le même résultat - Échantillons/Période : 256
- Fréquence d'échantillonnage : 48000
- Périodes/Tampon : 2
- Pour les autres paramètres, conserver les valeurs proposées par défaut et valider.
- Interface = hw:CODEC USB Audio Codec
- Ces réglages conduisent à une latence de 10,7 ms. Actionner le bouton « Démarrer ». NB : si on est trop ambitieux en latence, il est possible que QjackCtl refuse de démarrer ou indique qu'il ne peut travailler en temps réel.
- Actionner son bouton « Réglages » pour fixer les paramètres suivants :
- Lancer Qsynth (l'interface grapĥique de Fluidsynth) ;
- Sur le panneau de contrôle de QjackCtl, actionner le bouton « Connecter »
- Onglet Alsa → connecter le clavier à FluidSynth ;
- Onglet Audio → par défaut Qsynth est déjà connecté au système son. Les relier sinon.
- Alsa-mixer permet de contrôler le niveau envoyé à la carte son (Préférences > Audio Device Settings) Le réglage a priori conservé entre 2 sessions
- Et voilà :-)
Automatiser l'activation du dispositif
Pourquoi automatiser ?
L'idée est au final de pouvoir se passer d'écran et de clavier/souris. Il faut donc « scripter » la gestion de la chaîne audio.
Cet objectif occasionnera également de nombreuses difficultés dont la narration serait là encore sans grand intérêt. On s'en tiendra donc à la description du dispositif finalement mis au point.
Ce que doit faire le script
On se base sur l'expérimentation décrite dans la section précédente. On part donc d'une configuration dans laquelle les éléments sont configurés comme décrit : "blacklistage" du module de carte son interne au Raspi. Les connexions physiques avec la périphérie devront être en place avant de démarrer le Raspi.
Le script aura ainsi à charge de
- lancer Jackd et FluidSynth
- connecter le clavier sur FluidSynth et ce dernier sur la carte son
- régler le niveau du son sur une valeur moyenne qui offrira une excursion suffisante avec le réglage d'amplification des haut-parleurs.
Réalisation
L'examen des docs des logiciels utilisés permet de trouver comment les invoquer en ligne de commande pour se substituer à leur IHM.
lancement de Jackd
jackd -d alsa -d hw:CODEC -r 48000 -p 256 -n 2 &
lancement de FluidSynth avec son fichier de forme
fluidsynth -a jack -m alsa_seq -g 2 -r 48000 /usr/share/sounds/sf2/FluidR3_GM.sf2
Réalisation des connexions :
- FluidSynth vers carte audio (NB : obtenir la liste des ports audio et des connexions :
jack_lsp -c
) :jack_connect fluidsynth:l_00 system:playback_1 && jack_connect fluidsynth:r_00 system:playback_2
- Clavier vers FluidSynth (NB : obtenir la liste des ports Alsa et des connexions :
aconnect -i -o -l
) :aconnect 16:0 128:0
Régler l'amplification des canaux gauches et droits de la carte son
amixer -c CODEC sset PCM,0 80%,80%
Assemblage du script
Nous avons donc réuni ces lignes dans un script. Mais pour des raisons que nous n'avons pu éclaircir, il s'est avéré impossible de reprendre la main une fois Fluidsynth lancé. Qu'il soit lancé en tâche de fond (&) ou en processus libre (nohup), l'exécution du script se retrouve bloquée. Or la connexion de Fluidsynth avec les éléments indiqués ne peut bien sûr intervenir qu'une fois celui-ci actif.
Nous n'avons pas trouvé de solution vraiment satisfaisante à ce problème et finalement nous avons opté pour une procédure en aveugle faute de mieux. Il s'agit de placer les commandes de connexion de FluidSynth dans un script propre, que nous appellerons « script secondaire », au sens où il sera appelé en mode « tâche de fond » par le script principal. Ce script commencera par l'exécution d'une temporisation supposée laisser le temps au script principal d'effectuer le lancement de FluidSynth, suite à quoi, il procédera à la connexion de ce dernier.
Le script secondaire « aConnect.sh »
tempo=16 echo "Connexion dans ${tempo} seconds" sleep ${tempo} echo "aconnect 16:0 128:0" aconnect 16:0 128:0 echo "\"quit\" pour quitter"
À noter que c'est effectivement une temporisation de 16 secondes qui a été retenue pour l'implémentation finale (voir dans la suite).
Le script principal « playMidiK.sh »
#!/bin/bash # Mise en place config synthé clavier midi sur Raspi # V3 du 21 mai 2016 avec connexion auto de la sortie de FluidSynth echo "Lancement du serveur Jackd en background" echo "jackd -d alsa -d hw:CODEC -r 48000 -p 256 -n 2 &" jackd -d alsa -d hw:CODEC -r 48000 -p 256 -n 2 & echo "Lancement retardé de la connexion Alsa" aConnect.sh & echo "Régler l'amplification des canaux gauches et droits de la carte son" leftLevel=80 rightLevel=80 echo "Niveau gauche à ${leftLevel}%, niveau droit à ${rightLevel}%" echo "amixer -c CODEC sset PCM,0 ${leftLevel}%,${rightLevel}%" amixer -c CODEC sset PCM,0 ${leftLevel}%,${rightLevel}% echo "Lancer FluidSynth avec les font synthé qui vont bien" echo "fluidsynth -a jack -m alsa_seq -g 4 -r 48000 -j /usr/share/sounds/sf2/FluidR3_GM.sf2" fluidsynth -a jack -m alsa_seq -g 4 -r 48000 -j /usr/share/sounds/sf2/FluidR3_GM.sf2 echo "killall jackd" killall -9 jackd echo "Bye"
Le lancement de fluidsynth provoque le blocage du déroulement du script principal car il se lance dans un mode interpréteur de commande. Fluidsynth garde donc le contrôle du terminal sur lequel le script principal s'exécute jusqu'à ce qu'on saisisse « quit » ce qui provoquera sa terminaison. Le script principal reprend alors la main et supprime le serveur Jackd avant de se terminer lui-même.
Vers un fonctionnement « autonome »
Pour obtenir une mise en route « sans les mains » de la chaîne audio, on va utiliser le compte « pi » qui, dans la Raspbian, offre la possibilité d'une connexion automatique1. NB : pour que l'utilisateur « pi » puisse utiliser les périphériques son, il doit être membre du groupe audio (/etc/group). L'ajouter si ce n'est pas fait.
Il faut ensuite faire en sorte que le script "playMidiK.sh" s'exécute automatiquement une fois la session de pi lancée. Nous avons trouvé dans [9] une solution pour cela.
Pour lancer une application automatiquement à partir de la connexion graphique sous LXDE de l'utilisateur « user », on crée un
fichier.desktop
sous /home/<user>/.config/autostart/
.Voici le contenu de
~pi/.config/autostart/piano.desktop
que nous avons réalisé à cette fin sachant que les deux scripts concernés ont été placés dans ~pi/bin
:[Desktop Entry] Type=Application Encoding=UTF-8 Name=xdaliclock Exec=lxterminal -e /home/pi/bin/playMidiK.sh
Cependant, à la première exécution, cette procédure partira en erreur avec le message suivant :
« Cannot lock down 82278944 byte memory area (Cannot allocate memory) Cannot use real-time scheduling (RR/5)(1: Operation not permitted) JackClient::AcquireSelfRealTime error »
On réglera ceci grâce à stackoverflow.com qui préconise d'intervenir dans
/etc/security/limits.d/audio.conf
.Au final, on obtient un dispositif fonctionnel en réglant – comme déjà indiqué - la variable tempo qui apparaît dans notre script secondaire à 16 secondes.
Comme ce temps peut paraître un peu long à l'utilisateur, qui n'a d'autre possibilité que de taper sur le clavier maître pour savoir quand le système est enfin opérationnel, on a installé un buzzer sur le GPIO (broche GPIO-22 et GND). Et ajouté l'activation du buzzer dans le script secondaire à la suite du
aconnect
.Aléas de fonctionnement, constats et solutions
Lors de la mise en route du système, on est confronté de temps en temps à deux types de dysfonctionnement :- aconnect retourne un échec de connexion entre la sortie midi et l'entrée de FluidSynth ;
- le son s'évanouit au bout d'un temps aléatoire.
Résolution de l'échec de connexion par aconnect qui se manifeste de temps à autre
On liste les affectations client des ports concernés réalisé par le gestionnaire de connexion d'ALSA, ainsi que leurs interconnexions, par la commande «aconnect -i -o -l
». On constate que la sortie clavier (ESI KeyControl 49 xt) a pour numéro de client, non pas 16, comme prévu dans notre script aConnect.sh, mais 24 ! (NB : l'entrée de FluidSynth reste, elle, bien attachée au client 128).Comme on dispose d'un écran-clavier, on tente la connexion « à la main » en saisissant la commande suivante dans un shellbash : «
aconnect 24:0 128:0
» et le son du clavier s'établit.Le 5 octobre 2016, une solution a été trouvée en examinant la documentation « man aconnect ». :
« The address can be given using the client's name.
eg.
aconnect External:0 Emu8000:1
».Dans le script aConnect.sh, on remplace donc
-
aconnect 16:0 128:0
par -
aconnect 'ESI KeyControl 49 XT':0 'FLUID Synth':0
"aconnect -i -o"
, sauf qu'on ne reproduit pas la parenthèse et son contenu : "client 128: 'FLUID Synth (805)'"
. Le nombre entre parenthèses fluctue d'une session à l'autre et nous avons constaté qu'il n'est pas utile de le spécifier pour que la commande de connexion s'exécute correctement.Résolution de l'évanouissement aléatoire du son
On a un peu fouillé les logs lorsqu'on a été confronté au problème et on a constaté que « pulseaudio » était évoqué dans/var/log/kern.log
. Ceci nous a remémoré des posts qui relataient des conflits entre pulseaudio et Alsa. Comme le premier ne semble pas utile à notre dispositif, qui repose sur Alsa et Jackd, on a désinstallé complètement pulseaudio en supprimant les paquets « pulseaudio-module-x11 » et « pulseaudio-module-bluetooth ».Depuis cette opération réalisée et en caressant régulièrement la table en bois qui porte le dispositif , l'évanouissement du son ne s'est plus reproduit.
Réaliser un arrêt propre du Raspberry
Le besoin
Dans le contexte du synthétiseur Midi considéré ici, le Raspberry est sensé être utilisé au titre de système enfoui. Il faut donc être en capacité de lancer et d'arrêter le système sans clavier-écran. Il est réputé qu'un arrêt brutal du Raspi (ie sans recourir au « shutdown » ou au « halt ») peut entraîner la corruption du contenu de la carte SD.On va décrire dans la suite le dispositif que nous avons mis au point pour réaliser un arrêt propre du Raspi sans clavier ni écran.
Avant d'opter pour cette voie nécessitant la mise en place de quelques composants matériels, nous avons examiné une autre possibilité décrite dans Linux Pratique HS n°30, « Raspberry Pi, un guide d'initiation pour prendre en main ce mini-ordinateur sous Linux », p. 52 qui elle, propose un arrêt sécurisé sans ajout de matériel. La solution consiste à interdire toute accès en écriture sur la carte SD. Pour se faire, il est nécessaire de reconfigurer certaines parties du système de fichier de Raspbian. C'est le souhait de préserver une configuration standard qui nous a conduit à ne pas retenir cette solution au profit de celle qui va maintenant être décrite.
Le travail à réaliser
Provoquer le shutdown d'un ordinateur sans clavier ni accès distant implique selon notre solution d'ajouter un dispositif matériel : en l'occurrence, un bouton poussoir. Comme, il est désagréable de manipuler quoique ce soit sans retour d'information, il nous a paru utile de lui associer un LED à cette fin. Celui-ci informera l'utilisateur de la prise en compte de l'appui bouton. Avec le Raspberry pi, il suffit de câbler ces composants sur le GPIO (General Purpose Input/Output) et d'écrire les scripts qui les prendront en charge.Dans le même esprit, on peut considérer qu'il est un peu pénible lors de la mise en route de notre synthétiseur, qui prend un certain temps, de devoir taper sur une touche du clavier jusqu'à ce que cela produise du son, indiquant ainsi que le dispositif est opérationnel. Nous avons donc également implanté un buzzer sur le GPIO pour réaliser cette fonction.
Une fois le matériel câblé et testé via quelques codes simples, il restait à développer le code de gestion de ces matériels et de l'intégrer le logiciel d'ensemble.
Avant de présenter comment nous avons procéder, un mot sur une alternative évitant la mise en place d'un buzzer matériel. Elle consiste à utiliser à sa place la sortie audio built-in du Raspi et lui faire jouer le fichier sonore d'un buzzer. Pour ce faire, il faut réactiver la sortie son built-in en supprimant le blacklistage réalisé en Mise_en_place_de_la_carte_son_Behringer_UCA202. On a finalement constaté en effet que la cohabitation de celle-ci et de la carte son externe était viable dans le cadre de notre application dès lors que cette dernière était vue par Alsa comme carte n°1 et la carte built-in comme carte n°2. [NDLR problème : à cette heure, on ne sait plus comment on a réalisée cette configuration custom :-(]
Pour connaître le n° d'affectation des cartes vue par Alsa :
pi@raspi:~ $ aplay -l **** Liste des Périphériques Matériels PLAYBACK **** carte 1: CODEC [USB Audio CODEC], périphérique 0: USB Audio [USB Audio] Sous-périphériques: 0/1 Sous-périphérique #0: subdevice #0 carte 2: ALSA [bcm2835 ALSA], périphérique 0: bcm2835 ALSA [bcm2835 ALSA] Sous-périphériques: 8/8 Sous-périphérique #0: subdevice #0 ... Sous-périphérique #7: subdevice #7 carte 2: ALSA [bcm2835 ALSA], périphérique 1: bcm2835 ALSA [bcm2835 IEC958/HDMI] Sous-périphériques: 1/1 Sous-périphérique #0: subdevice #0
On récupère le son d'un beep, format wav, que l'on stocke sous /home/pi/.local/share/sounds.
Dans le script où l'on souhaite faire entendre le beep, on insère ceci :
# Emet un beep sur la carte son n°2 (built in card) aplay -q -D hw:2,0 /home/pi/.local/share/sounds/beep-12-2s-0dB.wavL'option -q (quiet) évite que aplay affiche un message.
Considérant que cette solution allait mobiliser un haut-parleur spécifique ou un casque qu'il fallait connecté à la sortie son built-in, on a préféré opter pour l'implantation d'un buzzer matériel dans la suite de notre projet.
Mise en place du matériel
Le câblage est décrit par le schéma ci-dessous:Le buzzer provient d'une carte mère mise au rebut. Il s'agit d'un buzzer passif, il faut donc l'alimenter avec un signal carré à une fréquence qu'il est susceptible de restituer (voir le code dans la suite).
Pour calculer la résistance de la charge du LED, on s'est basé sur un courant Icc d'une dizaine de milliampères. Il suffit alors d'appliquer la loi d'Ohm :
R=U/Icc avec U = Vcc – Vseuil où Vcc est le 3,3 V et Vseuil la tension de seuil de la diode qui en général est de l'ordre de 1,8 V.
Pour identifier cathode et anode du LED, on pourra se reporter à la figure suivante:
Pour le montage du switch et la gestion du shutdown, on s'est largement inspiré de http://hardware-libre.fr/2013/07/ajouter-un-bouton-dextinction-avec-python et de http://www.pihomeserver.fr/2013/10/25/raspberry-pi-home-server-ajouter-bouton-darret.
On y conseille de mettre une résistance de "pull-up" (10 KΩ ici) par sécurité. Ainsi, au cas où la programmation de la broche concernée du GPIO n'utiliserait pas le pull-up interne, le montage fonctionnerait néanmoins.
Enfin, pour confiner le tout dans un espace minimum, compatible avec la taille du coffret utilisé, on a choisi de câbler le matériel sur un socket enfichable de 2 x 8 broches:
Le coffret utilisé est en plastic transparent, ce qui a pour avantage
- d'apercevoir la belle électronique et
- de ne pas nécessiter de sortir le LED du coffret pour voir son état.
Voici à quoi ressemble le dispositif une fois mis en place dans le Raspi :
On a ensuite testé le dispositif à l'aide de petits scripts python et ainsi vérifier sa correction.
Réalisation du code
Gestion du shutdown/reboot et du LED
Pour implémenter le contrôle du shutdown par le switch, on s'est largement inspiré du script python http://hardware-libre.fr/2014/03/raspberry-pi.... Celui-ci offre le choix entre reboot et shutdown basé sur la durée d'appui du bouton. Ce peut-être effectivement pratique de pouvoir faire un reboot quand on le souhaite sans devoir passer par un off/on de l'alimentation du Raspi.Ce qu'on a ajouté pour l'essentiel, c'est le gestion du LED pour offrir un retour d'information à l'utilisateur. Cela est décrit par le graphe d'état ci-dessous :
Le principe consiste,
- lorsque l'appui est détecté, une tempo est initialisée à une valeur « very-long-press » (choix 4 s) et le LED est allumé.
- Si l'opérateur relâche le bouton avant que la tempo ait atteint la valeur « long-press » (choix 1 s), le LED s'éteint et c'est tout (filtrage d'appui fugace).
- Si le bouton est toujours enfoncé lorsque la tempo arrive à la valeur « long-press », le LED s'éteint. Ceci pour informer l'opérateur qu'en relâchant immédiatement le bouton, il déclenchera le reboot.
- S'il maintient le bouton enfoncé jusqu'à ce que la tempo arrive à zéro, le LED se rallume pour signifier que le shutdown va prendre place.
Le script correspondant « haltRaspi-withLEDctrl.py »
#!/usr/bin/env python2.7 # -*- coding: utf-8 -*- # Version du 26 sept 2016 appelée dans "playMidiK.sh" # Ce code gère le switch du Raspi pour provoquer, en fonction de la durée DA de l'appui : # - DA < long_press => no effect # - long_press < DA < very_long_press => reboot # - very_long_press < DA => shutdown # Origine du script 4 sept 2016 : # http://hardware-libre.fr/2014/03/raspberry-pi-plusieurs-fonctions-avec-un-seul-bouton-2/ # On gère également le LED de la manière séquentielle ainsi : # Allumage LED dès appui switch, extinction LED sur relâche switch ou si DA > long_press. # Rallumage LED sur relâchement avec durée > very_long_press pour # informer que le shutdown va démarrer # Ainsi l'opérateur est informé également du moment où il peut relâcher le switch # pour faire un reboot. # from time import sleep import subprocess # « import os » dans script buzzer import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) # on met RPi.GPIO en mode notation BCM GPIO.setwarnings(False) # disable warnings # voir http://raspi.tv/2013/rpi-gpio-basics-3-how-to-exit-gpio-programs-cleanly-avoid-warnings-and-protect-your-pi # Switch connecté entre GPIO 23 (pin 16) et GND SWITCH = 23 # LED connectée entre GPIO 17 (pin 11) et GND LED = 17 # On définit nos durées long_press = 1 very_long_press = 4 # # on initialise le GPIO 23 en mode entrée GPIO.setup(SWITCH, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(LED,GPIO.OUT) GPIO.output(LED,GPIO.LOW) # LED éteint # def shutdown(): subprocess.call(['sudo shutdown -h now "Arrêt du système par bouton GPIO" &'], shell=True) # def reboot(): subprocess.call(['sudo reboot "Reboot du système par bouton GPIO" &'], shell=True) # notre fonction de gestion du bouton def system_button(SWITCH): # cette variable servira a stocker le temps d'enfoncement du switch button_press_timer = 0 while True: if (GPIO.input(SWITCH) == False) : # le bouton est enfoncé button_press_timer += 0.2 # ... on enregistre le temps que cela dure if (button_press_timer > very_long_press) : # On ne requiert pas GPIO.output(LED,GPIO.HIGH) # Annonce le shutdown print "very long press : ", button_press_timer shutdown() elif (button_press_timer > long_press): print "Reboot armed : ", button_press_timer GPIO.output(LED,GPIO.LOW) # Informe reboot armé else: # Traite le premier allumage GPIO.output(LED,GPIO.HIGH) else: # le bouton a été relâché, on compte combien de temps cela a dure GPIO.output(LED,GPIO.LOW) if (button_press_timer > very_long_press): print "very long press : ", button_press_timer shutdown() elif (button_press_timer > long_press): print "long press : ", button_press_timer reboot() elif (button_press_timer > 0.2): print "short press : ", button_press_timer # do nothing button_press_timer = 0 GPIO.output(LED,GPIO.LOW) # on attend 0.2 secondes avant la boucle suivante afin de réduire la charge sur le CPU sleep(0.2) # on met le bouton en écoute par interruption, détection falling edge sur le canal choisi, et un débounce de 200 millisecondes GPIO.add_event_detect(SWITCH, GPIO.FALLING, callback=system_button, bouncetime=200) # ici vous pouvez mettre du code qui sera exécuté normalement, sans influence de la fonction bouton try: while True: # faites ce qui vous plaît sleep (2) # on réinitialise en entrée les ports GPIO utilisés en sortie de script # NB: ce script n'est pas sensé se terminer avant que le buzzer ait fait son oeuvre. except KeyboardInterrupt: GPIO.cleanup() # reinitialisation GPIO lors d'une sortie CTRL+C GPIO.cleanup() # reinitialisation GPIO lors d'une sortie normale
Activation du buzzer
Ci-dessous le code « buzzer.py » qui n'appelle pas de commentaire particulier :#!/usr/bin/env python2.7 # -*- coding: utf-8 -*- # Version du 26 sept 2016. Prend en compte import os import time import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) # nécessaire bien que déjà fait dans code gestion switch # GPIO.setwarnings(False) # Buzzer connecté entre GPIO 22 (pin 15) et GND BUZZER = 22 GPIO.setup(BUZZER,GPIO.OUT) timeDown=0.0004 # durée niveau bas timeHigh=0.0004 # durée niveau haut def beepCode (): for i in range(1, 2000): GPIO.output(BUZZER,GPIO.HIGH) time.sleep(timeHigh) GPIO.output(BUZZER,GPIO.LOW) time.sleep(timeDown) os.system('clear') # printing white space # print "beep!" beepCode()
À l'écoute, on constate que le son généré par le buzzer n'est pas très uniforme, probablement en raison d'une durée aléatoire du time.sleep, le paramètre spécifié n'étant en fait qu'une borne min.
Intégration/finalisation du code
Le principe simplement retenu est de lancer le script de gestion du switch/led « haltRaspi-withLEDctrl.py » en tâche de fond au début du script principal « playMidiK.sh ». Le script d'activation du buzzer, quant à lui, est placé à la fin du script « aConnect.sh » de connexion du clavier midi et de Fluidsynth. Comme on peut le voir ci-dessous, on a inséré en toute fin les explications de fonctionnement du switch qui seront ainsi affichées à la vue de l'utilisateur s'il utilise un écran.Le script « aConnect.sh » :
#!/bin/bash # Version du 4 oct 2016 : les ports des périphériques gérés par "Alsa connexion manager" # sont désormais référencés par leur nom plutôt que par leur n° (supposé) de client. # "aconnect 16:0 128:0" devient "aconnect 'ESI KeyControl 49 XT':0 'FLUID Synth':0" # voir "man aconnect" # Exécution temporisée pour permettre la mise en place de la chaîne de traitement son # Prévient de son accomplissement par un appel à la fct buzzer tempo=16 echo "Connexion dans ${tempo} seconds" sleep ${tempo} echo "aconnect 'ESI KeyControl 49 XT':0 'FLUID Synth':0" aconnect 'ESI KeyControl 49 XT':0 'FLUID Synth':0 # Emet un beep sur le buzzer buzzer.py echo "\"quit\" pour quitter" echo "Reboot : appui switch jusqu'à extinction LED et relâchement immédiat" echo "Shutdown : appui maintenu jusqu'à réallumage LED" echo "NB : appuis brefs sans effet"
Le shell-script principal « playMidiK.sh » :
#!/bin/bash # Mise en place config synthé clavier midi sur Raspi # V5 du 26 septembre 2016 avec gestion du switch et LED # pour reboot ou arrêt safe du Raspi # "Lancement du code de gestion switch et LED pour halt safe" # "Reboot : appui switch jusqu'à extinction LED et relâchement immédiat" # "Shutdown : appui maintenu jusqu'à réallumage LED" # "NB : appuis brefs sans effet" echo "haltRaspi-withLEDctrl.py &" haltRaspi-withLEDctrl.py & sleep 1 echo "Lancement du serveur Jackd en background" echo "jackd -d alsa -d hw:CODEC -r 48000 -p 256 -n 2 &" jackd -d alsa -d hw:CODEC -r 48000 -p 256 -n 2 & echo "Lancement retardé de la connexion Alsa" aConnect.sh & echo "Régler l'amplification des canaux gauches et droits de la carte son" leftLevel=80 rightLevel=80 echo "Niveau gauche à ${leftLevel}%, niveau droit à ${rightLevel}%" echo "amixer -c CODEC sset PCM,0 ${leftLevel}%,${rightLevel}%" amixer -c CODEC sset PCM,0 ${leftLevel}%,${rightLevel}% echo "Lancer FluidSynth avec les font synthé qui vont bien" echo "fluidsynth -a jack -m alsa_seq -g 4 -r 48000 -j /usr/share/sounds/sf2/FluidR3_GM.sf2" fluidsynth -a jack -m alsa_seq -g 4 -r 48000 -j /usr/share/sounds/sf2/FluidR3_GM.sf2 echo "killall jackd" killall -9 jackd echo "Bye"
Conclusion
Le dispositif décrit dans ces lignes a le mérite de fonctionner assez correctement. Il reste de mon point de vue au moins une amélioration à faire, ce serait de trouver et d'exploiter un retour d'information qui permettrait de savoir quand la chaîne audio est prête pour établir la connexion du clavier midi plutôt que de se baser sur une temporisation aveugle.En terme de bilan, lorsque je m'engageais dans ce projet, je n'imaginais pas le nombre de difficultés qui allaient émailler le parcours. Pour autant, même si au final il m'aura fallu ajouter une carte son externe, composer parfois avec des comportements quelque peu erratiques de l’environnement Raspbian-LXDE-Openbox, le Raspberry pi, notamment dans sa version 2, modèle B (NB : la V3 n'existait pas à l'époque), me paraît un produit magnifique et plein de ressources autant que financièrement abordable. Il ne faut toutefois pas lui demander plus qu'il ne peut raisonnablement assumer vu sa compacité et sa faible consommation.
Ci-dessous, un aperçu du dispositif en situation :
Gérald Ouvradou, le 05/10/2016
Liste des liens cités dans le texte
- Linux Pratique HS 30 consacré au Raspberry Pi
- http://raspbian-france.fr/telechargements/
- Linux Pratique HS 29, MUSIQUE & SON, chap 3.
- https://debian-facile.org/doc:environnements:x11:openbox
- http://www.framboise314.fr/ca-va-faire-du-bruit-chez-les-framboise314-comment-configurer-le-son-sur-le-raspberry-pi/
- https://debian-facile.org/doc:environnements:lxde:lxde#creer-des-raccourcis-sur-le-bureau
- La carte son UCA 202 sur : Behringer UCA202
- http://www.instructables.com/id/Use-USB-Sound-Card-in-Raspberry-Pi/ « Use USB Sound Card in Raspberry Pi », sect. « Disable Raspberry Pi's built-in sound chip » (http://www.instructables.com/id/Disable-the-Built-in-Sound-Card-of-Raspberry-Pi/step2/Configure-Linux-to-NOT-load-Broadcoms-sound-chip/ ).
- http://www.framboise314.fr/raspbian-tout-un-tas-de-trucs/#Demarrage_automatique
- http://stackoverflow.com/questions/30504470/how-to-setup-jackd-and-guitarix-with-real-time-priority-under-raspbian
- http://hardware-libre.fr/2013/07/ajouter-un-bouton-dextinction-avec-python/
- http://www.pihomeserver.fr/2013/10/25/raspberry-pi-home-server-ajouter-bouton-darret/
- http://hardware-libre.fr/2014/03/raspberry-pi-plusieurs-fonctions-avec-un-seul-bouton-2/