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 }));
Lead
  • Des notes absolues (C4, E4...) : pas besoin de key sur le song, 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.
  • toJSON avec pretty: true pour lire le Score produit 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();
Bass
Hi-hat
Snare
Clap
Kick
  • 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 la key: "C" du song. .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();
Lead
Pad
  • 1' dans le lead : le degré 1 une octave au-dessus (' monte, . descendrait).
  • [x-]*4 dans le rythme : le groupe "x-" répété 4 fois, soit x-x-x-x- — la même chose s'écrit en mélodie avec [...]*n.
  • 1,3,5 et 4,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();
Pad
  • 1,3,5 6,1',3' 4,6,1' 5,7,2' : quatre accords séparés par des espaces — I, vi, IV, V en key: "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();
Lead
Bass
Hi-hat
Crash
Kick
  • groove reste inchangé : verse et chorus en sont dérivés via .with(), qui retourne une section neuve sans jamais modifier celle de départ.
  • chorus ajoute un crash isolé avec at(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 });
Bass
Hi-hat
Kick
  • track(instrument, spec) est l'API derrière bass/lead/drums : elle exige notes ou rhythm dans spec, sans quoi elle lève une erreur.
  • cat(...) recolle des Pattern bout à bout — ici, une mesure répétée sans passer par song.arrange().
  • toScore(tracks, meta) accepte directement un tableau d'objets { instrument, pattern } : pas besoin d'une vraie Track construite par un sucre syntaxique.
  • toMIDI avec ppq: 960 : une résolution temporelle plus fine que la valeur par défaut (480), utile pour des rythmes très subdivisés.