5775 lines
201 KiB
JavaScript
5775 lines
201 KiB
JavaScript
|
/*!
|
||
|
* wavesurfer.js 4.6.0 (2024-02-05)
|
||
|
* https://wavesurfer-js.org
|
||
|
* @license BSD-3-Clause
|
||
|
*/
|
||
|
(function webpackUniversalModuleDefinition(root, factory) {
|
||
|
if(typeof exports === 'object' && typeof module === 'object')
|
||
|
module.exports = factory();
|
||
|
else if(typeof define === 'function' && define.amd)
|
||
|
define("WaveSurfer", [], factory);
|
||
|
else if(typeof exports === 'object')
|
||
|
exports["WaveSurfer"] = factory();
|
||
|
else
|
||
|
root["WaveSurfer"] = factory();
|
||
|
})(this, () => {
|
||
|
return /******/ (() => { // webpackBootstrap
|
||
|
/******/ var __webpack_modules__ = ({
|
||
|
|
||
|
/***/ "./src/drawer.canvasentry.js":
|
||
|
/*!***********************************!*\
|
||
|
!*** ./src/drawer.canvasentry.js ***!
|
||
|
\***********************************/
|
||
|
/***/ ((module, exports, __webpack_require__) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = void 0;
|
||
|
var _style = _interopRequireDefault(__webpack_require__(/*! ./util/style */ "./src/util/style.js"));
|
||
|
var _getId = _interopRequireDefault(__webpack_require__(/*! ./util/get-id */ "./src/util/get-id.js"));
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||
|
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, _toPropertyKey(descriptor.key), descriptor); } }
|
||
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /**
|
||
|
* @since 3.0.0
|
||
|
*/
|
||
|
/**
|
||
|
* The `CanvasEntry` class represents an element consisting of a wave `canvas`
|
||
|
* and an (optional) progress wave `canvas`.
|
||
|
*
|
||
|
* The `MultiCanvas` renderer uses one or more `CanvasEntry` instances to
|
||
|
* render a waveform, depending on the zoom level.
|
||
|
*/
|
||
|
var CanvasEntry = exports["default"] = /*#__PURE__*/function () {
|
||
|
function CanvasEntry() {
|
||
|
_classCallCheck(this, CanvasEntry);
|
||
|
/**
|
||
|
* The wave node
|
||
|
*
|
||
|
* @type {HTMLCanvasElement}
|
||
|
*/
|
||
|
this.wave = null;
|
||
|
/**
|
||
|
* The wave canvas rendering context
|
||
|
*
|
||
|
* @type {CanvasRenderingContext2D}
|
||
|
*/
|
||
|
this.waveCtx = null;
|
||
|
/**
|
||
|
* The (optional) progress wave node
|
||
|
*
|
||
|
* @type {HTMLCanvasElement}
|
||
|
*/
|
||
|
this.progress = null;
|
||
|
/**
|
||
|
* The (optional) progress wave canvas rendering context
|
||
|
*
|
||
|
* @type {CanvasRenderingContext2D}
|
||
|
*/
|
||
|
this.progressCtx = null;
|
||
|
/**
|
||
|
* Start of the area the canvas should render, between 0 and 1
|
||
|
*
|
||
|
* @type {number}
|
||
|
*/
|
||
|
this.start = 0;
|
||
|
/**
|
||
|
* End of the area the canvas should render, between 0 and 1
|
||
|
*
|
||
|
* @type {number}
|
||
|
*/
|
||
|
this.end = 1;
|
||
|
/**
|
||
|
* Unique identifier for this entry
|
||
|
*
|
||
|
* @type {string}
|
||
|
*/
|
||
|
this.id = (0, _getId.default)(typeof this.constructor.name !== 'undefined' ? this.constructor.name.toLowerCase() + '_' : 'canvasentry_');
|
||
|
/**
|
||
|
* Canvas 2d context attributes
|
||
|
*
|
||
|
* @type {object}
|
||
|
*/
|
||
|
this.canvasContextAttributes = {};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Store the wave canvas element and create the 2D rendering context
|
||
|
*
|
||
|
* @param {HTMLCanvasElement} element The wave `canvas` element.
|
||
|
*/
|
||
|
_createClass(CanvasEntry, [{
|
||
|
key: "initWave",
|
||
|
value: function initWave(element) {
|
||
|
this.wave = element;
|
||
|
this.waveCtx = this.wave.getContext('2d', this.canvasContextAttributes);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Store the progress wave canvas element and create the 2D rendering
|
||
|
* context
|
||
|
*
|
||
|
* @param {HTMLCanvasElement} element The progress wave `canvas` element.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "initProgress",
|
||
|
value: function initProgress(element) {
|
||
|
this.progress = element;
|
||
|
this.progressCtx = this.progress.getContext('2d', this.canvasContextAttributes);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update the dimensions
|
||
|
*
|
||
|
* @param {number} elementWidth Width of the entry
|
||
|
* @param {number} totalWidth Total width of the multi canvas renderer
|
||
|
* @param {number} width The new width of the element
|
||
|
* @param {number} height The new height of the element
|
||
|
*/
|
||
|
}, {
|
||
|
key: "updateDimensions",
|
||
|
value: function updateDimensions(elementWidth, totalWidth, width, height) {
|
||
|
// where the canvas starts and ends in the waveform, represented as a
|
||
|
// decimal between 0 and 1
|
||
|
this.start = this.wave.offsetLeft / totalWidth || 0;
|
||
|
this.end = this.start + elementWidth / totalWidth;
|
||
|
|
||
|
// set wave canvas dimensions
|
||
|
this.wave.width = width;
|
||
|
this.wave.height = height;
|
||
|
var elementSize = {
|
||
|
width: elementWidth + 'px'
|
||
|
};
|
||
|
(0, _style.default)(this.wave, elementSize);
|
||
|
if (this.hasProgressCanvas) {
|
||
|
// set progress canvas dimensions
|
||
|
this.progress.width = width;
|
||
|
this.progress.height = height;
|
||
|
(0, _style.default)(this.progress, elementSize);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear the wave and progress rendering contexts
|
||
|
*/
|
||
|
}, {
|
||
|
key: "clearWave",
|
||
|
value: function clearWave() {
|
||
|
// wave
|
||
|
this.waveCtx.clearRect(0, 0, this.waveCtx.canvas.width, this.waveCtx.canvas.height);
|
||
|
|
||
|
// progress
|
||
|
if (this.hasProgressCanvas) {
|
||
|
this.progressCtx.clearRect(0, 0, this.progressCtx.canvas.width, this.progressCtx.canvas.height);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the fill styles for wave and progress
|
||
|
*
|
||
|
* @param {string} waveColor Fill color for the wave canvas
|
||
|
* @param {?string} progressColor Fill color for the progress canvas
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setFillStyles",
|
||
|
value: function setFillStyles(waveColor, progressColor) {
|
||
|
this.waveCtx.fillStyle = waveColor;
|
||
|
if (this.hasProgressCanvas) {
|
||
|
this.progressCtx.fillStyle = progressColor;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Draw a rectangle for wave and progress
|
||
|
*
|
||
|
* @param {number} x X start position
|
||
|
* @param {number} y Y start position
|
||
|
* @param {number} width Width of the rectangle
|
||
|
* @param {number} height Height of the rectangle
|
||
|
* @param {number} radius Radius of the rectangle
|
||
|
*/
|
||
|
}, {
|
||
|
key: "fillRects",
|
||
|
value: function fillRects(x, y, width, height, radius) {
|
||
|
this.fillRectToContext(this.waveCtx, x, y, width, height, radius);
|
||
|
if (this.hasProgressCanvas) {
|
||
|
this.fillRectToContext(this.progressCtx, x, y, width, height, radius);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Draw the actual rectangle on a `canvas` element
|
||
|
*
|
||
|
* @param {CanvasRenderingContext2D} ctx Rendering context of target canvas
|
||
|
* @param {number} x X start position
|
||
|
* @param {number} y Y start position
|
||
|
* @param {number} width Width of the rectangle
|
||
|
* @param {number} height Height of the rectangle
|
||
|
* @param {number} radius Radius of the rectangle
|
||
|
*/
|
||
|
}, {
|
||
|
key: "fillRectToContext",
|
||
|
value: function fillRectToContext(ctx, x, y, width, height, radius) {
|
||
|
if (!ctx) {
|
||
|
return;
|
||
|
}
|
||
|
if (radius) {
|
||
|
this.drawRoundedRect(ctx, x, y, width, height, radius);
|
||
|
} else {
|
||
|
ctx.fillRect(x, y, width, height);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Draw a rounded rectangle on Canvas
|
||
|
*
|
||
|
* @param {CanvasRenderingContext2D} ctx Canvas context
|
||
|
* @param {number} x X-position of the rectangle
|
||
|
* @param {number} y Y-position of the rectangle
|
||
|
* @param {number} width Width of the rectangle
|
||
|
* @param {number} height Height of the rectangle
|
||
|
* @param {number} radius Radius of the rectangle
|
||
|
*
|
||
|
* @return {void}
|
||
|
* @example drawRoundedRect(ctx, 50, 50, 5, 10, 3)
|
||
|
*/
|
||
|
}, {
|
||
|
key: "drawRoundedRect",
|
||
|
value: function drawRoundedRect(ctx, x, y, width, height, radius) {
|
||
|
if (height === 0) {
|
||
|
return;
|
||
|
}
|
||
|
// peaks are float values from -1 to 1. Use absolute height values in
|
||
|
// order to correctly calculate rounded rectangle coordinates
|
||
|
if (height < 0) {
|
||
|
height *= -1;
|
||
|
y -= height;
|
||
|
}
|
||
|
ctx.beginPath();
|
||
|
ctx.moveTo(x + radius, y);
|
||
|
ctx.lineTo(x + width - radius, y);
|
||
|
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
||
|
ctx.lineTo(x + width, y + height - radius);
|
||
|
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
||
|
ctx.lineTo(x + radius, y + height);
|
||
|
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
||
|
ctx.lineTo(x, y + radius);
|
||
|
ctx.quadraticCurveTo(x, y, x + radius, y);
|
||
|
ctx.closePath();
|
||
|
ctx.fill();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Render the actual wave and progress lines
|
||
|
*
|
||
|
* @param {number[]} peaks Array with peaks data
|
||
|
* @param {number} absmax Maximum peak value (absolute)
|
||
|
* @param {number} halfH Half the height of the waveform
|
||
|
* @param {number} offsetY Offset to the top
|
||
|
* @param {number} start The x-offset of the beginning of the area that
|
||
|
* should be rendered
|
||
|
* @param {number} end The x-offset of the end of the area that
|
||
|
* should be rendered
|
||
|
*/
|
||
|
}, {
|
||
|
key: "drawLines",
|
||
|
value: function drawLines(peaks, absmax, halfH, offsetY, start, end) {
|
||
|
this.drawLineToContext(this.waveCtx, peaks, absmax, halfH, offsetY, start, end);
|
||
|
if (this.hasProgressCanvas) {
|
||
|
this.drawLineToContext(this.progressCtx, peaks, absmax, halfH, offsetY, start, end);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Render the actual waveform line on a `canvas` element
|
||
|
*
|
||
|
* @param {CanvasRenderingContext2D} ctx Rendering context of target canvas
|
||
|
* @param {number[]} peaks Array with peaks data
|
||
|
* @param {number} absmax Maximum peak value (absolute)
|
||
|
* @param {number} halfH Half the height of the waveform
|
||
|
* @param {number} offsetY Offset to the top
|
||
|
* @param {number} start The x-offset of the beginning of the area that
|
||
|
* should be rendered
|
||
|
* @param {number} end The x-offset of the end of the area that
|
||
|
* should be rendered
|
||
|
*/
|
||
|
}, {
|
||
|
key: "drawLineToContext",
|
||
|
value: function drawLineToContext(ctx, peaks, absmax, halfH, offsetY, start, end) {
|
||
|
if (!ctx) {
|
||
|
return;
|
||
|
}
|
||
|
var length = peaks.length / 2;
|
||
|
var first = Math.round(length * this.start);
|
||
|
|
||
|
// use one more peak value to make sure we join peaks at ends -- unless,
|
||
|
// of course, this is the last canvas
|
||
|
var last = Math.round(length * this.end) + 1;
|
||
|
var canvasStart = first;
|
||
|
var canvasEnd = last;
|
||
|
var scale = this.wave.width / (canvasEnd - canvasStart - 1);
|
||
|
|
||
|
// optimization
|
||
|
var halfOffset = halfH + offsetY;
|
||
|
var absmaxHalf = absmax / halfH;
|
||
|
ctx.beginPath();
|
||
|
ctx.moveTo((canvasStart - first) * scale, halfOffset);
|
||
|
ctx.lineTo((canvasStart - first) * scale, halfOffset - Math.round((peaks[2 * canvasStart] || 0) / absmaxHalf));
|
||
|
var i, peak, h;
|
||
|
for (i = canvasStart; i < canvasEnd; i++) {
|
||
|
peak = peaks[2 * i] || 0;
|
||
|
h = Math.round(peak / absmaxHalf);
|
||
|
ctx.lineTo((i - first) * scale + this.halfPixel, halfOffset - h);
|
||
|
}
|
||
|
|
||
|
// draw the bottom edge going backwards, to make a single
|
||
|
// closed hull to fill
|
||
|
var j = canvasEnd - 1;
|
||
|
for (j; j >= canvasStart; j--) {
|
||
|
peak = peaks[2 * j + 1] || 0;
|
||
|
h = Math.round(peak / absmaxHalf);
|
||
|
ctx.lineTo((j - first) * scale + this.halfPixel, halfOffset - h);
|
||
|
}
|
||
|
ctx.lineTo((canvasStart - first) * scale, halfOffset - Math.round((peaks[2 * canvasStart + 1] || 0) / absmaxHalf));
|
||
|
ctx.closePath();
|
||
|
ctx.fill();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Destroys this entry
|
||
|
*/
|
||
|
}, {
|
||
|
key: "destroy",
|
||
|
value: function destroy() {
|
||
|
this.waveCtx = null;
|
||
|
this.wave = null;
|
||
|
this.progressCtx = null;
|
||
|
this.progress = null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return image data of the wave `canvas` element
|
||
|
*
|
||
|
* When using a `type` of `'blob'`, this will return a `Promise` that
|
||
|
* resolves with a `Blob` instance.
|
||
|
*
|
||
|
* @param {string} format='image/png' An optional value of a format type.
|
||
|
* @param {number} quality=0.92 An optional value between 0 and 1.
|
||
|
* @param {string} type='dataURL' Either 'dataURL' or 'blob'.
|
||
|
* @return {string|Promise} When using the default `'dataURL'` `type` this
|
||
|
* returns a data URL. When using the `'blob'` `type` this returns a
|
||
|
* `Promise` that resolves with a `Blob` instance.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getImage",
|
||
|
value: function getImage(format, quality, type) {
|
||
|
var _this = this;
|
||
|
if (type === 'blob') {
|
||
|
return new Promise(function (resolve) {
|
||
|
_this.wave.toBlob(resolve, format, quality);
|
||
|
});
|
||
|
} else if (type === 'dataURL') {
|
||
|
return this.wave.toDataURL(format, quality);
|
||
|
}
|
||
|
}
|
||
|
}]);
|
||
|
return CanvasEntry;
|
||
|
}();
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/drawer.js":
|
||
|
/*!***********************!*\
|
||
|
!*** ./src/drawer.js ***!
|
||
|
\***********************/
|
||
|
/***/ ((module, exports, __webpack_require__) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = void 0;
|
||
|
var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js"));
|
||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||
|
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, _toPropertyKey(descriptor.key), descriptor); } }
|
||
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||
|
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
|
||
|
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() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
||
|
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||
|
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 } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||
|
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||
|
/**
|
||
|
* Parent class for renderers
|
||
|
*
|
||
|
* @extends {Observer}
|
||
|
*/
|
||
|
var Drawer = exports["default"] = /*#__PURE__*/function (_util$Observer) {
|
||
|
_inherits(Drawer, _util$Observer);
|
||
|
/**
|
||
|
* @param {HTMLElement} container The container node of the wavesurfer instance
|
||
|
* @param {WavesurferParams} params The wavesurfer initialisation options
|
||
|
*/
|
||
|
function Drawer(container, params) {
|
||
|
var _this;
|
||
|
_classCallCheck(this, Drawer);
|
||
|
_this = _callSuper(this, Drawer);
|
||
|
_this.container = container;
|
||
|
/**
|
||
|
* @type {WavesurferParams}
|
||
|
*/
|
||
|
_this.params = params;
|
||
|
/**
|
||
|
* The width of the renderer
|
||
|
* @type {number}
|
||
|
*/
|
||
|
_this.width = 0;
|
||
|
/**
|
||
|
* The height of the renderer
|
||
|
* @type {number}
|
||
|
*/
|
||
|
_this.height = params.height * _this.params.pixelRatio;
|
||
|
_this.lastPos = 0;
|
||
|
/**
|
||
|
* The `<wave>` element which is added to the container
|
||
|
* @type {HTMLElement}
|
||
|
*/
|
||
|
_this.wrapper = null;
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Alias of `util.style`
|
||
|
*
|
||
|
* @param {HTMLElement} el The element that the styles will be applied to
|
||
|
* @param {Object} styles The map of propName: attribute, both are used as-is
|
||
|
* @return {HTMLElement} el
|
||
|
*/
|
||
|
_createClass(Drawer, [{
|
||
|
key: "style",
|
||
|
value: function style(el, styles) {
|
||
|
return util.style(el, styles);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create the wrapper `<wave>` element, style it and set up the events for
|
||
|
* interaction
|
||
|
*/
|
||
|
}, {
|
||
|
key: "createWrapper",
|
||
|
value: function createWrapper() {
|
||
|
this.wrapper = this.container.appendChild(document.createElement('wave'));
|
||
|
this.style(this.wrapper, {
|
||
|
display: 'block',
|
||
|
position: 'relative',
|
||
|
userSelect: 'none',
|
||
|
webkitUserSelect: 'none',
|
||
|
height: this.params.height + 'px'
|
||
|
});
|
||
|
if (this.params.fillParent || this.params.scrollParent) {
|
||
|
this.style(this.wrapper, {
|
||
|
width: '100%',
|
||
|
overflowX: this.params.hideScrollbar ? 'hidden' : 'auto',
|
||
|
overflowY: 'hidden'
|
||
|
});
|
||
|
}
|
||
|
this.setupWrapperEvents();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle click event
|
||
|
*
|
||
|
* @param {Event} e Click event
|
||
|
* @param {?boolean} noPrevent Set to true to not call `e.preventDefault()`
|
||
|
* @return {number} Playback position from 0 to 1
|
||
|
*/
|
||
|
}, {
|
||
|
key: "handleEvent",
|
||
|
value: function handleEvent(e, noPrevent) {
|
||
|
!noPrevent && e.preventDefault();
|
||
|
var clientX = e.targetTouches ? e.targetTouches[0].clientX : e.clientX;
|
||
|
var bbox = this.wrapper.getBoundingClientRect();
|
||
|
var nominalWidth = this.width;
|
||
|
var parentWidth = this.getWidth();
|
||
|
var progress;
|
||
|
if (!this.params.fillParent && nominalWidth < parentWidth) {
|
||
|
progress = (this.params.rtl ? bbox.right - clientX : clientX - bbox.left) * (this.params.pixelRatio / nominalWidth) || 0;
|
||
|
} else {
|
||
|
progress = ((this.params.rtl ? bbox.right - clientX : clientX - bbox.left) + this.wrapper.scrollLeft) / this.wrapper.scrollWidth || 0;
|
||
|
}
|
||
|
return util.clamp(progress, 0, 1);
|
||
|
}
|
||
|
}, {
|
||
|
key: "setupWrapperEvents",
|
||
|
value: function setupWrapperEvents() {
|
||
|
var _this2 = this;
|
||
|
this.wrapper.addEventListener('click', function (e) {
|
||
|
var scrollbarHeight = _this2.wrapper.offsetHeight - _this2.wrapper.clientHeight;
|
||
|
if (scrollbarHeight !== 0) {
|
||
|
// scrollbar is visible. Check if click was on it
|
||
|
var bbox = _this2.wrapper.getBoundingClientRect();
|
||
|
if (e.clientY >= bbox.bottom - scrollbarHeight) {
|
||
|
// ignore mousedown as it was on the scrollbar
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
if (_this2.params.interact) {
|
||
|
_this2.fireEvent('click', e, _this2.handleEvent(e));
|
||
|
}
|
||
|
});
|
||
|
this.wrapper.addEventListener('dblclick', function (e) {
|
||
|
if (_this2.params.interact) {
|
||
|
_this2.fireEvent('dblclick', e, _this2.handleEvent(e));
|
||
|
}
|
||
|
});
|
||
|
this.wrapper.addEventListener('scroll', function (e) {
|
||
|
return _this2.fireEvent('scroll', e);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Draw peaks on the canvas
|
||
|
*
|
||
|
* @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays
|
||
|
* for split channel rendering
|
||
|
* @param {number} length The width of the area that should be drawn
|
||
|
* @param {number} start The x-offset of the beginning of the area that
|
||
|
* should be rendered
|
||
|
* @param {number} end The x-offset of the end of the area that should be
|
||
|
* rendered
|
||
|
*/
|
||
|
}, {
|
||
|
key: "drawPeaks",
|
||
|
value: function drawPeaks(peaks, length, start, end) {
|
||
|
if (!this.setWidth(length)) {
|
||
|
this.clearWave();
|
||
|
}
|
||
|
this.params.barWidth ? this.drawBars(peaks, 0, start, end) : this.drawWave(peaks, 0, start, end);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Scroll to the beginning
|
||
|
*/
|
||
|
}, {
|
||
|
key: "resetScroll",
|
||
|
value: function resetScroll() {
|
||
|
if (this.wrapper !== null) {
|
||
|
this.wrapper.scrollLeft = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Recenter the view-port at a certain percent of the waveform
|
||
|
*
|
||
|
* @param {number} percent Value from 0 to 1 on the waveform
|
||
|
*/
|
||
|
}, {
|
||
|
key: "recenter",
|
||
|
value: function recenter(percent) {
|
||
|
var position = this.wrapper.scrollWidth * percent;
|
||
|
this.recenterOnPosition(position, true);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Recenter the view-port on a position, either scroll there immediately or
|
||
|
* in steps of 5 pixels
|
||
|
*
|
||
|
* @param {number} position X-offset in pixels
|
||
|
* @param {boolean} immediate Set to true to immediately scroll somewhere
|
||
|
*/
|
||
|
}, {
|
||
|
key: "recenterOnPosition",
|
||
|
value: function recenterOnPosition(position, immediate) {
|
||
|
var scrollLeft = this.wrapper.scrollLeft;
|
||
|
var half = ~~(this.wrapper.clientWidth / 2);
|
||
|
var maxScroll = this.wrapper.scrollWidth - this.wrapper.clientWidth;
|
||
|
var target = position - half;
|
||
|
var offset = target - scrollLeft;
|
||
|
if (maxScroll == 0) {
|
||
|
// no need to continue if scrollbar is not there
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// if the cursor is currently visible...
|
||
|
if (!immediate && -half <= offset && offset < half) {
|
||
|
// set rate at which waveform is centered
|
||
|
var rate = this.params.autoCenterRate;
|
||
|
|
||
|
// make rate depend on width of view and length of waveform
|
||
|
rate /= half;
|
||
|
rate *= maxScroll;
|
||
|
offset = Math.max(-rate, Math.min(rate, offset));
|
||
|
target = scrollLeft + offset;
|
||
|
}
|
||
|
|
||
|
// limit target to valid range (0 to maxScroll)
|
||
|
target = Math.max(0, Math.min(maxScroll, target));
|
||
|
// no use attempting to scroll if we're not moving
|
||
|
if (target != scrollLeft) {
|
||
|
this.wrapper.scrollLeft = target;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the current scroll position in pixels
|
||
|
*
|
||
|
* @return {number} Horizontal scroll position in pixels
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getScrollX",
|
||
|
value: function getScrollX() {
|
||
|
var x = 0;
|
||
|
if (this.wrapper) {
|
||
|
var pixelRatio = this.params.pixelRatio;
|
||
|
x = Math.round(this.wrapper.scrollLeft * pixelRatio);
|
||
|
|
||
|
// In cases of elastic scroll (safari with mouse wheel) you can
|
||
|
// scroll beyond the limits of the container
|
||
|
// Calculate and floor the scrollable extent to make sure an out
|
||
|
// of bounds value is not returned
|
||
|
// Ticket #1312
|
||
|
if (this.params.scrollParent) {
|
||
|
var maxScroll = ~~(this.wrapper.scrollWidth * pixelRatio - this.getWidth());
|
||
|
x = Math.min(maxScroll, Math.max(0, x));
|
||
|
}
|
||
|
}
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the width of the container
|
||
|
*
|
||
|
* @return {number} The width of the container
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getWidth",
|
||
|
value: function getWidth() {
|
||
|
return Math.round(this.container.clientWidth * this.params.pixelRatio);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the width of the container
|
||
|
*
|
||
|
* @param {number} width The new width of the container
|
||
|
* @return {boolean} Whether the width of the container was updated or not
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setWidth",
|
||
|
value: function setWidth(width) {
|
||
|
if (this.width == width) {
|
||
|
return false;
|
||
|
}
|
||
|
this.width = width;
|
||
|
if (this.params.fillParent || this.params.scrollParent) {
|
||
|
this.style(this.wrapper, {
|
||
|
width: ''
|
||
|
});
|
||
|
} else {
|
||
|
this.style(this.wrapper, {
|
||
|
width: ~~(this.width / this.params.pixelRatio) + 'px'
|
||
|
});
|
||
|
}
|
||
|
this.updateSize();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the height of the container
|
||
|
*
|
||
|
* @param {number} height The new height of the container.
|
||
|
* @return {boolean} Whether the height of the container was updated or not
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setHeight",
|
||
|
value: function setHeight(height) {
|
||
|
if (height == this.height) {
|
||
|
return false;
|
||
|
}
|
||
|
this.height = height;
|
||
|
this.style(this.wrapper, {
|
||
|
height: ~~(this.height / this.params.pixelRatio) + 'px'
|
||
|
});
|
||
|
this.updateSize();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Called by wavesurfer when progress should be rendered
|
||
|
*
|
||
|
* @param {number} progress From 0 to 1
|
||
|
*/
|
||
|
}, {
|
||
|
key: "progress",
|
||
|
value: function progress(_progress) {
|
||
|
var minPxDelta = 1 / this.params.pixelRatio;
|
||
|
var pos = Math.round(_progress * this.width) * minPxDelta;
|
||
|
if (pos < this.lastPos || pos - this.lastPos >= minPxDelta) {
|
||
|
this.lastPos = pos;
|
||
|
if (this.params.scrollParent && this.params.autoCenter) {
|
||
|
var newPos = ~~(this.wrapper.scrollWidth * _progress);
|
||
|
this.recenterOnPosition(newPos, this.params.autoCenterImmediately);
|
||
|
}
|
||
|
this.updateProgress(pos);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This is called when wavesurfer is destroyed
|
||
|
*/
|
||
|
}, {
|
||
|
key: "destroy",
|
||
|
value: function destroy() {
|
||
|
this.unAll();
|
||
|
if (this.wrapper) {
|
||
|
if (this.wrapper.parentNode == this.container) {
|
||
|
this.container.removeChild(this.wrapper);
|
||
|
}
|
||
|
this.wrapper = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Renderer-specific methods */
|
||
|
|
||
|
/**
|
||
|
* Called after cursor related params have changed.
|
||
|
*
|
||
|
* @abstract
|
||
|
*/
|
||
|
}, {
|
||
|
key: "updateCursor",
|
||
|
value: function updateCursor() {}
|
||
|
|
||
|
/**
|
||
|
* Called when the size of the container changes so the renderer can adjust
|
||
|
*
|
||
|
* @abstract
|
||
|
*/
|
||
|
}, {
|
||
|
key: "updateSize",
|
||
|
value: function updateSize() {}
|
||
|
|
||
|
/**
|
||
|
* Draw a waveform with bars
|
||
|
*
|
||
|
* @abstract
|
||
|
* @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for split channel
|
||
|
* rendering
|
||
|
* @param {number} channelIndex The index of the current channel. Normally
|
||
|
* should be 0
|
||
|
* @param {number} start The x-offset of the beginning of the area that
|
||
|
* should be rendered
|
||
|
* @param {number} end The x-offset of the end of the area that should be
|
||
|
* rendered
|
||
|
*/
|
||
|
}, {
|
||
|
key: "drawBars",
|
||
|
value: function drawBars(peaks, channelIndex, start, end) {}
|
||
|
|
||
|
/**
|
||
|
* Draw a waveform
|
||
|
*
|
||
|
* @abstract
|
||
|
* @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for split channel
|
||
|
* rendering
|
||
|
* @param {number} channelIndex The index of the current channel. Normally
|
||
|
* should be 0
|
||
|
* @param {number} start The x-offset of the beginning of the area that
|
||
|
* should be rendered
|
||
|
* @param {number} end The x-offset of the end of the area that should be
|
||
|
* rendered
|
||
|
*/
|
||
|
}, {
|
||
|
key: "drawWave",
|
||
|
value: function drawWave(peaks, channelIndex, start, end) {}
|
||
|
|
||
|
/**
|
||
|
* Clear the waveform
|
||
|
*
|
||
|
* @abstract
|
||
|
*/
|
||
|
}, {
|
||
|
key: "clearWave",
|
||
|
value: function clearWave() {}
|
||
|
|
||
|
/**
|
||
|
* Render the new progress
|
||
|
*
|
||
|
* @abstract
|
||
|
* @param {number} position X-Offset of progress position in pixels
|
||
|
*/
|
||
|
}, {
|
||
|
key: "updateProgress",
|
||
|
value: function updateProgress(position) {}
|
||
|
}]);
|
||
|
return Drawer;
|
||
|
}(util.Observer);
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/drawer.multicanvas.js":
|
||
|
/*!***********************************!*\
|
||
|
!*** ./src/drawer.multicanvas.js ***!
|
||
|
\***********************************/
|
||
|
/***/ ((module, exports, __webpack_require__) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = void 0;
|
||
|
var _drawer = _interopRequireDefault(__webpack_require__(/*! ./drawer */ "./src/drawer.js"));
|
||
|
var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js"));
|
||
|
var _drawer2 = _interopRequireDefault(__webpack_require__(/*! ./drawer.canvasentry */ "./src/drawer.canvasentry.js"));
|
||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
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, _toPropertyKey(descriptor.key), descriptor); } }
|
||
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||
|
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
|
||
|
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() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
||
|
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||
|
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 } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||
|
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||
|
/**
|
||
|
* MultiCanvas renderer for wavesurfer. Is currently the default and sole
|
||
|
* builtin renderer.
|
||
|
*
|
||
|
* A `MultiCanvas` consists of one or more `CanvasEntry` instances, depending
|
||
|
* on the zoom level.
|
||
|
*/
|
||
|
var MultiCanvas = exports["default"] = /*#__PURE__*/function (_Drawer) {
|
||
|
_inherits(MultiCanvas, _Drawer);
|
||
|
/**
|
||
|
* @param {HTMLElement} container The container node of the wavesurfer instance
|
||
|
* @param {WavesurferParams} params The wavesurfer initialisation options
|
||
|
*/
|
||
|
function MultiCanvas(container, params) {
|
||
|
var _this;
|
||
|
_classCallCheck(this, MultiCanvas);
|
||
|
_this = _callSuper(this, MultiCanvas, [container, params]);
|
||
|
|
||
|
/**
|
||
|
* @type {number}
|
||
|
*/
|
||
|
_this.maxCanvasWidth = params.maxCanvasWidth;
|
||
|
|
||
|
/**
|
||
|
* @type {number}
|
||
|
*/
|
||
|
_this.maxCanvasElementWidth = Math.round(params.maxCanvasWidth / params.pixelRatio);
|
||
|
|
||
|
/**
|
||
|
* Whether or not the progress wave is rendered. If the `waveColor`
|
||
|
* and `progressColor` are the same color it is not.
|
||
|
*
|
||
|
* @type {boolean}
|
||
|
*/
|
||
|
_this.hasProgressCanvas = params.waveColor != params.progressColor;
|
||
|
|
||
|
/**
|
||
|
* @type {number}
|
||
|
*/
|
||
|
_this.halfPixel = 0.5 / params.pixelRatio;
|
||
|
|
||
|
/**
|
||
|
* List of `CanvasEntry` instances.
|
||
|
*
|
||
|
* @type {Array}
|
||
|
*/
|
||
|
_this.canvases = [];
|
||
|
|
||
|
/**
|
||
|
* @type {HTMLElement}
|
||
|
*/
|
||
|
_this.progressWave = null;
|
||
|
|
||
|
/**
|
||
|
* Class used to generate entries.
|
||
|
*
|
||
|
* @type {function}
|
||
|
*/
|
||
|
_this.EntryClass = _drawer2.default;
|
||
|
|
||
|
/**
|
||
|
* Canvas 2d context attributes.
|
||
|
*
|
||
|
* @type {object}
|
||
|
*/
|
||
|
_this.canvasContextAttributes = params.drawingContextAttributes;
|
||
|
|
||
|
/**
|
||
|
* Overlap added between entries to prevent vertical white stripes
|
||
|
* between `canvas` elements.
|
||
|
*
|
||
|
* @type {number}
|
||
|
*/
|
||
|
_this.overlap = 2 * Math.ceil(params.pixelRatio / 2);
|
||
|
|
||
|
/**
|
||
|
* The radius of the wave bars. Makes bars rounded
|
||
|
*
|
||
|
* @type {number}
|
||
|
*/
|
||
|
_this.barRadius = params.barRadius || 0;
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize the drawer
|
||
|
*/
|
||
|
_createClass(MultiCanvas, [{
|
||
|
key: "init",
|
||
|
value: function init() {
|
||
|
this.createWrapper();
|
||
|
this.createElements();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create the canvas elements and style them
|
||
|
*
|
||
|
*/
|
||
|
}, {
|
||
|
key: "createElements",
|
||
|
value: function createElements() {
|
||
|
this.progressWave = this.wrapper.appendChild(this.style(document.createElement('wave'), {
|
||
|
position: 'absolute',
|
||
|
zIndex: 3,
|
||
|
left: 0,
|
||
|
top: 0,
|
||
|
bottom: 0,
|
||
|
overflow: 'hidden',
|
||
|
width: '0',
|
||
|
display: 'none',
|
||
|
boxSizing: 'border-box',
|
||
|
borderRightStyle: 'solid',
|
||
|
pointerEvents: 'none'
|
||
|
}));
|
||
|
this.addCanvas();
|
||
|
this.updateCursor();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update cursor style
|
||
|
*/
|
||
|
}, {
|
||
|
key: "updateCursor",
|
||
|
value: function updateCursor() {
|
||
|
this.style(this.progressWave, {
|
||
|
borderRightWidth: this.params.cursorWidth + 'px',
|
||
|
borderRightColor: this.params.cursorColor
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adjust to the updated size by adding or removing canvases
|
||
|
*/
|
||
|
}, {
|
||
|
key: "updateSize",
|
||
|
value: function updateSize() {
|
||
|
var _this2 = this;
|
||
|
var totalWidth = Math.round(this.width / this.params.pixelRatio);
|
||
|
var requiredCanvases = Math.ceil(totalWidth / (this.maxCanvasElementWidth + this.overlap));
|
||
|
|
||
|
// add required canvases
|
||
|
while (this.canvases.length < requiredCanvases) {
|
||
|
this.addCanvas();
|
||
|
}
|
||
|
|
||
|
// remove older existing canvases, if any
|
||
|
while (this.canvases.length > requiredCanvases) {
|
||
|
this.removeCanvas();
|
||
|
}
|
||
|
var canvasWidth = this.maxCanvasWidth + this.overlap;
|
||
|
var lastCanvas = this.canvases.length - 1;
|
||
|
this.canvases.forEach(function (entry, i) {
|
||
|
if (i == lastCanvas) {
|
||
|
canvasWidth = _this2.width - _this2.maxCanvasWidth * lastCanvas;
|
||
|
}
|
||
|
_this2.updateDimensions(entry, canvasWidth, _this2.height);
|
||
|
entry.clearWave();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a canvas to the canvas list
|
||
|
*
|
||
|
*/
|
||
|
}, {
|
||
|
key: "addCanvas",
|
||
|
value: function addCanvas() {
|
||
|
var entry = new this.EntryClass();
|
||
|
entry.canvasContextAttributes = this.canvasContextAttributes;
|
||
|
entry.hasProgressCanvas = this.hasProgressCanvas;
|
||
|
entry.halfPixel = this.halfPixel;
|
||
|
var leftOffset = this.maxCanvasElementWidth * this.canvases.length;
|
||
|
|
||
|
// wave
|
||
|
entry.initWave(this.wrapper.appendChild(this.style(document.createElement('canvas'), {
|
||
|
position: 'absolute',
|
||
|
zIndex: 2,
|
||
|
left: leftOffset + 'px',
|
||
|
top: 0,
|
||
|
bottom: 0,
|
||
|
height: '100%',
|
||
|
pointerEvents: 'none'
|
||
|
})));
|
||
|
|
||
|
// progress
|
||
|
if (this.hasProgressCanvas) {
|
||
|
entry.initProgress(this.progressWave.appendChild(this.style(document.createElement('canvas'), {
|
||
|
position: 'absolute',
|
||
|
left: leftOffset + 'px',
|
||
|
top: 0,
|
||
|
bottom: 0,
|
||
|
height: '100%'
|
||
|
})));
|
||
|
}
|
||
|
this.canvases.push(entry);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Pop single canvas from the list
|
||
|
*
|
||
|
*/
|
||
|
}, {
|
||
|
key: "removeCanvas",
|
||
|
value: function removeCanvas() {
|
||
|
var lastEntry = this.canvases[this.canvases.length - 1];
|
||
|
|
||
|
// wave
|
||
|
lastEntry.wave.parentElement.removeChild(lastEntry.wave);
|
||
|
|
||
|
// progress
|
||
|
if (this.hasProgressCanvas) {
|
||
|
lastEntry.progress.parentElement.removeChild(lastEntry.progress);
|
||
|
}
|
||
|
|
||
|
// cleanup
|
||
|
if (lastEntry) {
|
||
|
lastEntry.destroy();
|
||
|
lastEntry = null;
|
||
|
}
|
||
|
this.canvases.pop();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update the dimensions of a canvas element
|
||
|
*
|
||
|
* @param {CanvasEntry} entry Target entry
|
||
|
* @param {number} width The new width of the element
|
||
|
* @param {number} height The new height of the element
|
||
|
*/
|
||
|
}, {
|
||
|
key: "updateDimensions",
|
||
|
value: function updateDimensions(entry, width, height) {
|
||
|
var elementWidth = Math.round(width / this.params.pixelRatio);
|
||
|
var totalWidth = Math.round(this.width / this.params.pixelRatio);
|
||
|
|
||
|
// update canvas dimensions
|
||
|
entry.updateDimensions(elementWidth, totalWidth, width, height);
|
||
|
|
||
|
// style element
|
||
|
this.style(this.progressWave, {
|
||
|
display: 'block'
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear the whole multi-canvas
|
||
|
*/
|
||
|
}, {
|
||
|
key: "clearWave",
|
||
|
value: function clearWave() {
|
||
|
var _this3 = this;
|
||
|
util.frame(function () {
|
||
|
_this3.canvases.forEach(function (entry) {
|
||
|
return entry.clearWave();
|
||
|
});
|
||
|
})();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Draw a waveform with bars
|
||
|
*
|
||
|
* @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays
|
||
|
* for split channel rendering
|
||
|
* @param {number} channelIndex The index of the current channel. Normally
|
||
|
* should be 0. Must be an integer.
|
||
|
* @param {number} start The x-offset of the beginning of the area that
|
||
|
* should be rendered
|
||
|
* @param {number} end The x-offset of the end of the area that should be
|
||
|
* rendered
|
||
|
* @returns {void}
|
||
|
*/
|
||
|
}, {
|
||
|
key: "drawBars",
|
||
|
value: function drawBars(peaks, channelIndex, start, end) {
|
||
|
var _this4 = this;
|
||
|
return this.prepareDraw(peaks, channelIndex, start, end, function (_ref) {
|
||
|
var absmax = _ref.absmax,
|
||
|
hasMinVals = _ref.hasMinVals,
|
||
|
height = _ref.height,
|
||
|
offsetY = _ref.offsetY,
|
||
|
halfH = _ref.halfH,
|
||
|
peaks = _ref.peaks,
|
||
|
ch = _ref.channelIndex;
|
||
|
// if drawBars was called within ws.empty we don't pass a start and
|
||
|
// don't want anything to happen
|
||
|
if (start === undefined) {
|
||
|
return;
|
||
|
}
|
||
|
// Skip every other value if there are negatives.
|
||
|
var peakIndexScale = hasMinVals ? 2 : 1;
|
||
|
var length = peaks.length / peakIndexScale;
|
||
|
var bar = _this4.params.barWidth * _this4.params.pixelRatio;
|
||
|
var gap = _this4.params.barGap === null ? Math.max(_this4.params.pixelRatio, ~~(bar / 2)) : Math.max(_this4.params.pixelRatio, _this4.params.barGap * _this4.params.pixelRatio);
|
||
|
var step = bar + gap;
|
||
|
var scale = length / _this4.width;
|
||
|
var first = start;
|
||
|
var last = end;
|
||
|
var i = first;
|
||
|
for (i; i < last; i += step) {
|
||
|
var peak = peaks[Math.floor(i * scale * peakIndexScale)] || 0;
|
||
|
var h = Math.round(peak / absmax * halfH);
|
||
|
|
||
|
/* in case of silences, allow the user to specify that we
|
||
|
* always draw *something* (normally a 1px high bar) */
|
||
|
if (h == 0 && _this4.params.barMinHeight) h = _this4.params.barMinHeight;
|
||
|
_this4.fillRect(i + _this4.halfPixel, halfH - h + offsetY, bar + _this4.halfPixel, h * 2, _this4.barRadius, ch);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Draw a waveform
|
||
|
*
|
||
|
* @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays
|
||
|
* for split channel rendering
|
||
|
* @param {number} channelIndex The index of the current channel. Normally
|
||
|
* should be 0
|
||
|
* @param {number?} start The x-offset of the beginning of the area that
|
||
|
* should be rendered (If this isn't set only a flat line is rendered)
|
||
|
* @param {number?} end The x-offset of the end of the area that should be
|
||
|
* rendered
|
||
|
* @returns {void}
|
||
|
*/
|
||
|
}, {
|
||
|
key: "drawWave",
|
||
|
value: function drawWave(peaks, channelIndex, start, end) {
|
||
|
var _this5 = this;
|
||
|
return this.prepareDraw(peaks, channelIndex, start, end, function (_ref2) {
|
||
|
var absmax = _ref2.absmax,
|
||
|
hasMinVals = _ref2.hasMinVals,
|
||
|
height = _ref2.height,
|
||
|
offsetY = _ref2.offsetY,
|
||
|
halfH = _ref2.halfH,
|
||
|
peaks = _ref2.peaks,
|
||
|
channelIndex = _ref2.channelIndex;
|
||
|
if (!hasMinVals) {
|
||
|
var reflectedPeaks = [];
|
||
|
var len = peaks.length;
|
||
|
var i = 0;
|
||
|
for (i; i < len; i++) {
|
||
|
reflectedPeaks[2 * i] = peaks[i];
|
||
|
reflectedPeaks[2 * i + 1] = -peaks[i];
|
||
|
}
|
||
|
peaks = reflectedPeaks;
|
||
|
}
|
||
|
|
||
|
// if drawWave was called within ws.empty we don't pass a start and
|
||
|
// end and simply want a flat line
|
||
|
if (start !== undefined) {
|
||
|
_this5.drawLine(peaks, absmax, halfH, offsetY, start, end, channelIndex);
|
||
|
}
|
||
|
|
||
|
// always draw a median line
|
||
|
_this5.fillRect(0, halfH + offsetY - _this5.halfPixel, _this5.width, _this5.halfPixel, _this5.barRadius, channelIndex);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Tell the canvas entries to render their portion of the waveform
|
||
|
*
|
||
|
* @param {number[]} peaks Peaks data
|
||
|
* @param {number} absmax Maximum peak value (absolute)
|
||
|
* @param {number} halfH Half the height of the waveform
|
||
|
* @param {number} offsetY Offset to the top
|
||
|
* @param {number} start The x-offset of the beginning of the area that
|
||
|
* should be rendered
|
||
|
* @param {number} end The x-offset of the end of the area that
|
||
|
* should be rendered
|
||
|
* @param {channelIndex} channelIndex The channel index of the line drawn
|
||
|
*/
|
||
|
}, {
|
||
|
key: "drawLine",
|
||
|
value: function drawLine(peaks, absmax, halfH, offsetY, start, end, channelIndex) {
|
||
|
var _this6 = this;
|
||
|
var _ref3 = this.params.splitChannelsOptions.channelColors[channelIndex] || {},
|
||
|
waveColor = _ref3.waveColor,
|
||
|
progressColor = _ref3.progressColor;
|
||
|
this.canvases.forEach(function (entry, i) {
|
||
|
_this6.setFillStyles(entry, waveColor, progressColor);
|
||
|
entry.drawLines(peaks, absmax, halfH, offsetY, start, end);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Draw a rectangle on the multi-canvas
|
||
|
*
|
||
|
* @param {number} x X-position of the rectangle
|
||
|
* @param {number} y Y-position of the rectangle
|
||
|
* @param {number} width Width of the rectangle
|
||
|
* @param {number} height Height of the rectangle
|
||
|
* @param {number} radius Radius of the rectangle
|
||
|
* @param {channelIndex} channelIndex The channel index of the bar drawn
|
||
|
*/
|
||
|
}, {
|
||
|
key: "fillRect",
|
||
|
value: function fillRect(x, y, width, height, radius, channelIndex) {
|
||
|
var startCanvas = Math.floor(x / this.maxCanvasWidth);
|
||
|
var endCanvas = Math.min(Math.ceil((x + width) / this.maxCanvasWidth) + 1, this.canvases.length);
|
||
|
var i = startCanvas;
|
||
|
for (i; i < endCanvas; i++) {
|
||
|
var entry = this.canvases[i];
|
||
|
var leftOffset = i * this.maxCanvasWidth;
|
||
|
var intersection = {
|
||
|
x1: Math.max(x, i * this.maxCanvasWidth),
|
||
|
y1: y,
|
||
|
x2: Math.min(x + width, i * this.maxCanvasWidth + entry.wave.width),
|
||
|
y2: y + height
|
||
|
};
|
||
|
if (intersection.x1 < intersection.x2) {
|
||
|
var _ref4 = this.params.splitChannelsOptions.channelColors[channelIndex] || {},
|
||
|
waveColor = _ref4.waveColor,
|
||
|
progressColor = _ref4.progressColor;
|
||
|
this.setFillStyles(entry, waveColor, progressColor);
|
||
|
entry.fillRects(intersection.x1 - leftOffset, intersection.y1, intersection.x2 - intersection.x1, intersection.y2 - intersection.y1, radius);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns whether to hide the channel from being drawn based on params.
|
||
|
*
|
||
|
* @param {number} channelIndex The index of the current channel.
|
||
|
* @returns {bool} True to hide the channel, false to draw.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "hideChannel",
|
||
|
value: function hideChannel(channelIndex) {
|
||
|
return this.params.splitChannels && this.params.splitChannelsOptions.filterChannels.includes(channelIndex);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Performs preparation tasks and calculations which are shared by `drawBars`
|
||
|
* and `drawWave`
|
||
|
*
|
||
|
* @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for
|
||
|
* split channel rendering
|
||
|
* @param {number} channelIndex The index of the current channel. Normally
|
||
|
* should be 0
|
||
|
* @param {number?} start The x-offset of the beginning of the area that
|
||
|
* should be rendered. If this isn't set only a flat line is rendered
|
||
|
* @param {number?} end The x-offset of the end of the area that should be
|
||
|
* rendered
|
||
|
* @param {function} fn The render function to call, e.g. `drawWave`
|
||
|
* @param {number} drawIndex The index of the current channel after filtering.
|
||
|
* @param {number?} normalizedMax Maximum modulation value across channels for use with relativeNormalization. Ignored when undefined
|
||
|
* @returns {void}
|
||
|
*/
|
||
|
}, {
|
||
|
key: "prepareDraw",
|
||
|
value: function prepareDraw(peaks, channelIndex, start, end, fn, drawIndex, normalizedMax) {
|
||
|
var _this7 = this;
|
||
|
return util.frame(function () {
|
||
|
// Split channels and call this function with the channelIndex set
|
||
|
if (peaks[0] instanceof Array) {
|
||
|
var channels = peaks;
|
||
|
if (_this7.params.splitChannels) {
|
||
|
var filteredChannels = channels.filter(function (c, i) {
|
||
|
return !_this7.hideChannel(i);
|
||
|
});
|
||
|
if (!_this7.params.splitChannelsOptions.overlay) {
|
||
|
_this7.setHeight(Math.max(filteredChannels.length, 1) * _this7.params.height * _this7.params.pixelRatio);
|
||
|
}
|
||
|
var overallAbsMax;
|
||
|
if (_this7.params.splitChannelsOptions && _this7.params.splitChannelsOptions.relativeNormalization) {
|
||
|
// calculate maximum peak across channels to use for normalization
|
||
|
overallAbsMax = util.max(channels.map(function (channelPeaks) {
|
||
|
return util.absMax(channelPeaks);
|
||
|
}));
|
||
|
}
|
||
|
return channels.forEach(function (channelPeaks, i) {
|
||
|
return _this7.prepareDraw(channelPeaks, i, start, end, fn, filteredChannels.indexOf(channelPeaks), overallAbsMax);
|
||
|
});
|
||
|
}
|
||
|
peaks = channels[0];
|
||
|
}
|
||
|
|
||
|
// Return and do not draw channel peaks if hidden.
|
||
|
if (_this7.hideChannel(channelIndex)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// calculate maximum modulation value, either from the barHeight
|
||
|
// parameter or if normalize=true from the largest value in the peak
|
||
|
// set
|
||
|
var absmax = 1 / _this7.params.barHeight;
|
||
|
if (_this7.params.normalize) {
|
||
|
absmax = normalizedMax === undefined ? util.absMax(peaks) : normalizedMax;
|
||
|
}
|
||
|
|
||
|
// Bar wave draws the bottom only as a reflection of the top,
|
||
|
// so we don't need negative values
|
||
|
var hasMinVals = [].some.call(peaks, function (val) {
|
||
|
return val < 0;
|
||
|
});
|
||
|
var height = _this7.params.height * _this7.params.pixelRatio;
|
||
|
var halfH = height / 2;
|
||
|
var offsetY = height * drawIndex || 0;
|
||
|
|
||
|
// Override offsetY if overlay is true
|
||
|
if (_this7.params.splitChannelsOptions && _this7.params.splitChannelsOptions.overlay) {
|
||
|
offsetY = 0;
|
||
|
}
|
||
|
return fn({
|
||
|
absmax: absmax,
|
||
|
hasMinVals: hasMinVals,
|
||
|
height: height,
|
||
|
offsetY: offsetY,
|
||
|
halfH: halfH,
|
||
|
peaks: peaks,
|
||
|
channelIndex: channelIndex
|
||
|
});
|
||
|
})();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the fill styles for a certain entry (wave and progress)
|
||
|
*
|
||
|
* @param {CanvasEntry} entry Target entry
|
||
|
* @param {string} waveColor Wave color to draw this entry
|
||
|
* @param {string} progressColor Progress color to draw this entry
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setFillStyles",
|
||
|
value: function setFillStyles(entry) {
|
||
|
var waveColor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.params.waveColor;
|
||
|
var progressColor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.params.progressColor;
|
||
|
entry.setFillStyles(waveColor, progressColor);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return image data of the multi-canvas
|
||
|
*
|
||
|
* When using a `type` of `'blob'`, this will return a `Promise`.
|
||
|
*
|
||
|
* @param {string} format='image/png' An optional value of a format type.
|
||
|
* @param {number} quality=0.92 An optional value between 0 and 1.
|
||
|
* @param {string} type='dataURL' Either 'dataURL' or 'blob'.
|
||
|
* @return {string|string[]|Promise} When using the default `'dataURL'`
|
||
|
* `type` this returns a single data URL or an array of data URLs,
|
||
|
* one for each canvas. When using the `'blob'` `type` this returns a
|
||
|
* `Promise` that resolves with an array of `Blob` instances, one for each
|
||
|
* canvas.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getImage",
|
||
|
value: function getImage(format, quality, type) {
|
||
|
if (type === 'blob') {
|
||
|
return Promise.all(this.canvases.map(function (entry) {
|
||
|
return entry.getImage(format, quality, type);
|
||
|
}));
|
||
|
} else if (type === 'dataURL') {
|
||
|
var images = this.canvases.map(function (entry) {
|
||
|
return entry.getImage(format, quality, type);
|
||
|
});
|
||
|
return images.length > 1 ? images : images[0];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Render the new progress
|
||
|
*
|
||
|
* @param {number} position X-offset of progress position in pixels
|
||
|
*/
|
||
|
}, {
|
||
|
key: "updateProgress",
|
||
|
value: function updateProgress(position) {
|
||
|
this.style(this.progressWave, {
|
||
|
width: position + 'px'
|
||
|
});
|
||
|
}
|
||
|
}]);
|
||
|
return MultiCanvas;
|
||
|
}(_drawer.default);
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/mediaelement-webaudio.js":
|
||
|
/*!**************************************!*\
|
||
|
!*** ./src/mediaelement-webaudio.js ***!
|
||
|
\**************************************/
|
||
|
/***/ ((module, exports, __webpack_require__) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = void 0;
|
||
|
var _mediaelement = _interopRequireDefault(__webpack_require__(/*! ./mediaelement */ "./src/mediaelement.js"));
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
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, _toPropertyKey(descriptor.key), descriptor); } }
|
||
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||
|
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
|
||
|
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() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
||
|
function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get.bind(); } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); }
|
||
|
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
|
||
|
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||
|
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 } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||
|
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||
|
/**
|
||
|
* MediaElementWebAudio backend: load audio via an HTML5 audio tag, but playback with the WebAudio API.
|
||
|
* The advantage here is that the html5 <audio> tag can perform range requests on the server and not
|
||
|
* buffer the entire file in one request, and you still get the filtering and scripting functionality
|
||
|
* of the webaudio API.
|
||
|
* Note that in order to use range requests and prevent buffering, you must provide peak data.
|
||
|
*
|
||
|
* @since 3.2.0
|
||
|
*/
|
||
|
var MediaElementWebAudio = exports["default"] = /*#__PURE__*/function (_MediaElement) {
|
||
|
_inherits(MediaElementWebAudio, _MediaElement);
|
||
|
/**
|
||
|
* Construct the backend
|
||
|
*
|
||
|
* @param {WavesurferParams} params Wavesurfer parameters
|
||
|
*/
|
||
|
function MediaElementWebAudio(params) {
|
||
|
var _this;
|
||
|
_classCallCheck(this, MediaElementWebAudio);
|
||
|
_this = _callSuper(this, MediaElementWebAudio, [params]);
|
||
|
/** @private */
|
||
|
_this.params = params;
|
||
|
/** @private */
|
||
|
_this.sourceMediaElement = null;
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialise the backend, called in `wavesurfer.createBackend()`
|
||
|
*/
|
||
|
_createClass(MediaElementWebAudio, [{
|
||
|
key: "init",
|
||
|
value: function init() {
|
||
|
this.setPlaybackRate(this.params.audioRate);
|
||
|
this.createTimer();
|
||
|
this.createVolumeNode();
|
||
|
this.createScriptNode();
|
||
|
this.createAnalyserNode();
|
||
|
}
|
||
|
/**
|
||
|
* Private method called by both `load` (from url)
|
||
|
* and `loadElt` (existing media element) methods.
|
||
|
*
|
||
|
* @param {HTMLMediaElement} media HTML5 Audio or Video element
|
||
|
* @param {number[]|Number.<Array[]>} peaks Array of peak data
|
||
|
* @param {string} preload HTML 5 preload attribute value
|
||
|
* @private
|
||
|
*/
|
||
|
}, {
|
||
|
key: "_load",
|
||
|
value: function _load(media, peaks, preload) {
|
||
|
_get(_getPrototypeOf(MediaElementWebAudio.prototype), "_load", this).call(this, media, peaks, preload);
|
||
|
this.createMediaElementSource(media);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create MediaElementSource node
|
||
|
*
|
||
|
* @since 3.2.0
|
||
|
* @param {HTMLMediaElement} mediaElement HTML5 Audio to load
|
||
|
*/
|
||
|
}, {
|
||
|
key: "createMediaElementSource",
|
||
|
value: function createMediaElementSource(mediaElement) {
|
||
|
this.sourceMediaElement = this.ac.createMediaElementSource(mediaElement);
|
||
|
this.sourceMediaElement.connect(this.analyser);
|
||
|
}
|
||
|
}, {
|
||
|
key: "play",
|
||
|
value: function play(start, end) {
|
||
|
this.resumeAudioContext();
|
||
|
return _get(_getPrototypeOf(MediaElementWebAudio.prototype), "play", this).call(this, start, end);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This is called when wavesurfer is destroyed
|
||
|
*
|
||
|
*/
|
||
|
}, {
|
||
|
key: "destroy",
|
||
|
value: function destroy() {
|
||
|
_get(_getPrototypeOf(MediaElementWebAudio.prototype), "destroy", this).call(this);
|
||
|
this.destroyWebAudio();
|
||
|
}
|
||
|
}]);
|
||
|
return MediaElementWebAudio;
|
||
|
}(_mediaelement.default);
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/mediaelement.js":
|
||
|
/*!*****************************!*\
|
||
|
!*** ./src/mediaelement.js ***!
|
||
|
\*****************************/
|
||
|
/***/ ((module, exports, __webpack_require__) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = void 0;
|
||
|
var _webaudio = _interopRequireDefault(__webpack_require__(/*! ./webaudio */ "./src/webaudio.js"));
|
||
|
var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js"));
|
||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
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, _toPropertyKey(descriptor.key), descriptor); } }
|
||
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||
|
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
|
||
|
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() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
||
|
function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get.bind(); } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); }
|
||
|
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
|
||
|
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||
|
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 } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||
|
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||
|
/**
|
||
|
* MediaElement backend
|
||
|
*/
|
||
|
var MediaElement = exports["default"] = /*#__PURE__*/function (_WebAudio) {
|
||
|
_inherits(MediaElement, _WebAudio);
|
||
|
/**
|
||
|
* Construct the backend
|
||
|
*
|
||
|
* @param {WavesurferParams} params Wavesurfer parameters
|
||
|
*/
|
||
|
function MediaElement(params) {
|
||
|
var _this;
|
||
|
_classCallCheck(this, MediaElement);
|
||
|
_this = _callSuper(this, MediaElement, [params]);
|
||
|
/** @private */
|
||
|
_this.params = params;
|
||
|
|
||
|
/**
|
||
|
* Initially a dummy media element to catch errors. Once `_load` is
|
||
|
* called, this will contain the actual `HTMLMediaElement`.
|
||
|
* @private
|
||
|
*/
|
||
|
_this.media = {
|
||
|
currentTime: 0,
|
||
|
duration: 0,
|
||
|
paused: true,
|
||
|
playbackRate: 1,
|
||
|
play: function play() {},
|
||
|
pause: function pause() {},
|
||
|
volume: 0
|
||
|
};
|
||
|
|
||
|
/** @private */
|
||
|
_this.mediaType = params.mediaType.toLowerCase();
|
||
|
/** @private */
|
||
|
_this.elementPosition = params.elementPosition;
|
||
|
/** @private */
|
||
|
_this.peaks = null;
|
||
|
/** @private */
|
||
|
_this.playbackRate = 1;
|
||
|
/** @private */
|
||
|
_this.volume = 1;
|
||
|
/** @private */
|
||
|
_this.isMuted = false;
|
||
|
/** @private */
|
||
|
_this.buffer = null;
|
||
|
/** @private */
|
||
|
_this.onPlayEnd = null;
|
||
|
/** @private */
|
||
|
_this.mediaListeners = {};
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialise the backend, called in `wavesurfer.createBackend()`
|
||
|
*/
|
||
|
_createClass(MediaElement, [{
|
||
|
key: "init",
|
||
|
value: function init() {
|
||
|
this.setPlaybackRate(this.params.audioRate);
|
||
|
this.createTimer();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Attach event listeners to media element.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "_setupMediaListeners",
|
||
|
value: function _setupMediaListeners() {
|
||
|
var _this2 = this;
|
||
|
this.mediaListeners.error = function () {
|
||
|
_this2.fireEvent('error', 'Error loading media element');
|
||
|
};
|
||
|
this.mediaListeners.canplay = function () {
|
||
|
_this2.fireEvent('canplay');
|
||
|
};
|
||
|
this.mediaListeners.ended = function () {
|
||
|
_this2.fireEvent('finish');
|
||
|
};
|
||
|
// listen to and relay play, pause and seeked events to enable
|
||
|
// playback control from the external media element
|
||
|
this.mediaListeners.play = function () {
|
||
|
_this2.fireEvent('play');
|
||
|
};
|
||
|
this.mediaListeners.pause = function () {
|
||
|
_this2.fireEvent('pause');
|
||
|
};
|
||
|
this.mediaListeners.seeked = function (event) {
|
||
|
_this2.fireEvent('seek');
|
||
|
};
|
||
|
this.mediaListeners.volumechange = function (event) {
|
||
|
_this2.isMuted = _this2.media.muted;
|
||
|
if (_this2.isMuted) {
|
||
|
_this2.volume = 0;
|
||
|
} else {
|
||
|
_this2.volume = _this2.media.volume;
|
||
|
}
|
||
|
_this2.fireEvent('volume');
|
||
|
};
|
||
|
|
||
|
// reset event listeners
|
||
|
Object.keys(this.mediaListeners).forEach(function (id) {
|
||
|
_this2.media.removeEventListener(id, _this2.mediaListeners[id]);
|
||
|
_this2.media.addEventListener(id, _this2.mediaListeners[id]);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a timer to provide a more precise `audioprocess` event.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "createTimer",
|
||
|
value: function createTimer() {
|
||
|
var _this3 = this;
|
||
|
var onAudioProcess = function onAudioProcess() {
|
||
|
if (_this3.isPaused()) {
|
||
|
return;
|
||
|
}
|
||
|
_this3.fireEvent('audioprocess', _this3.getCurrentTime());
|
||
|
|
||
|
// Call again in the next frame
|
||
|
util.frame(onAudioProcess)();
|
||
|
};
|
||
|
this.on('play', onAudioProcess);
|
||
|
|
||
|
// Update the progress one more time to prevent it from being stuck in
|
||
|
// case of lower framerates
|
||
|
this.on('pause', function () {
|
||
|
_this3.fireEvent('audioprocess', _this3.getCurrentTime());
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create media element with url as its source,
|
||
|
* and append to container element.
|
||
|
*
|
||
|
* @param {string} url Path to media file
|
||
|
* @param {HTMLElement} container HTML element
|
||
|
* @param {number[]|Number.<Array[]>} peaks Array of peak data
|
||
|
* @param {string} preload HTML 5 preload attribute value
|
||
|
* @throws Will throw an error if the `url` argument is not a valid media
|
||
|
* element.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "load",
|
||
|
value: function load(url, container, peaks, preload) {
|
||
|
var media = document.createElement(this.mediaType);
|
||
|
media.controls = this.params.mediaControls;
|
||
|
media.autoplay = this.params.autoplay || false;
|
||
|
media.preload = preload == null ? 'auto' : preload;
|
||
|
media.src = url;
|
||
|
media.style.width = '100%';
|
||
|
var prevMedia = container.querySelector(this.mediaType);
|
||
|
if (prevMedia) {
|
||
|
container.removeChild(prevMedia);
|
||
|
}
|
||
|
container.appendChild(media);
|
||
|
this._load(media, peaks, preload);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Load existing media element.
|
||
|
*
|
||
|
* @param {HTMLMediaElement} elt HTML5 Audio or Video element
|
||
|
* @param {number[]|Number.<Array[]>} peaks Array of peak data
|
||
|
*/
|
||
|
}, {
|
||
|
key: "loadElt",
|
||
|
value: function loadElt(elt, peaks) {
|
||
|
elt.controls = this.params.mediaControls;
|
||
|
elt.autoplay = this.params.autoplay || false;
|
||
|
this._load(elt, peaks, elt.preload);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Method called by both `load` (from url)
|
||
|
* and `loadElt` (existing media element) methods.
|
||
|
*
|
||
|
* @param {HTMLMediaElement} media HTML5 Audio or Video element
|
||
|
* @param {number[]|Number.<Array[]>} peaks Array of peak data
|
||
|
* @param {string} preload HTML 5 preload attribute value
|
||
|
* @throws Will throw an error if the `media` argument is not a valid media
|
||
|
* element.
|
||
|
* @private
|
||
|
*/
|
||
|
}, {
|
||
|
key: "_load",
|
||
|
value: function _load(media, peaks, preload) {
|
||
|
// verify media element is valid
|
||
|
if (!(media instanceof HTMLMediaElement) || typeof media.addEventListener === 'undefined') {
|
||
|
throw new Error('media parameter is not a valid media element');
|
||
|
}
|
||
|
|
||
|
// load must be called manually on iOS, otherwise peaks won't draw
|
||
|
// until a user interaction triggers load --> 'ready' event
|
||
|
//
|
||
|
// note that we avoid calling media.load here when given peaks and preload == 'none'
|
||
|
// as this almost always triggers some browser fetch of the media.
|
||
|
if (typeof media.load == 'function' && !(peaks && preload == 'none')) {
|
||
|
// Resets the media element and restarts the media resource. Any
|
||
|
// pending events are discarded. How much media data is fetched is
|
||
|
// still affected by the preload attribute.
|
||
|
media.load();
|
||
|
}
|
||
|
this.media = media;
|
||
|
this._setupMediaListeners();
|
||
|
this.peaks = peaks;
|
||
|
this.onPlayEnd = null;
|
||
|
this.buffer = null;
|
||
|
this.isMuted = media.muted;
|
||
|
this.setPlaybackRate(this.playbackRate);
|
||
|
this.setVolume(this.volume);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used by `wavesurfer.isPlaying()` and `wavesurfer.playPause()`
|
||
|
*
|
||
|
* @return {boolean} Media paused or not
|
||
|
*/
|
||
|
}, {
|
||
|
key: "isPaused",
|
||
|
value: function isPaused() {
|
||
|
return !this.media || this.media.paused;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used by `wavesurfer.getDuration()`
|
||
|
*
|
||
|
* @return {number} Duration
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getDuration",
|
||
|
value: function getDuration() {
|
||
|
if (this.explicitDuration) {
|
||
|
return this.explicitDuration;
|
||
|
}
|
||
|
var duration = (this.buffer || this.media).duration;
|
||
|
if (duration >= Infinity) {
|
||
|
// streaming audio
|
||
|
duration = this.media.seekable.end(0);
|
||
|
}
|
||
|
return duration;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the current time in seconds relative to the audio-clip's
|
||
|
* duration.
|
||
|
*
|
||
|
* @return {number} Current time
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getCurrentTime",
|
||
|
value: function getCurrentTime() {
|
||
|
return this.media && this.media.currentTime;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the position from 0 to 1
|
||
|
*
|
||
|
* @return {number} Current position
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getPlayedPercents",
|
||
|
value: function getPlayedPercents() {
|
||
|
return this.getCurrentTime() / this.getDuration() || 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the audio source playback rate.
|
||
|
*
|
||
|
* @return {number} Playback rate
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getPlaybackRate",
|
||
|
value: function getPlaybackRate() {
|
||
|
return this.playbackRate || this.media.playbackRate;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the audio source playback rate.
|
||
|
*
|
||
|
* @param {number} value Playback rate
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setPlaybackRate",
|
||
|
value: function setPlaybackRate(value) {
|
||
|
this.playbackRate = value || 1;
|
||
|
this.media.playbackRate = this.playbackRate;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used by `wavesurfer.seekTo()`
|
||
|
*
|
||
|
* @param {number} start Position to start at in seconds
|
||
|
*/
|
||
|
}, {
|
||
|
key: "seekTo",
|
||
|
value: function seekTo(start) {
|
||
|
if (start != null) {
|
||
|
this.media.currentTime = start;
|
||
|
}
|
||
|
this.clearPlayEnd();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Plays the loaded audio region.
|
||
|
*
|
||
|
* @param {number} start Start offset in seconds, relative to the beginning
|
||
|
* of a clip.
|
||
|
* @param {number} end When to stop, relative to the beginning of a clip.
|
||
|
* @emits MediaElement#play
|
||
|
* @return {Promise} Result
|
||
|
*/
|
||
|
}, {
|
||
|
key: "play",
|
||
|
value: function play(start, end) {
|
||
|
this.seekTo(start);
|
||
|
var promise = this.media.play();
|
||
|
end && this.setPlayEnd(end);
|
||
|
return promise;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Pauses the loaded audio.
|
||
|
*
|
||
|
* @emits MediaElement#pause
|
||
|
* @return {Promise} Result
|
||
|
*/
|
||
|
}, {
|
||
|
key: "pause",
|
||
|
value: function pause() {
|
||
|
var promise;
|
||
|
if (this.media) {
|
||
|
promise = this.media.pause();
|
||
|
}
|
||
|
this.clearPlayEnd();
|
||
|
return promise;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the play end
|
||
|
*
|
||
|
* @param {number} end Where to end
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setPlayEnd",
|
||
|
value: function setPlayEnd(end) {
|
||
|
var _this4 = this;
|
||
|
this.clearPlayEnd();
|
||
|
this._onPlayEnd = function (time) {
|
||
|
if (time >= end) {
|
||
|
_this4.pause();
|
||
|
_this4.seekTo(end);
|
||
|
}
|
||
|
};
|
||
|
this.on('audioprocess', this._onPlayEnd);
|
||
|
}
|
||
|
|
||
|
/** @private */
|
||
|
}, {
|
||
|
key: "clearPlayEnd",
|
||
|
value: function clearPlayEnd() {
|
||
|
if (this._onPlayEnd) {
|
||
|
this.un('audioprocess', this._onPlayEnd);
|
||
|
this._onPlayEnd = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Compute the max and min value of the waveform when broken into
|
||
|
* <length> subranges.
|
||
|
*
|
||
|
* @param {number} length How many subranges to break the waveform into.
|
||
|
* @param {number} first First sample in the required range.
|
||
|
* @param {number} last Last sample in the required range.
|
||
|
* @return {number[]|Number.<Array[]>} Array of 2*<length> peaks or array of
|
||
|
* arrays of peaks consisting of (max, min) values for each subrange.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getPeaks",
|
||
|
value: function getPeaks(length, first, last) {
|
||
|
if (this.buffer) {
|
||
|
return _get(_getPrototypeOf(MediaElement.prototype), "getPeaks", this).call(this, length, first, last);
|
||
|
}
|
||
|
return this.peaks || [];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the sink id for the media player
|
||
|
*
|
||
|
* @param {string} deviceId String value representing audio device id.
|
||
|
* @returns {Promise} A Promise that resolves to `undefined` when there
|
||
|
* are no errors.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setSinkId",
|
||
|
value: function setSinkId(deviceId) {
|
||
|
if (deviceId) {
|
||
|
if (!this.media.setSinkId) {
|
||
|
return Promise.reject(new Error('setSinkId is not supported in your browser'));
|
||
|
}
|
||
|
return this.media.setSinkId(deviceId);
|
||
|
}
|
||
|
return Promise.reject(new Error('Invalid deviceId: ' + deviceId));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the current volume
|
||
|
*
|
||
|
* @return {number} value A floating point value between 0 and 1.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getVolume",
|
||
|
value: function getVolume() {
|
||
|
return this.volume;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the audio volume
|
||
|
*
|
||
|
* @param {number} value A floating point value between 0 and 1.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setVolume",
|
||
|
value: function setVolume(value) {
|
||
|
this.volume = value;
|
||
|
// no need to change when it's already at that volume
|
||
|
if (this.media.volume !== this.volume) {
|
||
|
this.media.volume = this.volume;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enable or disable muted audio
|
||
|
*
|
||
|
* @since 4.0.0
|
||
|
* @param {boolean} muted Specify `true` to mute audio.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setMute",
|
||
|
value: function setMute(muted) {
|
||
|
// This causes a volume change to be emitted too through the
|
||
|
// volumechange event listener.
|
||
|
this.isMuted = this.media.muted = muted;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This is called when wavesurfer is destroyed
|
||
|
*
|
||
|
*/
|
||
|
}, {
|
||
|
key: "destroy",
|
||
|
value: function destroy() {
|
||
|
var _this5 = this;
|
||
|
this.pause();
|
||
|
this.unAll();
|
||
|
this.destroyed = true;
|
||
|
|
||
|
// cleanup media event listeners
|
||
|
Object.keys(this.mediaListeners).forEach(function (id) {
|
||
|
if (_this5.media) {
|
||
|
_this5.media.removeEventListener(id, _this5.mediaListeners[id]);
|
||
|
}
|
||
|
});
|
||
|
if (this.params.removeMediaElementOnDestroy && this.media && this.media.parentNode) {
|
||
|
this.media.parentNode.removeChild(this.media);
|
||
|
}
|
||
|
this.media = null;
|
||
|
}
|
||
|
}]);
|
||
|
return MediaElement;
|
||
|
}(_webaudio.default);
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/peakcache.js":
|
||
|
/*!**************************!*\
|
||
|
!*** ./src/peakcache.js ***!
|
||
|
\**************************/
|
||
|
/***/ ((module, exports) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = void 0;
|
||
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||
|
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, _toPropertyKey(descriptor.key), descriptor); } }
|
||
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||
|
/**
|
||
|
* Caches the decoded peaks data to improve rendering speed for large audio
|
||
|
*
|
||
|
* Is used if the option parameter `partialRender` is set to `true`
|
||
|
*/
|
||
|
var PeakCache = exports["default"] = /*#__PURE__*/function () {
|
||
|
/**
|
||
|
* Instantiate cache
|
||
|
*/
|
||
|
function PeakCache() {
|
||
|
_classCallCheck(this, PeakCache);
|
||
|
this.clearPeakCache();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Empty the cache
|
||
|
*/
|
||
|
_createClass(PeakCache, [{
|
||
|
key: "clearPeakCache",
|
||
|
value: function clearPeakCache() {
|
||
|
/**
|
||
|
* Flat array with entries that are always in pairs to mark the
|
||
|
* beginning and end of each subrange. This is a convenience so we can
|
||
|
* iterate over the pairs for easy set difference operations.
|
||
|
* @private
|
||
|
*/
|
||
|
this.peakCacheRanges = [];
|
||
|
/**
|
||
|
* Length of the entire cachable region, used for resetting the cache
|
||
|
* when this changes (zoom events, for instance).
|
||
|
* @private
|
||
|
*/
|
||
|
this.peakCacheLength = -1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a range of peaks to the cache
|
||
|
*
|
||
|
* @param {number} length The length of the range
|
||
|
* @param {number} start The x offset of the start of the range
|
||
|
* @param {number} end The x offset of the end of the range
|
||
|
* @return {Number.<Array[]>} Array with arrays of numbers
|
||
|
*/
|
||
|
}, {
|
||
|
key: "addRangeToPeakCache",
|
||
|
value: function addRangeToPeakCache(length, start, end) {
|
||
|
if (length != this.peakCacheLength) {
|
||
|
this.clearPeakCache();
|
||
|
this.peakCacheLength = length;
|
||
|
}
|
||
|
|
||
|
// Return ranges that weren't in the cache before the call.
|
||
|
var uncachedRanges = [];
|
||
|
var i = 0;
|
||
|
// Skip ranges before the current start.
|
||
|
while (i < this.peakCacheRanges.length && this.peakCacheRanges[i] < start) {
|
||
|
i++;
|
||
|
}
|
||
|
// If |i| is even, |start| falls after an existing range. Otherwise,
|
||
|
// |start| falls between an existing range, and the uncached region
|
||
|
// starts when we encounter the next node in |peakCacheRanges| or
|
||
|
// |end|, whichever comes first.
|
||
|
if (i % 2 == 0) {
|
||
|
uncachedRanges.push(start);
|
||
|
}
|
||
|
while (i < this.peakCacheRanges.length && this.peakCacheRanges[i] <= end) {
|
||
|
uncachedRanges.push(this.peakCacheRanges[i]);
|
||
|
i++;
|
||
|
}
|
||
|
// If |i| is even, |end| is after all existing ranges.
|
||
|
if (i % 2 == 0) {
|
||
|
uncachedRanges.push(end);
|
||
|
}
|
||
|
|
||
|
// Filter out the 0-length ranges.
|
||
|
uncachedRanges = uncachedRanges.filter(function (item, pos, arr) {
|
||
|
if (pos == 0) {
|
||
|
return item != arr[pos + 1];
|
||
|
} else if (pos == arr.length - 1) {
|
||
|
return item != arr[pos - 1];
|
||
|
}
|
||
|
return item != arr[pos - 1] && item != arr[pos + 1];
|
||
|
});
|
||
|
|
||
|
// Merge the two ranges together, uncachedRanges will either contain
|
||
|
// wholly new points, or duplicates of points in peakCacheRanges. If
|
||
|
// duplicates are detected, remove both and extend the range.
|
||
|
this.peakCacheRanges = this.peakCacheRanges.concat(uncachedRanges);
|
||
|
this.peakCacheRanges = this.peakCacheRanges.sort(function (a, b) {
|
||
|
return a - b;
|
||
|
}).filter(function (item, pos, arr) {
|
||
|
if (pos == 0) {
|
||
|
return item != arr[pos + 1];
|
||
|
} else if (pos == arr.length - 1) {
|
||
|
return item != arr[pos - 1];
|
||
|
}
|
||
|
return item != arr[pos - 1] && item != arr[pos + 1];
|
||
|
});
|
||
|
|
||
|
// Push the uncached ranges into an array of arrays for ease of
|
||
|
// iteration in the functions that call this.
|
||
|
var uncachedRangePairs = [];
|
||
|
for (i = 0; i < uncachedRanges.length; i += 2) {
|
||
|
uncachedRangePairs.push([uncachedRanges[i], uncachedRanges[i + 1]]);
|
||
|
}
|
||
|
return uncachedRangePairs;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* For testing
|
||
|
*
|
||
|
* @return {Number.<Array[]>} Array with arrays of numbers
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getCacheRanges",
|
||
|
value: function getCacheRanges() {
|
||
|
var peakCacheRangePairs = [];
|
||
|
var i;
|
||
|
for (i = 0; i < this.peakCacheRanges.length; i += 2) {
|
||
|
peakCacheRangePairs.push([this.peakCacheRanges[i], this.peakCacheRanges[i + 1]]);
|
||
|
}
|
||
|
return peakCacheRangePairs;
|
||
|
}
|
||
|
}]);
|
||
|
return PeakCache;
|
||
|
}();
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/absMax.js":
|
||
|
/*!****************************!*\
|
||
|
!*** ./src/util/absMax.js ***!
|
||
|
\****************************/
|
||
|
/***/ ((module, exports, __webpack_require__) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = absMax;
|
||
|
var _max = _interopRequireDefault(__webpack_require__(/*! ./max */ "./src/util/max.js"));
|
||
|
var _min = _interopRequireDefault(__webpack_require__(/*! ./min */ "./src/util/min.js"));
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
/**
|
||
|
* Get the largest absolute value in an array
|
||
|
*
|
||
|
* @param {Array} values Array of numbers
|
||
|
* @returns {Number} Largest number found
|
||
|
* @example console.log(max([-3, 2, 1]), max([-3, 2, 4])); // logs 3 4
|
||
|
* @since 4.3.0
|
||
|
*/
|
||
|
function absMax(values) {
|
||
|
var max = (0, _max.default)(values);
|
||
|
var min = (0, _min.default)(values);
|
||
|
return -min > max ? -min : max;
|
||
|
}
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/clamp.js":
|
||
|
/*!***************************!*\
|
||
|
!*** ./src/util/clamp.js ***!
|
||
|
\***************************/
|
||
|
/***/ ((module, exports) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = clamp;
|
||
|
/**
|
||
|
* Returns a number limited to the given range.
|
||
|
*
|
||
|
* @param {number} val The number to be limited to a range
|
||
|
* @param {number} min The lower boundary of the limit range
|
||
|
* @param {number} max The upper boundary of the limit range
|
||
|
* @returns {number} A number in the range [min, max]
|
||
|
*/
|
||
|
function clamp(val, min, max) {
|
||
|
return Math.min(Math.max(min, val), max);
|
||
|
}
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/fetch.js":
|
||
|
/*!***************************!*\
|
||
|
!*** ./src/util/fetch.js ***!
|
||
|
\***************************/
|
||
|
/***/ ((module, exports, __webpack_require__) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = fetchFile;
|
||
|
var _observer = _interopRequireDefault(__webpack_require__(/*! ./observer */ "./src/util/observer.js"));
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||
|
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, _toPropertyKey(descriptor.key), descriptor); } }
|
||
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /**
|
||
|
* @since 3.0.0
|
||
|
*/
|
||
|
var ProgressHandler = /*#__PURE__*/function () {
|
||
|
/**
|
||
|
* Instantiate ProgressHandler
|
||
|
*
|
||
|
* @param {Observer} instance The `fetchFile` observer instance.
|
||
|
* @param {Number} contentLength Content length.
|
||
|
* @param {Response} response Response object.
|
||
|
*/
|
||
|
function ProgressHandler(instance, contentLength, response) {
|
||
|
_classCallCheck(this, ProgressHandler);
|
||
|
this.instance = instance;
|
||
|
this.instance._reader = response.body.getReader();
|
||
|
this.total = parseInt(contentLength, 10);
|
||
|
this.loaded = 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A method that is called once, immediately after the `ReadableStream``
|
||
|
* is constructed.
|
||
|
*
|
||
|
* @param {ReadableStreamDefaultController} controller Controller instance
|
||
|
* used to control the stream.
|
||
|
*/
|
||
|
_createClass(ProgressHandler, [{
|
||
|
key: "start",
|
||
|
value: function start(controller) {
|
||
|
var _this = this;
|
||
|
var read = function read() {
|
||
|
// instance._reader.read() returns a promise that resolves
|
||
|
// when a value has been received
|
||
|
_this.instance._reader.read().then(function (_ref) {
|
||
|
var done = _ref.done,
|
||
|
value = _ref.value;
|
||
|
// result objects contain two properties:
|
||
|
// done - true if the stream has already given you all its data.
|
||
|
// value - some data. Always undefined when done is true.
|
||
|
if (done) {
|
||
|
// ensure onProgress called when content-length=0
|
||
|
if (_this.total === 0) {
|
||
|
_this.instance.onProgress.call(_this.instance, {
|
||
|
loaded: _this.loaded,
|
||
|
total: _this.total,
|
||
|
lengthComputable: false
|
||
|
});
|
||
|
}
|
||
|
// no more data needs to be consumed, close the stream
|
||
|
controller.close();
|
||
|
return;
|
||
|
}
|
||
|
_this.loaded += value.byteLength;
|
||
|
_this.instance.onProgress.call(_this.instance, {
|
||
|
loaded: _this.loaded,
|
||
|
total: _this.total,
|
||
|
lengthComputable: !(_this.total === 0)
|
||
|
});
|
||
|
// enqueue the next data chunk into our target stream
|
||
|
controller.enqueue(value);
|
||
|
read();
|
||
|
}).catch(function (error) {
|
||
|
controller.error(error);
|
||
|
});
|
||
|
};
|
||
|
read();
|
||
|
}
|
||
|
}]);
|
||
|
return ProgressHandler;
|
||
|
}();
|
||
|
/**
|
||
|
* Load a file using `fetch`.
|
||
|
*
|
||
|
* @param {object} options Request options to use. See example below.
|
||
|
* @returns {Observer} Observer instance
|
||
|
* @example
|
||
|
* // default options
|
||
|
* let options = {
|
||
|
* url: undefined,
|
||
|
* method: 'GET',
|
||
|
* mode: 'cors',
|
||
|
* credentials: 'same-origin',
|
||
|
* cache: 'default',
|
||
|
* responseType: 'json',
|
||
|
* requestHeaders: [],
|
||
|
* redirect: 'follow',
|
||
|
* referrer: 'client'
|
||
|
* };
|
||
|
*
|
||
|
* // override some options
|
||
|
* options.url = '../media/demo.wav';
|
||
|
|
||
|
* // available types: 'arraybuffer', 'blob', 'json' or 'text'
|
||
|
* options.responseType = 'arraybuffer';
|
||
|
*
|
||
|
* // make fetch call
|
||
|
* let request = util.fetchFile(options);
|
||
|
*
|
||
|
* // listen for events
|
||
|
* request.on('progress', e => {
|
||
|
* console.log('progress', e);
|
||
|
* });
|
||
|
*
|
||
|
* request.on('success', data => {
|
||
|
* console.log('success!', data);
|
||
|
* });
|
||
|
*
|
||
|
* request.on('error', e => {
|
||
|
* console.warn('fetchFile error: ', e);
|
||
|
* });
|
||
|
*/
|
||
|
function fetchFile(options) {
|
||
|
if (!options) {
|
||
|
throw new Error('fetch options missing');
|
||
|
} else if (!options.url) {
|
||
|
throw new Error('fetch url missing');
|
||
|
}
|
||
|
var instance = new _observer.default();
|
||
|
var fetchHeaders = new Headers();
|
||
|
var fetchRequest = new Request(options.url);
|
||
|
|
||
|
// add ability to abort
|
||
|
instance.controller = new AbortController();
|
||
|
|
||
|
// check if headers have to be added
|
||
|
if (options && options.requestHeaders) {
|
||
|
// add custom request headers
|
||
|
options.requestHeaders.forEach(function (header) {
|
||
|
fetchHeaders.append(header.key, header.value);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// parse fetch options
|
||
|
var responseType = options.responseType || 'json';
|
||
|
var fetchOptions = {
|
||
|
method: options.method || 'GET',
|
||
|
headers: fetchHeaders,
|
||
|
mode: options.mode || 'cors',
|
||
|
credentials: options.credentials || 'same-origin',
|
||
|
cache: options.cache || 'default',
|
||
|
redirect: options.redirect || 'follow',
|
||
|
referrer: options.referrer || 'client',
|
||
|
signal: instance.controller.signal
|
||
|
};
|
||
|
fetch(fetchRequest, fetchOptions).then(function (response) {
|
||
|
// store response reference
|
||
|
instance.response = response;
|
||
|
var progressAvailable = true;
|
||
|
if (!response.body) {
|
||
|
// ReadableStream is not yet supported in this browser
|
||
|
// see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
|
||
|
progressAvailable = false;
|
||
|
}
|
||
|
|
||
|
// Server must send CORS header "Access-Control-Expose-Headers: content-length"
|
||
|
var contentLength = response.headers.get('content-length');
|
||
|
if (contentLength === null) {
|
||
|
// Content-Length server response header missing.
|
||
|
// Don't evaluate download progress if we can't compare against a total size
|
||
|
// see https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Access-Control-Expose-Headers
|
||
|
progressAvailable = false;
|
||
|
}
|
||
|
if (!progressAvailable) {
|
||
|
// not able to check download progress so skip it
|
||
|
return response;
|
||
|
}
|
||
|
|
||
|
// fire progress event when during load
|
||
|
instance.onProgress = function (e) {
|
||
|
instance.fireEvent('progress', e);
|
||
|
};
|
||
|
return new Response(new ReadableStream(new ProgressHandler(instance, contentLength, response)), fetchOptions);
|
||
|
}).then(function (response) {
|
||
|
var errMsg;
|
||
|
if (response.ok) {
|
||
|
switch (responseType) {
|
||
|
case 'arraybuffer':
|
||
|
return response.arrayBuffer();
|
||
|
case 'json':
|
||
|
return response.json();
|
||
|
case 'blob':
|
||
|
return response.blob();
|
||
|
case 'text':
|
||
|
return response.text();
|
||
|
default:
|
||
|
errMsg = 'Unknown responseType: ' + responseType;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!errMsg) {
|
||
|
errMsg = 'HTTP error status: ' + response.status;
|
||
|
}
|
||
|
throw new Error(errMsg);
|
||
|
}).then(function (response) {
|
||
|
instance.fireEvent('success', response);
|
||
|
}).catch(function (error) {
|
||
|
instance.fireEvent('error', error);
|
||
|
});
|
||
|
|
||
|
// return the fetch request
|
||
|
instance.fetchRequest = fetchRequest;
|
||
|
return instance;
|
||
|
}
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/frame.js":
|
||
|
/*!***************************!*\
|
||
|
!*** ./src/util/frame.js ***!
|
||
|
\***************************/
|
||
|
/***/ ((module, exports, __webpack_require__) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = frame;
|
||
|
var _requestAnimationFrame = _interopRequireDefault(__webpack_require__(/*! ./request-animation-frame */ "./src/util/request-animation-frame.js"));
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
/**
|
||
|
* Create a function which will be called at the next requestAnimationFrame
|
||
|
* cycle
|
||
|
*
|
||
|
* @param {function} func The function to call
|
||
|
*
|
||
|
* @return {func} The function wrapped within a requestAnimationFrame
|
||
|
*/
|
||
|
function frame(func) {
|
||
|
return function () {
|
||
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||
|
args[_key] = arguments[_key];
|
||
|
}
|
||
|
return (0, _requestAnimationFrame.default)(function () {
|
||
|
return func.apply(void 0, args);
|
||
|
});
|
||
|
};
|
||
|
}
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/get-id.js":
|
||
|
/*!****************************!*\
|
||
|
!*** ./src/util/get-id.js ***!
|
||
|
\****************************/
|
||
|
/***/ ((module, exports) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = getId;
|
||
|
/**
|
||
|
* Get a random prefixed ID
|
||
|
*
|
||
|
* @param {String} prefix Prefix to use. Default is `'wavesurfer_'`.
|
||
|
* @returns {String} Random prefixed ID
|
||
|
* @example
|
||
|
* console.log(getId()); // logs 'wavesurfer_b5pors4ru6g'
|
||
|
*
|
||
|
* let prefix = 'foo-';
|
||
|
* console.log(getId(prefix)); // logs 'foo-b5pors4ru6g'
|
||
|
*/
|
||
|
function getId(prefix) {
|
||
|
if (prefix === undefined) {
|
||
|
prefix = 'wavesurfer_';
|
||
|
}
|
||
|
return prefix + Math.random().toString(32).substring(2);
|
||
|
}
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/index.js":
|
||
|
/*!***************************!*\
|
||
|
!*** ./src/util/index.js ***!
|
||
|
\***************************/
|
||
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
Object.defineProperty(exports, "Observer", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _observer.default;
|
||
|
}
|
||
|
}));
|
||
|
Object.defineProperty(exports, "absMax", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _absMax.default;
|
||
|
}
|
||
|
}));
|
||
|
Object.defineProperty(exports, "clamp", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _clamp.default;
|
||
|
}
|
||
|
}));
|
||
|
Object.defineProperty(exports, "debounce", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _debounce.default;
|
||
|
}
|
||
|
}));
|
||
|
Object.defineProperty(exports, "fetchFile", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _fetch.default;
|
||
|
}
|
||
|
}));
|
||
|
Object.defineProperty(exports, "frame", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _frame.default;
|
||
|
}
|
||
|
}));
|
||
|
Object.defineProperty(exports, "getId", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _getId.default;
|
||
|
}
|
||
|
}));
|
||
|
Object.defineProperty(exports, "max", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _max.default;
|
||
|
}
|
||
|
}));
|
||
|
Object.defineProperty(exports, "min", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _min.default;
|
||
|
}
|
||
|
}));
|
||
|
Object.defineProperty(exports, "preventClick", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _preventClick.default;
|
||
|
}
|
||
|
}));
|
||
|
Object.defineProperty(exports, "requestAnimationFrame", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _requestAnimationFrame.default;
|
||
|
}
|
||
|
}));
|
||
|
Object.defineProperty(exports, "style", ({
|
||
|
enumerable: true,
|
||
|
get: function get() {
|
||
|
return _style.default;
|
||
|
}
|
||
|
}));
|
||
|
var _getId = _interopRequireDefault(__webpack_require__(/*! ./get-id */ "./src/util/get-id.js"));
|
||
|
var _max = _interopRequireDefault(__webpack_require__(/*! ./max */ "./src/util/max.js"));
|
||
|
var _min = _interopRequireDefault(__webpack_require__(/*! ./min */ "./src/util/min.js"));
|
||
|
var _absMax = _interopRequireDefault(__webpack_require__(/*! ./absMax */ "./src/util/absMax.js"));
|
||
|
var _observer = _interopRequireDefault(__webpack_require__(/*! ./observer */ "./src/util/observer.js"));
|
||
|
var _style = _interopRequireDefault(__webpack_require__(/*! ./style */ "./src/util/style.js"));
|
||
|
var _requestAnimationFrame = _interopRequireDefault(__webpack_require__(/*! ./request-animation-frame */ "./src/util/request-animation-frame.js"));
|
||
|
var _frame = _interopRequireDefault(__webpack_require__(/*! ./frame */ "./src/util/frame.js"));
|
||
|
var _debounce = _interopRequireDefault(__webpack_require__(/*! debounce */ "./node_modules/debounce/index.js"));
|
||
|
var _preventClick = _interopRequireDefault(__webpack_require__(/*! ./prevent-click */ "./src/util/prevent-click.js"));
|
||
|
var _fetch = _interopRequireDefault(__webpack_require__(/*! ./fetch */ "./src/util/fetch.js"));
|
||
|
var _clamp = _interopRequireDefault(__webpack_require__(/*! ./clamp */ "./src/util/clamp.js"));
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/max.js":
|
||
|
/*!*************************!*\
|
||
|
!*** ./src/util/max.js ***!
|
||
|
\*************************/
|
||
|
/***/ ((module, exports) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = max;
|
||
|
/**
|
||
|
* Get the largest value
|
||
|
*
|
||
|
* @param {Array} values Array of numbers
|
||
|
* @returns {Number} Largest number found
|
||
|
* @example console.log(max([1, 2, 3])); // logs 3
|
||
|
*/
|
||
|
function max(values) {
|
||
|
var largest = -Infinity;
|
||
|
Object.keys(values).forEach(function (i) {
|
||
|
if (values[i] > largest) {
|
||
|
largest = values[i];
|
||
|
}
|
||
|
});
|
||
|
return largest;
|
||
|
}
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/min.js":
|
||
|
/*!*************************!*\
|
||
|
!*** ./src/util/min.js ***!
|
||
|
\*************************/
|
||
|
/***/ ((module, exports) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = min;
|
||
|
/**
|
||
|
* Get the smallest value
|
||
|
*
|
||
|
* @param {Array} values Array of numbers
|
||
|
* @returns {Number} Smallest number found
|
||
|
* @example console.log(min([1, 2, 3])); // logs 1
|
||
|
*/
|
||
|
function min(values) {
|
||
|
var smallest = Number(Infinity);
|
||
|
Object.keys(values).forEach(function (i) {
|
||
|
if (values[i] < smallest) {
|
||
|
smallest = values[i];
|
||
|
}
|
||
|
});
|
||
|
return smallest;
|
||
|
}
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/observer.js":
|
||
|
/*!******************************!*\
|
||
|
!*** ./src/util/observer.js ***!
|
||
|
\******************************/
|
||
|
/***/ ((module, exports) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = void 0;
|
||
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||
|
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, _toPropertyKey(descriptor.key), descriptor); } }
|
||
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||
|
/**
|
||
|
* @typedef {Object} ListenerDescriptor
|
||
|
* @property {string} name The name of the event
|
||
|
* @property {function} callback The callback
|
||
|
* @property {function} un The function to call to remove the listener
|
||
|
*/
|
||
|
/**
|
||
|
* Observer class
|
||
|
*/
|
||
|
var Observer = exports["default"] = /*#__PURE__*/function () {
|
||
|
/**
|
||
|
* Instantiate Observer
|
||
|
*/
|
||
|
function Observer() {
|
||
|
_classCallCheck(this, Observer);
|
||
|
/**
|
||
|
* @private
|
||
|
* @todo Initialise the handlers here already and remove the conditional
|
||
|
* assignment in `on()`
|
||
|
*/
|
||
|
this._disabledEventEmissions = [];
|
||
|
this.handlers = null;
|
||
|
}
|
||
|
/**
|
||
|
* Attach a handler function for an event.
|
||
|
*
|
||
|
* @param {string} event Name of the event to listen to
|
||
|
* @param {function} fn The callback to trigger when the event is fired
|
||
|
* @return {ListenerDescriptor} The event descriptor
|
||
|
*/
|
||
|
_createClass(Observer, [{
|
||
|
key: "on",
|
||
|
value: function on(event, fn) {
|
||
|
var _this = this;
|
||
|
if (!this.handlers) {
|
||
|
this.handlers = {};
|
||
|
}
|
||
|
var handlers = this.handlers[event];
|
||
|
if (!handlers) {
|
||
|
handlers = this.handlers[event] = [];
|
||
|
}
|
||
|
handlers.push(fn);
|
||
|
|
||
|
// Return an event descriptor
|
||
|
return {
|
||
|
name: event,
|
||
|
callback: fn,
|
||
|
un: function un(e, fn) {
|
||
|
return _this.un(e, fn);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove an event handler.
|
||
|
*
|
||
|
* @param {string} event Name of the event the listener that should be
|
||
|
* removed listens to
|
||
|
* @param {function} fn The callback that should be removed
|
||
|
*/
|
||
|
}, {
|
||
|
key: "un",
|
||
|
value: function un(event, fn) {
|
||
|
if (!this.handlers) {
|
||
|
return;
|
||
|
}
|
||
|
var handlers = this.handlers[event];
|
||
|
var i;
|
||
|
if (handlers) {
|
||
|
if (fn) {
|
||
|
for (i = handlers.length - 1; i >= 0; i--) {
|
||
|
if (handlers[i] == fn) {
|
||
|
handlers.splice(i, 1);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
handlers.length = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove all event handlers.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "unAll",
|
||
|
value: function unAll() {
|
||
|
this.handlers = null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Attach a handler to an event. The handler is executed at most once per
|
||
|
* event type.
|
||
|
*
|
||
|
* @param {string} event The event to listen to
|
||
|
* @param {function} handler The callback that is only to be called once
|
||
|
* @return {ListenerDescriptor} The event descriptor
|
||
|
*/
|
||
|
}, {
|
||
|
key: "once",
|
||
|
value: function once(event, handler) {
|
||
|
var _this2 = this;
|
||
|
var fn = function fn() {
|
||
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||
|
args[_key] = arguments[_key];
|
||
|
}
|
||
|
/* eslint-disable no-invalid-this */
|
||
|
handler.apply(_this2, args);
|
||
|
/* eslint-enable no-invalid-this */
|
||
|
setTimeout(function () {
|
||
|
_this2.un(event, fn);
|
||
|
}, 0);
|
||
|
};
|
||
|
return this.on(event, fn);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Disable firing a list of events by name. When specified, event handlers for any event type
|
||
|
* passed in here will not be called.
|
||
|
*
|
||
|
* @since 4.0.0
|
||
|
* @param {string[]} eventNames an array of event names to disable emissions for
|
||
|
* @example
|
||
|
* // disable seek and interaction events
|
||
|
* wavesurfer.setDisabledEventEmissions(['seek', 'interaction']);
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setDisabledEventEmissions",
|
||
|
value: function setDisabledEventEmissions(eventNames) {
|
||
|
this._disabledEventEmissions = eventNames;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* plugins borrow part of this class without calling the constructor,
|
||
|
* so we have to be careful about _disabledEventEmissions
|
||
|
*/
|
||
|
}, {
|
||
|
key: "_isDisabledEventEmission",
|
||
|
value: function _isDisabledEventEmission(event) {
|
||
|
return this._disabledEventEmissions && this._disabledEventEmissions.includes(event);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Manually fire an event
|
||
|
*
|
||
|
* @param {string} event The event to fire manually
|
||
|
* @param {...any} args The arguments with which to call the listeners
|
||
|
*/
|
||
|
}, {
|
||
|
key: "fireEvent",
|
||
|
value: function fireEvent(event) {
|
||
|
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
|
||
|
args[_key2 - 1] = arguments[_key2];
|
||
|
}
|
||
|
if (!this.handlers || this._isDisabledEventEmission(event)) {
|
||
|
return;
|
||
|
}
|
||
|
var handlers = this.handlers[event];
|
||
|
handlers && handlers.forEach(function (fn) {
|
||
|
fn.apply(void 0, args);
|
||
|
});
|
||
|
}
|
||
|
}]);
|
||
|
return Observer;
|
||
|
}();
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/prevent-click.js":
|
||
|
/*!***********************************!*\
|
||
|
!*** ./src/util/prevent-click.js ***!
|
||
|
\***********************************/
|
||
|
/***/ ((module, exports) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = preventClick;
|
||
|
/**
|
||
|
* Stops propagation of click event and removes event listener
|
||
|
*
|
||
|
* @private
|
||
|
* @param {object} event The click event
|
||
|
*/
|
||
|
function preventClickHandler(event) {
|
||
|
event.stopPropagation();
|
||
|
document.body.removeEventListener('click', preventClickHandler, true);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Starts listening for click event and prevent propagation
|
||
|
*
|
||
|
* @param {object} values Values
|
||
|
*/
|
||
|
function preventClick(values) {
|
||
|
document.body.addEventListener('click', preventClickHandler, true);
|
||
|
}
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/request-animation-frame.js":
|
||
|
/*!*********************************************!*\
|
||
|
!*** ./src/util/request-animation-frame.js ***!
|
||
|
\*********************************************/
|
||
|
/***/ ((module, exports) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = void 0;
|
||
|
/* eslint-disable valid-jsdoc */
|
||
|
/**
|
||
|
* Returns the `requestAnimationFrame` function for the browser, or a shim with
|
||
|
* `setTimeout` if the function is not found
|
||
|
*
|
||
|
* @return {function} Available `requestAnimationFrame` function for the browser
|
||
|
*/
|
||
|
var _default = exports["default"] = (window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback, element) {
|
||
|
return setTimeout(callback, 1000 / 60);
|
||
|
}).bind(window);
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/util/style.js":
|
||
|
/*!***************************!*\
|
||
|
!*** ./src/util/style.js ***!
|
||
|
\***************************/
|
||
|
/***/ ((module, exports) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = style;
|
||
|
/**
|
||
|
* Apply a map of styles to an element
|
||
|
*
|
||
|
* @param {HTMLElement} el The element that the styles will be applied to
|
||
|
* @param {Object} styles The map of propName: attribute, both are used as-is
|
||
|
*
|
||
|
* @return {HTMLElement} el
|
||
|
*/
|
||
|
function style(el, styles) {
|
||
|
Object.keys(styles).forEach(function (prop) {
|
||
|
if (el.style[prop] !== styles[prop]) {
|
||
|
el.style[prop] = styles[prop];
|
||
|
}
|
||
|
});
|
||
|
return el;
|
||
|
}
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/wavesurfer.js":
|
||
|
/*!***************************!*\
|
||
|
!*** ./src/wavesurfer.js ***!
|
||
|
\***************************/
|
||
|
/***/ ((module, exports, __webpack_require__) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = void 0;
|
||
|
var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js"));
|
||
|
var _drawer = _interopRequireDefault(__webpack_require__(/*! ./drawer.multicanvas */ "./src/drawer.multicanvas.js"));
|
||
|
var _webaudio = _interopRequireDefault(__webpack_require__(/*! ./webaudio */ "./src/webaudio.js"));
|
||
|
var _mediaelement = _interopRequireDefault(__webpack_require__(/*! ./mediaelement */ "./src/mediaelement.js"));
|
||
|
var _peakcache = _interopRequireDefault(__webpack_require__(/*! ./peakcache */ "./src/peakcache.js"));
|
||
|
var _mediaelementWebaudio = _interopRequireDefault(__webpack_require__(/*! ./mediaelement-webaudio */ "./src/mediaelement-webaudio.js"));
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||
|
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
|
||
|
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 _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
||
|
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||
|
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
||
|
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 } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||
|
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||
|
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, _toPropertyKey(descriptor.key), descriptor); } }
|
||
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||
|
/*
|
||
|
* This work is licensed under a BSD-3-Clause License.
|
||
|
*/
|
||
|
/** @external {HTMLElement} https://developer.mozilla.org/en/docs/Web/API/HTMLElement */
|
||
|
/** @external {OfflineAudioContext} https://developer.mozilla.org/en-US/docs/Web/API/OfflineAudioContext */
|
||
|
/** @external {File} https://developer.mozilla.org/en-US/docs/Web/API/File */
|
||
|
/** @external {Blob} https://developer.mozilla.org/en-US/docs/Web/API/Blob */
|
||
|
/** @external {CanvasRenderingContext2D} https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D */
|
||
|
/** @external {MediaStreamConstraints} https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints */
|
||
|
/** @external {AudioNode} https://developer.mozilla.org/de/docs/Web/API/AudioNode */
|
||
|
/**
|
||
|
* @typedef {Object} WavesurferParams
|
||
|
* @property {AudioContext} audioContext=null Use your own previously
|
||
|
* initialized AudioContext or leave blank.
|
||
|
* @property {number} audioRate=1 Speed at which to play audio. Lower number is
|
||
|
* slower.
|
||
|
* @property {ScriptProcessorNode} audioScriptProcessor=null Use your own previously
|
||
|
* initialized ScriptProcessorNode or leave blank.
|
||
|
* @property {boolean} autoCenter=true If a scrollbar is present, center the
|
||
|
* waveform on current progress
|
||
|
* @property {number} autoCenterRate=5 If autoCenter is active, rate at which the
|
||
|
* waveform is centered
|
||
|
* @property {boolean} autoCenterImmediately=false If autoCenter is active, immediately
|
||
|
* center waveform on current progress
|
||
|
* @property {string} backend='WebAudio' `'WebAudio'|'MediaElement'|'MediaElementWebAudio'` In most cases
|
||
|
* you don't have to set this manually. MediaElement is a fallback for unsupported browsers.
|
||
|
* MediaElementWebAudio allows to use WebAudio API also with big audio files, loading audio like with
|
||
|
* MediaElement backend (HTML5 audio tag). You have to use the same methods of MediaElement backend for loading and
|
||
|
* playback, giving also peaks, so the audio data are not decoded. In this way you can use WebAudio features, like filters,
|
||
|
* also with audio with big duration. For example:
|
||
|
* ` wavesurfer.load(url | HTMLMediaElement, peaks, preload, duration);
|
||
|
* wavesurfer.play();
|
||
|
* wavesurfer.setFilter(customFilter);
|
||
|
* `
|
||
|
* @property {string} backgroundColor=null Change background color of the
|
||
|
* waveform container.
|
||
|
* @property {number} barHeight=1 The height of the wave bars.
|
||
|
* @property {number} barRadius=0 The radius of the wave bars. Makes bars rounded
|
||
|
* @property {number} barGap=null The optional spacing between bars of the wave,
|
||
|
* if not provided will be calculated in legacy format.
|
||
|
* @property {number} barWidth=null Draw the waveform using bars.
|
||
|
* @property {number} barMinHeight=null If specified, draw at least a bar of this height,
|
||
|
* eliminating waveform gaps
|
||
|
* @property {boolean} closeAudioContext=false Close and nullify all audio
|
||
|
* contexts when the destroy method is called.
|
||
|
* @property {!string|HTMLElement} container CSS selector or HTML element where
|
||
|
* the waveform should be drawn. This is the only required parameter.
|
||
|
* @property {string} cursorColor='#333' The fill color of the cursor indicating
|
||
|
* the playhead position.
|
||
|
* @property {number} cursorWidth=1 Measured in pixels.
|
||
|
* @property {object} drawingContextAttributes={desynchronized: false} Drawing context
|
||
|
* attributes.
|
||
|
* @property {number} duration=null Optional audio length so pre-rendered peaks
|
||
|
* can be display immediately for example.
|
||
|
* @property {boolean} fillParent=true Whether to fill the entire container or
|
||
|
* draw only according to `minPxPerSec`.
|
||
|
* @property {boolean} forceDecode=false Force decoding of audio using web audio
|
||
|
* when zooming to get a more detailed waveform.
|
||
|
* @property {number} height=128 The height of the waveform. Measured in
|
||
|
* pixels.
|
||
|
* @property {boolean} hideScrollbar=false Whether to hide the horizontal
|
||
|
* scrollbar when one would normally be shown.
|
||
|
* @property {boolean} interact=true Whether the mouse interaction will be
|
||
|
* enabled at initialization. You can switch this parameter at any time later
|
||
|
* on.
|
||
|
* @property {boolean} loopSelection=true (Use with regions plugin) Enable
|
||
|
* looping of selected regions
|
||
|
* @property {number} maxCanvasWidth=4000 Maximum width of a single canvas in
|
||
|
* pixels, excluding a small overlap (2 * `pixelRatio`, rounded up to the next
|
||
|
* even integer). If the waveform is longer than this value, additional canvases
|
||
|
* will be used to render the waveform, which is useful for very large waveforms
|
||
|
* that may be too wide for browsers to draw on a single canvas.
|
||
|
* @property {boolean} mediaControls=false (Use with backend `MediaElement` or `MediaElementWebAudio`)
|
||
|
* this enables the native controls for the media element
|
||
|
* @property {string} mediaType='audio' (Use with backend `MediaElement` or `MediaElementWebAudio`)
|
||
|
* `'audio'|'video'` ('video' only for `MediaElement`)
|
||
|
* @property {number} minPxPerSec=20 Minimum number of pixels per second of
|
||
|
* audio.
|
||
|
* @property {boolean} normalize=false If true, normalize by the maximum peak
|
||
|
* instead of 1.0.
|
||
|
* @property {boolean} partialRender=false Use the PeakCache to improve
|
||
|
* rendering speed of large waveforms
|
||
|
* @property {number} pixelRatio=window.devicePixelRatio The pixel ratio used to
|
||
|
* calculate display
|
||
|
* @property {PluginDefinition[]} plugins=[] An array of plugin definitions to
|
||
|
* register during instantiation, they will be directly initialised unless they
|
||
|
* are added with the `deferInit` property set to true.
|
||
|
* @property {string} progressColor='#555' The fill color of the part of the
|
||
|
* waveform behind the cursor. When `progressColor` and `waveColor` are the same
|
||
|
* the progress wave is not rendered at all.
|
||
|
* @property {boolean} removeMediaElementOnDestroy=true Set to false to keep the
|
||
|
* media element in the DOM when the player is destroyed. This is useful when
|
||
|
* reusing an existing media element via the `loadMediaElement` method.
|
||
|
* @property {Object} renderer=MultiCanvas Can be used to inject a custom
|
||
|
* renderer.
|
||
|
* @property {boolean|number} responsive=false If set to `true` resize the
|
||
|
* waveform, when the window is resized. This is debounced with a `100ms`
|
||
|
* timeout by default. If this parameter is a number it represents that timeout.
|
||
|
* @property {boolean} rtl=false If set to `true`, renders waveform from
|
||
|
* right-to-left.
|
||
|
* @property {boolean} scrollParent=false Whether to scroll the container with a
|
||
|
* lengthy waveform. Otherwise the waveform is shrunk to the container width
|
||
|
* (see fillParent).
|
||
|
* @property {number} skipLength=2 Number of seconds to skip with the
|
||
|
* skipForward() and skipBackward() methods.
|
||
|
* @property {boolean} splitChannels=false Render with separate waveforms for
|
||
|
* the channels of the audio
|
||
|
* @property {SplitChannelOptions} splitChannelsOptions={} Options for splitChannel rendering
|
||
|
* @property {string} waveColor='#999' The fill color of the waveform after the
|
||
|
* cursor.
|
||
|
* @property {object} xhr={} XHR options. For example:
|
||
|
* `let xhr = {
|
||
|
* cache: 'default',
|
||
|
* mode: 'cors',
|
||
|
* method: 'GET',
|
||
|
* credentials: 'same-origin',
|
||
|
* redirect: 'follow',
|
||
|
* referrer: 'client',
|
||
|
* requestHeaders: [
|
||
|
* {
|
||
|
* key: 'Authorization',
|
||
|
* value: 'my-token'
|
||
|
* }
|
||
|
* ]
|
||
|
* };`
|
||
|
*/
|
||
|
/**
|
||
|
* @typedef {Object} PluginDefinition
|
||
|
* @desc The Object used to describe a plugin
|
||
|
* @example wavesurfer.addPlugin(pluginDefinition);
|
||
|
* @property {string} name The name of the plugin, the plugin instance will be
|
||
|
* added as a property to the wavesurfer instance under this name
|
||
|
* @property {?Object} staticProps The properties that should be added to the
|
||
|
* wavesurfer instance as static properties
|
||
|
* @property {?boolean} deferInit Don't initialise plugin
|
||
|
* automatically
|
||
|
* @property {Object} params={} The plugin parameters, they are the first parameter
|
||
|
* passed to the plugin class constructor function
|
||
|
* @property {PluginClass} instance The plugin instance factory, is called with
|
||
|
* the dependency specified in extends. Returns the plugin class.
|
||
|
*/
|
||
|
/**
|
||
|
* @typedef {Object} SplitChannelOptions
|
||
|
* @desc parameters applied when splitChannels option is true
|
||
|
* @property {boolean} overlay=false determines whether channels are rendered on top of each other or on separate tracks
|
||
|
* @property {object} channelColors={} object describing color for each channel. Example:
|
||
|
* {
|
||
|
* 0: {
|
||
|
* progressColor: 'green',
|
||
|
* waveColor: 'pink'
|
||
|
* },
|
||
|
* 1: {
|
||
|
* progressColor: 'orange',
|
||
|
* waveColor: 'purple'
|
||
|
* }
|
||
|
* }
|
||
|
* @property {number[]} filterChannels=[] indexes of channels to be hidden from rendering
|
||
|
* @property {boolean} relativeNormalization=false determines whether
|
||
|
* normalization is done per channel or maintains proportionality between
|
||
|
* channels. Only applied when normalize and splitChannels are both true.
|
||
|
* @since 4.3.0
|
||
|
*/
|
||
|
/**
|
||
|
* @interface PluginClass
|
||
|
*
|
||
|
* @desc This is the interface which is implemented by all plugin classes. Note
|
||
|
* that this only turns into an observer after being passed through
|
||
|
* `wavesurfer.addPlugin`.
|
||
|
*
|
||
|
* @extends {Observer}
|
||
|
*/
|
||
|
var PluginClass = /*#__PURE__*/function () {
|
||
|
/**
|
||
|
* Construct the plugin
|
||
|
*
|
||
|
* @param {Object} params={} The plugin params (specific to the plugin)
|
||
|
* @param {Object} ws The wavesurfer instance
|
||
|
*/
|
||
|
function PluginClass(params, ws) {
|
||
|
_classCallCheck(this, PluginClass);
|
||
|
}
|
||
|
/**
|
||
|
* Initialise the plugin
|
||
|
*
|
||
|
* Start doing something. This is called by
|
||
|
* `wavesurfer.initPlugin(pluginName)`
|
||
|
*/
|
||
|
_createClass(PluginClass, [{
|
||
|
key: "create",
|
||
|
value:
|
||
|
/**
|
||
|
* Plugin definition factory
|
||
|
*
|
||
|
* This function must be used to create a plugin definition which can be
|
||
|
* used by wavesurfer to correctly instantiate the plugin.
|
||
|
*
|
||
|
* It returns a `PluginDefinition` object representing the plugin.
|
||
|
*
|
||
|
* @param {Object} params={} The plugin params (specific to the plugin)
|
||
|
*/
|
||
|
function create(params) {}
|
||
|
}, {
|
||
|
key: "init",
|
||
|
value: function init() {}
|
||
|
/**
|
||
|
* Destroy the plugin instance
|
||
|
*
|
||
|
* Stop doing something. This is called by
|
||
|
* `wavesurfer.destroyPlugin(pluginName)`
|
||
|
*/
|
||
|
}, {
|
||
|
key: "destroy",
|
||
|
value: function destroy() {}
|
||
|
}]);
|
||
|
return PluginClass;
|
||
|
}();
|
||
|
/**
|
||
|
* WaveSurfer core library class
|
||
|
*
|
||
|
* @extends {Observer}
|
||
|
* @example
|
||
|
* const params = {
|
||
|
* container: '#waveform',
|
||
|
* waveColor: 'violet',
|
||
|
* progressColor: 'purple'
|
||
|
* };
|
||
|
*
|
||
|
* // initialise like this
|
||
|
* const wavesurfer = WaveSurfer.create(params);
|
||
|
*
|
||
|
* // or like this ...
|
||
|
* const wavesurfer = new WaveSurfer(params);
|
||
|
* wavesurfer.init();
|
||
|
*
|
||
|
* // load audio file
|
||
|
* wavesurfer.load('example/media/demo.wav');
|
||
|
*/
|
||
|
var WaveSurfer = exports["default"] = /*#__PURE__*/function (_util$Observer) {
|
||
|
_inherits(WaveSurfer, _util$Observer);
|
||
|
/**
|
||
|
* Initialise wavesurfer instance
|
||
|
*
|
||
|
* @param {WavesurferParams} params Instantiation options for wavesurfer
|
||
|
* @example
|
||
|
* const wavesurfer = new WaveSurfer(params);
|
||
|
* @returns {this} Wavesurfer instance
|
||
|
*/
|
||
|
function WaveSurfer(params) {
|
||
|
var _this;
|
||
|
_classCallCheck(this, WaveSurfer);
|
||
|
_this = _callSuper(this, WaveSurfer);
|
||
|
/**
|
||
|
* Extract relevant parameters (or defaults)
|
||
|
* @private
|
||
|
*/
|
||
|
_this.defaultParams = {
|
||
|
audioContext: null,
|
||
|
audioScriptProcessor: null,
|
||
|
audioRate: 1,
|
||
|
autoCenter: true,
|
||
|
autoCenterRate: 5,
|
||
|
autoCenterImmediately: false,
|
||
|
backend: 'WebAudio',
|
||
|
backgroundColor: null,
|
||
|
barHeight: 1,
|
||
|
barRadius: 0,
|
||
|
barGap: null,
|
||
|
barMinHeight: null,
|
||
|
container: null,
|
||
|
cursorColor: '#333',
|
||
|
cursorWidth: 1,
|
||
|
dragSelection: true,
|
||
|
drawingContextAttributes: {
|
||
|
// Boolean that hints the user agent to reduce the latency
|
||
|
// by desynchronizing the canvas paint cycle from the event
|
||
|
// loop
|
||
|
desynchronized: false
|
||
|
},
|
||
|
duration: null,
|
||
|
fillParent: true,
|
||
|
forceDecode: false,
|
||
|
height: 128,
|
||
|
hideScrollbar: false,
|
||
|
interact: true,
|
||
|
loopSelection: true,
|
||
|
maxCanvasWidth: 4000,
|
||
|
mediaContainer: null,
|
||
|
mediaControls: false,
|
||
|
mediaType: 'audio',
|
||
|
minPxPerSec: 20,
|
||
|
normalize: false,
|
||
|
partialRender: false,
|
||
|
pixelRatio: window.devicePixelRatio || screen.deviceXDPI / screen.logicalXDPI,
|
||
|
plugins: [],
|
||
|
progressColor: '#555',
|
||
|
removeMediaElementOnDestroy: true,
|
||
|
renderer: _drawer.default,
|
||
|
responsive: false,
|
||
|
rtl: false,
|
||
|
scrollParent: false,
|
||
|
skipLength: 2,
|
||
|
splitChannels: false,
|
||
|
splitChannelsOptions: {
|
||
|
overlay: false,
|
||
|
channelColors: {},
|
||
|
filterChannels: [],
|
||
|
relativeNormalization: false
|
||
|
},
|
||
|
waveColor: '#999',
|
||
|
xhr: {}
|
||
|
};
|
||
|
_this.backends = {
|
||
|
MediaElement: _mediaelement.default,
|
||
|
WebAudio: _webaudio.default,
|
||
|
MediaElementWebAudio: _mediaelementWebaudio.default
|
||
|
};
|
||
|
_this.util = util;
|
||
|
_this.params = Object.assign({}, _this.defaultParams, params);
|
||
|
_this.params.splitChannelsOptions = Object.assign({}, _this.defaultParams.splitChannelsOptions, params.splitChannelsOptions);
|
||
|
/** @private */
|
||
|
_this.container = 'string' == typeof params.container ? document.querySelector(_this.params.container) : _this.params.container;
|
||
|
if (!_this.container) {
|
||
|
throw new Error('Container element not found');
|
||
|
}
|
||
|
if (_this.params.mediaContainer == null) {
|
||
|
/** @private */
|
||
|
_this.mediaContainer = _this.container;
|
||
|
} else if (typeof _this.params.mediaContainer == 'string') {
|
||
|
/** @private */
|
||
|
_this.mediaContainer = document.querySelector(_this.params.mediaContainer);
|
||
|
} else {
|
||
|
/** @private */
|
||
|
_this.mediaContainer = _this.params.mediaContainer;
|
||
|
}
|
||
|
if (!_this.mediaContainer) {
|
||
|
throw new Error('Media Container element not found');
|
||
|
}
|
||
|
if (_this.params.maxCanvasWidth <= 1) {
|
||
|
throw new Error('maxCanvasWidth must be greater than 1');
|
||
|
} else if (_this.params.maxCanvasWidth % 2 == 1) {
|
||
|
throw new Error('maxCanvasWidth must be an even number');
|
||
|
}
|
||
|
if (_this.params.rtl === true) {
|
||
|
util.style(_this.container, {
|
||
|
transform: 'rotateY(180deg)'
|
||
|
});
|
||
|
}
|
||
|
if (_this.params.backgroundColor) {
|
||
|
_this.setBackgroundColor(_this.params.backgroundColor);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @private Used to save the current volume when muting so we can
|
||
|
* restore once unmuted
|
||
|
* @type {number}
|
||
|
*/
|
||
|
_this.savedVolume = 0;
|
||
|
|
||
|
/**
|
||
|
* @private The current muted state
|
||
|
* @type {boolean}
|
||
|
*/
|
||
|
_this.isMuted = false;
|
||
|
|
||
|
/**
|
||
|
* @private Will hold a list of event descriptors that need to be
|
||
|
* canceled on subsequent loads of audio
|
||
|
* @type {Object[]}
|
||
|
*/
|
||
|
_this.tmpEvents = [];
|
||
|
|
||
|
/**
|
||
|
* @private Holds any running audio downloads
|
||
|
* @type {Observer}
|
||
|
*/
|
||
|
_this.currentRequest = null;
|
||
|
/** @private */
|
||
|
_this.arraybuffer = null;
|
||
|
/** @private */
|
||
|
_this.drawer = null;
|
||
|
/** @private */
|
||
|
_this.backend = null;
|
||
|
/** @private */
|
||
|
_this.peakCache = null;
|
||
|
|
||
|
// cache constructor objects
|
||
|
if (typeof _this.params.renderer !== 'function') {
|
||
|
throw new Error('Renderer parameter is invalid');
|
||
|
}
|
||
|
/**
|
||
|
* @private The uninitialised Drawer class
|
||
|
*/
|
||
|
_this.Drawer = _this.params.renderer;
|
||
|
/**
|
||
|
* @private The uninitialised Backend class
|
||
|
*/
|
||
|
// Back compat
|
||
|
if (_this.params.backend == 'AudioElement') {
|
||
|
_this.params.backend = 'MediaElement';
|
||
|
}
|
||
|
if ((_this.params.backend == 'WebAudio' || _this.params.backend === 'MediaElementWebAudio') && !_webaudio.default.prototype.supportsWebAudio.call(null)) {
|
||
|
_this.params.backend = 'MediaElement';
|
||
|
}
|
||
|
_this.Backend = _this.backends[_this.params.backend];
|
||
|
|
||
|
/**
|
||
|
* @private map of plugin names that are currently initialised
|
||
|
*/
|
||
|
_this.initialisedPluginList = {};
|
||
|
/** @private */
|
||
|
_this.isDestroyed = false;
|
||
|
|
||
|
/**
|
||
|
* Get the current ready status.
|
||
|
*
|
||
|
* @example const isReady = wavesurfer.isReady;
|
||
|
* @return {boolean}
|
||
|
*/
|
||
|
_this.isReady = false;
|
||
|
|
||
|
// responsive debounced event listener. If this.params.responsive is not
|
||
|
// set, this is never called. Use 100ms or this.params.responsive as
|
||
|
// timeout for the debounce function.
|
||
|
var prevWidth = 0;
|
||
|
_this._onResize = util.debounce(function () {
|
||
|
if (prevWidth != _this.drawer.wrapper.clientWidth && !_this.params.scrollParent) {
|
||
|
prevWidth = _this.drawer.wrapper.clientWidth;
|
||
|
_this.drawer.fireEvent('redraw');
|
||
|
}
|
||
|
}, typeof _this.params.responsive === 'number' ? _this.params.responsive : 100);
|
||
|
return _possibleConstructorReturn(_this, _assertThisInitialized(_this));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialise the wave
|
||
|
*
|
||
|
* @example
|
||
|
* var wavesurfer = new WaveSurfer(params);
|
||
|
* wavesurfer.init();
|
||
|
* @return {this} The wavesurfer instance
|
||
|
*/
|
||
|
_createClass(WaveSurfer, [{
|
||
|
key: "init",
|
||
|
value: function init() {
|
||
|
this.registerPlugins(this.params.plugins);
|
||
|
this.createDrawer();
|
||
|
this.createBackend();
|
||
|
this.createPeakCache();
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add and initialise array of plugins (if `plugin.deferInit` is falsey),
|
||
|
* this function is called in the init function of wavesurfer
|
||
|
*
|
||
|
* @param {PluginDefinition[]} plugins An array of plugin definitions
|
||
|
* @emits {WaveSurfer#plugins-registered} Called with the array of plugin definitions
|
||
|
* @return {this} The wavesurfer instance
|
||
|
*/
|
||
|
}, {
|
||
|
key: "registerPlugins",
|
||
|
value: function registerPlugins(plugins) {
|
||
|
var _this2 = this;
|
||
|
// first instantiate all the plugins
|
||
|
plugins.forEach(function (plugin) {
|
||
|
return _this2.addPlugin(plugin);
|
||
|
});
|
||
|
|
||
|
// now run the init functions
|
||
|
plugins.forEach(function (plugin) {
|
||
|
// call init function of the plugin if deferInit is falsey
|
||
|
// in that case you would manually use initPlugins()
|
||
|
if (!plugin.deferInit) {
|
||
|
_this2.initPlugin(plugin.name);
|
||
|
}
|
||
|
});
|
||
|
this.fireEvent('plugins-registered', plugins);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a map of plugin names that are currently initialised
|
||
|
*
|
||
|
* @example wavesurfer.getPlugins();
|
||
|
* @return {Object} Object with plugin names
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getActivePlugins",
|
||
|
value: function getActivePlugins() {
|
||
|
return this.initialisedPluginList;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a plugin object to wavesurfer
|
||
|
*
|
||
|
* @param {PluginDefinition} plugin A plugin definition
|
||
|
* @emits {WaveSurfer#plugin-added} Called with the name of the plugin that was added
|
||
|
* @example wavesurfer.addPlugin(WaveSurfer.minimap());
|
||
|
* @return {this} The wavesurfer instance
|
||
|
*/
|
||
|
}, {
|
||
|
key: "addPlugin",
|
||
|
value: function addPlugin(plugin) {
|
||
|
var _this3 = this;
|
||
|
if (!plugin.name) {
|
||
|
throw new Error('Plugin does not have a name!');
|
||
|
}
|
||
|
if (!plugin.instance) {
|
||
|
throw new Error("Plugin ".concat(plugin.name, " does not have an instance property!"));
|
||
|
}
|
||
|
|
||
|
// staticProps properties are applied to wavesurfer instance
|
||
|
if (plugin.staticProps) {
|
||
|
Object.keys(plugin.staticProps).forEach(function (pluginStaticProp) {
|
||
|
/**
|
||
|
* Properties defined in a plugin definition's `staticProps` property are added as
|
||
|
* staticProps properties of the WaveSurfer instance
|
||
|
*/
|
||
|
_this3[pluginStaticProp] = plugin.staticProps[pluginStaticProp];
|
||
|
});
|
||
|
}
|
||
|
var Instance = plugin.instance;
|
||
|
|
||
|
// turn the plugin instance into an observer
|
||
|
var observerPrototypeKeys = Object.getOwnPropertyNames(util.Observer.prototype);
|
||
|
observerPrototypeKeys.forEach(function (key) {
|
||
|
Instance.prototype[key] = util.Observer.prototype[key];
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* Instantiated plugin classes are added as a property of the wavesurfer
|
||
|
* instance
|
||
|
* @type {Object}
|
||
|
*/
|
||
|
this[plugin.name] = new Instance(plugin.params || {}, this);
|
||
|
this.fireEvent('plugin-added', plugin.name);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialise a plugin
|
||
|
*
|
||
|
* @param {string} name A plugin name
|
||
|
* @emits WaveSurfer#plugin-initialised
|
||
|
* @example wavesurfer.initPlugin('minimap');
|
||
|
* @return {this} The wavesurfer instance
|
||
|
*/
|
||
|
}, {
|
||
|
key: "initPlugin",
|
||
|
value: function initPlugin(name) {
|
||
|
if (!this[name]) {
|
||
|
throw new Error("Plugin ".concat(name, " has not been added yet!"));
|
||
|
}
|
||
|
if (this.initialisedPluginList[name]) {
|
||
|
// destroy any already initialised plugins
|
||
|
this.destroyPlugin(name);
|
||
|
}
|
||
|
this[name].init();
|
||
|
this.initialisedPluginList[name] = true;
|
||
|
this.fireEvent('plugin-initialised', name);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Destroy a plugin
|
||
|
*
|
||
|
* @param {string} name A plugin name
|
||
|
* @emits WaveSurfer#plugin-destroyed
|
||
|
* @example wavesurfer.destroyPlugin('minimap');
|
||
|
* @returns {this} The wavesurfer instance
|
||
|
*/
|
||
|
}, {
|
||
|
key: "destroyPlugin",
|
||
|
value: function destroyPlugin(name) {
|
||
|
if (!this[name]) {
|
||
|
throw new Error("Plugin ".concat(name, " has not been added yet and cannot be destroyed!"));
|
||
|
}
|
||
|
if (!this.initialisedPluginList[name]) {
|
||
|
throw new Error("Plugin ".concat(name, " is not active and cannot be destroyed!"));
|
||
|
}
|
||
|
if (typeof this[name].destroy !== 'function') {
|
||
|
throw new Error("Plugin ".concat(name, " does not have a destroy function!"));
|
||
|
}
|
||
|
this[name].destroy();
|
||
|
delete this.initialisedPluginList[name];
|
||
|
this.fireEvent('plugin-destroyed', name);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Destroy all initialised plugins. Convenience function to use when
|
||
|
* wavesurfer is removed
|
||
|
*
|
||
|
* @private
|
||
|
*/
|
||
|
}, {
|
||
|
key: "destroyAllPlugins",
|
||
|
value: function destroyAllPlugins() {
|
||
|
var _this4 = this;
|
||
|
Object.keys(this.initialisedPluginList).forEach(function (name) {
|
||
|
return _this4.destroyPlugin(name);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create the drawer and draw the waveform
|
||
|
*
|
||
|
* @private
|
||
|
* @emits WaveSurfer#drawer-created
|
||
|
*/
|
||
|
}, {
|
||
|
key: "createDrawer",
|
||
|
value: function createDrawer() {
|
||
|
var _this5 = this;
|
||
|
this.drawer = new this.Drawer(this.container, this.params);
|
||
|
this.drawer.init();
|
||
|
this.fireEvent('drawer-created', this.drawer);
|
||
|
if (this.params.responsive !== false) {
|
||
|
window.addEventListener('resize', this._onResize, true);
|
||
|
window.addEventListener('orientationchange', this._onResize, true);
|
||
|
}
|
||
|
this.drawer.on('redraw', function () {
|
||
|
_this5.drawBuffer();
|
||
|
_this5.drawer.progress(_this5.backend.getPlayedPercents());
|
||
|
});
|
||
|
|
||
|
// Click-to-seek
|
||
|
this.drawer.on('click', function (e, progress) {
|
||
|
setTimeout(function () {
|
||
|
return _this5.seekTo(progress);
|
||
|
}, 0);
|
||
|
});
|
||
|
|
||
|
// Relay the scroll event from the drawer
|
||
|
this.drawer.on('scroll', function (e) {
|
||
|
if (_this5.params.partialRender) {
|
||
|
_this5.drawBuffer();
|
||
|
}
|
||
|
_this5.fireEvent('scroll', e);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create the backend
|
||
|
*
|
||
|
* @private
|
||
|
* @emits WaveSurfer#backend-created
|
||
|
*/
|
||
|
}, {
|
||
|
key: "createBackend",
|
||
|
value: function createBackend() {
|
||
|
var _this6 = this;
|
||
|
if (this.backend) {
|
||
|
this.backend.destroy();
|
||
|
}
|
||
|
this.backend = new this.Backend(this.params);
|
||
|
this.backend.init();
|
||
|
this.fireEvent('backend-created', this.backend);
|
||
|
this.backend.on('finish', function () {
|
||
|
_this6.drawer.progress(_this6.backend.getPlayedPercents());
|
||
|
_this6.fireEvent('finish');
|
||
|
});
|
||
|
this.backend.on('play', function () {
|
||
|
return _this6.fireEvent('play');
|
||
|
});
|
||
|
this.backend.on('pause', function () {
|
||
|
return _this6.fireEvent('pause');
|
||
|
});
|
||
|
this.backend.on('audioprocess', function (time) {
|
||
|
_this6.drawer.progress(_this6.backend.getPlayedPercents());
|
||
|
_this6.fireEvent('audioprocess', time);
|
||
|
});
|
||
|
|
||
|
// only needed for MediaElement and MediaElementWebAudio backend
|
||
|
if (this.params.backend === 'MediaElement' || this.params.backend === 'MediaElementWebAudio') {
|
||
|
this.backend.on('seek', function () {
|
||
|
_this6.drawer.progress(_this6.backend.getPlayedPercents());
|
||
|
});
|
||
|
this.backend.on('volume', function () {
|
||
|
var newVolume = _this6.getVolume();
|
||
|
_this6.fireEvent('volume', newVolume);
|
||
|
if (_this6.backend.isMuted !== _this6.isMuted) {
|
||
|
_this6.isMuted = _this6.backend.isMuted;
|
||
|
_this6.fireEvent('mute', _this6.isMuted);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create the peak cache
|
||
|
*
|
||
|
* @private
|
||
|
*/
|
||
|
}, {
|
||
|
key: "createPeakCache",
|
||
|
value: function createPeakCache() {
|
||
|
if (this.params.partialRender) {
|
||
|
this.peakCache = new _peakcache.default();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the duration of the audio clip
|
||
|
*
|
||
|
* @example const duration = wavesurfer.getDuration();
|
||
|
* @return {number} Duration in seconds
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getDuration",
|
||
|
value: function getDuration() {
|
||
|
return this.backend.getDuration();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the current playback position
|
||
|
*
|
||
|
* @example const currentTime = wavesurfer.getCurrentTime();
|
||
|
* @return {number} Playback position in seconds
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getCurrentTime",
|
||
|
value: function getCurrentTime() {
|
||
|
return this.backend.getCurrentTime();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the current play time in seconds.
|
||
|
*
|
||
|
* @param {number} seconds A positive number in seconds. E.g. 10 means 10
|
||
|
* seconds, 60 means 1 minute
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setCurrentTime",
|
||
|
value: function setCurrentTime(seconds) {
|
||
|
if (seconds >= this.getDuration()) {
|
||
|
this.seekTo(1);
|
||
|
} else {
|
||
|
this.seekTo(seconds / this.getDuration());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Starts playback from the current position. Optional start and end
|
||
|
* measured in seconds can be used to set the range of audio to play.
|
||
|
*
|
||
|
* @param {?number} start Position to start at
|
||
|
* @param {?number} end Position to end at
|
||
|
* @emits WaveSurfer#interaction
|
||
|
* @return {Promise} Result of the backend play method
|
||
|
* @example
|
||
|
* // play from second 1 to 5
|
||
|
* wavesurfer.play(1, 5);
|
||
|
*/
|
||
|
}, {
|
||
|
key: "play",
|
||
|
value: function play(start, end) {
|
||
|
var _this7 = this;
|
||
|
this.fireEvent('interaction', function () {
|
||
|
return _this7.play(start, end);
|
||
|
});
|
||
|
return this.backend.play(start, end);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set a point in seconds for playback to stop at.
|
||
|
*
|
||
|
* @param {number} position Position (in seconds) to stop at
|
||
|
* @version 3.3.0
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setPlayEnd",
|
||
|
value: function setPlayEnd(position) {
|
||
|
this.backend.setPlayEnd(position);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Stops and pauses playback
|
||
|
*
|
||
|
* @example wavesurfer.pause();
|
||
|
* @return {Promise} Result of the backend pause method
|
||
|
*/
|
||
|
}, {
|
||
|
key: "pause",
|
||
|
value: function pause() {
|
||
|
if (!this.backend.isPaused()) {
|
||
|
return this.backend.pause();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Toggle playback
|
||
|
*
|
||
|
* @example wavesurfer.playPause();
|
||
|
* @return {Promise} Result of the backend play or pause method
|
||
|
*/
|
||
|
}, {
|
||
|
key: "playPause",
|
||
|
value: function playPause() {
|
||
|
return this.backend.isPaused() ? this.play() : this.pause();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the current playback state
|
||
|
*
|
||
|
* @example const isPlaying = wavesurfer.isPlaying();
|
||
|
* @return {boolean} False if paused, true if playing
|
||
|
*/
|
||
|
}, {
|
||
|
key: "isPlaying",
|
||
|
value: function isPlaying() {
|
||
|
return !this.backend.isPaused();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Skip backward
|
||
|
*
|
||
|
* @param {?number} seconds Amount to skip back, if not specified `skipLength`
|
||
|
* is used
|
||
|
* @example wavesurfer.skipBackward();
|
||
|
*/
|
||
|
}, {
|
||
|
key: "skipBackward",
|
||
|
value: function skipBackward(seconds) {
|
||
|
this.skip(-seconds || -this.params.skipLength);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Skip forward
|
||
|
*
|
||
|
* @param {?number} seconds Amount to skip back, if not specified `skipLength`
|
||
|
* is used
|
||
|
* @example wavesurfer.skipForward();
|
||
|
*/
|
||
|
}, {
|
||
|
key: "skipForward",
|
||
|
value: function skipForward(seconds) {
|
||
|
this.skip(seconds || this.params.skipLength);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Skip a number of seconds from the current position (use a negative value
|
||
|
* to go backwards).
|
||
|
*
|
||
|
* @param {number} offset Amount to skip back or forwards
|
||
|
* @example
|
||
|
* // go back 2 seconds
|
||
|
* wavesurfer.skip(-2);
|
||
|
*/
|
||
|
}, {
|
||
|
key: "skip",
|
||
|
value: function skip(offset) {
|
||
|
var duration = this.getDuration() || 1;
|
||
|
var position = this.getCurrentTime() || 0;
|
||
|
position = Math.max(0, Math.min(duration, position + (offset || 0)));
|
||
|
this.seekAndCenter(position / duration);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Seeks to a position and centers the view
|
||
|
*
|
||
|
* @param {number} progress Between 0 (=beginning) and 1 (=end)
|
||
|
* @example
|
||
|
* // seek and go to the middle of the audio
|
||
|
* wavesurfer.seekTo(0.5);
|
||
|
*/
|
||
|
}, {
|
||
|
key: "seekAndCenter",
|
||
|
value: function seekAndCenter(progress) {
|
||
|
this.seekTo(progress);
|
||
|
this.drawer.recenter(progress);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Seeks to a position
|
||
|
*
|
||
|
* @param {number} progress Between 0 (=beginning) and 1 (=end)
|
||
|
* @emits WaveSurfer#interaction
|
||
|
* @emits WaveSurfer#seek
|
||
|
* @example
|
||
|
* // seek to the middle of the audio
|
||
|
* wavesurfer.seekTo(0.5);
|
||
|
*/
|
||
|
}, {
|
||
|
key: "seekTo",
|
||
|
value: function seekTo(progress) {
|
||
|
var _this8 = this;
|
||
|
// return an error if progress is not a number between 0 and 1
|
||
|
if (typeof progress !== 'number' || !isFinite(progress) || progress < 0 || progress > 1) {
|
||
|
throw new Error('Error calling wavesurfer.seekTo, parameter must be a number between 0 and 1!');
|
||
|
}
|
||
|
this.fireEvent('interaction', function () {
|
||
|
return _this8.seekTo(progress);
|
||
|
});
|
||
|
var isWebAudioBackend = this.params.backend === 'WebAudio';
|
||
|
var paused = this.backend.isPaused();
|
||
|
if (isWebAudioBackend && !paused) {
|
||
|
this.backend.pause();
|
||
|
}
|
||
|
|
||
|
// avoid small scrolls while paused seeking
|
||
|
var oldScrollParent = this.params.scrollParent;
|
||
|
this.params.scrollParent = false;
|
||
|
this.backend.seekTo(progress * this.getDuration());
|
||
|
this.drawer.progress(progress);
|
||
|
if (isWebAudioBackend && !paused) {
|
||
|
this.backend.play();
|
||
|
}
|
||
|
this.params.scrollParent = oldScrollParent;
|
||
|
this.fireEvent('seek', progress);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Stops and goes to the beginning.
|
||
|
*
|
||
|
* @example wavesurfer.stop();
|
||
|
*/
|
||
|
}, {
|
||
|
key: "stop",
|
||
|
value: function stop() {
|
||
|
this.pause();
|
||
|
this.seekTo(0);
|
||
|
this.drawer.progress(0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the ID of the audio device to use for output and returns a Promise.
|
||
|
*
|
||
|
* @param {string} deviceId String value representing underlying output
|
||
|
* device
|
||
|
* @returns {Promise} `Promise` that resolves to `undefined` when there are
|
||
|
* no errors detected.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setSinkId",
|
||
|
value: function setSinkId(deviceId) {
|
||
|
return this.backend.setSinkId(deviceId);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the playback volume.
|
||
|
*
|
||
|
* @param {number} newVolume A value between 0 and 1, 0 being no
|
||
|
* volume and 1 being full volume.
|
||
|
* @emits WaveSurfer#volume
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setVolume",
|
||
|
value: function setVolume(newVolume) {
|
||
|
this.backend.setVolume(newVolume);
|
||
|
this.fireEvent('volume', newVolume);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the playback volume.
|
||
|
*
|
||
|
* @return {number} A value between 0 and 1, 0 being no
|
||
|
* volume and 1 being full volume.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getVolume",
|
||
|
value: function getVolume() {
|
||
|
return this.backend.getVolume();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the playback rate.
|
||
|
*
|
||
|
* @param {number} rate A positive number. E.g. 0.5 means half the normal
|
||
|
* speed, 2 means double speed and so on.
|
||
|
* @example wavesurfer.setPlaybackRate(2);
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setPlaybackRate",
|
||
|
value: function setPlaybackRate(rate) {
|
||
|
this.backend.setPlaybackRate(rate);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the playback rate.
|
||
|
*
|
||
|
* @return {number} The current playback rate.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getPlaybackRate",
|
||
|
value: function getPlaybackRate() {
|
||
|
return this.backend.getPlaybackRate();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Toggle the volume on and off. If not currently muted it will save the
|
||
|
* current volume value and turn the volume off. If currently muted then it
|
||
|
* will restore the volume to the saved value, and then rest the saved
|
||
|
* value.
|
||
|
*
|
||
|
* @example wavesurfer.toggleMute();
|
||
|
*/
|
||
|
}, {
|
||
|
key: "toggleMute",
|
||
|
value: function toggleMute() {
|
||
|
this.setMute(!this.isMuted);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enable or disable muted audio
|
||
|
*
|
||
|
* @param {boolean} mute Specify `true` to mute audio.
|
||
|
* @emits WaveSurfer#volume
|
||
|
* @emits WaveSurfer#mute
|
||
|
* @example
|
||
|
* // unmute
|
||
|
* wavesurfer.setMute(false);
|
||
|
* console.log(wavesurfer.getMute()) // logs false
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setMute",
|
||
|
value: function setMute(mute) {
|
||
|
// ignore all muting requests if the audio is already in that state
|
||
|
if (mute === this.isMuted) {
|
||
|
this.fireEvent('mute', this.isMuted);
|
||
|
return;
|
||
|
}
|
||
|
if (this.backend.setMute) {
|
||
|
// Backends such as the MediaElement backend have their own handling
|
||
|
// of mute, let them handle it.
|
||
|
this.backend.setMute(mute);
|
||
|
this.isMuted = mute;
|
||
|
} else {
|
||
|
if (mute) {
|
||
|
// If currently not muted then save current volume,
|
||
|
// turn off the volume and update the mute properties
|
||
|
this.savedVolume = this.backend.getVolume();
|
||
|
this.backend.setVolume(0);
|
||
|
this.isMuted = true;
|
||
|
this.fireEvent('volume', 0);
|
||
|
} else {
|
||
|
// If currently muted then restore to the saved volume
|
||
|
// and update the mute properties
|
||
|
this.backend.setVolume(this.savedVolume);
|
||
|
this.isMuted = false;
|
||
|
this.fireEvent('volume', this.savedVolume);
|
||
|
}
|
||
|
}
|
||
|
this.fireEvent('mute', this.isMuted);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the current mute status.
|
||
|
*
|
||
|
* @example const isMuted = wavesurfer.getMute();
|
||
|
* @return {boolean} Current mute status
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getMute",
|
||
|
value: function getMute() {
|
||
|
return this.isMuted;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the list of current set filters as an array.
|
||
|
*
|
||
|
* Filters must be set with setFilters method first
|
||
|
*
|
||
|
* @return {array} List of enabled filters
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getFilters",
|
||
|
value: function getFilters() {
|
||
|
return this.backend.filters || [];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Toggles `scrollParent` and redraws
|
||
|
*
|
||
|
* @example wavesurfer.toggleScroll();
|
||
|
*/
|
||
|
}, {
|
||
|
key: "toggleScroll",
|
||
|
value: function toggleScroll() {
|
||
|
this.params.scrollParent = !this.params.scrollParent;
|
||
|
this.drawBuffer();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Toggle mouse interaction
|
||
|
*
|
||
|
* @example wavesurfer.toggleInteraction();
|
||
|
*/
|
||
|
}, {
|
||
|
key: "toggleInteraction",
|
||
|
value: function toggleInteraction() {
|
||
|
this.params.interact = !this.params.interact;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the fill color of the waveform after the cursor.
|
||
|
*
|
||
|
* @return {string} A CSS color string.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getWaveColor",
|
||
|
value: function getWaveColor() {
|
||
|
return this.params.waveColor;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the fill color of the waveform after the cursor.
|
||
|
*
|
||
|
* @param {string} color A CSS color string.
|
||
|
* @example wavesurfer.setWaveColor('#ddd');
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setWaveColor",
|
||
|
value: function setWaveColor(color) {
|
||
|
this.params.waveColor = color;
|
||
|
this.drawBuffer();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the fill color of the waveform behind the cursor.
|
||
|
*
|
||
|
* @return {string} A CSS color string.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getProgressColor",
|
||
|
value: function getProgressColor() {
|
||
|
return this.params.progressColor;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the fill color of the waveform behind the cursor.
|
||
|
*
|
||
|
* @param {string} color A CSS color string.
|
||
|
* @example wavesurfer.setProgressColor('#400');
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setProgressColor",
|
||
|
value: function setProgressColor(color) {
|
||
|
this.params.progressColor = color;
|
||
|
this.drawBuffer();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the background color of the waveform container.
|
||
|
*
|
||
|
* @return {string} A CSS color string.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getBackgroundColor",
|
||
|
value: function getBackgroundColor() {
|
||
|
return this.params.backgroundColor;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the background color of the waveform container.
|
||
|
*
|
||
|
* @param {string} color A CSS color string.
|
||
|
* @example wavesurfer.setBackgroundColor('#FF00FF');
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setBackgroundColor",
|
||
|
value: function setBackgroundColor(color) {
|
||
|
this.params.backgroundColor = color;
|
||
|
util.style(this.container, {
|
||
|
background: this.params.backgroundColor
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the fill color of the cursor indicating the playhead
|
||
|
* position.
|
||
|
*
|
||
|
* @return {string} A CSS color string.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getCursorColor",
|
||
|
value: function getCursorColor() {
|
||
|
return this.params.cursorColor;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the fill color of the cursor indicating the playhead
|
||
|
* position.
|
||
|
*
|
||
|
* @param {string} color A CSS color string.
|
||
|
* @example wavesurfer.setCursorColor('#222');
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setCursorColor",
|
||
|
value: function setCursorColor(color) {
|
||
|
this.params.cursorColor = color;
|
||
|
this.drawer.updateCursor();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the height of the waveform.
|
||
|
*
|
||
|
* @return {number} Height measured in pixels.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getHeight",
|
||
|
value: function getHeight() {
|
||
|
return this.params.height;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the height of the waveform.
|
||
|
*
|
||
|
* @param {number} height Height measured in pixels.
|
||
|
* @example wavesurfer.setHeight(200);
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setHeight",
|
||
|
value: function setHeight(height) {
|
||
|
this.params.height = height;
|
||
|
this.drawer.setHeight(height * this.params.pixelRatio);
|
||
|
this.drawBuffer();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Hide channels from being drawn on the waveform if splitting channels.
|
||
|
*
|
||
|
* For example, if we want to draw only the peaks for the right stereo channel:
|
||
|
*
|
||
|
* const wavesurfer = new WaveSurfer.create({...splitChannels: true});
|
||
|
* wavesurfer.load('stereo_audio.mp3');
|
||
|
*
|
||
|
* wavesurfer.setFilteredChannel([0]); <-- hide left channel peaks.
|
||
|
*
|
||
|
* @param {array} channelIndices Channels to be filtered out from drawing.
|
||
|
* @version 4.0.0
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setFilteredChannels",
|
||
|
value: function setFilteredChannels(channelIndices) {
|
||
|
this.params.splitChannelsOptions.filterChannels = channelIndices;
|
||
|
this.drawBuffer();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the correct peaks for current wave view-port and render wave
|
||
|
*
|
||
|
* @private
|
||
|
* @emits WaveSurfer#redraw
|
||
|
*/
|
||
|
}, {
|
||
|
key: "drawBuffer",
|
||
|
value: function drawBuffer() {
|
||
|
var nominalWidth = Math.round(this.getDuration() * this.params.minPxPerSec * this.params.pixelRatio);
|
||
|
var parentWidth = this.drawer.getWidth();
|
||
|
var width = nominalWidth;
|
||
|
// always start at 0 after zooming for scrolling : issue redraw left part
|
||
|
var start = 0;
|
||
|
var end = Math.max(start + parentWidth, width);
|
||
|
// Fill container
|
||
|
if (this.params.fillParent && (!this.params.scrollParent || nominalWidth < parentWidth)) {
|
||
|
width = parentWidth;
|
||
|
start = 0;
|
||
|
end = width;
|
||
|
}
|
||
|
var peaks;
|
||
|
if (this.params.partialRender) {
|
||
|
var newRanges = this.peakCache.addRangeToPeakCache(width, start, end);
|
||
|
var i;
|
||
|
for (i = 0; i < newRanges.length; i++) {
|
||
|
peaks = this.backend.getPeaks(width, newRanges[i][0], newRanges[i][1]);
|
||
|
this.drawer.drawPeaks(peaks, width, newRanges[i][0], newRanges[i][1]);
|
||
|
}
|
||
|
} else {
|
||
|
peaks = this.backend.getPeaks(width, start, end);
|
||
|
this.drawer.drawPeaks(peaks, width, start, end);
|
||
|
}
|
||
|
this.fireEvent('redraw', peaks, width);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Horizontally zooms the waveform in and out. It also changes the parameter
|
||
|
* `minPxPerSec` and enables the `scrollParent` option. Calling the function
|
||
|
* with a falsey parameter will reset the zoom state.
|
||
|
*
|
||
|
* @param {?number} pxPerSec Number of horizontal pixels per second of
|
||
|
* audio, if none is set the waveform returns to unzoomed state
|
||
|
* @emits WaveSurfer#zoom
|
||
|
* @example wavesurfer.zoom(20);
|
||
|
*/
|
||
|
}, {
|
||
|
key: "zoom",
|
||
|
value: function zoom(pxPerSec) {
|
||
|
if (!pxPerSec) {
|
||
|
this.params.minPxPerSec = this.defaultParams.minPxPerSec;
|
||
|
this.params.scrollParent = false;
|
||
|
} else {
|
||
|
this.params.minPxPerSec = pxPerSec;
|
||
|
this.params.scrollParent = true;
|
||
|
}
|
||
|
this.drawBuffer();
|
||
|
this.drawer.progress(this.backend.getPlayedPercents());
|
||
|
this.drawer.recenter(this.getCurrentTime() / this.getDuration());
|
||
|
this.fireEvent('zoom', pxPerSec);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Decode buffer and load
|
||
|
*
|
||
|
* @private
|
||
|
* @param {ArrayBuffer} arraybuffer Buffer to process
|
||
|
*/
|
||
|
}, {
|
||
|
key: "loadArrayBuffer",
|
||
|
value: function loadArrayBuffer(arraybuffer) {
|
||
|
var _this9 = this;
|
||
|
this.decodeArrayBuffer(arraybuffer, function (data) {
|
||
|
if (!_this9.isDestroyed) {
|
||
|
_this9.loadDecodedBuffer(data);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Directly load an externally decoded AudioBuffer
|
||
|
*
|
||
|
* @private
|
||
|
* @param {AudioBuffer} buffer Buffer to process
|
||
|
* @emits WaveSurfer#ready
|
||
|
*/
|
||
|
}, {
|
||
|
key: "loadDecodedBuffer",
|
||
|
value: function loadDecodedBuffer(buffer) {
|
||
|
this.backend.load(buffer);
|
||
|
this.drawBuffer();
|
||
|
this.isReady = true;
|
||
|
this.fireEvent('ready');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Loads audio data from a Blob or File object
|
||
|
*
|
||
|
* @param {Blob|File} blob Audio data
|
||
|
* @example
|
||
|
*/
|
||
|
}, {
|
||
|
key: "loadBlob",
|
||
|
value: function loadBlob(blob) {
|
||
|
var _this10 = this;
|
||
|
// Create file reader
|
||
|
var reader = new FileReader();
|
||
|
reader.addEventListener('progress', function (e) {
|
||
|
return _this10.onProgress(e);
|
||
|
});
|
||
|
reader.addEventListener('load', function (e) {
|
||
|
return _this10.loadArrayBuffer(e.target.result);
|
||
|
});
|
||
|
reader.addEventListener('error', function () {
|
||
|
return _this10.fireEvent('error', 'Error reading file');
|
||
|
});
|
||
|
reader.readAsArrayBuffer(blob);
|
||
|
this.empty();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Loads audio and re-renders the waveform.
|
||
|
*
|
||
|
* @param {string|HTMLMediaElement} url The url of the audio file or the
|
||
|
* audio element with the audio
|
||
|
* @param {number[]|Number.<Array[]>} peaks Wavesurfer does not have to decode
|
||
|
* the audio to render the waveform if this is specified
|
||
|
* @param {?string} preload (Use with backend `MediaElement` and `MediaElementWebAudio`)
|
||
|
* `'none'|'metadata'|'auto'` Preload attribute for the media element
|
||
|
* @param {?number} duration The duration of the audio. This is used to
|
||
|
* render the peaks data in the correct size for the audio duration (as
|
||
|
* befits the current `minPxPerSec` and zoom value) without having to decode
|
||
|
* the audio.
|
||
|
* @returns {void}
|
||
|
* @throws Will throw an error if the `url` argument is empty.
|
||
|
* @example
|
||
|
* // uses fetch or media element to load file (depending on backend)
|
||
|
* wavesurfer.load('http://example.com/demo.wav');
|
||
|
*
|
||
|
* // setting preload attribute with media element backend and supplying
|
||
|
* // peaks
|
||
|
* wavesurfer.load(
|
||
|
* 'http://example.com/demo.wav',
|
||
|
* [0.0218, 0.0183, 0.0165, 0.0198, 0.2137, 0.2888],
|
||
|
* true
|
||
|
* );
|
||
|
*/
|
||
|
}, {
|
||
|
key: "load",
|
||
|
value: function load(url, peaks, preload, duration) {
|
||
|
if (!url) {
|
||
|
throw new Error('url parameter cannot be empty');
|
||
|
}
|
||
|
this.empty();
|
||
|
if (preload) {
|
||
|
// check whether the preload attribute will be usable and if not log
|
||
|
// a warning listing the reasons why not and nullify the variable
|
||
|
var preloadIgnoreReasons = {
|
||
|
"Preload is not 'auto', 'none' or 'metadata'": ['auto', 'metadata', 'none'].indexOf(preload) === -1,
|
||
|
'Peaks are not provided': !peaks,
|
||
|
"Backend is not of type 'MediaElement' or 'MediaElementWebAudio'": ['MediaElement', 'MediaElementWebAudio'].indexOf(this.params.backend) === -1,
|
||
|
'Url is not of type string': typeof url !== 'string'
|
||
|
};
|
||
|
var activeReasons = Object.keys(preloadIgnoreReasons).filter(function (reason) {
|
||
|
return preloadIgnoreReasons[reason];
|
||
|
});
|
||
|
if (activeReasons.length) {
|
||
|
// eslint-disable-next-line no-console
|
||
|
console.warn('Preload parameter of wavesurfer.load will be ignored because:\n\t- ' + activeReasons.join('\n\t- '));
|
||
|
// stop invalid values from being used
|
||
|
preload = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// loadBuffer(url, peaks, duration) requires that url is a string
|
||
|
// but users can pass in a HTMLMediaElement to WaveSurfer
|
||
|
if (this.params.backend === 'WebAudio' && url instanceof HTMLMediaElement) {
|
||
|
url = url.src;
|
||
|
}
|
||
|
switch (this.params.backend) {
|
||
|
case 'WebAudio':
|
||
|
return this.loadBuffer(url, peaks, duration);
|
||
|
case 'MediaElement':
|
||
|
case 'MediaElementWebAudio':
|
||
|
return this.loadMediaElement(url, peaks, preload, duration);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Loads audio using Web Audio buffer backend.
|
||
|
*
|
||
|
* @private
|
||
|
* @emits WaveSurfer#waveform-ready
|
||
|
* @param {string} url URL of audio file
|
||
|
* @param {number[]|Number.<Array[]>} peaks Peaks data
|
||
|
* @param {?number} duration Optional duration of audio file
|
||
|
* @returns {void}
|
||
|
*/
|
||
|
}, {
|
||
|
key: "loadBuffer",
|
||
|
value: function loadBuffer(url, peaks, duration) {
|
||
|
var _this11 = this;
|
||
|
var load = function load(action) {
|
||
|
if (action) {
|
||
|
_this11.tmpEvents.push(_this11.once('ready', action));
|
||
|
}
|
||
|
return _this11.getArrayBuffer(url, function (data) {
|
||
|
return _this11.loadArrayBuffer(data);
|
||
|
});
|
||
|
};
|
||
|
if (peaks) {
|
||
|
this.backend.setPeaks(peaks, duration);
|
||
|
this.drawBuffer();
|
||
|
this.fireEvent('waveform-ready');
|
||
|
this.tmpEvents.push(this.once('interaction', load));
|
||
|
} else {
|
||
|
return load();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Either create a media element, or load an existing media element.
|
||
|
*
|
||
|
* @private
|
||
|
* @emits WaveSurfer#waveform-ready
|
||
|
* @param {string|HTMLMediaElement} urlOrElt Either a path to a media file, or an
|
||
|
* existing HTML5 Audio/Video Element
|
||
|
* @param {number[]|Number.<Array[]>} peaks Array of peaks. Required to bypass web audio
|
||
|
* dependency
|
||
|
* @param {?boolean} preload Set to true if the preload attribute of the
|
||
|
* audio element should be enabled
|
||
|
* @param {?number} duration Optional duration of audio file
|
||
|
*/
|
||
|
}, {
|
||
|
key: "loadMediaElement",
|
||
|
value: function loadMediaElement(urlOrElt, peaks, preload, duration) {
|
||
|
var _this12 = this;
|
||
|
var url = urlOrElt;
|
||
|
if (typeof urlOrElt === 'string') {
|
||
|
this.backend.load(url, this.mediaContainer, peaks, preload);
|
||
|
} else {
|
||
|
var elt = urlOrElt;
|
||
|
this.backend.loadElt(elt, peaks);
|
||
|
|
||
|
// If peaks are not provided,
|
||
|
// url = element.src so we can get peaks with web audio
|
||
|
url = elt.src;
|
||
|
}
|
||
|
this.tmpEvents.push(this.backend.once('canplay', function () {
|
||
|
// ignore when backend was already destroyed
|
||
|
if (!_this12.backend.destroyed) {
|
||
|
_this12.drawBuffer();
|
||
|
_this12.isReady = true;
|
||
|
_this12.fireEvent('ready');
|
||
|
}
|
||
|
}), this.backend.once('error', function (err) {
|
||
|
return _this12.fireEvent('error', err);
|
||
|
}));
|
||
|
|
||
|
// If peaks are provided, render them and fire the `waveform-ready` event.
|
||
|
if (peaks) {
|
||
|
this.backend.setPeaks(peaks, duration);
|
||
|
this.drawBuffer();
|
||
|
this.fireEvent('waveform-ready');
|
||
|
}
|
||
|
|
||
|
// If no pre-decoded peaks are provided, or are provided with
|
||
|
// forceDecode flag, attempt to download the audio file and decode it
|
||
|
// with Web Audio.
|
||
|
if ((!peaks || this.params.forceDecode) && this.backend.supportsWebAudio()) {
|
||
|
this.getArrayBuffer(url, function (arraybuffer) {
|
||
|
_this12.decodeArrayBuffer(arraybuffer, function (buffer) {
|
||
|
_this12.backend.buffer = buffer;
|
||
|
_this12.backend.setPeaks(null);
|
||
|
_this12.drawBuffer();
|
||
|
_this12.fireEvent('waveform-ready');
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Decode an array buffer and pass data to a callback
|
||
|
*
|
||
|
* @private
|
||
|
* @param {Object} arraybuffer The array buffer to decode
|
||
|
* @param {function} callback The function to call on complete
|
||
|
*/
|
||
|
}, {
|
||
|
key: "decodeArrayBuffer",
|
||
|
value: function decodeArrayBuffer(arraybuffer, callback) {
|
||
|
var _this13 = this;
|
||
|
this.arraybuffer = arraybuffer;
|
||
|
this.backend.decodeArrayBuffer(arraybuffer, function (data) {
|
||
|
// Only use the decoded data if we haven't been destroyed or
|
||
|
// another decode started in the meantime
|
||
|
if (!_this13.isDestroyed && _this13.arraybuffer == arraybuffer) {
|
||
|
callback(data);
|
||
|
_this13.arraybuffer = null;
|
||
|
}
|
||
|
}, function () {
|
||
|
return _this13.fireEvent('error', 'Error decoding audiobuffer');
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Load an array buffer using fetch and pass the result to a callback
|
||
|
*
|
||
|
* @param {string} url The URL of the file object
|
||
|
* @param {function} callback The function to call on complete
|
||
|
* @returns {util.fetchFile} fetch call
|
||
|
* @private
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getArrayBuffer",
|
||
|
value: function getArrayBuffer(url, callback) {
|
||
|
var _this14 = this;
|
||
|
var options = Object.assign({
|
||
|
url: url,
|
||
|
responseType: 'arraybuffer'
|
||
|
}, this.params.xhr);
|
||
|
var request = util.fetchFile(options);
|
||
|
this.currentRequest = request;
|
||
|
this.tmpEvents.push(request.on('progress', function (e) {
|
||
|
_this14.onProgress(e);
|
||
|
}), request.on('success', function (data) {
|
||
|
callback(data);
|
||
|
_this14.currentRequest = null;
|
||
|
}), request.on('error', function (e) {
|
||
|
_this14.fireEvent('error', e);
|
||
|
_this14.currentRequest = null;
|
||
|
}));
|
||
|
return request;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Called while the audio file is loading
|
||
|
*
|
||
|
* @private
|
||
|
* @param {Event} e Progress event
|
||
|
* @emits WaveSurfer#loading
|
||
|
*/
|
||
|
}, {
|
||
|
key: "onProgress",
|
||
|
value: function onProgress(e) {
|
||
|
var percentComplete;
|
||
|
if (e.lengthComputable) {
|
||
|
percentComplete = e.loaded / e.total;
|
||
|
} else {
|
||
|
// Approximate progress with an asymptotic
|
||
|
// function, and assume downloads in the 1-3 MB range.
|
||
|
percentComplete = e.loaded / (e.loaded + 1000000);
|
||
|
}
|
||
|
this.fireEvent('loading', Math.round(percentComplete * 100), e.target);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Exports PCM data into a JSON array and opens in a new window.
|
||
|
*
|
||
|
* @param {number} length=1024 The scale in which to export the peaks
|
||
|
* @param {number} accuracy=10000
|
||
|
* @param {?boolean} noWindow Set to true to disable opening a new
|
||
|
* window with the JSON
|
||
|
* @param {number} start Start index
|
||
|
* @param {number} end End index
|
||
|
* @return {Promise} Promise that resolves with array of peaks
|
||
|
*/
|
||
|
}, {
|
||
|
key: "exportPCM",
|
||
|
value: function exportPCM(length, accuracy, noWindow, start, end) {
|
||
|
length = length || 1024;
|
||
|
start = start || 0;
|
||
|
accuracy = accuracy || 10000;
|
||
|
noWindow = noWindow || false;
|
||
|
var peaks = this.backend.getPeaks(length, start, end);
|
||
|
var arr = [].map.call(peaks, function (val) {
|
||
|
return Math.round(val * accuracy) / accuracy;
|
||
|
});
|
||
|
return new Promise(function (resolve, reject) {
|
||
|
var json = JSON.stringify(arr);
|
||
|
if (!noWindow) {
|
||
|
window.open('data:application/json;charset=utf-8,' + encodeURIComponent(json));
|
||
|
}
|
||
|
resolve(json);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Save waveform image as data URI.
|
||
|
*
|
||
|
* The default format is `'image/png'`. Other supported types are
|
||
|
* `'image/jpeg'` and `'image/webp'`.
|
||
|
*
|
||
|
* @param {string} format='image/png' A string indicating the image format.
|
||
|
* The default format type is `'image/png'`.
|
||
|
* @param {number} quality=1 A number between 0 and 1 indicating the image
|
||
|
* quality to use for image formats that use lossy compression such as
|
||
|
* `'image/jpeg'`` and `'image/webp'`.
|
||
|
* @param {string} type Image data type to return. Either 'dataURL' (default)
|
||
|
* or 'blob'.
|
||
|
* @return {string|string[]|Promise} When using `'dataURL'` type this returns
|
||
|
* a single data URL or an array of data URLs, one for each canvas. When using
|
||
|
* `'blob'` type this returns a `Promise` resolving with an array of `Blob`
|
||
|
* instances, one for each canvas.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "exportImage",
|
||
|
value: function exportImage(format, quality, type) {
|
||
|
if (!format) {
|
||
|
format = 'image/png';
|
||
|
}
|
||
|
if (!quality) {
|
||
|
quality = 1;
|
||
|
}
|
||
|
if (!type) {
|
||
|
type = 'dataURL';
|
||
|
}
|
||
|
return this.drawer.getImage(format, quality, type);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Cancel any fetch request currently in progress
|
||
|
*/
|
||
|
}, {
|
||
|
key: "cancelAjax",
|
||
|
value: function cancelAjax() {
|
||
|
if (this.currentRequest && this.currentRequest.controller) {
|
||
|
// If the current request has a ProgressHandler, then its ReadableStream might need to be cancelled too
|
||
|
// See: Wavesurfer issue #2042
|
||
|
// See Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1583815
|
||
|
if (this.currentRequest._reader) {
|
||
|
// Ignoring exceptions thrown by call to cancel()
|
||
|
this.currentRequest._reader.cancel().catch(function (err) {});
|
||
|
}
|
||
|
this.currentRequest.controller.abort();
|
||
|
this.currentRequest = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @private
|
||
|
*/
|
||
|
}, {
|
||
|
key: "clearTmpEvents",
|
||
|
value: function clearTmpEvents() {
|
||
|
this.tmpEvents.forEach(function (e) {
|
||
|
return e.un();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Display empty waveform.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "empty",
|
||
|
value: function empty() {
|
||
|
if (!this.backend.isPaused()) {
|
||
|
this.stop();
|
||
|
this.backend.disconnectSource();
|
||
|
}
|
||
|
this.isReady = false;
|
||
|
this.cancelAjax();
|
||
|
this.clearTmpEvents();
|
||
|
|
||
|
// empty drawer
|
||
|
this.drawer.progress(0);
|
||
|
this.drawer.setWidth(0);
|
||
|
this.drawer.drawPeaks({
|
||
|
length: this.drawer.getWidth()
|
||
|
}, 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove events, elements and disconnect WebAudio nodes.
|
||
|
*
|
||
|
* @emits WaveSurfer#destroy
|
||
|
*/
|
||
|
}, {
|
||
|
key: "destroy",
|
||
|
value: function destroy() {
|
||
|
this.destroyAllPlugins();
|
||
|
this.fireEvent('destroy');
|
||
|
this.cancelAjax();
|
||
|
this.clearTmpEvents();
|
||
|
this.unAll();
|
||
|
if (this.params.responsive !== false) {
|
||
|
window.removeEventListener('resize', this._onResize, true);
|
||
|
window.removeEventListener('orientationchange', this._onResize, true);
|
||
|
}
|
||
|
if (this.backend) {
|
||
|
this.backend.destroy();
|
||
|
// clears memory usage
|
||
|
this.backend = null;
|
||
|
}
|
||
|
if (this.drawer) {
|
||
|
this.drawer.destroy();
|
||
|
}
|
||
|
this.isDestroyed = true;
|
||
|
this.isReady = false;
|
||
|
this.arraybuffer = null;
|
||
|
}
|
||
|
}], [{
|
||
|
key: "create",
|
||
|
value: /** @private */
|
||
|
|
||
|
/** @private */
|
||
|
|
||
|
/**
|
||
|
* Instantiate this class, call its `init` function and returns it
|
||
|
*
|
||
|
* @param {WavesurferParams} params The wavesurfer parameters
|
||
|
* @return {Object} WaveSurfer instance
|
||
|
* @example const wavesurfer = WaveSurfer.create(params);
|
||
|
*/
|
||
|
function create(params) {
|
||
|
var wavesurfer = new WaveSurfer(params);
|
||
|
return wavesurfer.init();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The library version number is available as a static property of the
|
||
|
* WaveSurfer class
|
||
|
*
|
||
|
* @type {String}
|
||
|
* @example
|
||
|
* console.log('Using wavesurfer.js ' + WaveSurfer.VERSION);
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Functions in the `util` property are available as a prototype property to
|
||
|
* all instances
|
||
|
*
|
||
|
* @type {Object}
|
||
|
* @example
|
||
|
* const wavesurfer = WaveSurfer.create(params);
|
||
|
* wavesurfer.util.style(myElement, { background: 'blue' });
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Functions in the `util` property are available as a static property of the
|
||
|
* WaveSurfer class
|
||
|
*
|
||
|
* @type {Object}
|
||
|
* @example
|
||
|
* WaveSurfer.util.style(myElement, { background: 'blue' });
|
||
|
*/
|
||
|
}]);
|
||
|
return WaveSurfer;
|
||
|
}(util.Observer);
|
||
|
WaveSurfer.VERSION = "4.6.0";
|
||
|
WaveSurfer.util = util;
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./src/webaudio.js":
|
||
|
/*!*************************!*\
|
||
|
!*** ./src/webaudio.js ***!
|
||
|
\*************************/
|
||
|
/***/ ((module, exports, __webpack_require__) => {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
|
||
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
||
|
Object.defineProperty(exports, "__esModule", ({
|
||
|
value: true
|
||
|
}));
|
||
|
exports["default"] = void 0;
|
||
|
var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js"));
|
||
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
||
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||
|
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, _toPropertyKey(descriptor.key), descriptor); } }
|
||
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
||
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
||
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
||
|
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
|
||
|
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() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
||
|
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||
|
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 } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||
|
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||
|
// using constants to prevent someone writing the string wrong
|
||
|
var PLAYING = 'playing';
|
||
|
var PAUSED = 'paused';
|
||
|
var FINISHED = 'finished';
|
||
|
|
||
|
/**
|
||
|
* WebAudio backend
|
||
|
*
|
||
|
* @extends {Observer}
|
||
|
*/
|
||
|
var WebAudio = exports["default"] = /*#__PURE__*/function (_util$Observer) {
|
||
|
_inherits(WebAudio, _util$Observer);
|
||
|
/**
|
||
|
* Construct the backend
|
||
|
*
|
||
|
* @param {WavesurferParams} params Wavesurfer parameters
|
||
|
*/
|
||
|
function WebAudio(params) {
|
||
|
var _this;
|
||
|
_classCallCheck(this, WebAudio);
|
||
|
_this = _callSuper(this, WebAudio);
|
||
|
/** @private */
|
||
|
_this.audioContext = null;
|
||
|
_this.offlineAudioContext = null;
|
||
|
_this.stateBehaviors = _defineProperty(_defineProperty(_defineProperty({}, PLAYING, {
|
||
|
init: function init() {
|
||
|
this.addOnAudioProcess();
|
||
|
},
|
||
|
getPlayedPercents: function getPlayedPercents() {
|
||
|
var duration = this.getDuration();
|
||
|
return this.getCurrentTime() / duration || 0;
|
||
|
},
|
||
|
getCurrentTime: function getCurrentTime() {
|
||
|
return this.startPosition + this.getPlayedTime();
|
||
|
}
|
||
|
}), PAUSED, {
|
||
|
init: function init() {
|
||
|
this.removeOnAudioProcess();
|
||
|
},
|
||
|
getPlayedPercents: function getPlayedPercents() {
|
||
|
var duration = this.getDuration();
|
||
|
return this.getCurrentTime() / duration || 0;
|
||
|
},
|
||
|
getCurrentTime: function getCurrentTime() {
|
||
|
return this.startPosition;
|
||
|
}
|
||
|
}), FINISHED, {
|
||
|
init: function init() {
|
||
|
this.removeOnAudioProcess();
|
||
|
this.fireEvent('finish');
|
||
|
},
|
||
|
getPlayedPercents: function getPlayedPercents() {
|
||
|
return 1;
|
||
|
},
|
||
|
getCurrentTime: function getCurrentTime() {
|
||
|
return this.getDuration();
|
||
|
}
|
||
|
});
|
||
|
_this.params = params;
|
||
|
/** ac: Audio Context instance */
|
||
|
_this.ac = params.audioContext || (_this.supportsWebAudio() ? _this.getAudioContext() : {});
|
||
|
/**@private */
|
||
|
_this.lastPlay = _this.ac.currentTime;
|
||
|
/** @private */
|
||
|
_this.startPosition = 0;
|
||
|
/** @private */
|
||
|
_this.scheduledPause = null;
|
||
|
/** @private */
|
||
|
_this.states = _defineProperty(_defineProperty(_defineProperty({}, PLAYING, Object.create(_this.stateBehaviors[PLAYING])), PAUSED, Object.create(_this.stateBehaviors[PAUSED])), FINISHED, Object.create(_this.stateBehaviors[FINISHED]));
|
||
|
/** @private */
|
||
|
_this.buffer = null;
|
||
|
/** @private */
|
||
|
_this.filters = [];
|
||
|
/** gainNode: allows to control audio volume */
|
||
|
_this.gainNode = null;
|
||
|
/** @private */
|
||
|
_this.mergedPeaks = null;
|
||
|
/** @private */
|
||
|
_this.offlineAc = null;
|
||
|
/** @private */
|
||
|
_this.peaks = null;
|
||
|
/** @private */
|
||
|
_this.playbackRate = 1;
|
||
|
/** analyser: provides audio analysis information */
|
||
|
_this.analyser = null;
|
||
|
/** scriptNode: allows processing audio */
|
||
|
_this.scriptNode = null;
|
||
|
/** @private */
|
||
|
_this.source = null;
|
||
|
/** @private */
|
||
|
_this.splitPeaks = [];
|
||
|
/** @private */
|
||
|
_this.state = null;
|
||
|
/** @private */
|
||
|
_this.explicitDuration = params.duration;
|
||
|
/**
|
||
|
* Boolean indicating if the backend was destroyed.
|
||
|
*/
|
||
|
_this.destroyed = false;
|
||
|
return _this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialise the backend, called in `wavesurfer.createBackend()`
|
||
|
*/
|
||
|
_createClass(WebAudio, [{
|
||
|
key: "supportsWebAudio",
|
||
|
value: /** scriptBufferSize: size of the processing buffer */
|
||
|
|
||
|
/** audioContext: allows to process audio with WebAudio API */
|
||
|
|
||
|
/** @private */
|
||
|
|
||
|
/** @private */
|
||
|
|
||
|
/**
|
||
|
* Does the browser support this backend
|
||
|
*
|
||
|
* @return {boolean} Whether or not this browser supports this backend
|
||
|
*/
|
||
|
function supportsWebAudio() {
|
||
|
return !!(window.AudioContext || window.webkitAudioContext);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the audio context used by this backend or create one
|
||
|
*
|
||
|
* @return {AudioContext} Existing audio context, or creates a new one
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getAudioContext",
|
||
|
value: function getAudioContext() {
|
||
|
if (!window.WaveSurferAudioContext) {
|
||
|
window.WaveSurferAudioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||
|
}
|
||
|
return window.WaveSurferAudioContext;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the offline audio context used by this backend or create one
|
||
|
*
|
||
|
* @param {number} sampleRate The sample rate to use
|
||
|
* @return {OfflineAudioContext} Existing offline audio context, or creates
|
||
|
* a new one
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getOfflineAudioContext",
|
||
|
value: function getOfflineAudioContext(sampleRate) {
|
||
|
if (!window.WaveSurferOfflineAudioContext) {
|
||
|
window.WaveSurferOfflineAudioContext = new (window.OfflineAudioContext || window.webkitOfflineAudioContext)(1, 2, sampleRate);
|
||
|
}
|
||
|
return window.WaveSurferOfflineAudioContext;
|
||
|
}
|
||
|
}, {
|
||
|
key: "init",
|
||
|
value: function init() {
|
||
|
this.createVolumeNode();
|
||
|
this.createScriptNode();
|
||
|
this.createAnalyserNode();
|
||
|
this.setState(PAUSED);
|
||
|
this.setPlaybackRate(this.params.audioRate);
|
||
|
this.setLength(0);
|
||
|
}
|
||
|
|
||
|
/** @private */
|
||
|
}, {
|
||
|
key: "disconnectFilters",
|
||
|
value: function disconnectFilters() {
|
||
|
if (this.filters) {
|
||
|
this.filters.forEach(function (filter) {
|
||
|
filter && filter.disconnect();
|
||
|
});
|
||
|
this.filters = null;
|
||
|
// Reconnect direct path
|
||
|
this.analyser.connect(this.gainNode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @private
|
||
|
*
|
||
|
* @param {string} state The new state
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setState",
|
||
|
value: function setState(state) {
|
||
|
if (this.state !== this.states[state]) {
|
||
|
this.state = this.states[state];
|
||
|
this.state.init.call(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Unpacked `setFilters()`
|
||
|
*
|
||
|
* @param {...AudioNode} filters One or more filters to set
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setFilter",
|
||
|
value: function setFilter() {
|
||
|
for (var _len = arguments.length, filters = new Array(_len), _key = 0; _key < _len; _key++) {
|
||
|
filters[_key] = arguments[_key];
|
||
|
}
|
||
|
this.setFilters(filters);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Insert custom Web Audio nodes into the graph
|
||
|
*
|
||
|
* @param {AudioNode[]} filters Packed filters array
|
||
|
* @example
|
||
|
* const lowpass = wavesurfer.backend.ac.createBiquadFilter();
|
||
|
* wavesurfer.backend.setFilter(lowpass);
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setFilters",
|
||
|
value: function setFilters(filters) {
|
||
|
// Remove existing filters
|
||
|
this.disconnectFilters();
|
||
|
|
||
|
// Insert filters if filter array not empty
|
||
|
if (filters && filters.length) {
|
||
|
this.filters = filters;
|
||
|
|
||
|
// Disconnect direct path before inserting filters
|
||
|
this.analyser.disconnect();
|
||
|
|
||
|
// Connect each filter in turn
|
||
|
filters.reduce(function (prev, curr) {
|
||
|
prev.connect(curr);
|
||
|
return curr;
|
||
|
}, this.analyser).connect(this.gainNode);
|
||
|
}
|
||
|
}
|
||
|
/** Create ScriptProcessorNode to process audio */
|
||
|
}, {
|
||
|
key: "createScriptNode",
|
||
|
value: function createScriptNode() {
|
||
|
if (this.params.audioScriptProcessor) {
|
||
|
this.scriptNode = this.params.audioScriptProcessor;
|
||
|
} else {
|
||
|
if (this.ac.createScriptProcessor) {
|
||
|
this.scriptNode = this.ac.createScriptProcessor(WebAudio.scriptBufferSize);
|
||
|
} else {
|
||
|
this.scriptNode = this.ac.createJavaScriptNode(WebAudio.scriptBufferSize);
|
||
|
}
|
||
|
}
|
||
|
this.scriptNode.connect(this.ac.destination);
|
||
|
}
|
||
|
|
||
|
/** @private */
|
||
|
}, {
|
||
|
key: "addOnAudioProcess",
|
||
|
value: function addOnAudioProcess() {
|
||
|
var _this2 = this;
|
||
|
this.scriptNode.onaudioprocess = function () {
|
||
|
var time = _this2.getCurrentTime();
|
||
|
if (time >= _this2.getDuration()) {
|
||
|
_this2.setState(FINISHED);
|
||
|
_this2.fireEvent('pause');
|
||
|
} else if (time >= _this2.scheduledPause) {
|
||
|
_this2.pause();
|
||
|
} else if (_this2.state === _this2.states[PLAYING]) {
|
||
|
_this2.fireEvent('audioprocess', time);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/** @private */
|
||
|
}, {
|
||
|
key: "removeOnAudioProcess",
|
||
|
value: function removeOnAudioProcess() {
|
||
|
this.scriptNode.onaudioprocess = function () {};
|
||
|
}
|
||
|
/** Create analyser node to perform audio analysis */
|
||
|
}, {
|
||
|
key: "createAnalyserNode",
|
||
|
value: function createAnalyserNode() {
|
||
|
this.analyser = this.ac.createAnalyser();
|
||
|
this.analyser.connect(this.gainNode);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create the gain node needed to control the playback volume.
|
||
|
*
|
||
|
*/
|
||
|
}, {
|
||
|
key: "createVolumeNode",
|
||
|
value: function createVolumeNode() {
|
||
|
// Create gain node using the AudioContext
|
||
|
if (this.ac.createGain) {
|
||
|
this.gainNode = this.ac.createGain();
|
||
|
} else {
|
||
|
this.gainNode = this.ac.createGainNode();
|
||
|
}
|
||
|
// Add the gain node to the graph
|
||
|
this.gainNode.connect(this.ac.destination);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the sink id for the media player
|
||
|
*
|
||
|
* @param {string} deviceId String value representing audio device id.
|
||
|
* @returns {Promise} A Promise that resolves to `undefined` when there
|
||
|
* are no errors.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setSinkId",
|
||
|
value: function setSinkId(deviceId) {
|
||
|
if (deviceId) {
|
||
|
/**
|
||
|
* The webaudio API doesn't currently support setting the device
|
||
|
* output. Here we create an HTMLAudioElement, connect the
|
||
|
* webaudio stream to that element and setSinkId there.
|
||
|
*/
|
||
|
var audio = new window.Audio();
|
||
|
if (!audio.setSinkId) {
|
||
|
return Promise.reject(new Error('setSinkId is not supported in your browser'));
|
||
|
}
|
||
|
audio.autoplay = true;
|
||
|
var dest = this.ac.createMediaStreamDestination();
|
||
|
this.gainNode.disconnect();
|
||
|
this.gainNode.connect(dest);
|
||
|
audio.srcObject = dest.stream;
|
||
|
return audio.setSinkId(deviceId);
|
||
|
} else {
|
||
|
return Promise.reject(new Error('Invalid deviceId: ' + deviceId));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the audio volume
|
||
|
*
|
||
|
* @param {number} value A floating point value between 0 and 1.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setVolume",
|
||
|
value: function setVolume(value) {
|
||
|
this.gainNode.gain.setValueAtTime(value, this.ac.currentTime);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the current volume
|
||
|
*
|
||
|
* @return {number} value A floating point value between 0 and 1.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getVolume",
|
||
|
value: function getVolume() {
|
||
|
return this.gainNode.gain.value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Decode an array buffer and pass data to a callback
|
||
|
*
|
||
|
* @private
|
||
|
* @param {ArrayBuffer} arraybuffer The array buffer to decode
|
||
|
* @param {function} callback The function to call on complete.
|
||
|
* @param {function} errback The function to call on error.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "decodeArrayBuffer",
|
||
|
value: function decodeArrayBuffer(arraybuffer, callback, errback) {
|
||
|
if (!this.offlineAc) {
|
||
|
this.offlineAc = this.getOfflineAudioContext(this.ac && this.ac.sampleRate ? this.ac.sampleRate : 44100);
|
||
|
}
|
||
|
if ('webkitAudioContext' in window) {
|
||
|
// Safari: no support for Promise-based decodeAudioData enabled
|
||
|
// Enable it in Safari using the Experimental Features > Modern WebAudio API option
|
||
|
this.offlineAc.decodeAudioData(arraybuffer, function (data) {
|
||
|
return callback(data);
|
||
|
}, errback);
|
||
|
} else {
|
||
|
this.offlineAc.decodeAudioData(arraybuffer).then(function (data) {
|
||
|
return callback(data);
|
||
|
}).catch(function (err) {
|
||
|
return errback(err);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set pre-decoded peaks
|
||
|
*
|
||
|
* @param {number[]|Number.<Array[]>} peaks Peaks data
|
||
|
* @param {?number} duration Explicit duration
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setPeaks",
|
||
|
value: function setPeaks(peaks, duration) {
|
||
|
if (duration != null) {
|
||
|
this.explicitDuration = duration;
|
||
|
}
|
||
|
this.peaks = peaks;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the rendered length (different from the length of the audio)
|
||
|
*
|
||
|
* @param {number} length The rendered length
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setLength",
|
||
|
value: function setLength(length) {
|
||
|
// No resize, we can preserve the cached peaks.
|
||
|
if (this.mergedPeaks && length == 2 * this.mergedPeaks.length - 1 + 2) {
|
||
|
return;
|
||
|
}
|
||
|
this.splitPeaks = [];
|
||
|
this.mergedPeaks = [];
|
||
|
// Set the last element of the sparse array so the peak arrays are
|
||
|
// appropriately sized for other calculations.
|
||
|
var channels = this.buffer ? this.buffer.numberOfChannels : 1;
|
||
|
var c;
|
||
|
for (c = 0; c < channels; c++) {
|
||
|
this.splitPeaks[c] = [];
|
||
|
this.splitPeaks[c][2 * (length - 1)] = 0;
|
||
|
this.splitPeaks[c][2 * (length - 1) + 1] = 0;
|
||
|
}
|
||
|
this.mergedPeaks[2 * (length - 1)] = 0;
|
||
|
this.mergedPeaks[2 * (length - 1) + 1] = 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Compute the max and min value of the waveform when broken into <length> subranges.
|
||
|
*
|
||
|
* @param {number} length How many subranges to break the waveform into.
|
||
|
* @param {number} first First sample in the required range.
|
||
|
* @param {number} last Last sample in the required range.
|
||
|
* @return {number[]|Number.<Array[]>} Array of 2*<length> peaks or array of arrays of
|
||
|
* peaks consisting of (max, min) values for each subrange.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getPeaks",
|
||
|
value: function getPeaks(length, first, last) {
|
||
|
if (this.peaks) {
|
||
|
return this.peaks;
|
||
|
}
|
||
|
if (!this.buffer) {
|
||
|
return [];
|
||
|
}
|
||
|
first = first || 0;
|
||
|
last = last || length - 1;
|
||
|
this.setLength(length);
|
||
|
if (!this.buffer) {
|
||
|
return this.params.splitChannels ? this.splitPeaks : this.mergedPeaks;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The following snippet fixes a buffering data issue on the Safari
|
||
|
* browser which returned undefined It creates the missing buffer based
|
||
|
* on 1 channel, 4096 samples and the sampleRate from the current
|
||
|
* webaudio context 4096 samples seemed to be the best fit for rendering
|
||
|
* will review this code once a stable version of Safari TP is out
|
||
|
*/
|
||
|
if (!this.buffer.length) {
|
||
|
var newBuffer = this.createBuffer(1, 4096, this.sampleRate);
|
||
|
this.buffer = newBuffer.buffer;
|
||
|
}
|
||
|
var sampleSize = this.buffer.length / length;
|
||
|
var sampleStep = ~~(sampleSize / 10) || 1;
|
||
|
var channels = this.buffer.numberOfChannels;
|
||
|
var c;
|
||
|
for (c = 0; c < channels; c++) {
|
||
|
var peaks = this.splitPeaks[c];
|
||
|
var chan = this.buffer.getChannelData(c);
|
||
|
var i = void 0;
|
||
|
for (i = first; i <= last; i++) {
|
||
|
var start = ~~(i * sampleSize);
|
||
|
var end = ~~(start + sampleSize);
|
||
|
/**
|
||
|
* Initialize the max and min to the first sample of this
|
||
|
* subrange, so that even if the samples are entirely
|
||
|
* on one side of zero, we still return the true max and
|
||
|
* min values in the subrange.
|
||
|
*/
|
||
|
var min = chan[start];
|
||
|
var max = min;
|
||
|
var j = void 0;
|
||
|
for (j = start; j < end; j += sampleStep) {
|
||
|
var value = chan[j];
|
||
|
if (value > max) {
|
||
|
max = value;
|
||
|
}
|
||
|
if (value < min) {
|
||
|
min = value;
|
||
|
}
|
||
|
}
|
||
|
peaks[2 * i] = max;
|
||
|
peaks[2 * i + 1] = min;
|
||
|
if (c == 0 || max > this.mergedPeaks[2 * i]) {
|
||
|
this.mergedPeaks[2 * i] = max;
|
||
|
}
|
||
|
if (c == 0 || min < this.mergedPeaks[2 * i + 1]) {
|
||
|
this.mergedPeaks[2 * i + 1] = min;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return this.params.splitChannels ? this.splitPeaks : this.mergedPeaks;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the position from 0 to 1
|
||
|
*
|
||
|
* @return {number} Position
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getPlayedPercents",
|
||
|
value: function getPlayedPercents() {
|
||
|
return this.state.getPlayedPercents.call(this);
|
||
|
}
|
||
|
|
||
|
/** @private */
|
||
|
}, {
|
||
|
key: "disconnectSource",
|
||
|
value: function disconnectSource() {
|
||
|
if (this.source) {
|
||
|
this.source.disconnect();
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Destroy all references with WebAudio, disconnecting audio nodes and closing Audio Context
|
||
|
*/
|
||
|
}, {
|
||
|
key: "destroyWebAudio",
|
||
|
value: function destroyWebAudio() {
|
||
|
this.disconnectFilters();
|
||
|
this.disconnectSource();
|
||
|
this.gainNode.disconnect();
|
||
|
this.scriptNode.disconnect();
|
||
|
this.analyser.disconnect();
|
||
|
|
||
|
// close the audioContext if closeAudioContext option is set to true
|
||
|
if (this.params.closeAudioContext) {
|
||
|
// check if browser supports AudioContext.close()
|
||
|
if (typeof this.ac.close === 'function' && this.ac.state != 'closed') {
|
||
|
this.ac.close();
|
||
|
}
|
||
|
// clear the reference to the audiocontext
|
||
|
this.ac = null;
|
||
|
// clear the actual audiocontext, either passed as param or the
|
||
|
// global singleton
|
||
|
if (!this.params.audioContext) {
|
||
|
window.WaveSurferAudioContext = null;
|
||
|
} else {
|
||
|
this.params.audioContext = null;
|
||
|
}
|
||
|
// clear the offlineAudioContext
|
||
|
window.WaveSurferOfflineAudioContext = null;
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* This is called when wavesurfer is destroyed
|
||
|
*/
|
||
|
}, {
|
||
|
key: "destroy",
|
||
|
value: function destroy() {
|
||
|
if (!this.isPaused()) {
|
||
|
this.pause();
|
||
|
}
|
||
|
this.unAll();
|
||
|
this.buffer = null;
|
||
|
this.destroyed = true;
|
||
|
this.destroyWebAudio();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Loaded a decoded audio buffer
|
||
|
*
|
||
|
* @param {Object} buffer Decoded audio buffer to load
|
||
|
*/
|
||
|
}, {
|
||
|
key: "load",
|
||
|
value: function load(buffer) {
|
||
|
this.startPosition = 0;
|
||
|
this.lastPlay = this.ac.currentTime;
|
||
|
this.buffer = buffer;
|
||
|
this.createSource();
|
||
|
}
|
||
|
|
||
|
/** @private */
|
||
|
}, {
|
||
|
key: "createSource",
|
||
|
value: function createSource() {
|
||
|
this.disconnectSource();
|
||
|
this.source = this.ac.createBufferSource();
|
||
|
|
||
|
// adjust for old browsers
|
||
|
this.source.start = this.source.start || this.source.noteGrainOn;
|
||
|
this.source.stop = this.source.stop || this.source.noteOff;
|
||
|
this.setPlaybackRate(this.playbackRate);
|
||
|
this.source.buffer = this.buffer;
|
||
|
this.source.connect(this.analyser);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @private
|
||
|
*
|
||
|
* some browsers require an explicit call to #resume before they will play back audio
|
||
|
*/
|
||
|
}, {
|
||
|
key: "resumeAudioContext",
|
||
|
value: function resumeAudioContext() {
|
||
|
if (this.ac.state == 'suspended') {
|
||
|
this.ac.resume && this.ac.resume();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used by `wavesurfer.isPlaying()` and `wavesurfer.playPause()`
|
||
|
*
|
||
|
* @return {boolean} Whether or not this backend is currently paused
|
||
|
*/
|
||
|
}, {
|
||
|
key: "isPaused",
|
||
|
value: function isPaused() {
|
||
|
return this.state !== this.states[PLAYING];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used by `wavesurfer.getDuration()`
|
||
|
*
|
||
|
* @return {number} Duration of loaded buffer
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getDuration",
|
||
|
value: function getDuration() {
|
||
|
if (this.explicitDuration) {
|
||
|
return this.explicitDuration;
|
||
|
}
|
||
|
if (!this.buffer) {
|
||
|
return 0;
|
||
|
}
|
||
|
return this.buffer.duration;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Used by `wavesurfer.seekTo()`
|
||
|
*
|
||
|
* @param {number} start Position to start at in seconds
|
||
|
* @param {number} end Position to end at in seconds
|
||
|
* @return {{start: number, end: number}} Object containing start and end
|
||
|
* positions
|
||
|
*/
|
||
|
}, {
|
||
|
key: "seekTo",
|
||
|
value: function seekTo(start, end) {
|
||
|
if (!this.buffer) {
|
||
|
return;
|
||
|
}
|
||
|
this.scheduledPause = null;
|
||
|
if (start == null) {
|
||
|
start = this.getCurrentTime();
|
||
|
if (start >= this.getDuration()) {
|
||
|
start = 0;
|
||
|
}
|
||
|
}
|
||
|
if (end == null) {
|
||
|
end = this.getDuration();
|
||
|
}
|
||
|
this.startPosition = start;
|
||
|
this.lastPlay = this.ac.currentTime;
|
||
|
if (this.state === this.states[FINISHED]) {
|
||
|
this.setState(PAUSED);
|
||
|
}
|
||
|
return {
|
||
|
start: start,
|
||
|
end: end
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the playback position in seconds
|
||
|
*
|
||
|
* @return {number} The playback position in seconds
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getPlayedTime",
|
||
|
value: function getPlayedTime() {
|
||
|
return (this.ac.currentTime - this.lastPlay) * this.playbackRate;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Plays the loaded audio region.
|
||
|
*
|
||
|
* @param {number} start Start offset in seconds, relative to the beginning
|
||
|
* of a clip.
|
||
|
* @param {number} end When to stop relative to the beginning of a clip.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "play",
|
||
|
value: function play(start, end) {
|
||
|
if (!this.buffer) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// need to re-create source on each playback
|
||
|
this.createSource();
|
||
|
var adjustedTime = this.seekTo(start, end);
|
||
|
start = adjustedTime.start;
|
||
|
end = adjustedTime.end;
|
||
|
this.scheduledPause = end;
|
||
|
this.source.start(0, start);
|
||
|
this.resumeAudioContext();
|
||
|
this.setState(PLAYING);
|
||
|
this.fireEvent('play');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Pauses the loaded audio.
|
||
|
*/
|
||
|
}, {
|
||
|
key: "pause",
|
||
|
value: function pause() {
|
||
|
this.scheduledPause = null;
|
||
|
this.startPosition += this.getPlayedTime();
|
||
|
this.source && this.source.stop(0);
|
||
|
this.setState(PAUSED);
|
||
|
this.fireEvent('pause');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the current time in seconds relative to the audio-clip's
|
||
|
* duration.
|
||
|
*
|
||
|
* @return {number} The current time in seconds
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getCurrentTime",
|
||
|
value: function getCurrentTime() {
|
||
|
return this.state.getCurrentTime.call(this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the current playback rate. (0=no playback, 1=normal playback)
|
||
|
*
|
||
|
* @return {number} The current playback rate
|
||
|
*/
|
||
|
}, {
|
||
|
key: "getPlaybackRate",
|
||
|
value: function getPlaybackRate() {
|
||
|
return this.playbackRate;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the audio source playback rate.
|
||
|
*
|
||
|
* @param {number} value The playback rate to use
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setPlaybackRate",
|
||
|
value: function setPlaybackRate(value) {
|
||
|
this.playbackRate = value || 1;
|
||
|
this.source && this.source.playbackRate.setValueAtTime(this.playbackRate, this.ac.currentTime);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set a point in seconds for playback to stop at.
|
||
|
*
|
||
|
* @param {number} end Position to end at
|
||
|
* @version 3.3.0
|
||
|
*/
|
||
|
}, {
|
||
|
key: "setPlayEnd",
|
||
|
value: function setPlayEnd(end) {
|
||
|
this.scheduledPause = end;
|
||
|
}
|
||
|
}]);
|
||
|
return WebAudio;
|
||
|
}(util.Observer);
|
||
|
WebAudio.scriptBufferSize = 256;
|
||
|
module.exports = exports.default;
|
||
|
|
||
|
/***/ }),
|
||
|
|
||
|
/***/ "./node_modules/debounce/index.js":
|
||
|
/*!****************************************!*\
|
||
|
!*** ./node_modules/debounce/index.js ***!
|
||
|
\****************************************/
|
||
|
/***/ ((module) => {
|
||
|
|
||
|
/**
|
||
|
* Returns a function, that, as long as it continues to be invoked, will not
|
||
|
* be triggered. The function will be called after it stops being called for
|
||
|
* N milliseconds. If `immediate` is passed, trigger the function on the
|
||
|
* leading edge, instead of the trailing. The function also has a property 'clear'
|
||
|
* that is a function which will clear the timer to prevent previously scheduled executions.
|
||
|
*
|
||
|
* @source underscore.js
|
||
|
* @see http://unscriptable.com/2009/03/20/debouncing-javascript-methods/
|
||
|
* @param {Function} function to wrap
|
||
|
* @param {Number} timeout in ms (`100`)
|
||
|
* @param {Boolean} whether to execute at the beginning (`false`)
|
||
|
* @api public
|
||
|
*/
|
||
|
function debounce(func, wait, immediate){
|
||
|
var timeout, args, context, timestamp, result;
|
||
|
if (null == wait) wait = 100;
|
||
|
|
||
|
function later() {
|
||
|
var last = Date.now() - timestamp;
|
||
|
|
||
|
if (last < wait && last >= 0) {
|
||
|
timeout = setTimeout(later, wait - last);
|
||
|
} else {
|
||
|
timeout = null;
|
||
|
if (!immediate) {
|
||
|
result = func.apply(context, args);
|
||
|
context = args = null;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var debounced = function(){
|
||
|
context = this;
|
||
|
args = arguments;
|
||
|
timestamp = Date.now();
|
||
|
var callNow = immediate && !timeout;
|
||
|
if (!timeout) timeout = setTimeout(later, wait);
|
||
|
if (callNow) {
|
||
|
result = func.apply(context, args);
|
||
|
context = args = null;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
};
|
||
|
|
||
|
debounced.clear = function() {
|
||
|
if (timeout) {
|
||
|
clearTimeout(timeout);
|
||
|
timeout = null;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
debounced.flush = function() {
|
||
|
if (timeout) {
|
||
|
result = func.apply(context, args);
|
||
|
context = args = null;
|
||
|
|
||
|
clearTimeout(timeout);
|
||
|
timeout = null;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
return debounced;
|
||
|
};
|
||
|
|
||
|
// Adds compatibility for ES modules
|
||
|
debounce.debounce = debounce;
|
||
|
|
||
|
module.exports = debounce;
|
||
|
|
||
|
|
||
|
/***/ })
|
||
|
|
||
|
/******/ });
|
||
|
/************************************************************************/
|
||
|
/******/ // 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;
|
||
|
/******/ }
|
||
|
/******/
|
||
|
/************************************************************************/
|
||
|
/******/
|
||
|
/******/ // startup
|
||
|
/******/ // Load entry module and return exports
|
||
|
/******/ // This entry module is referenced by other modules so it can't be inlined
|
||
|
/******/ var __webpack_exports__ = __webpack_require__("./src/wavesurfer.js");
|
||
|
/******/
|
||
|
/******/ return __webpack_exports__;
|
||
|
/******/ })()
|
||
|
;
|
||
|
});
|
||
|
//# sourceMappingURL=wavesurfer.js.map
|