T
ThePrimeagen
#Strudel.js#Live Coding#Musique Électronique

Live Coding Musique Électronique avec Strudel.js et Neovim

Apprenez à créer de la musique électronique en direct avec Strudel.js, un REPL JavaScript, et Neovim. Ce guide couvre la notation musicale, les effets audio et l'intégration avec Playwright et Bun.

5 min de lectureGuide IA

Introduction

Strudel.js est un environnement de programmation musicale en direct qui permet aux développeurs de composer et de manipuler des sons en temps réel à l'aide de JavaScript. Il offre un retour immédiat sur les modifications de code, ce qui en fait un outil puissant pour l'expérimentation musicale et la performance.

Précis de configuration

Élément Version / Lien
Langage / Runtime JavaScript (ES6+) / Node.js ou Bun
Librairie principale Strudel.js (via strudel.cc ou installation locale)
APIs requises Playwright (pour le contrôle du navigateur)
Outils d'édition Neovim (ou tout éditeur de texte compatible)
Serveur local Bun (pour exécuter le serveur Playwright)

Guide étape par étape

Étape 1 — Démarrer avec un son simple

Pour commencer à créer de la musique avec Strudel.js, vous pouvez utiliser la fonction sound() pour générer des sons de base. Strudel.js fonctionne sur des cycles, ce qui signifie que les sons sont joués de manière répétée.

sound("hh") // Joue un son de charleston (high-hat) en boucle

Étape 2 — Introduire la mini-notation pour des rythmes complexes

La mini-notation de Strudel.js permet de créer des motifs rythmiques plus complexes en ajoutant des silences (~) et en regroupant des sons. Les groupes entre crochets [] sont joués séquentiellement, et l'utilisation de chevrons < > permet de jouer chaque sous-groupe sur un cycle distinct, offrant une plus grande flexibilité rythmique.

sound("hh hh hh hh") // Quatre charlestons par cycle
sound("hh ~ hh ~") // Charleston, silence, charleston, silence
sound("[hh ~ hh ~] [hh ~ hh hh] [hh ~ hh ~] [hh ~ hh hh]") // Motif plus complexe avec sous-groupes
sound("<[hh ~ hh ~] [hh ~ hh hh] [hh ~ hh ~] [hh ~ hh hh]>") // Chaque sous-groupe est joué sur un cycle distinct

Étape 3 — Ajouter des instruments et des effets

Strudel.js permet d'intégrer différents instruments et d'appliquer des effets pour enrichir la composition. Vous pouvez définir des variables pour vos motifs sonores et les combiner.

setcpm(20) // Définit le tempo à 20 cycles par minute

var hh = sound('<[hh ~ hh ~] [hh ~ hh hh] [hh ~ hh ~] [hh ~ hh hh]>') // Motif de charleston
var kick = sound('<[bd ~ bd ~] [bd ~ ~ bd ~] [bd ~ ~ ~] [bd ~ bd ~]>') // Motif de grosse caisse

// Définition d'une ligne de basse avec des notes et des effets
let bass = note "<[a1 ~] [a2 g2] ~ [~ e2] [d2 ~] [c2 a1] [e2 g2]>"
  .sound "gm_synth_bass_2" // Utilise un son de synthétiseur de basse
  .room (0.5) // Ajoute de la réverbération
  .lpf (sine.range 300, 800 .slow 8) // Filtre passe-bas avec une plage de fréquences modulée par une onde sinusoïdale

// Définition d'une mélodie avec des notes, une structure rythmique, des coupures aléatoires, du strumming, de la réverbération, un filtre passe-bas, un supersaw et un délai
let melody = note "<[a3, e3, c4, g4] [c4, e4, g4, a4] [d4, f#4, a4, c5]>"
  .struct "[<[x ~]*2 [4 2]> <[~ x]*2 [x*4 x]>>" // Ajoute une structure rythmique complexe
  .mask "<1@1000>" // Applique un masque pour des variations
  .clip (rand.range .2, .4) // Coupe aléatoirement des parties du son
  .ply "<2@3 [2 4] 2@3 [2 4]>" // Ajoute un effet de strumming
  .room (0.25) // Ajoute de la réverbération
  .lpf (sine.range 500, 1400 .slow 8) // Filtre passe-bas modulé
  .lpq 5 // Qualité du filtre passe-bas
  .sound "supersaw" // Utilise un son de supersaw
  .delay (sine.range 0.1, 1 .slow 8) // Ajoute un délai modulé

// Définition d'un son de "crow" (corbeau) pour ajouter une texture unique
var crow = sound('<[~ ~ crow ~] [~ ~ crow ~] [~ crow ~ crow ~]>')

Étape 4 — Structurer la musique avec arrange et stack

