Skip to content

jazz-soft/JZZ-midi-SMF

Repository files navigation

JZZ-midi-SMF

Standard MIDI Files: read / write / play
(MIDI 1.0 and MIDI 2.0)

Node.js Firefox Chrome Opera Safari Internet Explorer Edge
npm npm build Coverage Status Try jzz-midi-smf on RunKit

Install

npm install jzz-midi-smf --save
or yarn add jzz-midi-smf
or get the full development version and minified scripts from GitHub

Usage

Plain HTML
<script src="JZZ.js"></script>
<script src="JZZ.midi.SMF.js"></script>
//...
CDN (jsdelivr)
<script src="https://cdn.jsdelivr.net/npm/jzz"></script>
<script src="https://cdn.jsdelivr.net/npm/jzz-midi-smf"></script>
//...
CDN (unpkg)
<script src="https://unpkg.com/jzz"></script>
<script src="https://unpkg.com/jzz-midi-smf"></script>
//...
CommonJS
var JZZ = require('jzz');
require('jzz-midi-smf')(JZZ);
//...
TypeScript / ES6
import { JZZ } from 'jzz';
import { SMF } from 'jzz-midi-smf';
SMF(JZZ);
//...
AMD
require(['JZZ', 'JZZ.midi.SMF'], function(JZZ, dummy) {
  // ...
});

MIDI files

Supported file formats: .mid, .kar, .rmi

Please check the API Reference !

Playing MIDI file
var midiout = JZZ().openMidiOut();
var data = require('fs').readFileSync('file.mid', 'binary');
// data can be String, Buffer, ArrayBuffer, Uint8Array or Int8Array
var smf = new JZZ.MIDI.SMF(data);
var player = smf.player();
player.connect(midiout);
player.play();
//...
player.speed(0.5); // play twice slower
Viewing the contents of MIDI file
console.log(smf.toString());
Validating MIDI file
var warn = smf.validate();
if (warn) console.log(warn);
Creating MIDI file from scratch
var smf = new JZZ.MIDI.SMF(0, 96); // type 0, 96 ticks per quarter note
var trk = new JZZ.MIDI.SMF.MTrk();
smf.push(trk);
// add contents:
trk.add(0, JZZ.MIDI.smfSeqName('This is a sequence name'))
   .add(0, JZZ.MIDI.smfBPM(90)) // tempo 90 bpm
   .add(96, JZZ.MIDI.noteOn(0, 'C6', 127))
   .add(96, JZZ.MIDI.noteOn(0, 'Eb6', 127))
   .add(96, JZZ.MIDI.noteOn(0, 'G6', 127))
   .add(192, JZZ.MIDI.noteOff(0, 'C6'))
   .add(192, JZZ.MIDI.noteOff(0, 'Eb6'))
   .add(192, JZZ.MIDI.noteOff(0, 'G6'))
   .add(288, JZZ.MIDI.smfEndOfTrack());
// or an alternative way:
trk.smfSeqName('This is a sequence name').smfBPM(90).tick(96)
   .noteOn(0, 'C6', 127).noteOn(0, 'Eb6', 127).noteOn(0, 'G6', 127)
   .tick(96).noteOff(0, 'C6').noteOff(0, 'Eb6').noteOff(0, 'G6')
   .tick(96).smfEndOfTrack();
// or even shorter:
trk.smfSeqName('This is a sequence name').smfBPM(90).tick(96)
   .ch(0).note('C6', 127, 96).note('Eb6', 127, 96).note('G6', 127, 96)
   .tick(192).smfEndOfTrack();
Exporting MIDI file data as JSON or any custom format

One easy thing to remember: SMF is an Array of Tracks and Track is an Array of MIDI events:

