music21j-port
Version:
A toolkit for computer-aided musicology, Javascript version
1,234 lines (1,037 loc) • 35.6 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8">
<title>Music21j Source: music21/miditools.js</title>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/sunlight.default.css">
<link type="text/css" rel="stylesheet" href="styles/site.spruce.css">
</head>
<body>
<div class="container-fluid">
<div class="navbar navbar-fixed-top navbar-inverse">
<div class="navbar-inner">
<a class="brand" href="index.html">Music21j</a>
<ul class="nav">
<li class="dropdown">
<a href="namespaces.list.html" class="dropdown-toggle" data-toggle="dropdown">Namespaces<b
class="caret"></b></a>
<ul class="dropdown-menu ">
<li>
<a href="music21.html">music21</a>
</li>
<li>
<a href="music21.articulations.html">music21.articulations</a>
</li>
<li>
<a href="music21.audioSearch.html">music21.audioSearch</a>
</li>
<li>
<a href="music21.base.html">music21.base</a>
</li>
<li>
<a href="music21.beam.html">music21.beam</a>
</li>
<li>
<a href="music21.chord.html">music21.chord</a>
</li>
<li>
<a href="music21.clef.html">music21.clef</a>
</li>
<li>
<a href="music21.common.html">music21.common</a>
</li>
<li>
<a href="music21.duration.html">music21.duration</a>
</li>
<li>
<a href="music21.dynamics.html">music21.dynamics</a>
</li>
<li>
<a href="music21.expressions.html">music21.expressions</a>
</li>
<li>
<a href="music21.fromPython.html">music21.fromPython</a>
</li>
<li>
<a href="music21.instrument.html">music21.instrument</a>
</li>
<li>
<a href="music21.interval.html">music21.interval</a>
</li>
<li>
<a href="music21.key.html">music21.key</a>
</li>
<li>
<a href="music21.keyboard.html">music21.keyboard</a>
</li>
<li>
<a href="music21.meter.html">music21.meter</a>
</li>
<li>
<a href="music21.miditools.html">music21.miditools</a>
</li>
<li>
<a href="music21.note.html">music21.note</a>
</li>
<li>
<a href="music21.pitch.html">music21.pitch</a>
</li>
<li>
<a href="music21.prebase.html">music21.prebase</a>
</li>
<li>
<a href="music21.renderOptions.html">music21.renderOptions</a>
</li>
<li>
<a href="music21.roman.html">music21.roman</a>
</li>
<li>
<a href="music21.scale.html">music21.scale</a>
</li>
<li>
<a href="music21.sites.html">music21.sites</a>
</li>
<li>
<a href="music21.stream.html">music21.stream</a>
</li>
<li>
<a href="music21.tempo.html">music21.tempo</a>
</li>
<li>
<a href="music21.tie.html">music21.tie</a>
</li>
<li>
<a href="music21.tinyNotation.html">music21.tinyNotation</a>
</li>
<li>
<a href="music21.vfShow.html">music21.vfShow</a>
</li>
<li>
<a href="music21.webmidi.html">music21.webmidi</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="classes.list.html" class="dropdown-toggle" data-toggle="dropdown">Classes<b
class="caret"></b></a>
<ul class="dropdown-menu ">
<li>
<a href="music21.articulations.Accent.html">music21.articulations.Accent</a>
</li>
<li>
<a href="music21.articulations.Articulation.html">music21.articulations.Articulation</a>
</li>
<li>
<a href="music21.articulations.DynamicArticulation.html">music21.articulations.DynamicArticulation</a>
</li>
<li>
<a href="music21.articulations.LengthArticulation.html">music21.articulations.LengthArticulation</a>
</li>
<li>
<a href="music21.articulations.Marcato.html">music21.articulations.Marcato</a>
</li>
<li>
<a href="music21.articulations.PitchArticulation.html">music21.articulations.PitchArticulation</a>
</li>
<li>
<a href="music21.articulations.Spiccato.html">music21.articulations.Spiccato</a>
</li>
<li>
<a href="music21.articulations.Staccatissimo.html">music21.articulations.Staccatissimo</a>
</li>
<li>
<a href="music21.articulations.Staccato.html">music21.articulations.Staccato</a>
</li>
<li>
<a href="music21.articulations.StrongAccent.html">music21.articulations.StrongAccent</a>
</li>
<li>
<a href="music21.articulations.Tenuto.html">music21.articulations.Tenuto</a>
</li>
<li>
<a href="music21.articulations.TimbreArticulation.html">music21.articulations.TimbreArticulation</a>
</li>
<li>
<a href="music21.base.Music21Object.html">music21.base.Music21Object</a>
</li>
<li>
<a href="music21.beam.Beam.html">music21.beam.Beam</a>
</li>
<li>
<a href="music21.beam.Beams.html">music21.beam.Beams</a>
</li>
<li>
<a href="music21.chord.Chord.html">music21.chord.Chord</a>
</li>
<li>
<a href="music21.clef.AltoClef.html">music21.clef.AltoClef</a>
</li>
<li>
<a href="music21.clef.BassClef.html">music21.clef.BassClef</a>
</li>
<li>
<a href="music21.clef.Clef.html">music21.clef.Clef</a>
</li>
<li>
<a href="music21.clef.MezzoSopranoClef.html">music21.clef.MezzoSopranoClef</a>
</li>
<li>
<a href="music21.clef.PercussionClef.html">music21.clef.PercussionClef</a>
</li>
<li>
<a href="music21.clef.SopranoClef.html">music21.clef.SopranoClef</a>
</li>
<li>
<a href="music21.clef.TenorClef.html">music21.clef.TenorClef</a>
</li>
<li>
<a href="music21.clef.Treble8vaClef.html">music21.clef.Treble8vaClef</a>
</li>
<li>
<a href="music21.clef.Treble8vbClef.html">music21.clef.Treble8vbClef</a>
</li>
<li>
<a href="music21.clef.TrebleClef.html">music21.clef.TrebleClef</a>
</li>
<li>
<a href="music21.duration.Duration.html">music21.duration.Duration</a>
</li>
<li>
<a href="music21.duration.Tuplet.html">music21.duration.Tuplet</a>
</li>
<li>
<a href="music21.dynamics.Dynamic.html">music21.dynamics.Dynamic</a>
</li>
<li>
<a href="music21.expressions.Expression.html">music21.expressions.Expression</a>
</li>
<li>
<a href="music21.expressions.Fermata.html">music21.expressions.Fermata</a>
</li>
<li>
<a href="music21.fromPython.Converter.html">music21.fromPython.Converter</a>
</li>
<li>
<a href="music21.instrument.Instrument.html">music21.instrument.Instrument</a>
</li>
<li>
<a href="music21.interval.ChromaticInterval.html">music21.interval.ChromaticInterval</a>
</li>
<li>
<a href="music21.interval.DiatonicInterval.html">music21.interval.DiatonicInterval</a>
</li>
<li>
<a href="music21.interval.GenericInterval.html">music21.interval.GenericInterval</a>
</li>
<li>
<a href="music21.interval.Interval.html">music21.interval.Interval</a>
</li>
<li>
<a href="music21.key.Key.html">music21.key.Key</a>
</li>
<li>
<a href="music21.key.KeySignature.html">music21.key.KeySignature</a>
</li>
<li>
<a href="music21.keyboard.BlackKey.html">music21.keyboard.BlackKey</a>
</li>
<li>
<a href="music21.keyboard.Key.html">music21.keyboard.Key</a>
</li>
<li>
<a href="music21.keyboard.Keyboard.html">music21.keyboard.Keyboard</a>
</li>
<li>
<a href="music21.keyboard.WhiteKey.html">music21.keyboard.WhiteKey</a>
</li>
<li>
<a href="music21.meter.TimeSignature.html">music21.meter.TimeSignature</a>
</li>
<li>
<a href="music21.miditools.Event.html">music21.miditools.Event</a>
</li>
<li>
<a href="music21.miditools.MidiPlayer.html">music21.miditools.MidiPlayer</a>
</li>
<li>
<a href="music21.note.GeneralNote.html">music21.note.GeneralNote</a>
</li>
<li>
<a href="music21.note.Lyric.html">music21.note.Lyric</a>
</li>
<li>
<a href="music21.note.Note.html">music21.note.Note</a>
</li>
<li>
<a href="music21.note.NotRest.html">music21.note.NotRest</a>
</li>
<li>
<a href="music21.note.Rest.html">music21.note.Rest</a>
</li>
<li>
<a href="music21.pitch.Accidental.html">music21.pitch.Accidental</a>
</li>
<li>
<a href="music21.pitch.Pitch.html">music21.pitch.Pitch</a>
</li>
<li>
<a href="music21.prebase.ProtoM21Object.html">music21.prebase.ProtoM21Object</a>
</li>
<li>
<a href="music21.renderOptions.RenderOptions.html">music21.renderOptions.RenderOptions</a>
</li>
<li>
<a href="music21.roman.RomanNumeral.html">music21.roman.RomanNumeral</a>
</li>
<li>
<a href="music21.stream.Measure.html">music21.stream.Measure</a>
</li>
<li>
<a href="music21.stream.Part.html">music21.stream.Part</a>
</li>
<li>
<a href="music21.stream.Score.html">music21.stream.Score</a>
</li>
<li>
<a href="music21.stream.Stream.html">music21.stream.Stream</a>
</li>
<li>
<a href="music21.stream.Voice.html">music21.stream.Voice</a>
</li>
<li>
<a href="music21.tempo.Metronome.html">music21.tempo.Metronome</a>
</li>
<li>
<a href="music21.tie.Tie.html">music21.tie.Tie</a>
</li>
<li>
<a href="music21.vfShow.Renderer.html">music21.vfShow.Renderer</a>
</li>
<li>
<a href="music21.vfShow.RenderStack.html">music21.vfShow.RenderStack</a>
</li>
<li>
<a href="Notation.html">Notation</a>
</li>
<li>
<a href="Page.html">Page</a>
</li>
<li>
<a href="Recorder.html">Recorder</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="global.html" class="dropdown-toggle" data-toggle="dropdown">Global<b
class="caret"></b></a>
<ul class="dropdown-menu ">
<li>
<a href="global.html#getM21attribute">getM21attribute</a>
</li>
<li>
<a href="global.html#layout">layout</a>
</li>
<li>
<a href="global.html#midiNumDiffFromFrequency">midiNumDiffFromFrequency</a>
</li>
<li>
<a href="global.html#recorderWorkerJs">recorderWorkerJs</a>
</li>
<li>
<a href="global.html#unpickler">unpickler</a>
</li>
<li>
<a href="global.html#warnBanner">warnBanner</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="main">
<h1 class="page-title">Source: music21/miditools.js</h1>
<section>
<article>
<pre
class="sunlight-highlight-javascript linenums">/**
* music21j -- Javascript reimplementation of Core music21p features.
* music21/miditools -- A collection of tools for midi. See the namespace {@link music21.miditools}
*
* Copyright (c) 2014-17, Michael Scott Cuthbert and cuthbertLab
* Based on music21 (=music21p), Copyright (c) 2006–17, Michael Scott Cuthbert and cuthbertLab
*
* @author Michael Scott Cuthbert
*/
import * as $ from 'jquery';
import * as eventjs from 'eventjs'; // drag handler...
import * as MIDI from 'MIDI';
import { chord } from './chord.js';
import { common } from './common.js';
import { debug } from './debug.js';
import { instrument } from './instrument.js';
import { note } from './note.js';
/**
* A collection of tools for midi. See the namespace {@link music21.miditools}
*
* @exports music21/miditools
*/
/**
* Module that holds **music21** tools for connecting with MIDI.js and somewhat with the
* events from the Jazz plugin or the WebMIDI protocol.
*
* @namespace music21.miditools
* @memberof music21
*/
export const miditools = {};
/**
* Number of octaves to transpose all incoming midi signals
*
* @type {number}
* @default 0
*/
miditools.transposeOctave = 0;
/**
* @class Event
* @memberof music21.miditools
* @param {number} t - timing information
* @param {number} a - midi data 1 (N.B. a >> 4 = midiCommand )
* @param {number} b - midi data 2
* @param {number} c - midi data 3
*/
export class Event {
constructor(t, a, b, c) {
this.timing = t;
this.data1 = a;
this.data2 = b;
this.data3 = c;
this.midiCommand = a >> 4;
this.noteOff = this.midiCommand === 8;
this.noteOn = this.midiCommand === 9;
this.midiNote = undefined;
if (this.noteOn || this.noteOff) {
this.midiNote = this.data2 + 12 * miditools.transposeOctave;
this.velocity = this.data3;
}
}
/**
* Calls MIDI.noteOn or MIDI.noteOff for the note
* represented by the Event (if appropriate)
*
* @memberof music21.miditools.Event
* @returns {undefined}
*/
sendToMIDIjs() {
if (MIDI !== undefined && MIDI.noteOn !== undefined) {
// noteOn check because does not exist if no audio context
// or soundfont has been loaded, such as if a play event
// is triggered before soundfont has been loaded.
if (this.noteOn) {
MIDI.noteOn(0, this.midiNote, this.velocity, 0);
} else if (this.noteOff) {
MIDI.noteOff(0, this.midiNote, 0);
}
} else {
console.warn('could not playback note because no MIDIout defined');
}
}
/**
* Makes a {@link music21.note.Note} object from the event's midiNote number.
*
* @memberof music21.miditools.Event
* @returns {music21.note.Note} - the {@link music21.note.Note} object represented by Event.midiNote
*/
music21Note() {
const m21n = new note.Note();
m21n.pitch.ps = this.midiNote;
return m21n;
}
}
miditools.Event = Event;
/**
* How long to wait in milliseconds before deciding that a note belongs to another chord. Default 100ms
*
* @memberof music21.miditools
* @type {number}
*/
miditools.maxDelay = 100; // in ms
/**
* At what time (in ms since Epoch) the chord started.
*
* @memberof music21.miditools
* @type {number}
*/
miditools.heldChordTime = 0;
/**
* An Array (or undefined) of currently held chords that have not been sent out yet.
*
* @memberof music21.miditools
* @type {Array|undefined}
*/
miditools.heldChordNotes = undefined;
/**
* When, in MS since Jan 1, 1970, was the last {@link music21.note.Note} played.
* Defaults to the time that the module was loaded.
*
* @memberof music21.miditools
* @type {number}
*/
miditools.timeOfLastNote = Date.now(); // in ms
miditools._baseTempo = 60;
/**
* Assign (or query) a Metronome object to run all timing information.
*
* @memberof music21.miditools
* @type {music21.tempo.Metronome}
*/
miditools.metronome = undefined;
Object.defineProperties(miditools, {
tempo: {
enumerable: true,
get() {
if (this.metronome === undefined) {
return this._baseTempo;
} else {
return this.metronome.tempo;
}
},
set(t) {
if (this.metronome === undefined) {
this._baseTempo = t;
} else {
this.metronome.tempo = t;
}
},
},
});
/* --------- chords ------------- */
/**
* Clears chords that are older than miditools.heldChordTime
*
* Runs a setTimeout on itself.
* Calls miditools.sendOutChord
*
* @memberof music21.miditools
*/
miditools.clearOldChords = function clearOldChords() {
// clear out notes that may be a chord...
const nowInMs = Date.now(); // in ms
if (miditools.heldChordTime + miditools.maxDelay < nowInMs) {
miditools.heldChordTime = nowInMs;
if (miditools.heldChordNotes !== undefined) {
// console.log('to send out chords');
miditools.sendOutChord(miditools.heldChordNotes);
miditools.heldChordNotes = undefined;
}
}
setTimeout(miditools.clearOldChords, miditools.maxDelay);
};
/**
* Take a series of jEvent noteOn objects and convert them to a single Chord object
* so long as they are all sounded within miditools.maxDelay milliseconds of each other.
* this method stores notes in miditools.heldChordNotes (Array).
*
* @param {music21.miditools.Event} jEvent
* @memberof music21.miditools
* @returns undefined
*/
miditools.makeChords = function makeChords(jEvent) {
// jEvent is a miditools.Event object
if (jEvent.noteOn) {
const m21n = jEvent.music21Note();
if (miditools.heldChordNotes === undefined) {
miditools.heldChordNotes = [m21n];
} else {
for (let i = 0; i < miditools.heldChordNotes.length; i++) {
const foundNote = miditools.heldChordNotes[i];
if (foundNote.pitch.ps === m21n.pitch.ps) {
return; // no duplicates
}
}
miditools.heldChordNotes.push(m21n);
}
}
};
/**
* The last Note or Chord to be sent out from miditools. This is an important semi-global
* attribute, since the last element may need to be quantized by quantizeLastNote() to
* determine its length, since the note may need to be placed into a staff before its total
* length can be determined.
*
* @memberof music21.miditools
* @type {music21.chord.Chord|music21.note.Note|undefined}
*/
miditools.lastElement = undefined;
/**
* Take the list of Notes and makes a chord out of it, if appropriate and call
* {@link music21.miditools.callBacks.sendOutChord} callback with the Chord or Note as a parameter.
*
* @memberof music21.miditools
* @param {Array<music21.note.Note>} chordNoteList - an Array of {@link music21.note.Note} objects
* @returns {(music21.note.Note|music21.chord.Chord|undefined)} A {@link music21.chord.Chord} object,
* most likely, but maybe a {@link music21.note.Note} object)
*/
miditools.sendOutChord = function sendOutChord(chordNoteList) {
let appendObject;
if (chordNoteList.length > 1) {
// console.log(chordNoteList[0].name, chordNoteList[1].name);
appendObject = new chord.Chord(chordNoteList);
} else if (chordNoteList.length === 1) {
appendObject = chordNoteList[0]; // note object
} else {
return undefined;
}
appendObject.stemDirection = 'noStem';
miditools.quantizeLastNote();
miditools.lastElement = appendObject;
if (miditools.callBacks.sendOutChord !== undefined) {
miditools.callBacks.sendOutChord(appendObject);
}
return appendObject;
};
/* ----------- callbacks --------- */
// TODO: all callbacks (incl. raw, sendOutChord) should be able to be a function or an array of functions
/**
* callBacks is an object with three keys:
*
* - raw: function (t, a, b,c) to call when any midievent arrives. Default: `function (t, a, b, c) { return new miditools.Event(t, a, b, c); }`
* - general: function ( miditools.Event() ) to call when an Event object has been created. Default: `[miditools.sendToMIDIjs, miditools.quantizeLastNote]`
* - sendOutChord: function (array_of_note.Note_objects) to call when a sufficient time has passed to build a chord from input. Default: empty function.
*
* At present, only "general" can take an Array of event listening functions, but I hope to change that for sendOutChord also.
*
* "general" is usually the callback list to play around with.
*
* @memberof music21.miditools
*/
miditools.callBacks = {
raw: (t, a, b, c) => new miditools.Event(t, a, b, c),
general: [miditools.sendToMIDIjs, miditools.quantizeLastNote],
sendOutChord: arrayOfNotes => {},
};
/**
* Quantizes the lastElement (passed in) or music21.miditools.lastElement.
*
* @memberof music21.miditools
* @param {music21.note.GeneralNote} lastElement - A {@link music21.note.Note} to be quantized
* @returns {music21.note.GeneralNote} The same {@link music21.note.Note} object passed in with
* duration quantized
*/
miditools.quantizeLastNote = function quantizeLastNote(lastElement) {
if (lastElement === undefined) {
lastElement = this.lastElement;
if (lastElement === undefined) {
return undefined;
}
}
lastElement.stemDirection = undefined;
const nowInMS = Date.now();
const msSinceLastNote = nowInMS - this.timeOfLastNote;
this.timeOfLastNote = nowInMS;
const normalQuarterNoteLength = 1000 * 60 / this.tempo;
const numQuarterNotes = msSinceLastNote / normalQuarterNoteLength;
let roundedQuarterLength = Math.round(4 * numQuarterNotes) / 4;
if (roundedQuarterLength >= 4) {
roundedQuarterLength = 4;
} else if (roundedQuarterLength >= 3) {
roundedQuarterLength = 3;
} else if (roundedQuarterLength > 2) {
roundedQuarterLength = 2;
} else if (roundedQuarterLength === 1.25) {
roundedQuarterLength = 1;
} else if (roundedQuarterLength === 0.75) {
roundedQuarterLength = 0.5;
} else if (roundedQuarterLength === 0) {
roundedQuarterLength = 0.125;
}
lastElement.duration.quarterLength = roundedQuarterLength;
return lastElement;
};
/* ----------- callbacks --------- */
/**
* Callback to midiEvent.sendToMIDIjs.
*
* @memberof music21.miditools
* @param {music21.miditools.Event} midiEvent - event to send out.
* @returns undefined
*/
miditools.sendToMIDIjs = midiEvent => {
midiEvent.sendToMIDIjs();
};
/* ------------ MIDI.js ----------- */
/**
* a mapping of soundfont text names to true, false, or "loading".
*
* @memberof music21.miditools
* @type {Object}
*/
miditools.loadedSoundfonts = {};
/**
* Called after a soundfont has been loaded. The callback is better to be specified elsewhere
* rather than overriding this important method.
*
* @memberof music21.miditools
* @param {String} soundfont The name of the soundfont that was just loaded
* @param {function} callback A function to be called after the soundfont is loaded.
*/
miditools.postLoadCallback = function postLoadCallback(soundfont, callback) {
// this should be bound to MIDI
if (debug) {
console.log('soundfont loaded about to execute callback.');
console.log(
'first playing two notes very softly -- seems to flush the buffer.'
);
}
$('.loadingSoundfont').remove();
const isFirefox = typeof InstallTrigger !== 'undefined'; // Firefox 1.0+
const isAudioTag = MIDI.technology === 'HTML Audio Tag';
const instrumentObj = instrument.find(soundfont);
if (instrumentObj !== undefined) {
MIDI.programChange(
instrumentObj.midiChannel,
instrumentObj.midiProgram
);
if (debug) {
console.log(
soundfont + ' (' + instrumentObj.midiProgram + ') loaded on ',
instrumentObj.midiChannel
);
}
if (isFirefox === false && isAudioTag === false) {
// Firefox ignores sound volume! so don't play!
// as does IE and others using HTML audio tag.
const channel = instrumentObj.midiChannel;
MIDI.noteOn(channel, 36, 1, 0); // if no notes have been played before then
MIDI.noteOff(channel, 36, 1, 0.1); // the second note to be played is always
MIDI.noteOn(channel, 48, 1, 0.2); // very clipped (on Safari at least)
MIDI.noteOff(channel, 48, 1, 0.3); // this helps a lot.
MIDI.noteOn(channel, 60, 1, 0.3); // chrome needs three notes?
MIDI.noteOff(channel, 60, 1, 0.4);
}
}
if (callback !== undefined) {
callback(instrumentObj);
}
miditools.loadedSoundfonts[soundfont] = true;
};
/**
* method to load soundfonts while waiting for other processes that need them
* to load first.
*
* @memberof music21.miditools
* @param {String} soundfont The name of the soundfont that was just loaded
* @param {function} callback A function to be called after the soundfont is loaded.
* @example
* s = new music21.stream.Stream();
* music21.miditools.loadSoundfont(
* 'clarinet',
* function(i) {
* console.log('instrument object', i, 'loaded');
* s.instrument = i;
* });
*/
miditools.loadSoundfont = function loadSoundfont(soundfont, callback) {
if (miditools.loadedSoundfonts[soundfont] === true) {
// this soundfont has already been loaded once, so just call the callback.
if (callback !== undefined) {
const instrumentObj = instrument.find(soundfont);
callback(instrumentObj);
}
} else if (miditools.loadedSoundfonts[soundfont] === 'loading') {
// we are still waiting for this instrument to load, so
// wait for it before calling callback.
const waitThenCall = () => {
if (miditools.loadedSoundfonts[soundfont] === true) {
if (debug) {
console.log(
'other process has finished loading; calling callback'
);
}
if (callback !== undefined) {
const instrumentObj = instrument.find(soundfont);
callback(instrumentObj);
}
} else {
if (debug) {
console.log('waiting for other process load');
}
setTimeout(waitThenCall, 100);
}
};
waitThenCall();
} else {
// soundfont we have not seen before:
// set its status to loading and then load it.
miditools.loadedSoundfonts[soundfont] = 'loading';
if (debug) {
console.log('waiting for document ready');
}
$(document).ready(() => {
if (debug) {
console.log('document ready, waiting to load soundfont');
}
$(document.body).append(
$(
"<div class='loadingSoundfont'><b>Loading MIDI Instrument</b>: "
+ 'audio will begin when this message disappears.</div>'
)
);
MIDI.loadPlugin({
soundfontUrl: common.urls.soundfontUrl,
instrument: soundfont,
onsuccess: miditools.postLoadCallback.bind(
MIDI,
soundfont,
callback
),
});
});
}
};
/**
* MidiPlayer -- an embedded midi player including the ability to create a
* playback device.
*
* @class MidiPlayer
* @memberOf music21.miditools
* @property {number} speed - playback speed scaling (1=default).
* @property {JQueryDOMObject|undefined} $playDiv - div holding the player,
*/
export class MidiPlayer {
constructor() {
this.player = new MIDI.Players.PlayInstance();
this.speed = 1.0;
this.$playDiv = undefined;
}
/**
* @param where
* @returns DOMElement
*/
addPlayer(where) {
let $where = where;
if (where === undefined) {
where = document.body;
}
if (where.jquery === undefined) {
$where = $(where);
}
const $playDiv = $('<div class="midiPlayer">');
const $controls = $('<div class="positionControls">');
const $playPause = $(
'<input type="image" src="'
+ this.playPng()
+ '" align="absmiddle" value="play" class="playPause">'
);
const $stop = $(
'<input type="image" src="'
+ this.stopPng()
+ '" align="absmiddle" value="stop" class="stopButton">'
);
$playPause.on('click', this.pausePlayStop.bind(this));
$stop.on('click', this.stopButton.bind(this));
$controls.append($playPause);
$controls.append($stop);
$playDiv.append($controls);
const $time = $('<div class="timeControls">');
const $timePlayed = $('<span class="timePlayed">0:00</span>');
const $capsule = $(
'<span class="capsule"><span class="cursor"></span></span>'
);
const $timeRemaining = $('<span class="timeRemaining">-0:00</span>');
$time.append($timePlayed);
$time.append($capsule);
$time.append($timeRemaining);
$playDiv.append($time);
$where.append($playDiv);
this.$playDiv = $playDiv;
return $playDiv;
}
stopButton() {
this.pausePlayStop('yes');
}
playPng() {
return common.urls.midiPlayer + '/play.png';
}
pausePng() {
return common.urls.midiPlayer + '/pause.png';
}
stopPng() {
return common.urls.midiPlayer + '/stop.png';
}
pausePlayStop(stop) {
let d;
if (this.$playDiv === undefined) {
d = { src: 'doesnt matter' };
} else {
d = this.$playDiv.find('.playPause')[0];
}
if (stop === 'yes') {
this.player.stop();
d.src = this.playPng();
} else if (this.player.playing || stop === 'pause') {
d.src = this.playPng();
this.player.pause(true);
} else {
d.src = this.pausePng();
this.player.resume();
}
}
base64Load(b64data) {
const player = this.player;
player.timeWarp = this.speed;
const m21midiplayer = this;
miditools.loadSoundfont('acoustic_grand_piano', () => {
player.loadFile(
b64data,
() => {
// success
m21midiplayer.fileLoaded();
},
undefined, // loading
e => {
// failure
console.log(e);
}
);
});
}
songFinished() {
this.pausePlayStop('yes');
}
fileLoaded() {
this.updatePlaying();
}
startAndUpdate() {
this.player.start();
this.updatePlaying();
}
updatePlaying() {
const self = this;
const player = this.player;
if (this.$playDiv === undefined) {
return;
}
const $d = this.$playDiv;
// update the timestamp
const timePlayed = $d.find('.timePlayed')[0];
const timeRemaining = $d.find('.timeRemaining')[0];
const timeCursor = $d.find('.cursor')[0];
const $capsule = $d.find('.capsule');
//
eventjs.add($capsule[0], 'drag', (event, self) => {
eventjs.cancel(event);
const player = this.player;
player.currentTime = self.x / 420 * player.endTime;
if (player.currentTime < 0) {
player.currentTime = 0;
}
if (player.currentTime > player.endTime) {
player.currentTime = player.endTime;
}
if (self.state === 'down') {
this.pausePlayStop('pause');
} else if (self.state === 'up') {
this.pausePlayStop('play');
}
});
//
function timeFormatting(n) {
const minutes = (n / 60) >> 0;
let seconds = String((n - minutes * 60) >> 0);
if (seconds.length === 1) {
seconds = '0' + seconds;
}
return minutes + ':' + seconds;
}
player.setAnimation(data => {
const percent = data.now / data.end;
const now = data.now >> 0; // where we are now
const end = data.end >> 0; // end of song
if (now === end) {
// go to next song
self.songFinished();
}
// display the information to the user
timeCursor.style.width = percent * 100 + '%';
timePlayed.innerHTML = timeFormatting(now);
timeRemaining.innerHTML = '-' + timeFormatting(end - now);
});
}
}
miditools.MidiPlayer = MidiPlayer;
</pre>
</article>
</section>
</div>
<div class="clearfix"></div>
<footer>
<span class="copyright">
Music21j, Copyright © 2013-2018 Michael Scott Cuthbert and cuthbertLab.
</span>
<br />
<span class="jsdoc-message">
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a>
on Wed Jul 4th 2018 using the <a
href="https://github.com/terryweiss/docstrap">DocStrap template</a>.
</span>
</footer>
</div>
<br clear="both">
</div>
</div>
<!--<script src="scripts/sunlight.js"></script>-->
<script src="scripts/docstrap.lib.js"></script>
<script src="scripts/bootstrap-dropdown.js"></script>
<script src="scripts/toc.js"></script>
<script>
$( function () {
$( "[id*='$']" ).each( function () {
var $this = $( this );
$this.attr( "id", $this.attr( "id" ).replace( "$", "__" ) );
} );
$( "#toc" ).toc( {
anchorName : function ( i, heading, prefix ) {
return $( heading ).attr( "id" ) || ( prefix + i );
},
selectors : "h1,h2,h3,h4",
showAndHide : false,
scrollTo : "100px"
} );
$( "#toc>ul" ).addClass( "nav nav-pills nav-stacked" );
$( "#main span[id^='toc']" ).addClass( "toc-shim" );
$( '.dropdown-toggle' ).dropdown();
// $( ".tutorial-section pre, .readme-section pre" ).addClass( "sunlight-highlight-javascript" ).addClass( "linenums" );
$( ".tutorial-section pre, .readme-section pre" ).each( function () {
var $this = $( this );
var example = $this.find( "code" );
exampleText = example.html();
var lang = /{@lang (.*?)}/.exec( exampleText );
if ( lang && lang[1] ) {
exampleText = exampleText.replace( lang[0], "" );
example.html( exampleText );
lang = lang[1];
} else {
lang = "javascript";
}
if ( lang ) {
$this
.addClass( "sunlight-highlight-" + lang )
.addClass( "linenums" )
.html( example.html() );
}
} );
Sunlight.highlightAll( {
lineNumbers : true,
showMenu : true,
enableDoclinks : true
} );
} );
</script>
<!--Navigation and Symbol Display-->
<!--Google Analytics-->
</body>
</html>