Avec un LLM
Le FM-1 et votre LLM
Un morceau du FM-1 est un fichier texte, et un modèle de langage le lit et l'écrit. Cette page dit ce que ça change en pratique : ce qu'on peut lui demander d'écrire, comment vérifier ce qu'il a écrit avant de l'envoyer à la machine, et comment il peut prendre la machine en main pour juger son travail lui-même.
Tous les outils de cette page sont dans le dépôt du firmware et
s'exécutent depuis sa racine. Ils demandent python3, et ceux
qui parlent à la machine python-rtmidi.
Écrire des morceaux
Ce qu'on demande à un modèle, c'est la frappe, pas la musique : une basse sous une grille qui existe déjà, une section doublée puis transposée, des paroles épelées en phonèmes, les mêmes quatre mesures avec un autre groove. Le morceau reste le vôtre — le modèle tape.
Ce qu'un .trk est
Du texte UTF-8, une instruction par ligne. version 3
d'abord, et obligatoire ; puis l'en-tête, les instruments, les blocs de
steps, et l'arrangement. Un bloc est une ligne pat suivie de
ses lignes de step ; une phrase est une ligne arr suivie
d'un bloc par piste. Hors ces deux cas, l'ordre des lignes ne compte
pas.
version 3 bpm 96 ticks 6 pat t0 00 len 16 00 C-2 i09 fVOL6A 04 D-2 i09 08 C-2 i09 0C D-2 i09 pat t1 00 len 16 00 C-3 i00 fGAT03 06 C-3 i00 fGAT03 0A G-2 i00 fGAT03 arr 00 steps auto name INTRO t0 00 t1 00
Ce morceau joue : il ne définit aucun instrument, et
i09 comme i00 sont deux des dix sons que la
machine donne d'office. Une ligne de step porte jusqu'à quatre notes
jointes par +, l'instrument, jusqu'à trois effets et une
syllabe chantée ('MA). Les octaves sont celles du MIDI :
C-4 est le do du milieu, A-4 le la 440. Le
reste de la grammaire — les instruments et leurs couches, les zones de
samples, les sections, les phrases qui en rejouent une autre — est dans
le skill.
Ce que le modèle doit savoir
- Tout le morceau, jamais un fragment. Le FM-1 refuse un
patch: chaque fichier envoyé est un morceau entier, et ce qu'il ne dit pas n'est pas « inchangé », il est supprimé. Donnez au modèle le fichier que la machine a sauvé, pas un extrait. - Une ligne que le lecteur ne reconnaît pas est ignorée en silence. Un mot-clé mal orthographié n'est pas une erreur : c'est une ligne qui disparaît. C'est la chose la plus coûteuse de ce format, et la raison de la section suivante.
- Ce que cette machine ne porte pas tombe à l'entrée :
drive,ampettoned'un instrument, les lignespreviewetref, le moteur PLUCK, les pistes au-delà de la huitième. Un modèle à qui il faut l'une de ces choses devrait le dire plutôt qu'écrire un fichier qui revient autrement. - Un instrument qu'aucun step ne joue n'est pas enregistré. L'écrivain ne garde que les instruments qu'un step nomme : un son défini et jamais joué revient absent, sans que rien le signale.
Ce que la machine tient : huit pistes, seize blocs par piste, 128 steps par bloc et 6 000 pour tout le morceau, quatre notes et trois effets par step, 64 instruments, 128 phrases. Les dix premiers instruments ont déjà un son — BASS, SUB, LEAD, KEYS, HARP, PAD, ORGAN, BELL, VOICE, KIT.
Vérifier le fichier
Le lecteur et l'écrivain du firmware se compilent sur l'ordinateur et
n'ont pas besoin de la machine. trkdump dit ce que le FM-1
enregistrerait, à partir de votre fichier :
cc -std=c11 -O2 tests/trkdump.c core/song.c core/trk.c -o build/trkdump
build/trkdump song.trk
La boucle à tenir : écrire le fichier, le passer par
trkdump, lire ce qui revient. Le but n'est pas un diff vide
— la machine réécrit tout en entier, défauts compris. Le but est
l'écart : une ligne absente est un mot-clé que le lecteur a ignoré, une
valeur changée est une valeur ramenée dans ses bornes, un champ parti est
un champ que cette machine ne porte pas.
Du morceau de seize lignes ci-dessus, par exemple, il rend les deux presets écrits en entier, les huit lignes de mixage et les réglages que le fichier laissait de côté :
meter 4 4 tracks 8 reverb 64 64 delay 18 60 inst 00 engine PULSE timbre 1 adsr 0 34 170 14 filter 150 60 84 name BASS inst 09 engine DRUM timbre 0 adsr 0 0 255 20 level 6 name KIT trk 0 vol 100 pan 0 ...
tests/trkcheck.c se compile de la même façon et va plus
loin : il charge le fichier, dit ce qui n'est pas porté, réécrit,
recharge, et exige que les deux écritures soient identiques.
Envoyer
# un morceau vers la machine python3 tools/fm1_send.py song.trk # ce qu'elle porte python3 tools/fm1_send.py --list # un morceau qui revient python3 tools/fm1_send.py --get NAME -o song.trk
La machine doit être sur HOME. Le morceau est réduit par le même lecteur
et le même écrivain avant de partir : ce qui arrive est exactement ce que
trkdump montrait. Rien n'est remplacé sur la machine sans
--replace.
Le même outil ramène un morceau de la machine, et c'est par là qu'une
séance commence quand le morceau existe déjà : --get, puis
le fichier au modèle.
Les samples ne voyagent pas dans un morceau : une ligne
smp est un nom, et la banque se remplit à part, avec
tools/fm1_samples.py — le
manuel le dit.
Piloter la machine
C'est la moitié qu'on ne devine pas. tools/fm1_ctl.py est une
télécommande pour un FM-1 allumé : elle lit ce qu'il fait, appuie sur ses
boutons, tourne ses encodeurs, joue ses touches, écrit dans le morceau en
mémoire, allume ses LED et photographie son écran. La machine n'a aucun
mode à mettre — ça marche pendant que quelqu'un joue dessus.
Ce que ça rend possible, et que rien d'autre ne permet ici : un modèle
juge son propre travail. Il fait quelque chose, prend l'écran, regarde.
Tout passe par où passerait un pouce : ARP est l'UNDO de la
machine, et rien n'atteint la flash avant save.
Lire ce qu'elle fait
python3 tools/fm1_ctl.py status
Une ligne de clé=valeur, faite pour être lue telle
quelle.
| screen= | l'écran où elle est : HOME, SONG, PHRASE, PATTERN, INSTRUMENT, MIXER, PROJECT, CANVAS, SETTINGS, HELP, SYSTEM |
|---|---|
| song= slot= dirty= | le morceau ouvert, sa place en flash, et s'il reste des modifications non enregistrées |
| playing= phrase= step= | ce qui joue, et où en est exactement la tête de lecture |
| bpm= phrases= tracks= | la forme du morceau |
| cpu= max= | la charge maintenant, et le pire bloc depuis le dernier --reset |
| batt= free= | la pile, et les chunks qui restent dans le pool de steps |
Les autres lectures servent quand quelque chose va mal :
--profile (le pire bloc étage par étage, en µs),
--boot (pourquoi la machine a démarré la dernière fois),
--trail (où en était le run précédent quand il s'est
arrêté), --crash (les registres du dernier plantage).
Voir l'écran
python3 tools/fm1_ctl.py screen -o shot.png
Le panneau en image, 240 × 240, lu bande par bande. C'est la commande qui rend un modèle capable de vérifier ce qu'il vient de faire sans demander à personne de lui décrire l'écran.
Appuyer, tourner, jouer
python3 tools/fm1_ctl.py press SEL python3 tools/fm1_ctl.py press HOME SEQ # plusieurs ensemble python3 tools/fm1_ctl.py turn K2 -3 # trois crans à gauche python3 tools/fm1_ctl.py key C-4 --hold 20 # une touche, tenue
Les boutons portent les noms imprimés sur la façade :
FX SEL ENV LFO EDIT GLO HOME SAVE ARP SEQ PLAY REC OCT- OCT+
Les encodeurs sont SELECT ALGO
PRESETS K1 K2 K3
K4, et un tour est un entier signé, en décimal. Les touches
vont de F-3 à G-5 ; OCT− et
OCT+ ne les déplacent pas, ils déplacent ce qu'elles
jouent.
Les noms de la façade ne sont pas ceux des écrans : ARP est UNDO et ALGO tourné à droite REDO, SEQ FX ENV choisissent la colonne sur PATTERN, GLO ouvre le morceau et FX la table de mixage. Les gestes de chaque écran sont dans le manuel.
Écrire dans le morceau en mémoire
python3 tools/fm1_ctl.py open NAME python3 tools/fm1_ctl.py step 1 03 10 'C-4+E-4+G-4 i03 fVOL40' python3 tools/fm1_ctl.py step 1 03 10 -- # vide le step python3 tools/fm1_ctl.py cell 00 1 03 # la piste 1 joue le bloc 03 dans la phrase 00 python3 tools/fm1_ctl.py length 1 03 32 python3 tools/fm1_ctl.py loop 00 python3 tools/fm1_ctl.py save
Les pistes se comptent de 1 à 8 ici, comme elles sont écrites
sur les écrans, et non t0–t7 comme dans le
fichier : step 1 ... est la piste que le
.trk appelle t0, et
step 0 ... sort sur « tracks are 1 to 8 ».
Un step s'écrit exactement comme une ligne de .trk
l'écrit, moins le numéro de rang, et en un seul argument. Phrases, blocs,
rangs, instruments et valeurs d'effet sont en hexadécimal, comme sur les
écrans ; les steps de length et les crans d'un encodeur sont
en décimal. open et new refusent d'abandonner
des modifications non enregistrées sans --force, et un
morceau entier passe par fm1_send.py, puis
open.
Les LED
python3 tools/fm1_ctl.py led FX lit python3 tools/fm1_ctl.py led C-4 breath A-4 mid python3 tools/fm1_ctl.py led all app # rendues au firmware
N'importe quel bouton, n'importe quelle touche, par son nom. Les niveaux
sont off glow mid
lit breath, les pourcentages 50
62 75 87 100, ou
app pour rendre cette LED au firmware ; all les
nomme toutes.
Dessiner sur l'écran
La machine sait montrer un canvas dont l'ordinateur est le propriétaire : le script dessine, la machine affiche, et aucun firmware n'est compilé pour ça.
python3 tools/fm1_ctl.py canvas clear black python3 tools/fm1_ctl.py canvas fill 20 40 200 60 amber python3 tools/fm1_ctl.py canvas text 12 80 'HELLO' --font 20 --colour cyan python3 tools/fm1_ctl.py canvas line 0 120 239 120 grey python3 tools/fm1_ctl.py canvas image picture.png python3 tools/fm1_ctl.py canvas events
L'écran est 240 × 240. Une couleur est #RRGGBB ou un mot du
thème — black white grey green cyan amber red. Le
y d'un texte est sa ligne de base, et les fontes sont
20, 14, 12 et mono.
HOME quitte le canvas et rend l'écran au firmware.
canvas events est l'autre moitié : il rapporte les boutons
appuyés et relâchés, les touches, et de combien chaque encodeur a tourné
depuis le dernier regard. Dessiner et relire en boucle, c'est une
interface entière qui tourne sur le panneau pendant que le programme vit
sur l'ordinateur. Au-delà de quelques commandes, on écrit un script
contre tools/fm1_proto.py :
from fm1_proto import Canvas c = Canvas() c.keep_glo() # GLO devient le « retour » du script def frame(ev): # au départ, puis à chaque geste c.clear("black") c.text(12, 30, f"K2 {ev.turns.get('K2', 0)}", "cyan") if "SEL" in ev.pressed: c.fill(0, 100, 240, 40, "amber") c.show() c.run(frame)
tools/protos/ en contient une douzaine, chacune un écran
essayé sur le panneau avant d'être construit dans le firmware.
--udp parle au simulateur sur l'ordinateur plutôt qu'à la
machine, pour fm1_ctl.py comme pour un script : c'est le bon
endroit pour essayer quelque chose.
Mettre son modèle dans la boucle
Tout ce qu'un modèle a besoin de savoir de cette machine est écrit pour
lui, dans le dépôt du firmware, sous skill/moondaw-fm1/ :
SKILL.md— ce qu'est la machine, les deux choses qui se font depuis un ordinateur, et les habitudes qui comptent quand on modifie le morceau de quelqu'un.references/format.md— la grammaire du.trk, ses limites, et ce que la machine laisse tomber à l'entrée.references/step-fx.md— les 28 effets de step, chacun avec sa portée et la page que la machine affiche.references/engines.md— les moteurs, les dix kits de batterie et leur map General MIDI, l'alphabet phonétique de la voix VOX.references/control.md— le pilotage : la ligne de status, les boutons, l'écran, le canvas et ses gestes.
C'est du Markdown, écrit pour un modèle et pas pour un lecteur. Un modèle qui lit les fichiers du clone le trouve là ; sinon, les cinq fichiers se collent dans une conversation. Ils sont sur GitHub avec le reste du firmware, et ce qui s'y trouve de généré sort des sources de la machine : le skill ne peut pas prétendre un effet que le firmware n'a pas.
Il existe un skill voisin, moondaw, pour les
autres MOONDAW — celui du navigateur, du Mac, d'iOS, de la console. Il
ne décrit pas cette machine : le suivre ici produit des fichiers que le
FM-1 refuse, ou qu'il vide en silence.
Le reste est une conversation ordinaire. Vous posez les steps au pouce, vous décrivez ce qui manque, et le fichier revient avec. Si le modèle a la machine au bout du câble, il peut aussi aller voir si c'était bien ça.