UNPKG

@logue/smfplayer

Version:

smfplayer.js is JavaScript based Standard Midi Player for WebMidiLink based synthesizer.

792 lines (610 loc) 25.8 kB
/*! @logue/smfplayer v0.3.6 | imaya / GREE Inc. / Logue | license: MIT | build: 2021-11-04T14:33:02.374Z */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define("SMF", [], factory); else if(typeof exports === 'object') exports["SMF"] = factory(); else root["SMF"] = factory(); })((typeof self !== 'undefined' ? self : this), function() { return /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./src/meta.js": /*!*********************!*\ !*** ./src/meta.js ***! \*********************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) /* harmony export */ }); // This file is auto-generated by the build system. var Meta = { version: '0.3.6', date: '2021-11-04T11:03:20.907Z' }; /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Meta); /***/ }), /***/ "./src/midi_event.js": /*!***************************!*\ !*** ./src/midi_event.js ***! \***************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "ChannelEvent": () => (/* binding */ ChannelEvent), /* harmony export */ "SystemExclusiveEvent": () => (/* binding */ SystemExclusiveEvent), /* harmony export */ "MetaEvent": () => (/* binding */ MetaEvent) /* harmony export */ }); function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Midi Event abstract Structure */ var Event = /** * @param {string} subtype event subtype name. * @param {number} deltaTime delta time. * @param {number} time time. */ function Event(subtype, deltaTime, time) { _classCallCheck(this, Event); /** @type {string} */ this.subtype = subtype; /** @type {number} */ this.deltaTime = deltaTime; /** @type {number} */ this.time = time; }; /** * Midi Channel Event Structure * @extends {Event} */ var ChannelEvent = /*#__PURE__*/function (_Event) { _inherits(ChannelEvent, _Event); var _super = _createSuper(ChannelEvent); /** * @param {string} subtype * @param {number} deltaTime delta time. * @param {number} time time. * @param {number} channel * @param {number=} optParameter1 * @param {number=} optParameter2 */ function ChannelEvent(subtype, deltaTime, time, channel, optParameter1, optParameter2) { var _this; _classCallCheck(this, ChannelEvent); _this = _super.call(this, subtype, deltaTime, time); /** @type {number} */ _this.channel = channel; /** @type {(number|undefined)} */ _this.parameter1 = optParameter1; /** @type {(number|undefined)} */ _this.parameter2 = optParameter2; return _this; } return ChannelEvent; }(Event); /** * System Exclusive Event Structure * @extends {Event} */ var SystemExclusiveEvent = /*#__PURE__*/function (_Event2) { _inherits(SystemExclusiveEvent, _Event2); var _super2 = _createSuper(SystemExclusiveEvent); /** * @param {string} subtype * @param {number} deltaTime delta time. * @param {number} time time. * @param {ByteArray} data */ function SystemExclusiveEvent(subtype, deltaTime, time, data) { var _this2; _classCallCheck(this, SystemExclusiveEvent); _this2 = _super2.call(this, subtype, deltaTime, time); /** @type {ByteArray} */ _this2.data = data; return _this2; } return SystemExclusiveEvent; }(Event); /** * Midi Meta Event Structure * @extends {Event} */ var MetaEvent = /*#__PURE__*/function (_Event3) { _inherits(MetaEvent, _Event3); var _super3 = _createSuper(MetaEvent); /** * @param {string} subtype * @param {number} deltaTime delta time. * @param {number} time time. * @param {Array.<*>} data meta data. */ function MetaEvent(subtype, deltaTime, time, data) { var _this3; _classCallCheck(this, MetaEvent); _this3 = _super3.call(this, subtype, deltaTime, time); /** @type {Array.<*>} */ _this3.data = data; return _this3; } return MetaEvent; }(Event); /***/ }), /***/ "./src/riff.js": /*!*********************!*\ !*** ./src/riff.js ***! \*********************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* binding */ Riff) /* harmony export */ }); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } /** * Riff Parser class */ var Riff = /*#__PURE__*/function () { /** * @param {ByteArray} input input buffer. * @param {Object=} optParams option parameters. */ function Riff(input) { var optParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, Riff); /** @type {ByteArray} */ this.input = input; /** @type {number} */ this.ip = optParams.index || 0; /** @type {number} */ this.length = optParams.length || input.length - this.ip; /** @type {Array.<RiffChunk>} */ this.chunkList = []; /** @type {number} */ this.offset = this.ip; /** @type {boolean} */ this.padding = optParams.padding !== void 0 ? optParams.padding : true; /** @type {boolean} */ this.bigEndian = optParams.bigEndian !== void 0 ? optParams.bigEndian : false; } /** */ _createClass(Riff, [{ key: "parse", value: function parse() { /** @type {number} */ var length = this.length + this.offset; this.chunkList = []; while (this.ip < length) { this.parseChunk(); } } /** */ }, { key: "parseChunk", value: function parseChunk() { /** @type {ByteArray} */ var input = this.input; /** @type {number} */ var ip = this.ip; /** @type {number} */ var size; this.chunkList.push(new RiffChunk(String.fromCharCode(input[ip++], input[ip++], input[ip++], input[ip++]), size = this.bigEndian ? (input[ip++] << 24 | input[ip++] << 16 | input[ip++] << 8 | input[ip++]) >>> 0 : (input[ip++] | input[ip++] << 8 | input[ip++] << 16 | input[ip++] << 24) >>> 0, ip)); ip += size; // padding if (this.padding && (ip - this.offset & 1) === 1) { ip++; } this.ip = ip; } /** * @param {number} index chunk index. * @return {?RiffChunk} */ }, { key: "getChunk", value: function getChunk(index) { /** @type {RiffChunk} */ var chunk = this.chunkList[index]; if (chunk === void 0) { return null; } return chunk; } /** * @return {number} */ }, { key: "getNumberOfChunks", value: function getNumberOfChunks() { return this.chunkList.length; } }]); return Riff; }(); /** * Riff Chunk Structure * @interface */ var RiffChunk = /** * @param {string} type * @param {number} size * @param {number} offset */ function RiffChunk(type, size, offset) { _classCallCheck(this, RiffChunk); /** @type {string} */ this.type = type; /** @type {number} */ this.size = size; /** @type {number} */ this.offset = offset; }; /***/ }) /******/ }); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ /******/ /* webpack/runtime/define property getters */ /******/ (() => { /******/ // define getter functions for harmony exports /******/ __webpack_require__.d = (exports, definition) => { /******/ for(var key in definition) { /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ (() => { /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ /******/ /* webpack/runtime/make namespace object */ /******/ (() => { /******/ // define __esModule on exports /******/ __webpack_require__.r = (exports) => { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ })(); /******/ /************************************************************************/ var __webpack_exports__ = {}; // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. (() => { /*!********************!*\ !*** ./src/smf.js ***! \********************/ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "default": () => (/* binding */ SMF) /* harmony export */ }); /* harmony import */ var _midi_event__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./midi_event */ "./src/midi_event.js"); /* harmony import */ var _meta__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./meta */ "./src/meta.js"); /* harmony import */ var _riff__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./riff */ "./src/riff.js"); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } /** * Standard Midi File Parser class */ var SMF = /*#__PURE__*/function () { /** * @param {ByteArray} input input buffer. * @param {Object=} optParams option parameters. */ function SMF(input) { var optParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, SMF); optParams.padding = false; optParams.bigEndian = true; /** @type {ByteArray} */ this.input = input; /** @type {number} */ this.ip = optParams.index || 0; /** @type {number} */ this.chunkIndex = 0; /** * @type {Riff} * @private */ this.riffParser_ = new _riff__WEBPACK_IMPORTED_MODULE_2__["default"](input, optParams); // MIDI File Information /** @type {number} */ this.formatType = 0; /** @type {number} */ this.numberOfTracks = 0; /** @type {number} */ this.timeDivision = 480; /** @type {Array.<Array.<Midi.Event>>} */ this.tracks = []; /** @type {Array.<Array.<ByteArray>>} */ this.plainTracks = []; /** @type {number} */ this.version = _meta__WEBPACK_IMPORTED_MODULE_1__["default"].version; /** @type {string} */ this.build = _meta__WEBPACK_IMPORTED_MODULE_1__["default"].build; } /** */ _createClass(SMF, [{ key: "parse", value: function parse() { /** @type {number} */ var i = 0; /** @type {number} */ var il = 0; // parse riff chunks this.riffParser_.parse(); // parse header chunk this.parseHeaderChunk(); // parse track chunks for (i = 0, il = this.numberOfTracks; i < il; ++i) { this.parseTrackChunk(); } } /** */ }, { key: "parseHeaderChunk", value: function parseHeaderChunk() { /** @type {?{type: string, size: number, offset: number}} */ var chunk = this.riffParser_.getChunk(this.chunkIndex++); /** @type {ByteArray} */ var data = this.input; /** @type {number} */ var ip = chunk.offset; if (!chunk || chunk.type !== 'MThd') { throw new Error('invalid header signature'); } this.formatType = data[ip++] << 8 | data[ip++]; this.numberOfTracks = data[ip++] << 8 | data[ip++]; this.timeDivision = data[ip++] << 8 | data[ip++]; } /** */ }, { key: "parseTrackChunk", value: function parseTrackChunk() { /** @type {?{type: string, size: number, offset: number}} */ var chunk = this.riffParser_.getChunk(this.chunkIndex++); /** @type {ByteArray} */ var data = this.input; /** @type {number} */ var ip = chunk.offset; /** @type {number} */ var size = 0; /** @type {number} */ var deltaTime = 0; /** @type {number} */ var eventType = 0; /** @type {number} */ var channel = 0; /** @type {number} */ var prevEventType = -1; /** @type {number} */ var prevChannel = -1; /** @type {number} */ var tmp = 0; /** @type {number} */ var totalTime = 0; /** @type {number} */ var offset = 0; /** @type {number} */ var length = 0; /** @type {number} */ var status = 0; /** @type {Event} */ var event; /** @type {ByteArray} */ var plainBytes; /** @return {number} */ var readNumber = function readNumber() { /** @type {number} */ var result = 0; tmp = 0; do { tmp = data[ip++]; result = result << 7 | tmp & 0x7f; } while ((tmp & 0x80) !== 0); return result; }; if (!chunk || chunk.type !== 'MTrk') { throw new Error('invalid header signature'); } size = chunk.offset + chunk.size; var eventQueue = []; var plainQueue = []; while (ip < size) { // delta time deltaTime = readNumber(); totalTime += deltaTime; // offset offset = ip; // event type value, midi channel status = data[ip++]; eventType = status >> 4 & 0xf; channel = status & 0xf; // run status rule if (eventType < 8) { eventType = prevEventType; channel = prevChannel; status = prevEventType << 4 | prevChannel; ip--; offset--; } else { prevEventType = eventType; prevChannel = channel; } // TODO var table = [null, null, null, null, null, null, null, null, 'NoteOff', // 0x8 'NoteOn', 'NoteAftertouch', 'ControlChange', 'ProgramChange', 'ChannelAftertouch', 'PitchBend']; switch (eventType) { // channel events case 0x8: /* FALLTHROUGH */ case 0x9: /* FALLTHROUGH */ case 0xa: /* FALLTHROUGH */ case 0xb: /* FALLTHROUGH */ case 0xd: /* FALLTHROUGH */ case 0xe: event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.ChannelEvent(table[eventType], deltaTime, totalTime, channel, data[ip++], data[ip++]); break; case 0xc: event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.ChannelEvent(table[eventType], deltaTime, totalTime, channel, data[ip++]); break; // meta events, system exclusive event case 0xf: switch (channel) { // SysEx event case 0x0: tmp = readNumber(); if (data[ip + tmp - 1] !== 0xf7) { throw new Error('invalid SysEx event'); } event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.SystemExclusiveEvent('SystemExclusive', deltaTime, totalTime, data.subarray(ip, (ip += tmp) - 1)); break; case 0x7: tmp = readNumber(); event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.SystemExclusiveEvent('SystemExclusive(F7)', deltaTime, totalTime, data.subarray(ip, ip += tmp)); break; // meta event case 0xf: eventType = data[ip++]; tmp = readNumber(); switch (eventType) { case 0x00: // sequence number event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('SequenceNumber', deltaTime, totalTime, [data[ip++], data[ip++]]); break; case 0x01: // text event event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('TextEvent', deltaTime, totalTime, [String.fromCharCode.apply(null, data.subarray(ip, ip += tmp))]); break; case 0x02: // copyright notice event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('CopyrightNotice', deltaTime, totalTime, [String.fromCharCode.apply(null, data.subarray(ip, ip += tmp))]); break; case 0x03: // sequence/track name event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('SequenceTrackName', deltaTime, totalTime, [String.fromCharCode.apply(null, data.subarray(ip, ip += tmp))]); break; case 0x04: // instrument name event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('InstrumentName', deltaTime, totalTime, [String.fromCharCode.apply(null, data.subarray(ip, ip += tmp))]); break; case 0x05: // lyrics event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('Lyrics', deltaTime, totalTime, [String.fromCharCode.apply(null, data.subarray(ip, ip += tmp))]); break; case 0x06: // marker event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('Marker', deltaTime, totalTime, [String.fromCharCode.apply(null, data.subarray(ip, ip += tmp))]); break; case 0x07: // cue point event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('CuePoint', deltaTime, totalTime, [String.fromCharCode.apply(null, data.subarray(ip, ip += tmp))]); break; case 0x20: // midi channel prefix event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('MidiChannelPrefix', deltaTime, totalTime, [data[ip++]]); break; case 0x2f: // end of track event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('EndOfTrack', deltaTime, totalTime, []); break; case 0x51: // set tempo event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('SetTempo', deltaTime, totalTime, [data[ip++] << 16 | data[ip++] << 8 | data[ip++]]); break; case 0x54: // smpte offset event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('SmpteOffset', deltaTime, totalTime, [data[ip++], data[ip++], data[ip++], data[ip++], data[ip++]]); break; case 0x58: // time signature event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('TimeSignature', deltaTime, totalTime, [data[ip++], data[ip++], data[ip++], data[ip++]]); break; case 0x59: // key signature event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('KeySignature', deltaTime, totalTime, [data[ip++], data[ip++]]); break; case 0x7f: // sequencer specific event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('SequencerSpecific', deltaTime, totalTime, [data.subarray(ip, ip += tmp)]); break; default: // unknown event = new _midi_event__WEBPACK_IMPORTED_MODULE_0__.MetaEvent('Unknown', deltaTime, totalTime, [eventType, data.subarray(ip, ip += tmp)]); } break; default: console.warn('unknown message:', status.toString(16)); } break; // error default: throw new Error('invalid status'); } // plain queue length = ip - offset; plainBytes = data.subarray(offset, offset + length); plainBytes[0] = status; if (event instanceof _midi_event__WEBPACK_IMPORTED_MODULE_0__.ChannelEvent && event.subtype === 'NoteOn' && /** @type {ChannelEvent} */ event.parameter2 === 0) { event.subtype = table[8]; plainBytes = new Uint8Array([0x80 | event.channel, event.parameter1, event.parameter2]); } plainQueue.push(plainBytes); // event queue eventQueue.push(event); } this.tracks.push(eventQueue); this.plainTracks.push(plainQueue); } }]); return SMF; }(); })(); /******/ return __webpack_exports__; /******/ })() ; }); //# sourceMappingURL=smf.parser.js.map