Music
trigscript/music plays music written as patterns: a note's name per step, held with -,
rests with .. Everything is made when the script is built. Each note an instrument plays
becomes a sound once, and a song becomes a table of which sound starts on which frame.
import { music } from "trigscript/music";
const lead = music.presets.chip();
const bass = music.presets.bass();
const drums = music.drums();
const song = music.song({ bpm: 120 },
music.track(lead, "C5 - E5 - G5 - - . | A5 - G5 - E5 - - ."),
music.track(bass, "C3 - - - - - - - | F3 - - - - - - -"),
music.track(drums, "kh h sh h kh h sh h | kh h sh h kh kh sh h"),
);
program(async () => {
start(music.loop(song)); // the song again and again, beside the program
playWav(lead.note("C6", 0.1), 0); // an instrument's note on its own, as a sound effect
});
Patterns. A step is a word, a quarter of a beat unless the song says otherwise. A
note's name starts a note (C4, F#5, Bb2); notes joined by + sound together
(C4+E4+G4); - holds what sounds a step longer; . is a rest; | is only for the eye.
@ after a step's notes says how loud (C4@0.5). A drum kit's step is its sounds'
letters: kh is the kick and the hat together.
Instruments. music.synth makes notes from waves, music.fmSynth by FM, and
music.strings plucks them; music.sampler plays a recording of one note at every pitch,
from a sound file the script imports. music.presets has instruments to start from:
supersaw, pad, bass, acid, lead, chip, keys, bell and pluck.
music.drums() is a kit made from nothing, a letter a drum: k kick, s snare, c clap, h
closed hat, o open hat, t tom, r rim and x crash. music.kit makes one from clips of your
own.
Longer songs are written in sections. Parts are named once, and each section says what they play in it; a pattern shorter than its section plays again until the section is full. Chords are written by name, with a template that says how their notes are played:
import { music } from "trigscript/music";
const parts = {
keys: music.part(music.presets.keys()),
bass: music.part(music.presets.bass()),
drums: music.part(music.drums()),
};
const verse = music.section(4, {
keys: music.chords("Am F C G", "1+2+3 - . 1+2+3 . . 1+2+3 -", { octave: 4 }),
bass: music.chords("Am F C G", "1 - . 1 . 1 3 -", { octave: 2 }),
drums: music.beat({ k: [0, 3, 4], s: [2, 6], h: [0, 1, 2, 3, 4, 5, 6, 7] }, 8),
}, { name: "verse" });
const song = music.arrange({ bpm: 120, steps: 2 }, parts, verse, verse.with({ keys: null }));
program(async () => {
await music.play(song);
print("The song is over.");
});
Music that follows the game. music.stems cuts a song of sections into a sound for
each bar of each layer, and music.conduct plays them as a Band says: which section,
which layers, and a stinger on the next beat.
import { music, Band } from "trigscript/music";
const parts = { keys: music.part(music.presets.keys()), drums: music.part(music.drums()) };
const calm = music.section(2, { keys: music.chords("Am F", "1+2+3 - - -", { octave: 4 }) }, { name: "calm" });
const fight = music.section(2, {
keys: music.chords("Am F", "1+2+3 . 1+2+3 .", { octave: 4 }),
drums: music.beat({ k: [0, 4], s: [2, 6], h: [0, 1, 2, 3, 4, 5, 6, 7] }, 8),
}, { name: "fight" });
const cut = music.stems(music.arrange({ bpm: 120, steps: 2 }, parts, calm, fight), {
layers: { drums: ["drums"], keys: ["keys"] },
});
program(async () => {
const band = new Band();
band.loop = true; // a section plays again until told otherwise
start(music.conduct(cut, band));
await until(bring(P8, units.AnyUnit, locations.Base, ">=", 1));
band.go(cut.section("fight")); // from the next bar
});
What the game does with it. A bar lasts a whole number of frames (42 ms each at
Fastest), so the tempo is the nearest that fits, and song.bpm says which. A sound starts
within 21 ms of its frame. The game plays 32 sounds at once, and a song that would need
more is refused. Notes that overlap add up with no limiter, so keep volumes down.
music.render(song) mixes a song into one sound when the script is built instead: every
note exactly in time and only one sound playing, but nothing can change while it plays. A
program plays to its own player; a program of every player plays to each.
| Name | What it is |
|---|---|
synth(options), fmSynth(options), strings(options), sampler(clip, root?) |
Instruments. instrument.note(name, seconds) is one note as a sound. |
presets.supersaw() … presets.pluck() |
Instruments to start from, each taking the options of what it is made with. |
drums(options?), kit(hits) |
Drum kits: one made from nothing, or a clip a letter. |
track(instrument, pattern, options?), song(options, ...tracks) |
A pattern for an instrument, and tracks played together. Options: pan, volume, fx, reverb; bpm, steps, beats, swing. |
part(instrument, options?), section(bars, parts, options?), arrange(options, parts, ...sections) |
A song in sections. section.with({ … }) is a section with some parts changed, null resting one. |
chord(symbol, octave?), chords(progression, template, options?), over(notes, template) |
Chords by name, and a template that plays them. |
beat(hits, steps?) |
A drum bar from the steps each letter plays on. |
transpose(p, semitones), stretch(p, by), repeat(p, n) |
Patterns changed. |
midi(notes, parts, options?) |
A song from a MIDI file the script imports. |
riser(seconds), impact(), pump(beat) |
A rise into a drop, a boom to land on, and a pumping effect. |
render(song, options?) |
The song mixed into one sound when the script is built. |
play(song), loop(song) |
Play a song note by note, once or again and again. |
stems(song, options), new Band(), conduct(stems, band) |
Music that follows the game: band.go, queue, mute, unmute, sting, stop. |