La base
N’étant pas un habitué de Sonic Pi lequel j’ai trouvé en faisant l’année dernière des recherches internet sur la codage musical et les algorithmes de même ordre. J’ai abandonné cette piste relativement rapidement du fait d’autres préoccupations.
Je reprend cette année mes recherches et j’espère aller un peu plus à fond dans ces principes.
Sonic Pi est à la fois simple, ludique, musical mais aussi allié à de puissants outils utilisés mondialement depuis au moins 2 sinon 3 générations.
Du coté musical et multimédia on a le protocole Midi, créé spécialement pour la communication entre instruments de musique électronique. Un autre protocole OSC (Open sound Control) est venu quelques années plus tard, s’adjoindre au midi pour améliorer les possibilités et les vitesses de transferts des informations. Le protocole midi s’apparente plus à une simple liaison RS232 avec les limites en terme d’envoi et de réception de flux d’informations à la fois dans la quantité — ceci dû à la vitesse très limitée de 31,25 kbits/sec (moins qu’un modem à 56 kb dans la valeur de chaque information envoyé (0 to 127). De même c’est un protocole symetrique unidirectionnel nécessitant 2 câbles pour envoyer et recevoir les informations. Il a tendance à saturer les instruments et chaque musicien l’utilisant a eu au moins une fois la peur de sa vie devant le comportement étrange d’un synthé qui se met à vomir des sons sans plus aucune volonté de sa part. Panique. Le format de transmission OSC permet lui un transfert sur des réseaux car il est soutenu par les protocoles UDP et TCP qui sont la base d’ethernet. Il n’est donc quasiment pas limité par la vitesse et permet des interactions en temps réel. Il a été conçu au départ pour des interfaces gestuelles permettant de faire de la musique et maintenant il permet la transmission d’ordre sur des réseaux mélant instruments de musique électronique, informatique, matériel d’éclairage, interfaces temps réel diverses. Je vous mets en fin d’articles quelques liens si votre curiosité vous amène à vouloir en savoir plus.
Du coté programmation, si Sonic Pi est clairement dirigé vers l’apprentissage de la musique et du codage de celle-ci, il s’appuie sur le langage Ruby qui est un langage open-source moderne s’apparentant peut-être à notre vieux basic des années 80-90 mais aussi au langage Lisp qui depuis sa création dans les années 50 par John McCarthy est le langage de l’intelligence artificielle et de ce fait de l’algorithmique. La syntaxe de Ruby est simple et facile à retenir et malgré cela, comporte les mêmes fonctionnalités que des langages objets plus complexes que lui. C’est donc un langage en plein développement qui permet de multiples projets dont Sonic Pi. Sonic Pi s’appuie aussi sur un moteur de synthèse audio en temps réel qui s’appelle super-collider. et qui permet le live coding donc la possibilité de faire évoluer une musique en temps réel.
L’intérêt de ces outils : Sonic Pi, Super-Collider, Ruby sont qu’ils sont tous en open-source et donc accessibles et modifiables à volonté ce qui permet une grande diversité des cibles et un développement constant dans le temps du fait d’une large base d’utilisateur. Le fait par exemple d’initier aux enfants la musique par l’intermédiaire de Sonic Pi les fait entrer dans le monde de la programmation de plein pied à un âge auquel le cerveau intègre beaucoup plus facilement la nouveauté qu’à un vieux comme moi. D’ailleurs la base d’utilisateur en constante augmentation et la présence sur le web d’une communauté importante autour de Sonic Pi permettent un apprentissage simplifié ne serait ce que par rapport à des langages comme Java ou le C.
Passons au programme
En cherchant sur le web des exemples de programmes en Sonic Pi, je suis tombé sur le compte de G. Martin Butz et sur son séquenceur en Sonic Pi. Qui dit séquenceur dit stockage des informations et envoie de celle-ci suivant son ordre dans une série de tableau ou une matrice de stockage. Je cherchais comment stocker et envoyer des informations en midi. Son petit programme m’en a donné la solution. et n’étant pas familiarisé particulièrement avec le Ruby cela m’a fait gagner un temps précieux.
J’ai pris l’œuvre de Steve Reich, Clapping Music pour faire mes tests.
1. parce que j’aime bien la musique minimaliste américaine des années 60-70 qui nous a certainement fait évoluer énormément en matière de musique électronique.
2. parce que ce morceau est d’une simplicité déconcertante (pour faire un jeu de mot facile. Ceux qui n’ont pas compris lève la main) et qu’il ne demande pas de ce fait un nombre de ligne de code insurmontable à mon apprentissage
En gros le programme ne nécessite que 2 tableaux de 12 notes puisque le rythme de base de Clapping Music est constitué de 7 notes et de 5 espaces, quelques variables et tableaux permettant le stockage temporaire d’information, quelques boucles, quelques fonctions.
Nous les appelerons
- Les tableaux (ou pattern pour parler musik)
- base_pattern_clap (tableau du pattern de base, s’exécutant tout du long du morceau)
- base_mob_pattern_clap (tableau en rotation constante le long du morceau)
- tmp_pattern (tableau de stockage temporaire dans la fonction bouge_element, non utilisé dans la version actuelle du programme)
- Les fil d’exécution (threads)
- base_jeu (la boucle, fonction jouant le pattern base_pattern_clap)
- mobile_jeu (la boucle, fonction jouant le pattern base_mob_pattern_clap
- Les fonctions
- play_rythm_bar (la fonction lançant le jeu d’un pattern)
- bouge_element (la fonction permettant de translater le pattern)
- get_bmp_val (la fonction permettant de connaitre la durée d’une note ou d’un silence
- Les variables
- d ( la valeur en temps d’un silence)
- s (sens de rotation du pattern)
- clap, clap2 (variable contenant le chemin d’accès aux samples)
- el__a_bouger (l’élement du tableau base_mob_pattern_clap à passer du début à la fin de celui-ci ou inversement)
- d’autres variables temporaires utilisées dans les paramètres de fonctions
On va commencer par les variables fixes ou fonctions de paramétrage du programme
use_debug
ce premier élément permet d’indiquer dans la fenêtre journal de Sonic Pi certains résultats de fonctions. Il permet dans notre cas de vérifier le bon chargement des samples (voir plus loin) et leur bonne exécution dans le programme. Ses paramètres sont true ou false. le paramètre par défaut étant true il n’est pas utile d’utiliser la fonction dans le cas ou vous voulez en avoir l’usage. En revanche, pour limiter les sorties dans le journal, indiquez
use_debug false
en début de votre programme.
Maintenant nous indiquons
set_volume! 1
Pour indiquer le niveau de sortie du son. Ici aussi, nous utilisons la valeur par défaut; le paramètrage pouvant se faire de 0 à 5 et accepte les fractions.
use_bpm 170
use_bpm correspond au tempo du morceau. Dans les œuvres de Steve Reich, le tempo est souvent modulable ainsi que le nombre de répétitions de chaque motif. Pour Clapping Music, on a un tempo de 160 à 210 environ et un nombre de répétitions de 4 à 8 fois le motif de base. Dans notre cas et pour démarrer, nous allons utiliser un tempo de 60 ou de 90. à votre convenance. Vous pouvez tester le temps mis par une boucle en entrant le code suivant :
use_debug true
set_volume! 1
use_bpm 60
live_loop :test do
sleep 1
puts "j'ai fait un tour"
end
J’explique ce petit bout de code
Nous avons crée un boucle (live_loop) appelée test (:test) qui fait (do)
sleep 1 (pause de une mesure)
puts (placer, imprimer à l’écran) « j’ai fait un tour » (texte affiché dans le journal)
end (fin de la boucle)
on recommence à sleep 1
En changeant la valeur de use_bpmet en relançant le programme, on s’aperçoit dans le journal que notre boucle est plus ou moins rapide en fonction de la nouvelle valeur mise.
d est dépendant de la longueur du motif de base (ici base_pattern_clap). On le calcule donc par rapport à celui-ci avec la fonction get_bpm_val.
La fonction bouge_element est on ne peut plus simple. Elle est appelée avec une variable s qui est le sens de rotation du pattern et le pattern qu’on doit modifier suivant ce sens, on met dans el_a_bouger, l’élément qu’on doit reporter de l’autre coté du pattern
on enlève du tableau l’élément qu’on vient de stocker dans el_a_bouger
et on ajoute el_a_bouger en fin ou en début de tableau
ce qui donne :
define :bouge_element do |s, tab|
if s == 1
el_a_bouger = base_mob_pattern_clap[11]
base_mob_pattern_clap.delete_at (11)
base_mob_pattern_clap = [el_a_bouger] + base_mob_pattern_clap
elsif s == -1
el_a_bouger = base_mob_pattern_clap[0]
base_mob_pattern_clap.delete_at (0)
base_mob_pattern_clap = base_mob_pattern_clap + [el_a_bouger]
end
end
La fonction play_rythm_bar (traduction littérale jouer le rythm de la mesure) est appelée elle aussi avec deux arguments : l’instrument qui va être utilisé pour la première mesure et le pattern à jouer.
define :play_rhythm_bar do |instr, instr_pattern|
instr_pattern.each do |i|
if i != 0
instr.call
end
sleep d
end
end
Vous aurez observé l’appel des méthode each et call que je vous explique.
inst_pattern.each do |i|
inst_pattern.each est une méthode simplifié pour une boucle de type for ou while et se traduirait pour chaque élément du tableau.
do |i| : pour chaque inst_pattern[i] faire… la suite.
Vous remarquerez qu’une fonction, une boucle, enfin quoi que ce soit qui s’exécute ou qui se définit utilisent les termes do au départ et end à la fin.
La méthode call est utilisé de la même façon pour lancer un objet définit précédemment.
Dans notre cas, on va créer une fonction permettant de jouer un son enregistré précédemment (sample). Pour cela, il nous faut ce son qu’on va appeler clap.
clap = "~/Music/SonicPi/Samples/Base Drums/Clap_1_VideoStar.wav"
On va appeler ce son par la méthode load_sample
load_sample clap
On crée une méthode lambda qu’on appelle base
base = lambda do
sample clap, amp: 4, pan: 0.5
end
il ne suffit plus qu’à lancer cette méthode
base.call
Et voilà notre son est joué.
On recommence avec un deuxième son pour notre deuxième instrument
clap2 = "~/Music/SonicPi/Samples/Base Drums/Clap_2_VideoStar.wav"
load_sample clap
mobile = lambda do
sample clap2, amp: 4, pan: 0.5
end
mobile.call
vous pouvez lancer le programme avec les deux claps. Vous n’entendez qu’un son ? C’est normal. Pour entendre les deux sons, on va utiliser une pause entre le premier et le deuxième call. Où vous voulez… sauf à l’intérieur de la méthode lambda.
sleep 1
Soit une pause de 1 mesure.
Voilà nous avons posé les bases de fonctionnement de notre programme
Si vous avez bien observé, on a utilisé sleep dans le fonction play_rythm_bar avec l’argument d. Cette variable n’a pour l’instant pas été définie.
J’ai donc utilisé et amélioré la fonction de G. Martin Butz pour définir cette valeur d. qui correspond à la valeur d’un silence identique aux notes de notre pattern.
Considérons une mesure; Au plus il y a de notes dans la mesure, c’est à priori parce que le temps alloué à chaque note est plus petit.
Par exemple en 4/4, on peut mettre au choix 1 ronde, 2 blanches, 4 noires, 8 croches, 16 doubles-croches, 32 triples-croches, 64 quadruples-croches, 128 quintuples-croches.
Donc, au plus notre tableau (pattern) contient de notes au plus petites elle sont. Nous en déduisons donc la valeur de d de cette façon. On prend bien entendu l’hypothèse qu’un pattern correspond à une mesure.
Voici notre fonction get_bmp_val que j’ai limité de la noire à la double croche en incluant les rythmes à trois temps.
define :get_bmp_val do |a|
if a.count == 4 || a.count == 6
d = 1
elsif a.count == 8 || a.count == 12
d = 0.5
elsif a.count == 16 || a.count == 24
d = 0.25
else
puts "NOTE: Aucune idée du rythme de votre morceau !"
end
return d
end
a étant bien entendu notre pattern et la méthode count permettant le calcul du nombre d’éléments de celui-ci.
On peut mettre indifféremment notre appel à cette fonction à l’intérieur d’une autre fonction ou de façon indépendante pour définir une fois, pour toute la durée du programme la valeur d. L’intérêt de la mettre à l’intérieur d’une fonction est que si 2 patterns sont d’un nombre de notes différentes alors les deux patterns peuvent s’enchevêtrer pendant le jeu. Ce qui bien sûr en musique arrive régulièrement. Je vous laisserai le soin de l’imaginer et de modifier le mode de calcul de d. Sur un synthétiseur muni d’un séquenceur l’appel est souvent en multiples de 4 (4, 8, 16, 32) et il est rare de pouvoir faire des mesures tronquées par rapport à ces mesures, de même que de faire des mesures sur 3 ou 5 temps. La possibilité de le faire avec Sonic Pi peut dans ce cas être un plus et sortir nos musiciens de galères qui quelquefois réduisent leurs superbes imaginations.
C’est bien mais avec tout ça on n’a pas encore défini le contenu de notre pattern.
Le motif de base de Clapping musique est
,tac,tac,tac, ,tac,tac, ,tac, ,tac,tac, ,
soit transformé par le miracle de l’intelligence (non artificielle de l’être humain) en [ 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0 ].
On a donc défini nos deux tableaux qui sont pour l’instant identique.
pattern_clap = [ 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0 ]
base_mob_pattern_clap = [ 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0 ]
Maintenant il ne nous reste plus qu’à appeler nos fonctions avec des threads. Les threads dont la traduction est fil (ici plus précisément fil d’exécution) est en fait le déroulement d’une partie d’un programme. un peu comme un instrumentiste qui jouerait uniquement sa partie en solo.
Comme il y a deux instruments sur Clapping Music, on va créer 2 threads dans lesquels on va jouer nos patterns.
in_thread(name: :base_jeu) do
end
in_thread(name: :mobile_jeu) do
end
Comme le morceau de Steve Reich fait 104 mesures, on va voir combien de fois chaque mesure est joué.
Pour l’instrument 1, c’est facile le motif étant joué ad vitam aeternam, enfin jusqu’à la fin du morceau donc 104 mesures.
On crée donc une boucle que l’on va reproduire 104 fois. C’est bien facile puisqu’une instruction est faite pour cela. l’instruction times que l’on fait précéder du nombre de fois où elle doit être joué et d’un point de séparation comme call, each, count, etc. Comme chaque instruction de ce type, on la fait suivre par do et finir par end (voir précédemment).
On insère dans cette boucle notre fonction play_rhythm_bar avec ses arguments : le son à jouer et le pattern correspondant. Chez vous, faites un test sur une mesure ou si cela n’est pas le rendu espéré, pensez aux raccourcis pour arrêter votre programme.
in_thread(name: :base_jeu) do
104.times do
play_rhythm_bar base, base_pattern_clap
end
end
Pour le deuxième instrument, on a le même nombre de mesures total mais on joue chaque pattern huit fois avant de faire une rotation dans le tableau. Le nombre de rotations total pour revenir au motif de base est de la longueur du pattern, soit 12 rotations plus le motif de base qui multipliées par nos 8 répétitions donnent bien 104.
On a donc deux boucles imbriquées de cette façon.
in_thread(name: :mobile_jeu) do
13.times do
8.times do |count|
play_rhythm_bar base, base_mob_pattern_clap
end
end
end
Il ne suffit plus qu’à rajouter notre fonction de rotation de pattern pour faire évoluer comme il se doit le morceau.
in_thread(name: :mobile_jeu) do
13.times do
8.times do |count|
play_rhythm_bar base, base_mob_pattern_clap
end
bouge_element -1, base_mob_pattern_clap
end
end
Si vous avez bien suivi, votre programme sous Sonic Pi devrait fonctionner. Si vous rencontrez des problèmes, n’hésitez pas à utiliser l’instruction puts, l’équivalent de notre print du basic du commodore 64 et de l’employer pour suivre la valeur de vos variables, l’aspect de vos tableaux ou encore les valeurs de retour de vos fonctions.
En dernier et j’en ai parlé au début, l’un des buts de Sonic Pi n’est pas de faire de la musique seul dans son coin même si à priori il le fait bien mais grâce au protocole midi et OSC à envoyer des informations à d’autres périphériques.
Je vous donne donc les rudiments pour y arriver.
Pour un simple envoi d’une note sur le synthé midi de votre choix utilisez la fonction midi_note_on suivi au minumum de la valeur de la note comme cela :
midi_note_on 60
qui joue un do et qui pourrait s’écrire aussi :c4 en notation anglaise. Vous pouvez ensuite ajouter à cela la vélocité de la note jouée :
midi_note_on 60, 90
sur une valeur de 0 à 127,
puis l’instrument sur lequel vous voulez jouer avec le paramètre port:. Par exemple :
midi_note_on :c4, 90, port: [”mon_synthé_rien_quà_moi”]
et enfin le canal midi correspondant à vos réglages sur le synthé.
midi_note_on :c4, 90, port: [”mon_synthé_rien_quà_moi”], channel: 1
Vous rajouter à cela un sleep correspondant à la longueur de la note jouée et vous finissez par éteindre la note avec l’instruction midi midi_note_off avec le port et le canal correspondant
ce qui pourrait donner dans votre programme une portion de code similaire à celle ci :
midi_note_on note, velocity, port: "gestionnaire_iac_bus_1", channel: 10
sleep d
midi_note_off note, port: "gestionnaire_iac_bus_1"
Je vous renvoie sur mon précédent article sur Clapping Music sur lequel vous trouverez un enregistrement complet de celui-ci disponible sur Youtube.
https://unprojetparjour.fr/2020/03/28/steve-reich-clapping-music-realisation-avec-sonic-pi-logic-pro-x-et-drumlab/
N’hésitez pas à vous mettre à Sonic Pi et à en faire profiter vos enfants par exemple car c’est ludique et cela peut vous faire créer de belles choses. En cette période de confinement, c’est un atout pour ne pas s’ennuyer (les enfants) ou ne pas stresser (les parents).
Sonic Pi existe sur les différentes plateformes informatique, Mac, Windows, et Unix et il a été développé au départ pour Raspberry Pi sur lequel il fonctionne toujours.
Bonne programmation.
Liens divers
https://fr.wikipedia.org/wiki/Musical_Instrument_Digital_Interface
https://fr.wikipedia.org/wiki/Open_Sound_Control
https://en.wikipedia.org/wiki/Sonic_Pi
https://fr.wikipedia.org/wiki/Ruby
https://fr.wikipedia.org/wiki/SuperCollider