TerryJ2 / js-sequencer

a JS library to process MIDI messages, to render audio frames via js-synthesizer

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

js-sequencer

js-sequencer is a JS library sending MIDI-based messages to js-synthesizer, to render audio frames with Web Audio. js-sequencer also parses and serializes Standard MIDI Format (.smf; also known as .mid) files. Since js-sequencer processes MIDI-based messages, js-sequencer can play those files.

Currently js-sequencer is not published as an NPM package, and is intended to use on pg-fl.jp web site. You can use js-sequencer by building manually from this repository, but incompatible update may be applied in the future.

Note that some source codes have non-English (Japanese) comments.

Build

npm install
npm run build           # for normal library
npm run build:minified  # for minified library

Usage

js-synthesizer and Web Audio feature are required to play MIDI messages. If you want only to use parsing and/or serializing SMF files, they are not required.

Initialize engine

var engine = new JSSeq.Core.Engine();

Load/Export SMF files

load from existing binary

var bin;  // loaded SMF binary data
engine.loadSMFData(bin, 0, function (err) {
    if (err) {
        // 'err' is an object (mostly Error object)
        alert('Error: ' + err);
    } else {
        // load done
        console.log('Part count:', engine.parts.length);
        console.log('Duration [sec]:', engine.calculateDuration());
    }
});
// --- or ---
engine.loadSMFDataPromise(bin, 0).then(function () {
    // load done
    console.log('Part count:', engine.parts.length);
    console.log('Duration [sec]:', engine.calculateDuration());
}, function (err) {
    // 'err' is an object (mostly Error object)
    alert('Error: ' + err);
});

load from file element

var elem = document.getElementById('MyFileElement');
elem.addEventListener('change', function () {
    engine.loadFromFile(elem, function (err) {
        if (err) {
            // 'err' is an object (mostly Error object)
            alert('Error: ' + err);
        } else {
            // load done
            console.log('Part count:', engine.parts.length);
            console.log('Duration [sec]:', engine.calculateDuration());
        }
    });
    // --- or ---
    engine.loadFromFilePromise(elem).then(function () {
        // load done
        console.log('Part count:', engine.parts.length);
        console.log('Duration [sec]:', engine.calculateDuration());
    }, function (err) {
        // 'err' is an object (mostly Error object)
        alert('Error: ' + err);
    });
});

generate SMF data

var bin = engine.exportSMFToArrayBuffer(); // 'bin' will be ArrayBuffer
var blobUrl = engine.makeSMFBlobURL();     // 'blob:' URL

Check if the playing feature is available

if (!JSSeq.Core.Player.isSupported()) {
    throw new Error('Not supported on this browser.');	
}

Initialize player (to play MIDI messages)

// the worker file of js-sequencer (dist/js-sequencer.worker.*.js)
const JSFile_SeqWorker = 'js-sequencer.worker.min.js';
// the dependencies of js-sequencer (js-synthesizer and its dependencies)
// these paths must be relative to JSFile_SeqWorker
const JSFile_SeqWorker = [
	'libfluidsynth-2.0.2.js',
	'js-synthesizer.min.js'
];
// the worklet files of js-sequencer (used if supported)
// these paths must be relative to the document
const JSFile_SeqWorklet = [
	'js-sequencer.worklet.min.js'
];

let player;

JSSeq.Core.Player.instantiate(
    // JSSeq.Core.Engine instance
    engine,
    // worker script path
    JSFile_SeqWorker,
    // dependencies of worker
    JSFile_Deps,
    // if you create multiple player instance and
    // want to share worker between instances, set true
    // (default: false)
    false,
    // timer interval of worker processing
    // (default: 30)
    30,
    // frame count per one render process
    // (default: 8192)
    8192,
    // sample rate (default: 48000)
    48000
).then((p) => {
    player = p;
    // if you want not to use AudioWorklet even if supported,
    // call with null parameter
    player.setAudioWorkletScripts(JSFile_SeqWorklet);
})

Play notes manually

const notes = [
    // parameters: posNum, posDen, lenNum, lenDen, value, channel
    //  - posNum: numerator of position (used by sequencer process and SMF data)
    //  - posDen: denominator of position (used by sequencer process and SMF data)
    //    - e.g. (posNum,posDen)=(1,4) represents the position after one quarter note from beginning
    //  - lenNum: numerator of length (used by sequencer process and SMF data)
    //  - lenDen: denominator of length (used by sequencer process and SMF data)
    //    - e.g. (lenNum,lenDen)=(1,4) represents the length of one quarter note
    //  - value: MIDI note value
    //  - channel: MIDI channel (zero-based index)
    new JSSeq.Core.NoteObject(0, 1, 0, 1, 60, 0),
    new JSSeq.Core.NoteObject(0, 1, 0, 1, 64, 0),
];
player.playNoteMultiple(notes);
window.setTimeout(() => {
    // playNoteMultiple doesn't stop notes automatically even if
    // 'noteLength's are specified.
    player.stopNoteMultiple(
        notes,
        true // set true to release internal player data after a few seconds
    );
}, 5000);

Play sequential notes

// (usage of 'loadFromFilePromise' is described above)
engine.loadFromFilePromise(elem).then(function () {
    player.playSequence();
});

License

BSD 3-Clause License

About

a JS library to process MIDI messages, to render audio frames via js-synthesizer

License:BSD 3-Clause "New" or "Revised" License


Languages

Language:TypeScript 98.2%Language:JavaScript 1.8%