- ×
♬ JavaScript library providing an API for programmatically generating expressive multi-track MIDI files in browser and Node.
Filed under audioShow All♬ MidiWriterJS
MidiWriterJS is a JavaScript library providing an API for generating expressive multi-track MIDI files.
Note that the
master
branch is in active development so if you're looking for a tried and true stable version please use the latest release.Install
npm install midi-writer-js
Getting Started
import MidiWriter from 'midi-writer-js'; // Start with a new track const track = new MidiWriter.Track(); // Define an instrument (optional): track.addEvent(new MidiWriter.ProgramChangeEvent({instrument: 1})); // Add some notes: const note = new MidiWriter.NoteEvent({pitch: ['C4', 'D4', 'E4'], duration: '4'}); track.addEvent(note); // Generate a data URI const write = new MidiWriter.Writer(track); console.log(write.dataUri());
Documentation
MidiWriter.Track()
addEvent({event}, mapFunction)
setTempo(tempo)
addText(text)
addCopyright(text)
addTrackName(text)
addInstrumentName(text)
addMarker(text)
addCuePoint(text)
addLyric(text)
setTimeSignature(numerator, denominator)
MidiWriter.NoteEvent({options})
The MIDI spec defines that each note must have a
NoteOnEvent
andNoteOffEvent
(orNoteOnEvent
with zero velocity) event, marking the beginning and end of the sounding note. While it's possible to manually add these events to a track withTrack.addEvent()
, theNoteEvent
provides a more intuitive interface for doing this with a single, "pseudo" event. Under the hood, theNoteEvent
event generates the relevantNoteOnEvent
andNoteOffEvent
events.Each MIDI event has a
delta
property, which is used to define the number of ticks to wait after the previous event. This can be challenging to calculate if you're not necessarily adding events in a serial fashion. Because of this, you can alternatively use thetick
property to define the exact tick where the event should fall.The
NoteEvent
supports these options:Name Type Default Description pitch string or array Each pitch can be a string or valid MIDI note code. Format for string is C#4
. Pro tip: You can use the output from tonal functions to build scales, chords, intervals, etc. in this parameter.duration string or array How long the note should sound. 1
: whole2
: halfd2
: dotted halfdd2
: double dotted half4
: quarter4t
: quarter tripletd4
: dotted quarterdd4
: double dotted quarter8
: eighth8t
: eighth tripletd8
: dotted eighthdd8
: double dotted eighth16
: sixteenth16t
: sixteenth triplet32
: thirty-second64
: sixty-fourthTn
: where n is an explicit number of ticks (T128 = 1 beat)
wait string or array 0
How long to wait before sounding note (rest). Takes same values as duration. sequential boolean false
If true then array of pitches will be played sequentially as opposed to simulatanously. velocity number 50
How loud the note should sound, values 1-100. repeat number 1
How many times this event should be repeated. channel number 1
MIDI channel to use. grace string or array Grace note to be applied to note event. Takes same value format as pitch
tick number Specific tick where this event should be played. If this parameter is supplied then wait
is disregarded if also supplied.MidiWriter.Writer(tracks)
The
Writer
class provides a few ways to output the file:buildFile()
Uint8Arraybase64()
stringdataUri()
stringstdout()
file stream (cli)
Hot Cross Buns
Here's an example of how everyone's favorite song "Hot Cross Buns" could be written. Note use of the mapping function passed as the second argument of
addEvent()
. This can be used to apply specific properties to all events. With some street smarts you could also use it for programmatic crescendos and other property 'animation'.import MidiWriter from 'midi-writer-js'; const track = new MidiWriter.Track(); track.addEvent([ new MidiWriter.NoteEvent({pitch: ['E4','D4'], duration: '4'}), new MidiWriter.NoteEvent({pitch: ['C4'], duration: '2'}), new MidiWriter.NoteEvent({pitch: ['E4','D4'], duration: '4'}), new MidiWriter.NoteEvent({pitch: ['C4'], duration: '2'}), new MidiWriter.NoteEvent({pitch: ['C4', 'C4', 'C4', 'C4', 'D4', 'D4', 'D4', 'D4'], duration: '8'}), new MidiWriter.NoteEvent({pitch: ['E4','D4'], duration: '4'}), new MidiWriter.NoteEvent({pitch: ['C4'], duration: '2'}) ], function(event, index) { return {sequential: true}; } ); const write = new MidiWriter.Writer(track); console.log(write.dataUri());
VexFlow Integration
MidiWriterJS can export MIDI from VexFlow voices, though this feature is still experimental. Current usage is to use
MidiWriter.VexFlow.trackFromVoice(voice)
to create a MidiWriterJSTrack
object:// ...VexFlow code defining notes const voice = create_4_4_voice().addTickables(notes); const vexWriter = new MidiWriter.VexFlow(); const track = vexWriter.trackFromVoice(voice); const writer = new MidiWriter.Writer([track]); console.log(writer.dataUri());
Demos
- Example with Magenta player by Dirk Krause @dirkk0