La fonction stack() permet de superposer plusieurs motifs sonores pour les jouer simultanément. La fonction arrange() est utilisée pour séquencer des motifs sur plusieurs cycles, créant ainsi une structure musicale évolutive.

// Superpose les charlestons, la grosse caisse et la ligne de basse
stack(hh, kick, bass)

// Séquence la ligne de basse, puis la ligne de basse avec la grosse caisse, puis l'ensemble avec les charlestons et le crow
arrange(
  [2, bass], // Joue la ligne de basse pendant 2 cycles
  [2, stack(bass, kick)], // Joue la ligne de basse et la grosse caisse pendant 2 cycles
  [8, stack(bass, kick, hh, crow)] // Joue l'ensemble des instruments pendant 8 cycles
).play() // Lance la lecture de la séquence

Intégration de l'environnement de développement

Pour une expérience de live coding fluide, il est possible d'intégrer Strudel.js avec des outils de développement comme Neovim, Bun et Playwright. Cela permet d'écrire du code dans son éditeur préféré et de le synchroniser instantanément avec le navigateur où Strudel.js est exécuté.

  1. Lancer un serveur Bun avec Playwright : Un serveur Bun est configuré pour écouter les requêtes HTTP. Il utilise Playwright pour contrôler une instance de navigateur sans tête (ou visible) où Strudel.js est chargé.

    
    

bun run src/server.ts --ozone-platform=wayland # Exemple de commande pour lancer le serveur Bun
```

  1. Envoyer le code depuis Neovim : Depuis Neovim, une commande curl est utilisée pour envoyer le contenu du fichier de code en cours d'édition au serveur Bun. Le serveur Playwright reçoit ce code et l'injecte dans le REPL de Strudel.js dans le navigateur.

    
    

local ok = pcall(curl.post, "http://localhost:3000/update",
body = table.concat(result, "\n"),
headers = {},
callback = function()
print("Updated!")
end
)
```
* Commentaire : Ce script Lua, exécuté dans Neovim, envoie le contenu du buffer actuel (représenté par result) à l'endpoint /update du serveur local. pcall est utilisé pour gérer les erreurs.

  1. Gestion des imports et des fichiers : Le système est conçu pour gérer l'importation de différents fichiers musicaux (par exemple, hh.js, kick.js, bass/1.js) et les combiner. Cela permet de modulariser la composition musicale.

    
    

// import:hh // Importe le motif de charleston
// import:kick // Importe le motif de grosse caisse
// import:bass/1 // Importe la ligne de basse numéro 1

// Exemple de code pour la gestion des imports (Lua)
local function get_import(line)
if vim.startswith(line, "// import:") then
local parts = vim.split(line, ":")
return parts[2]
end
return nil
end

local function load_template(template_name)
local template_path = root_dir .. "/jukebox/templs/" .. template_name .. ".lua"
local ok, template = pcall(dofile, template_path)
if not ok or not template then
print("Could not load template " .. template_name)
return nil
end
return template
end

local function update_strudel(buf)
math.randomseed(42069)
print("== Heading to the Strudel ==")
refresh_internals()

local lines = vim.api.buf_get_lines(buf, 0, -1, false)
local result = {}
local ok = false
for _, line in ipairs(lines) do
local transformed = false
local import = get_import(line)
if import then
local path = root_dir .. "/jukebox/songs/" .. import .. ".js"
local ok, file_content = pcall(io.open, path, "r")
if ok then
table.insert(result, file_content:read("*all"))
file_content:close()
transformed = true
else
print("Could not import file: " .. path)
end
end
if not transformed then
table.insert(result, line)
end
end
-- [Note de l'éditeur : le code complet pour l'envoi via curl est implicite et non entièrement montré dans la vidéo]
end
```
* Commentaire : Ces fonctions Lua gèrent l'extraction des directives d'importation (// import:) et chargent le contenu des fichiers correspondants. update_strudel lit le buffer de Neovim, remplace les imports par le contenu des fichiers importés, et prépare le code final à envoyer à Strudel.js.

Tableaux comparatifs

Caractéristique Boucle d'événements (Event Loop) Cycles musicaux (Strudel.js)
Nature Traitement asynchrone des tâches Séquencement temporel des sons
Objectif Gérer les opérations non bloquantes Synchroniser les éléments musicaux
Contexte Programmation web (Node.js, navigateurs) Composition musicale en temps réel
Comportement Exécute les callbacks quand les événements se produisent Joue des motifs sonores sur des intervalles de temps fixes
Exemple Traitement des requêtes réseau, interactions utilisateur Battements de grosse caisse, mélodies, effets audio

⚠️ Erreurs fréquentes et pièges

  1. Sons répétitifs et ennuyeux : Les sons de base comme `sound(