for (var i = 0; i < smf.length; i++) {
  for (var j = 0; j < smf[i].length; j++) {
    console.log('track:', i, 'tick:', smf[i][j].tt, smf[i][j].toString());
    // or do whatever else with the message
  }
}
Transposing MIDI file
for (var i = 0; i < smf.length; i++) {
  if (smf[i] instanceof JZZ.MIDI.SMF.MTrk) {
    for (var j = 0; j < smf[i].length; j++) {
      var note = smf[i][j].getNote();
      if (typeof note != 'undefined') {
        if (smf[i][j].getChannel() != 9) { // skip the percussion channel
          smf[i][j].setNote(note + 12);    // transpose one octave up
        }
      }
    }
  }
}
Getting the info
var player = smf.player();
var dump = smf.dump();
console.log('Type:', player.type());
console.log('Number of tracks:', player.tracks());
console.log('Size:', dump.length, 'bytes');
console.log('Duration:', player.duration(), 'ticks');
console.log('Total time:', player.durationMS(), 'milliseconds');
Saving MIDI file
require('fs').writeFileSync('out.mid', smf.dump(), 'binary');

SYX files

All calls are almost identical to those for the SMF files:
var data = require('fs').readFileSync('file.syx', 'binary');
var syx = new JZZ.MIDI.SYX(data);
syx.send([0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7]);
syx.sxMasterVolumeF(0.5);
var player = syx.player();
player.connect(midiout);
player.play();
require('fs').writeFileSync('out.syx', syx.dump(), 'binary');

MIDI 2.0 Clips

Playing MIDI 2.0 clip
var midiout = JZZ().openMidiOut();
var data = require('fs').readFileSync('file.midi2', 'binary');
var clip = new JZZ.MIDI.Clip(data);
var player = clip.player();
// since the majority of today's MIDI devices only support MIDI 1.0,
// we can use a converter:
var conv = JZZ.M2M1();
conv.connect(midiout);
player.connect(conv);
player.play();
Creating MIDI 2.0 clip from scratch
var clip = new JZZ.MIDI.Clip();
clip.gr(0).ch(0).noteOn('C5').tick(96).noteOff('C5');
require('fs').writeFileSync('out.midi2', clip.dump(), 'binary');
Most of other calls - same as above
var player = clip.player();
var dump = clip.dump();
console.log(clip.toString());
console.log('Size:', dump.length, 'bytes');
console.log('Duration:', player.duration(), 'ticks');
console.log('Total time:', player.durationMS(), 'milliseconds');

Live DEMOs (source code included)

Read MIDI file - from file, URL, Base64
Write MIDI file - create MIDI file from scratch
MIDI Player - various ways to play MIDI file
Karaoke - playing MIDI files in .kar format

By popular demand

Boilerplate code for bulk MIDI file editing
const fs = require('fs');
const JZZ = require('jzz');
require('jzz-midi-smf')(JZZ);

if (process.argv.length != 4) {
  console.log('Usage: node ' + process.argv[1] + ' <input.mid> <output.mid>');
  process.exit(1);
}

var old_midi = new JZZ.MIDI.SMF(fs.readFileSync(process.argv[2], 'binary'));
var new_midi = new JZZ.MIDI.SMF(old_midi); // copy all settings from the old file
new_midi.length = 0; // remove all tracks

for (var i = 0; i < old_midi.length; i++) {
  var old_track = old_midi[i];
  if (!(old_track instanceof JZZ.MIDI.SMF.MTrk)) continue;
  var new_track = new JZZ.MIDI.SMF.MTrk();
  new_midi.push(new_track);
  for (var j = 0; j < old_track.length; j++) {
    var old_msg = old_track[j];
    var tick = old_msg.tt; // change it if you like, e.g. tick = old_msg.tt / 2;
    if (true) { // add your own condition
      new_track.add(tick, old_msg);
    }
    else if (false) { // add new messages or don't add anything
      var new_msg = JZZ.MIDI.whatever(READ_THE_REFERENCE);
      new_track.add(tick, new_msg);
    }
  }
}
fs.writeFileSync(process.argv[3], new_midi.dump(), 'binary');

See also

Test MIDI Files - these may be useful if you write a MIDI application...

More information

Please visit https://jazz-soft.net for more information.

Thanks for your support!

Stargazers for @jazz-soft/JZZ-midi-SMF