Exemples
Six morceaux de code, chacun pensé pour couvrir une partie différente de l'API : de l'écriture la plus courte possible jusqu'à l'export bas niveau, en passant par la percussion, les accords et un arrangement complet.
Écriture minimale
Le plus petit programme Lulalib complet : une track, un song, un export. Pas de key, pas de percussion, pas d'arrangement à plusieurs sections — juste de quoi voir la forme générale de l'API.
import { song, section, lead, toJSON } from "Lulalib";
const melody = section([
lead("sine").notes("C4 E4 G4 C5").rhythm("x-x-x-x-"),
]);
const score = song({ bpm: 100 }).arrange([melody]).export();
console.log(toJSON(score, { pretty: true }));- Des notes absolues (
C4,E4...) : pas besoin dekeysur lesong, rien à résoudre à l'export. - Une
sectionà une seule track suffit ;.arrange()accepte un tableau d'une seule section tout aussi bien qu'un arrangement complet. toJSONavecpretty: truepour lire leScoreproduit directement dans la console.
Hip-hop beat
Une boucle de batterie à quatre parts et une basse qui suit les degrés de la gamme, répétée telle quelle sur tout le morceau.
import { song, section, drums, bass } from "Lulalib";
const beat = section([
drums("808")
.kick("x---x---x---x---")
.snare("----x-------x---")
.hihat("x-x-x-x-x-x-x-x-")
.part("clap", "----x-------x---"),
bass("sub").notes("1 1 4 5").rhythm("x---x---x---x---").octave(2),
]);
const score = song({ bpm: 85, key: "C" })
.arrange(beat.repeat(4))
.export();drums()empile quatre parts sur le même instrument"808":kick,snare,hihat, et une part libre"clap"via.part().bass("sub")joue les degrés 1, 1, 4, 5 — résolus en notes réelles à l'export, contre lakey: "C"dusong..octave(2)les place dans le grave.beat.repeat(4)retourne un tableau de 4 fois la même section, passé directement à.arrange()qui l'aplatit.
Mélodie et accords
Un lead qui dessine une ligne mélodique et un pad qui tient des accords, pour couvrir la mini-notation de mélodie au-delà des notes isolées : accords, groupes, altérations, décalages d'octave.
import { song, section, lead, inst } from "Lulalib";
const chorus = section([
lead("pluck").notes("1 3 5 1'").rhythm("[x-]*4"),
inst("pad").notes("1,3,5 4,6,1'").rhythm("x_______x_______").step("1/2"),
]);
const score = song({ bpm: 112, key: "Dm" }).arrange([chorus]).export();1'dans le lead : le degré 1 une octave au-dessus ('monte,.descendrait).[x-]*4dans le rythme : le groupe"x-"répété 4 fois, soitx-x-x-x-— la même chose s'écrit en mélodie avec[...]*n.1,3,5et4,6,1'dans le pad : deux accords (des virgules, jamais d'espace), tenus chacun sur 8 pas de"1/2"grâce à.step()— un pad tenu plutôt qu'un rythme haché.
Les altérations se placent juste après le chiffre du degré :
// degré 3 bémol, naturel, dièse, une octave au-dessus, une octave en-dessous
lead("pluck").notes("3b 3 3# 1' 1.").rhythm("xxxxx");Progression d'accords
Ici pas de mélodie ni d'arpège : une seule track qui ne joue que des accords, empilés et tenus, pour montrer que la mini-notation joue vraiment plusieurs notes en même temps sur un même pas plutôt que de les dérouler l'une après l'autre.
import { song, section, inst } from "Lulalib";
const piano = section([
inst("piano")
.notes("1,3,5 6,1',3' 4,6,1' 5,7,2'")
.rhythm("x_______x_______x_______x_______")
.step("1/2"),
]);
const score = song({ bpm: 92, key: "C" }).arrange([piano]).export();1,3,5 6,1',3' 4,6,1' 5,7,2': quatre accords séparés par des espaces — I, vi, IV, V enkey: "C"(Do majeur, La mineur, Fa majeur, Sol majeur) — chacun écrit comme un seul pas de mini-notation.- Dans un accord, les notes sont séparées par des virgules et jouées simultanément ; c'est l'espace qui sépare les pas dans le temps, jamais la virgule.
.step("1/2")donne un pas assez long pour tenir l'accord (ici une ronde par accord), au lieu du haché qu'on utiliserait pour une ligne mélodique.
Arrangement complet
Une structure intro / couplet / refrain construite par couches successives, plutôt que réécrite section par section — pour montrer comment .with(), .repeat() et at() s'articulent dans un vrai arrangement.
import { song, section, at, frac, drums, bass, lead } from "Lulalib";
const groove = section([
drums("909").kick("x---x---").hihat("x-x-x-x-"),
bass("acid").notes("1 4 5 1").rhythm("x-x-x-x-"),
]);
const intro = section([drums("909").hihat("x-x-x-x-")]);
const verse = groove.with([
lead("saw").notes("1 3 5 3").rhythm("x-x-x-x-"),
]);
const chorus = verse.with([
at(frac(3), drums("909").part("crash", "x")),
]);
const score = song({ bpm: 118, key: "Em" })
.arrange([intro, verse.repeat(2), chorus, verse])
.export();groovereste inchangé :verseetchorusen sont dérivés via.with(), qui retourne une section neuve sans jamais modifier celle de départ.chorusajoute un crash isolé avecat(frac(3), ...)— décalé au 3e beat sans toucher au reste du rythme de la track..arrange()mélange une section seule (intro), un tableau via.repeat()(verse.repeat(2)), et d'autres sections seules (chorus,verse) — tout est aplati automatiquement.
Export bas niveau
Sans song ni section : construire des tracks avec track(), les combiner à la main avec cat, et exporter avec toScore(). Utile pour qui veut composer ses propres abstractions au-dessus du cœur de la lib.
import { track, toScore, toJSON, toMIDI, cat } from "Lulalib";
const kick = track("909", { rhythm: "x---x---", part: "kick" });
const hats = track("909", { rhythm: "x-x-x-x-", part: "hihat" });
const bassline = track("acid", { notes: "C2 C2 Eb2 F2", rhythm: "x-x-x-x-" });
// deux mesures identiques, mises bout à bout à la main
const kickX2 = { instrument: "909", pattern: cat(kick.pattern, kick.pattern) };
const hatsX2 = { instrument: "909", pattern: cat(hats.pattern, hats.pattern) };
const bassX2 = { instrument: "acid", pattern: cat(bassline.pattern, bassline.pattern) };
const score = toScore([kickX2, hatsX2, bassX2], { bpm: 100 });
const json = toJSON(score, { pretty: true });
const midi = toMIDI(score, { ppq: 960 });track(instrument, spec)est l'API derrièrebass/lead/drums: elle exigenotesourhythmdansspec, sans quoi elle lève une erreur.cat(...)recolle desPatternbout à bout — ici, une mesure répétée sans passer parsong.arrange().toScore(tracks, meta)accepte directement un tableau d'objets{ instrument, pattern }: pas besoin d'une vraieTrackconstruite par un sucre syntaxique.toMIDIavecppq: 960: une résolution temporelle plus fine que la valeur par défaut (480), utile pour des rythmes très subdivisés.