midijs
Version:
Read and write Standard MIDI files and enable communication with MIDI devices!
328 lines (309 loc) • 10.9 kB
JavaScript
/**
* @private
* @module midijs/lib/file/event
*/
'use strict';
var util = require('util');
var buffer = require('buffer');
var error = require('../error');
/**
* Event
*
* Represent an abstract MIDI event
*
* @abstract
* @constructor
* @param {Object} [specs={}] - Event details
* @param {Number} [defaults={}] - Event delay in ticks
* @param {Number} [delay=0] - Event delay in ticks
* @property {Number} delay - Event delay in ticks
*/
function Event(specs, defaults, delay) {
var name;
this.delay = delay || 0;
specs = specs || {};
for (name in defaults) {
if (defaults.hasOwnProperty(name)) {
if (specs.hasOwnProperty(name)) {
this[name] = specs[name];
} else {
this[name] = defaults[name];
}
}
}
}
exports.Event = Event;
/**
* MetaEvent
*
* Represent a meta event. This type of event only occurs in Standard
* MIDI files and are not transmitted to MIDI devices. They are only
* meant to contain metadata about the file
*
* @constructor
* @extends Event
* @param {module:midijs/lib/file/event~MetaEvent.TYPE} type - Meta type
* @property {module:midijs/lib/file/event~MetaEvent.TYPE} type - Meta type
* @param {Object} [specs={}] - Meta details
* @param {Number} [specs.number=0] - Sequence number (SEQUENCE_NUMBER)
* @param {String} [specs.text=''] - Text (TEXT)
* @param {String} [specs.text=''] - Copyright notice (COPYRIGHT_NOTICE)
* @param {String} [specs.text=''] - Sequence name (SEQUENCE_NAME)
* @param {String} [specs.text=''] - Instrument name (INSTRUMENT_NAME)
* @param {String} [specs.text=''] - Lyrics (LYRICS)
* @param {String} [specs.text=''] - Marker (MARKER)
* @param {String} [specs.text=''] - Cue point (CUE_POINT)
* @param {String} [specs.text=''] - Program name (PROGRAM_NAME)
* @param {String} [specs.text=''] - Device name (DEVICE_NAME)
* @param {Number} [specs.channelPrefix=0] - Channel prefix (CHANNEL_PREFIX)
* @param {Number} [specs.tempo=120]
* Tempo in beats per minute (SET_TEMPO)
* @param {Number} [specs.rate=24]
* Frame rate (24, 25, 30 fps) (SMPTE_OFFSET)
* @param {Number} [specs.hours=0]
* Hour offset (0 - 23) (SMPTE_OFFSET)
* @param {Number} [specs.minutes=0]
* Minute offset (0 - 59) (SMPTE_OFFSET)
* @param {Number} [specs.seconds=0]
* Second offset (0 - 59) (SMPTE_OFFSET)
* @param {Number} [specs.frames=0]
* Frame offset (0 - rate) (SMPTE_OFFSET)
* @param {Number} [specs.subframes=0]
* Subframe offset (0 - 99) (SMPTE_OFFSET)
* @param {Number} [specs.numerator=4] - Numerator (TIME_SIGNATURE)
* @param {Number} [specs.denominator=4] - Denominator (TIME_SIGNATURE)
* @param {Number} [specs.metronome=24]
* Metronome frequency in number of clock signals per click (TIME_SIGNATURE)
* @param {Number} [specs.clockSignalsPerBeat=24]
* Clock signals in one beat (TIME_SIGNATURE)
* @param {Boolean} [specs.major=true]
* Whether the key signature is major or minor (KEY_SIGNATURE)
* @param {Number} [specs.note=0]
* Number of sharps (if positive), or number of flats (if negative)
* (KEY_SIGNATURE)
* @param {Buffer} [specs.data=new Buffer(0)]
* Message bytes (SEQUENCER_SPECIFIC)
* @param {Number} [delay=0] - Meta info delay in ticks
* @exception {module:midijs/lib/error~MIDIInvalidEventError}
* Invalid meta event type
*/
function MetaEvent(type, specs, delay) {
var defaults = {};
switch (type) {
case MetaEvent.TYPE.SEQUENCE_NUMBER:
defaults.number = 0;
break;
case MetaEvent.TYPE.TEXT:
case MetaEvent.TYPE.COPYRIGHT_NOTICE:
case MetaEvent.TYPE.SEQUENCE_NAME:
case MetaEvent.TYPE.INSTRUMENT_NAME:
case MetaEvent.TYPE.LYRICS:
case MetaEvent.TYPE.MARKER:
case MetaEvent.TYPE.CUE_POINT:
case MetaEvent.TYPE.PROGRAM_NAME:
case MetaEvent.TYPE.DEVICE_NAME:
defaults.text = '';
break;
case MetaEvent.TYPE.MIDI_CHANNEL:
defaults.channel = 0;
break;
case MetaEvent.TYPE.MIDI_PORT:
defaults.port = 0;
break;
case MetaEvent.TYPE.END_OF_TRACK:
break;
case MetaEvent.TYPE.SET_TEMPO:
defaults.tempo = 120;
break;
case MetaEvent.TYPE.SMPTE_OFFSET:
defaults.rate = 24;
defaults.hours = 0;
defaults.minutes = 0;
defaults.seconds = 0;
defaults.frames = 0;
defaults.subframes = 0;
break;
case MetaEvent.TYPE.TIME_SIGNATURE:
defaults.numerator = 4;
defaults.denominator = 4;
defaults.metronome = 24;
defaults.clockSignalsPerBeat = 24;
break;
case MetaEvent.TYPE.KEY_SIGNATURE:
defaults.note = 0;
defaults.major = true;
break;
case MetaEvent.TYPE.SEQUENCER_SPECIFIC:
defaults.data = new buffer.Buffer(0);
break;
default:
throw new error.MIDIInvalidEventError(
'Invalid MetaEvent type "' + type + '"'
);
}
this.type = type;
Event.call(this, specs, defaults, delay);
}
util.inherits(MetaEvent, Event);
exports.MetaEvent = MetaEvent;
/**
* Meta events types
*
* @readonly
* @enum {Number}
*/
MetaEvent.TYPE = {
/** Sequence number of this track. Should always be at the start
* of a track, with a delay of 0 */
SEQUENCE_NUMBER: 0,
/** Arbitrary text (comments, notes, ...) */
TEXT: 1,
/** Copyright of this file (© YYYY, Author). Should always be at the
* start of a track, with a delay of 0 */
COPYRIGHT_NOTICE: 2,
/** Name of the track. Should always be at the start of a track,
* with a delay of 0 */
SEQUENCE_NAME: 3,
/** Name of the instrument used in this track */
INSTRUMENT_NAME: 4,
/** Lyrics to be sung at this time in the song */
LYRICS: 5,
/** Mark a significant point in the song */
MARKER: 6,
/** Mark a point where some type of action should start */
CUE_POINT: 7,
/** Embed the patch/program name that is called up by the
* immediately subsequent Bank Select and Program Change messages. */
PROGRAM_NAME: 8,
/** Identify the hardware device used to produce sounds for this track. */
DEVICE_NAME: 9,
/** DEPRECATED: Associate following events to given channel */
MIDI_CHANNEL: 32,
/** DEPRECATED: The MIDI port used to get around 16 MIDI channels limit */
MIDI_PORT: 33,
/** Mark the end of the track */
END_OF_TRACK: 47,
/** Change the tempo for the next events */
SET_TEMPO: 81,
/** Set the SMPTE starting point relative to the beginning of the track */
SMPTE_OFFSET: 84,
/** Change the time signature. Default values are 4, 4, 24 and 8 */
TIME_SIGNATURE: 88,
/** Change the key signature */
KEY_SIGNATURE: 89,
/** Add sequencer-specific data */
SEQUENCER_SPECIFIC: 127
};
/**
* SysexEvent
*
* Represent a system exclusive event. These events can have various
* meanings from device to device, and are defined by the manufacturer's
* specification. This API only exposes the raw bytes of the message, and
* doesn't try to interpret them
*
* @constructor
* @extends Event
* @param {Number} type - Sysex type
* @param {Buffer} data - Sysex data
* @param {Number} delay - Sysex message delay in ticks
*/
function SysexEvent(type, data, delay) {
this.type = type;
this.data = data;
Event.call(this, {}, {}, delay);
}
util.inherits(SysexEvent, Event);
exports.SysexEvent = SysexEvent;
/**
* ChannelEvent
*
* Represent a channel event. These events change the state of channels
* by setting notes on, off, changing controllers parameters or channel
* programs. They can be transmitted to the MIDI devices
*
* @constructor
* @extends Event
* @param {module:midijs/lib/file/event~ChannelEvent.TYPE}
* type - Channel event type
* @property {module:midijs/lib/file/event~ChannelEvent.TYPE}
* type - Channel event type
* @param {Object} [specs={}] - Event details
* @param {Number} specs.note - Event note (NOTE_OFF)
* @param {Number} specs.velocity - Note velocity (NOTE_OFF)
* @param {Number} specs.note - Event note (NOTE_ON)
* @param {Number} specs.velocity - Note velocity (NOTE_ON)
* @param {Number} specs.note - Event note (NOTE_AFTERTOUCH)
* @param {Number} specs.pressure - Note pressure (NOTE_AFTERTOUCH)
* @param {Number} specs.controller - Controller type (CONTROLLER)
* @param {Number} specs.value - Controller value (CONTROLLER)
* @param {Number} specs.program - Program ID (PROGRAM_CHANGE)
* @param {Number} specs.pressure - Global channel pressure (CHANNEL_AFTERTOUCH)
* @param {Number} specs.value - Pitch bend value (-8192 - 8191) (PITCH_BEND)
* @param {Number} [channel=0] - Channel to which the event applies
* @property {Number} channel - Channel to which the event applies
* @param {Number} [delay=0] - Event delay in ticks
* @exception {module:midijs/lib/error~MIDIInvalidEventError}
* Invalid channel event type
*/
function ChannelEvent(type, specs, channel, delay) {
var defaults = {};
switch (type) {
case ChannelEvent.TYPE.NOTE_OFF:
defaults.note = 0;
defaults.velocity = 127;
break;
case ChannelEvent.TYPE.NOTE_ON:
defaults.note = 0;
defaults.velocity = 127;
break;
case ChannelEvent.TYPE.NOTE_AFTERTOUCH:
defaults.note = 0;
defaults.pressure = 0;
break;
case ChannelEvent.TYPE.CONTROLLER:
defaults.controller = 0;
defaults.value = 127;
break;
case ChannelEvent.TYPE.PROGRAM_CHANGE:
defaults.program = 0;
break;
case ChannelEvent.TYPE.CHANNEL_AFTERTOUCH:
defaults.pressure = 0;
break;
case ChannelEvent.TYPE.PITCH_BEND:
defaults.value = 0;
break;
default:
throw new error.MIDIInvalidEventError(
'Invalid ChannelEvent type "' + type + '"'
);
}
this.type = type;
this.channel = channel || 0;
Event.call(this, specs, defaults, delay);
}
util.inherits(ChannelEvent, Event);
exports.ChannelEvent = ChannelEvent;
/**
* Channel events types
*
* @readonly
* @enum {Number}
*/
ChannelEvent.TYPE = {
/** Set a note off */
NOTE_OFF: 8,
/** Set a note on */
NOTE_ON: 9,
/** Change the pressure applied on a note */
NOTE_AFTERTOUCH: 10,
/** Set the value of a controller */
CONTROLLER: 11,
/** Change the program on a channel */
PROGRAM_CHANGE: 12,
/** Change the global pressure on a channel */
CHANNEL_AFTERTOUCH: 13,
/** Change the pitch on a channel */
PITCH_BEND: 14
};