MidiWriterJS

repository·master·Indexed 20 days ago

https://github.com/grimmdude/midiwriterjs

A JavaScript library for generating expressive, multi-track MIDI files in Node.js and the browser. It provides an API to create tracks, add NoteEvents (supporting chords and arpeggios), and manage MIDI controller events, pitch bends, and meta events like copyright and instrument names. The library includes a Writer class to output MIDI data as Uint8Array, Base64, or Data URIs.

Tokens
6.1K
Snippets
25
Records
32
Agent score
70%

What's inside midi-writer-js

  1. How chords and sequential notes work

    master

    When the pitch property of a NoteEvent is an array, the notes play as a chord (simultaneously) by default. To play them one after another (an arpeggio), set sequential: true in the options object.

    // Chord (notes play simultaneously)
    track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'E4', 'G4'], duration: '1'}));
    
    // Arpeggio (notes play sequentially)
    track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'E4', 'G4'], duration: '8', sequential: true}));
  2. Quick Start with MidiWriterJS

    master

    To generate a MIDI file, create a Track, add events (like NoteEvent or ProgramChangeEvent), and then use a Writer to output the data.

    import MidiWriter from 'midi-writer-js';
    
    const track = new MidiWriter.Track();
    track.addEvent(new MidiWriter.ProgramChangeEvent({instrument: 1}));
    track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'D4', 'E4'], duration: '4', sequential: true}));
    
    const writer = new MidiWriter.Writer(track);
    console.log(writer.dataUri());

    CommonJS

    const MidiWriter = require('midi-writer-js');
    
    const track = new MidiWriter.Track();
    track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'E4', 'G4'], duration: '2'}));
    
    const writer = new MidiWriter.Writer(track);
    console.log(writer.dataUri());

    TypeScript

    import MidiWriter from 'midi-writer-js';
    
    const track = new MidiWriter.Track();
    track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'E4', 'G4'], duration: '2'}));
    
    const writer = new MidiWriter.Writer(track);
    const data: Uint8Array = writer.buildFile();
    import MidiWriter from 'midi-writer-js';
    
    const track = new MidiWriter.Track();
    track.addEvent(new MidiWriter.ProgramChangeEvent({instrument: 1}));
    track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'D4', 'E4'], duration: '4', sequential: true}));
    
    const writer = new MidiWriter.Writer(track);
    console.log(writer.dataUri());
  3. Distinguish between Chords and Sequential Notes in NoteEvent

    master

    The NoteEvent class handles two distinct musical behaviors based on the sequential property:

    1. Chords (Default): When sequential is false (or omitted), providing an array of pitches in the pitch field results in a chord. The class generates a single set of NoteOn events for all pitches, followed by a single set of NoteOff events.
    2. Sequential Notes: When sequential is true, the class iterates through the pitch array and creates a complete NoteOn and NoteOff pair for each pitch in the array, playing them one after another.

    This distinction is controlled by the sequential boolean flag in the constructor.

    // Chord: C, E, and G play at the same time
    new NoteEvent({ pitch: ['C4', 'E4', 'G4'], sequential: false });
    
    // Sequential: C plays, then E plays, then G plays
    new NoteEvent({ pitch: ['C4', 'E4', 'G4'], sequential: true });
  4. Recipe: Saving a MIDI file in Node.js

    master

    To save the generated MIDI data to a physical file in a Node.js environment, use writer.buildFile() to get a Uint8Array and write it using the fs module.

    import fs from 'fs';
    import MidiWriter from 'midi-writer-js';
    
    const track = new MidiWriter.Track();
    track.addEvent(new MidiWriter.NoteEvent({pitch: ['C4', 'E4', 'G4'], duration: '1'}));
    
    const writer = new MidiWriter.Writer(track);
    fs.writeFileSync('output.mid', writer.buildFile());
  5. Recipe: Adding rests

    master

    MidiWriterJS does not have a dedicated rest event. Instead, use the wait property in a NoteEvent to add silence before a note. The wait property accepts the same duration values as duration.

    // Quarter rest followed by a quarter note
    track.addEvent(new MidiWriter.NoteEvent({pitch: 'C4', duration: '4', wait: '4'}));
    
    // Half rest followed by a whole note
    track.addEvent(new MidiWriter.NoteEvent({pitch: 'E4', duration: '1', wait: '2'}));
  6. Recipe: Programming drums (Channel 10)

    master

    MIDI channel 10 is reserved for percussion. Pitch values map to specific drum sounds (e.g., C2 is a kick, D2 is a snare, F#2 is a hi-hat).

    const drums = new MidiWriter.Track();
    drums.addTrackName('Drums');
    drums.addEvent(new MidiWriter.NoteEvent({pitch: ['C2'], duration: '4', channel: 10, velocity: 80}));
    drums.addEvent(new MidiWriter.NoteEvent({pitch: ['D2'], duration: '4', channel: 10, velocity: 80}));
    drums.addEvent(new MidiWriter.NoteEvent({pitch: ['F#2'], duration: '8', channel: 10, repeat: 4}));
  7. Use Controller Changes and Pitch Bend

    master

    You can add MIDI controller changes (CC) and pitch bend events to a track to control dynamics and pitch modulation.

    • Controller Changes: Use ControllerChangeEvent with controllerNumber and controllerValue.
    • Pitch Bend: Use PitchBendEvent with a bend value ranging from -1.0 to 1.0 (where 0 is no bend).
    import MidiWriter from 'midi-writer-js';
    
    const track = new MidiWriter.Track();
    
    // Set volume via CC #7
    track.addEvent(new MidiWriter.ControllerChangeEvent({controllerNumber: 7, controllerValue: 100}));
    
    // Pitch bend ranging from -1.0 to 1.0 (0 = no bend)
    track.addEvent(new MidiWriter.PitchBendEvent({bend: 0.5}));
    
    track.addEvent(new MidiWriter.NoteEvent({pitch: ['E4'], duration: '2'}));
    
    const writer = new MidiWriter.Writer(track);
    console.log(writer.dataUri());
  8. Create multi-track MIDI files

    master

    To create a MIDI file with multiple tracks, pass an array of Track instances to the Writer constructor instead of a single track.

    import MidiWriter from 'midi-writer-js';
    
    const melody = new MidiWriter.Track();
    melody.addTrackName('Melody');
    melody.addEvent(new MidiWriter.ProgramChangeEvent({instrument: 1}));
    melody.addEvent(new MidiWriter.NoteEvent({pitch: ['E5', 'D5', 'C5'], duration: '4', sequential: true}));
    
    const bass = new MidiWriter.Track();
    bass.addTrackName('Bass');
    bass.addEvent(new MidiWriter.ProgramChangeEvent({instrument: 33}));
    bass.addEvent(new MidiWriter.NoteEvent({pitch: ['C2'], duration: '1'}));
    
    const writer = new MidiWriter.Writer([melody, bass]);
    console.log(writer.dataUri());
  9. Reference: Duration values

    master

    Durations can be specified using the following values:

    ValueDuration
    1Whole
    2Half
    d2Dotted half
    dd2Double dotted half
    4Quarter
    4tQuarter triplet
    d4Dotted quarter
    dd4Double dotted quarter
    8Eighth
    8tEighth triplet
    d8Dotted eighth
    dd8Double dotted eighth
    16Sixteenth
    16tSixteenth triplet
    32Thirty-second
    64Sixty-fourth
    TnExplicit number of ticks (e.g., T128 = 1 beat)
  10. Reference: Track methods

    master

    The Track class is used to manage a single MIDI track and its events. Key methods include:

    MethodDescription
    addEvent(event, mapFunction?)Add one or more events. Supports method chaining.
    setTempo(bpm, tick?)Set tempo in beats per minute.
    setTimeSignature(numerator, denominator)Set time signature.
    setKeySignature(sf, mi?)Set key signature (e.g., 'C', 'Dm', 'F#').
    setPitchBend(bend)Set pitch bend (-1.0 to 1.0).
    controllerChange(number, value, channel?, delta?)Add a controller change event.
    addTrackName(text)Set the track name.
    addText(text)Add a text event.
    addCopyright(text)Add a copyright notice.
    addInstrumentName(text)Set the instrument name.
    addMarker(text)Add a marker event.
    addCuePoint(text)Add a cue point event.
    addLyric(text)Add a lyric event.
    mergeTrack(track)Merge another track's events into this track.
    removeEventsByName(name)Remove all events of a given type.
    polyModeOn()Enable poly mode.
  11. Reference: NoteEvent options

    master

    The NoteEvent class generates NoteOn/NoteOff pairs. Use the following options to configure notes:

    NameTypeDefaultDescription
    pitchstring or arrayEach pitch can be a string (e.g., C#4) or a valid MIDI note code.
    durationstring or array'4'How long the note should sound.
    waitstring or array0Rest before sounding note. Takes same values as duration.
    sequentialbooleanfalseIf true, array of pitches plays sequentially instead of as a chord.
    velocitynumber50How loud the note should sound (1-100).
    repeatnumber1How many times this event should repeat.
    channelnumber1MIDI channel to use (1-16).
    gracestring or arrayGrace note(s) applied before the main note.
    startTicknumberExplicit tick position for this event. If supplied, wait is ignored.
    ticknumberAlias for startTick.