UNPKG

midi-writer-js

Version:

A library providing an API for generating MIDI files.

201 lines (173 loc) 7.19 kB
&#9836; MidiWriterJS =============== [![npm version](https://badge.fury.io/js/midi-writer-js.svg)](https://badge.fury.io/js/midi-writer-js) ![Tests](https://github.com/grimmdude/MidiWriterJS/actions/workflows/lint.js.yml/badge.svg) ![Lint](https://github.com/grimmdude/MidiWriterJS/actions/workflows/node.js.yml/badge.svg) [![Try midi-writer-js on RunKit](https://badge.runkitcdn.com/midi-writer-js.svg)](https://npm.runkit.com/midi-writer-js) 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. [Source Documentation](https://grimmdude.com/MidiWriterJS/docs/) Install ------------ ```sh npm install midi-writer-js ``` Getting Started ------------ ```javascript 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` and `NoteOffEvent` (or `NoteOnEvent` 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 with `Track.addEvent()`, the `NoteEvent` provides a more intuitive interface for doing this with a single, "pseudo" event. Under the hood, the `NoteEvent` event generates the relevant `NoteOnEvent` and `NoteOffEvent` 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 the `tick` property to define the exact tick where the event should fall. The `NoteEvent` supports these options: <table> <thead> <tr> <th>Name</th> <th>Type</th> <th>Default</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><b>pitch</b></td> <td>string or array</td> <td></td> <td>Each pitch can be a string or valid MIDI note code. Format for string is <code>C#4</code>. Pro tip: You can use the output from <a href="https://github.com/danigb/tonal" target="_blank">tonal</a> functions to build scales, chords, intervals, etc. in this parameter.</td> </tr> <tr> <td><b>duration</b></td> <td>string or array</td> <td></td> <td> How long the note should sound. <ul> <li><code>1</code> : whole</li> <li><code>2</code> : half</li> <li><code>d2</code> : dotted half</li> <li><code>dd2</code> : double dotted half</li> <li><code>4</code> : quarter</li> <li><code>4t</code> : quarter triplet</li> <li><code>d4</code> : dotted quarter</li> <li><code>dd4</code> : double dotted quarter</li> <li><code>8</code> : eighth</li> <li><code>8t</code> : eighth triplet</li> <li><code>d8</code> : dotted eighth</li> <li><code>dd8</code> : double dotted eighth</li> <li><code>16</code> : sixteenth</li> <li><code>16t</code> : sixteenth triplet</li> <li><code>32</code> : thirty-second</li> <li><code>64</code> : sixty-fourth</li> <li><code>Tn</code> : where n is an explicit number of ticks (T128 = 1 beat)</li> </ul> If an array of durations is passed then the sum of the durations will be used. </td> </tr> <tr> <td><b>wait</b></td> <td>string or array</td> <td><code>0</code></td> <td>How long to wait before sounding note (rest). Takes same values as <b>duration</b>.</td> </tr> <tr> <td><b>sequential</b></td> <td>boolean</td> <td><code>false</code></td> <td>If true then array of pitches will be played sequentially as opposed to simulatanously.</td> </tr> <tr> <td><b>velocity</b></td> <td>number</td> <td><code>50</code></td> <td>How loud the note should sound, values 1-100.</td> </tr> <tr> <td><b>repeat</b></td> <td>number</td> <td><code>1</code></td> <td>How many times this event should be repeated.</td> </tr> <tr> <td><b>channel</b></td> <td>number</td> <td><code>1</code></td> <td>MIDI channel to use.</td> </tr> <tr> <td><b>grace</b></td> <td>string or array</td> <td></td> <td>Grace note to be applied to note event. Takes same value format as <code>pitch</code></td> </tr> <tr> <td><b>tick</b></td> <td>number</td> <td></td> <td>Specific tick where this event should be played. If this parameter is supplied then <code>wait</code> is disregarded if also supplied.</td> </tr> </tbody> </table> ### `MidiWriter.Writer(tracks)` The `Writer` class provides a few ways to output the file: - `buildFile()` *Uint8Array* - `base64()` *string* - `dataUri()` *string* - `stdout()` *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'. ```javascript 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 MidiWriterJS `Track` object: ```javascript // ...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](https://codepen.io/dirkk0/pen/rNZLXjZ) by Dirk Krause [@dirkk0](https://github.com/dirkk0)