UNPKG

218 kBJavaScriptView Raw
1/*!
2 * wavesurfer.js 6.4.0 (2022-11-05)
3 * https://wavesurfer-js.org
4 * @license BSD-3-Clause
5 */
6(function webpackUniversalModuleDefinition(root, factory) {
7 if(typeof exports === 'object' && typeof module === 'object')
8 module.exports = factory();
9 else if(typeof define === 'function' && define.amd)
10 define("WaveSurfer", [], factory);
11 else if(typeof exports === 'object')
12 exports["WaveSurfer"] = factory();
13 else
14 root["WaveSurfer"] = factory();
15})(self, () => {
16return /******/ (() => { // webpackBootstrap
17/******/ var __webpack_modules__ = ({
18
19/***/ "./src/drawer.canvasentry.js":
20/*!***********************************!*\
21 !*** ./src/drawer.canvasentry.js ***!
22 \***********************************/
23/***/ ((module, exports, __webpack_require__) => {
24
25"use strict";
26
27
28Object.defineProperty(exports, "__esModule", ({
29 value: true
30}));
31exports["default"] = void 0;
32var _style = _interopRequireDefault(__webpack_require__(/*! ./util/style */ "./src/util/style.js"));
33var _getId = _interopRequireDefault(__webpack_require__(/*! ./util/get-id */ "./src/util/get-id.js"));
34function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
35function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
36function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
37function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
38/**
39 * The `CanvasEntry` class represents an element consisting of a wave `canvas`
40 * and an (optional) progress wave `canvas`.
41 *
42 * The `MultiCanvas` renderer uses one or more `CanvasEntry` instances to
43 * render a waveform, depending on the zoom level.
44 */
45var CanvasEntry = /*#__PURE__*/function () {
46 function CanvasEntry() {
47 _classCallCheck(this, CanvasEntry);
48 /**
49 * The wave node
50 *
51 * @type {HTMLCanvasElement}
52 */
53 this.wave = null;
54 /**
55 * The wave canvas rendering context
56 *
57 * @type {CanvasRenderingContext2D}
58 */
59 this.waveCtx = null;
60 /**
61 * The (optional) progress wave node
62 *
63 * @type {HTMLCanvasElement}
64 */
65 this.progress = null;
66 /**
67 * The (optional) progress wave canvas rendering context
68 *
69 * @type {CanvasRenderingContext2D}
70 */
71 this.progressCtx = null;
72 /**
73 * Start of the area the canvas should render, between 0 and 1
74 *
75 * @type {number}
76 */
77 this.start = 0;
78 /**
79 * End of the area the canvas should render, between 0 and 1
80 *
81 * @type {number}
82 */
83 this.end = 1;
84 /**
85 * Unique identifier for this entry
86 *
87 * @type {string}
88 */
89 this.id = (0, _getId.default)(typeof this.constructor.name !== 'undefined' ? this.constructor.name.toLowerCase() + '_' : 'canvasentry_');
90 /**
91 * Canvas 2d context attributes
92 *
93 * @type {object}
94 */
95 this.canvasContextAttributes = {};
96 }
97
98 /**
99 * Store the wave canvas element and create the 2D rendering context
100 *
101 * @param {HTMLCanvasElement} element The wave `canvas` element.
102 */
103 _createClass(CanvasEntry, [{
104 key: "initWave",
105 value: function initWave(element) {
106 this.wave = element;
107 this.waveCtx = this.wave.getContext('2d', this.canvasContextAttributes);
108 }
109
110 /**
111 * Store the progress wave canvas element and create the 2D rendering
112 * context
113 *
114 * @param {HTMLCanvasElement} element The progress wave `canvas` element.
115 */
116 }, {
117 key: "initProgress",
118 value: function initProgress(element) {
119 this.progress = element;
120 this.progressCtx = this.progress.getContext('2d', this.canvasContextAttributes);
121 }
122
123 /**
124 * Update the dimensions
125 *
126 * @param {number} elementWidth Width of the entry
127 * @param {number} totalWidth Total width of the multi canvas renderer
128 * @param {number} width The new width of the element
129 * @param {number} height The new height of the element
130 */
131 }, {
132 key: "updateDimensions",
133 value: function updateDimensions(elementWidth, totalWidth, width, height) {
134 // where the canvas starts and ends in the waveform, represented as a
135 // decimal between 0 and 1
136 this.start = this.wave.offsetLeft / totalWidth || 0;
137 this.end = this.start + elementWidth / totalWidth;
138
139 // set wave canvas dimensions
140 this.wave.width = width;
141 this.wave.height = height;
142 var elementSize = {
143 width: elementWidth + 'px'
144 };
145 (0, _style.default)(this.wave, elementSize);
146 if (this.hasProgressCanvas) {
147 // set progress canvas dimensions
148 this.progress.width = width;
149 this.progress.height = height;
150 (0, _style.default)(this.progress, elementSize);
151 }
152 }
153
154 /**
155 * Clear the wave and progress rendering contexts
156 */
157 }, {
158 key: "clearWave",
159 value: function clearWave() {
160 // wave
161 this.waveCtx.clearRect(0, 0, this.waveCtx.canvas.width, this.waveCtx.canvas.height);
162
163 // progress
164 if (this.hasProgressCanvas) {
165 this.progressCtx.clearRect(0, 0, this.progressCtx.canvas.width, this.progressCtx.canvas.height);
166 }
167 }
168
169 /**
170 * Set the fill styles for wave and progress
171 * @param {string|string[]} waveColor Fill color for the wave canvas,
172 * or an array of colors to apply as a gradient
173 * @param {?string|string[]} progressColor Fill color for the progress canvas,
174 * or an array of colors to apply as a gradient
175 */
176 }, {
177 key: "setFillStyles",
178 value: function setFillStyles(waveColor, progressColor) {
179 this.waveCtx.fillStyle = this.getFillStyle(this.waveCtx, waveColor);
180 if (this.hasProgressCanvas) {
181 this.progressCtx.fillStyle = this.getFillStyle(this.progressCtx, progressColor);
182 }
183 }
184
185 /**
186 * Utility function to handle wave color arguments
187 *
188 * When the color argument type is a string or CanvasGradient instance,
189 * it will be returned as is. Otherwise, it will be treated as an array,
190 * and a new CanvasGradient will be returned
191 *
192 * @since 6.0.0
193 * @param {CanvasRenderingContext2D} ctx Rendering context of target canvas
194 * @param {string|string[]|CanvasGradient} color Either a single fill color
195 * for the wave canvas, an existing CanvasGradient instance, or an array
196 * of colors to apply as a gradient
197 * @returns {string|CanvasGradient} Returns a string fillstyle value, or a
198 * canvas gradient
199 */
200 }, {
201 key: "getFillStyle",
202 value: function getFillStyle(ctx, color) {
203 if (typeof color == 'string' || color instanceof CanvasGradient) {
204 return color;
205 }
206 var waveGradient = ctx.createLinearGradient(0, 0, 0, ctx.canvas.height);
207 color.forEach(function (value, index) {
208 return waveGradient.addColorStop(index / color.length, value);
209 });
210 return waveGradient;
211 }
212
213 /**
214 * Set the canvas transforms for wave and progress
215 *
216 * @param {boolean} vertical Whether to render vertically
217 */
218 }, {
219 key: "applyCanvasTransforms",
220 value: function applyCanvasTransforms(vertical) {
221 if (vertical) {
222 // Reflect the waveform across the line y = -x
223 this.waveCtx.setTransform(0, 1, 1, 0, 0, 0);
224 if (this.hasProgressCanvas) {
225 this.progressCtx.setTransform(0, 1, 1, 0, 0, 0);
226 }
227 }
228 }
229
230 /**
231 * Draw a rectangle for wave and progress
232 *
233 * @param {number} x X start position
234 * @param {number} y Y start position
235 * @param {number} width Width of the rectangle
236 * @param {number} height Height of the rectangle
237 * @param {number} radius Radius of the rectangle
238 */
239 }, {
240 key: "fillRects",
241 value: function fillRects(x, y, width, height, radius) {
242 this.fillRectToContext(this.waveCtx, x, y, width, height, radius);
243 if (this.hasProgressCanvas) {
244 this.fillRectToContext(this.progressCtx, x, y, width, height, radius);
245 }
246 }
247
248 /**
249 * Draw the actual rectangle on a `canvas` element
250 *
251 * @param {CanvasRenderingContext2D} ctx Rendering context of target canvas
252 * @param {number} x X start position
253 * @param {number} y Y start position
254 * @param {number} width Width of the rectangle
255 * @param {number} height Height of the rectangle
256 * @param {number} radius Radius of the rectangle
257 */
258 }, {
259 key: "fillRectToContext",
260 value: function fillRectToContext(ctx, x, y, width, height, radius) {
261 if (!ctx) {
262 return;
263 }
264 if (radius) {
265 this.drawRoundedRect(ctx, x, y, width, height, radius);
266 } else {
267 ctx.fillRect(x, y, width, height);
268 }
269 }
270
271 /**
272 * Draw a rounded rectangle on Canvas
273 *
274 * @param {CanvasRenderingContext2D} ctx Canvas context
275 * @param {number} x X-position of the rectangle
276 * @param {number} y Y-position of the rectangle
277 * @param {number} width Width of the rectangle
278 * @param {number} height Height of the rectangle
279 * @param {number} radius Radius of the rectangle
280 *
281 * @return {void}
282 * @example drawRoundedRect(ctx, 50, 50, 5, 10, 3)
283 */
284 }, {
285 key: "drawRoundedRect",
286 value: function drawRoundedRect(ctx, x, y, width, height, radius) {
287 if (height === 0) {
288 return;
289 }
290 // peaks are float values from -1 to 1. Use absolute height values in
291 // order to correctly calculate rounded rectangle coordinates
292 if (height < 0) {
293 height *= -1;
294 y -= height;
295 }
296 ctx.beginPath();
297 ctx.moveTo(x + radius, y);
298 ctx.lineTo(x + width - radius, y);
299 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
300 ctx.lineTo(x + width, y + height - radius);
301 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
302 ctx.lineTo(x + radius, y + height);
303 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
304 ctx.lineTo(x, y + radius);
305 ctx.quadraticCurveTo(x, y, x + radius, y);
306 ctx.closePath();
307 ctx.fill();
308 }
309
310 /**
311 * Render the actual wave and progress lines
312 *
313 * @param {number[]} peaks Array with peaks data
314 * @param {number} absmax Maximum peak value (absolute)
315 * @param {number} halfH Half the height of the waveform
316 * @param {number} offsetY Offset to the top
317 * @param {number} start The x-offset of the beginning of the area that
318 * should be rendered
319 * @param {number} end The x-offset of the end of the area that
320 * should be rendered
321 */
322 }, {
323 key: "drawLines",
324 value: function drawLines(peaks, absmax, halfH, offsetY, start, end) {
325 this.drawLineToContext(this.waveCtx, peaks, absmax, halfH, offsetY, start, end);
326 if (this.hasProgressCanvas) {
327 this.drawLineToContext(this.progressCtx, peaks, absmax, halfH, offsetY, start, end);
328 }
329 }
330
331 /**
332 * Render the actual waveform line on a `canvas` element
333 *
334 * @param {CanvasRenderingContext2D} ctx Rendering context of target canvas
335 * @param {number[]} peaks Array with peaks data
336 * @param {number} absmax Maximum peak value (absolute)
337 * @param {number} halfH Half the height of the waveform
338 * @param {number} offsetY Offset to the top
339 * @param {number} start The x-offset of the beginning of the area that
340 * should be rendered
341 * @param {number} end The x-offset of the end of the area that
342 * should be rendered
343 */
344 }, {
345 key: "drawLineToContext",
346 value: function drawLineToContext(ctx, peaks, absmax, halfH, offsetY, start, end) {
347 if (!ctx) {
348 return;
349 }
350 var length = peaks.length / 2;
351 var first = Math.round(length * this.start);
352
353 // use one more peak value to make sure we join peaks at ends -- unless,
354 // of course, this is the last canvas
355 var last = Math.round(length * this.end) + 1;
356 var canvasStart = first;
357 var canvasEnd = last;
358 var scale = this.wave.width / (canvasEnd - canvasStart - 1);
359
360 // optimization
361 var halfOffset = halfH + offsetY;
362 var absmaxHalf = absmax / halfH;
363 ctx.beginPath();
364 ctx.moveTo((canvasStart - first) * scale, halfOffset);
365 ctx.lineTo((canvasStart - first) * scale, halfOffset - Math.round((peaks[2 * canvasStart] || 0) / absmaxHalf));
366 var i, peak, h;
367 for (i = canvasStart; i < canvasEnd; i++) {
368 peak = peaks[2 * i] || 0;
369 h = Math.round(peak / absmaxHalf);
370 ctx.lineTo((i - first) * scale + this.halfPixel, halfOffset - h);
371 }
372
373 // draw the bottom edge going backwards, to make a single
374 // closed hull to fill
375 var j = canvasEnd - 1;
376 for (j; j >= canvasStart; j--) {
377 peak = peaks[2 * j + 1] || 0;
378 h = Math.round(peak / absmaxHalf);
379 ctx.lineTo((j - first) * scale + this.halfPixel, halfOffset - h);
380 }
381 ctx.lineTo((canvasStart - first) * scale, halfOffset - Math.round((peaks[2 * canvasStart + 1] || 0) / absmaxHalf));
382 ctx.closePath();
383 ctx.fill();
384 }
385
386 /**
387 * Destroys this entry
388 */
389 }, {
390 key: "destroy",
391 value: function destroy() {
392 this.waveCtx = null;
393 this.wave = null;
394 this.progressCtx = null;
395 this.progress = null;
396 }
397
398 /**
399 * Return image data of the wave `canvas` element
400 *
401 * When using a `type` of `'blob'`, this will return a `Promise` that
402 * resolves with a `Blob` instance.
403 *
404 * @param {string} format='image/png' An optional value of a format type.
405 * @param {number} quality=0.92 An optional value between 0 and 1.
406 * @param {string} type='dataURL' Either 'dataURL' or 'blob'.
407 * @return {string|Promise} When using the default `'dataURL'` `type` this
408 * returns a data URL. When using the `'blob'` `type` this returns a
409 * `Promise` that resolves with a `Blob` instance.
410 */
411 }, {
412 key: "getImage",
413 value: function getImage(format, quality, type) {
414 var _this = this;
415 if (type === 'blob') {
416 return new Promise(function (resolve) {
417 _this.wave.toBlob(resolve, format, quality);
418 });
419 } else if (type === 'dataURL') {
420 return this.wave.toDataURL(format, quality);
421 }
422 }
423 }]);
424 return CanvasEntry;
425}();
426exports["default"] = CanvasEntry;
427module.exports = exports.default;
428
429/***/ }),
430
431/***/ "./src/drawer.js":
432/*!***********************!*\
433 !*** ./src/drawer.js ***!
434 \***********************/
435/***/ ((module, exports, __webpack_require__) => {
436
437"use strict";
438
439
440function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
441Object.defineProperty(exports, "__esModule", ({
442 value: true
443}));
444exports["default"] = void 0;
445var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js"));
446function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
447function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
448function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
449function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
450function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
451function _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); }
452function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
453function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
454function _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); }
455function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
456function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
457function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
458/**
459 * Parent class for renderers
460 *
461 * @extends {Observer}
462 */
463var Drawer = /*#__PURE__*/function (_util$Observer) {
464 _inherits(Drawer, _util$Observer);
465 var _super = _createSuper(Drawer);
466 /**
467 * @param {HTMLElement} container The container node of the wavesurfer instance
468 * @param {WavesurferParams} params The wavesurfer initialisation options
469 */
470 function Drawer(container, params) {
471 var _this;
472 _classCallCheck(this, Drawer);
473 _this = _super.call(this);
474 _this.container = util.withOrientation(container, params.vertical);
475 /**
476 * @type {WavesurferParams}
477 */
478 _this.params = params;
479 /**
480 * The width of the renderer
481 * @type {number}
482 */
483 _this.width = 0;
484 /**
485 * The height of the renderer
486 * @type {number}
487 */
488 _this.height = params.height * _this.params.pixelRatio;
489 _this.lastPos = 0;
490 /**
491 * The `<wave>` element which is added to the container
492 * @type {HTMLElement}
493 */
494 _this.wrapper = null;
495 return _this;
496 }
497
498 /**
499 * Alias of `util.style`
500 *
501 * @param {HTMLElement} el The element that the styles will be applied to
502 * @param {Object} styles The map of propName: attribute, both are used as-is
503 * @return {HTMLElement} el
504 */
505 _createClass(Drawer, [{
506 key: "style",
507 value: function style(el, styles) {
508 return util.style(el, styles);
509 }
510
511 /**
512 * Create the wrapper `<wave>` element, style it and set up the events for
513 * interaction
514 */
515 }, {
516 key: "createWrapper",
517 value: function createWrapper() {
518 this.wrapper = util.withOrientation(this.container.appendChild(document.createElement('wave')), this.params.vertical);
519 this.style(this.wrapper, {
520 display: 'block',
521 position: 'relative',
522 userSelect: 'none',
523 webkitUserSelect: 'none',
524 height: this.params.height + 'px'
525 });
526 if (this.params.fillParent || this.params.scrollParent) {
527 this.style(this.wrapper, {
528 width: '100%',
529 cursor: this.params.hideCursor ? 'none' : 'auto',
530 overflowX: this.params.hideScrollbar ? 'hidden' : 'auto',
531 overflowY: 'hidden'
532 });
533 }
534 this.setupWrapperEvents();
535 }
536
537 /**
538 * Handle click event
539 *
540 * @param {Event} e Click event
541 * @param {?boolean} noPrevent Set to true to not call `e.preventDefault()`
542 * @return {number} Playback position from 0 to 1
543 */
544 }, {
545 key: "handleEvent",
546 value: function handleEvent(e, noPrevent) {
547 !noPrevent && e.preventDefault();
548 var clientX = util.withOrientation(e.targetTouches ? e.targetTouches[0] : e, this.params.vertical).clientX;
549 var bbox = this.wrapper.getBoundingClientRect();
550 var nominalWidth = this.width;
551 var parentWidth = this.getWidth();
552 var progressPixels = this.getProgressPixels(bbox, clientX);
553 var progress;
554 if (!this.params.fillParent && nominalWidth < parentWidth) {
555 progress = progressPixels * (this.params.pixelRatio / nominalWidth) || 0;
556 } else {
557 progress = (progressPixels + this.wrapper.scrollLeft) / this.wrapper.scrollWidth || 0;
558 }
559 return util.clamp(progress, 0, 1);
560 }
561 }, {
562 key: "getProgressPixels",
563 value: function getProgressPixels(wrapperBbox, clientX) {
564 if (this.params.rtl) {
565 return wrapperBbox.right - clientX;
566 } else {
567 return clientX - wrapperBbox.left;
568 }
569 }
570 }, {
571 key: "setupWrapperEvents",
572 value: function setupWrapperEvents() {
573 var _this2 = this;
574 this.wrapper.addEventListener('click', function (e) {
575 var orientedEvent = util.withOrientation(e, _this2.params.vertical);
576 var scrollbarHeight = _this2.wrapper.offsetHeight - _this2.wrapper.clientHeight;
577 if (scrollbarHeight !== 0) {
578 // scrollbar is visible. Check if click was on it
579 var bbox = _this2.wrapper.getBoundingClientRect();
580 if (orientedEvent.clientY >= bbox.bottom - scrollbarHeight) {
581 // ignore mousedown as it was on the scrollbar
582 return;
583 }
584 }
585 if (_this2.params.interact) {
586 _this2.fireEvent('click', e, _this2.handleEvent(e));
587 }
588 });
589 this.wrapper.addEventListener('dblclick', function (e) {
590 if (_this2.params.interact) {
591 _this2.fireEvent('dblclick', e, _this2.handleEvent(e));
592 }
593 });
594 this.wrapper.addEventListener('scroll', function (e) {
595 return _this2.fireEvent('scroll', e);
596 });
597 }
598
599 /**
600 * Draw peaks on the canvas
601 *
602 * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays
603 * for split channel rendering
604 * @param {number} length The width of the area that should be drawn
605 * @param {number} start The x-offset of the beginning of the area that
606 * should be rendered
607 * @param {number} end The x-offset of the end of the area that should be
608 * rendered
609 */
610 }, {
611 key: "drawPeaks",
612 value: function drawPeaks(peaks, length, start, end) {
613 if (!this.setWidth(length)) {
614 this.clearWave();
615 }
616 this.params.barWidth ? this.drawBars(peaks, 0, start, end) : this.drawWave(peaks, 0, start, end);
617 }
618
619 /**
620 * Scroll to the beginning
621 */
622 }, {
623 key: "resetScroll",
624 value: function resetScroll() {
625 if (this.wrapper !== null) {
626 this.wrapper.scrollLeft = 0;
627 }
628 }
629
630 /**
631 * Recenter the view-port at a certain percent of the waveform
632 *
633 * @param {number} percent Value from 0 to 1 on the waveform
634 */
635 }, {
636 key: "recenter",
637 value: function recenter(percent) {
638 var position = this.wrapper.scrollWidth * percent;
639 this.recenterOnPosition(position, true);
640 }
641
642 /**
643 * Recenter the view-port on a position, either scroll there immediately or
644 * in steps of 5 pixels
645 *
646 * @param {number} position X-offset in pixels
647 * @param {boolean} immediate Set to true to immediately scroll somewhere
648 */
649 }, {
650 key: "recenterOnPosition",
651 value: function recenterOnPosition(position, immediate) {
652 var scrollLeft = this.wrapper.scrollLeft;
653 var half = ~~(this.wrapper.clientWidth / 2);
654 var maxScroll = this.wrapper.scrollWidth - this.wrapper.clientWidth;
655 var target = position - half;
656 var offset = target - scrollLeft;
657 if (maxScroll == 0) {
658 // no need to continue if scrollbar is not there
659 return;
660 }
661
662 // if the cursor is currently visible...
663 if (!immediate && -half <= offset && offset < half) {
664 // set rate at which waveform is centered
665 var rate = this.params.autoCenterRate;
666
667 // make rate depend on width of view and length of waveform
668 rate /= half;
669 rate *= maxScroll;
670 offset = Math.max(-rate, Math.min(rate, offset));
671 target = scrollLeft + offset;
672 }
673
674 // limit target to valid range (0 to maxScroll)
675 target = Math.max(0, Math.min(maxScroll, target));
676 // no use attempting to scroll if we're not moving
677 if (target != scrollLeft) {
678 this.wrapper.scrollLeft = target;
679 }
680 }
681
682 /**
683 * Get the current scroll position in pixels
684 *
685 * @return {number} Horizontal scroll position in pixels
686 */
687 }, {
688 key: "getScrollX",
689 value: function getScrollX() {
690 var x = 0;
691 if (this.wrapper) {
692 var pixelRatio = this.params.pixelRatio;
693 x = Math.round(this.wrapper.scrollLeft * pixelRatio);
694
695 // In cases of elastic scroll (safari with mouse wheel) you can
696 // scroll beyond the limits of the container
697 // Calculate and floor the scrollable extent to make sure an out
698 // of bounds value is not returned
699 // Ticket #1312
700 if (this.params.scrollParent) {
701 var maxScroll = ~~(this.wrapper.scrollWidth * pixelRatio - this.getWidth());
702 x = Math.min(maxScroll, Math.max(0, x));
703 }
704 }
705 return x;
706 }
707
708 /**
709 * Get the width of the container
710 *
711 * @return {number} The width of the container
712 */
713 }, {
714 key: "getWidth",
715 value: function getWidth() {
716 return Math.round(this.container.clientWidth * this.params.pixelRatio);
717 }
718
719 /**
720 * Set the width of the container
721 *
722 * @param {number} width The new width of the container
723 * @return {boolean} Whether the width of the container was updated or not
724 */
725 }, {
726 key: "setWidth",
727 value: function setWidth(width) {
728 if (this.width == width) {
729 return false;
730 }
731 this.width = width;
732 if (this.params.fillParent || this.params.scrollParent) {
733 this.style(this.wrapper, {
734 width: ''
735 });
736 } else {
737 var newWidth = ~~(this.width / this.params.pixelRatio) + 'px';
738 this.style(this.wrapper, {
739 width: newWidth
740 });
741 }
742 this.updateSize();
743 return true;
744 }
745
746 /**
747 * Set the height of the container
748 *
749 * @param {number} height The new height of the container.
750 * @return {boolean} Whether the height of the container was updated or not
751 */
752 }, {
753 key: "setHeight",
754 value: function setHeight(height) {
755 if (height == this.height) {
756 return false;
757 }
758 this.height = height;
759 this.style(this.wrapper, {
760 height: ~~(this.height / this.params.pixelRatio) + 'px'
761 });
762 this.updateSize();
763 return true;
764 }
765
766 /**
767 * Called by wavesurfer when progress should be rendered
768 *
769 * @param {number} progress From 0 to 1
770 */
771 }, {
772 key: "progress",
773 value: function progress(_progress) {
774 var minPxDelta = 1 / this.params.pixelRatio;
775 var pos = Math.round(_progress * this.width) * minPxDelta;
776 if (pos < this.lastPos || pos - this.lastPos >= minPxDelta) {
777 this.lastPos = pos;
778 if (this.params.scrollParent && this.params.autoCenter) {
779 var newPos = ~~(this.wrapper.scrollWidth * _progress);
780 this.recenterOnPosition(newPos, this.params.autoCenterImmediately);
781 }
782 this.updateProgress(pos);
783 }
784 }
785
786 /**
787 * This is called when wavesurfer is destroyed
788 */
789 }, {
790 key: "destroy",
791 value: function destroy() {
792 this.unAll();
793 if (this.wrapper) {
794 if (this.wrapper.parentNode == this.container.domElement) {
795 this.container.removeChild(this.wrapper.domElement);
796 }
797 this.wrapper = null;
798 }
799 }
800
801 /* Renderer-specific methods */
802
803 /**
804 * Called after cursor related params have changed.
805 *
806 * @abstract
807 */
808 }, {
809 key: "updateCursor",
810 value: function updateCursor() {}
811
812 /**
813 * Called when the size of the container changes so the renderer can adjust
814 *
815 * @abstract
816 */
817 }, {
818 key: "updateSize",
819 value: function updateSize() {}
820
821 /**
822 * Draw a waveform with bars
823 *
824 * @abstract
825 * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for split channel
826 * rendering
827 * @param {number} channelIndex The index of the current channel. Normally
828 * should be 0
829 * @param {number} start The x-offset of the beginning of the area that
830 * should be rendered
831 * @param {number} end The x-offset of the end of the area that should be
832 * rendered
833 */
834 }, {
835 key: "drawBars",
836 value: function drawBars(peaks, channelIndex, start, end) {}
837
838 /**
839 * Draw a waveform
840 *
841 * @abstract
842 * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for split channel
843 * rendering
844 * @param {number} channelIndex The index of the current channel. Normally
845 * should be 0
846 * @param {number} start The x-offset of the beginning of the area that
847 * should be rendered
848 * @param {number} end The x-offset of the end of the area that should be
849 * rendered
850 */
851 }, {
852 key: "drawWave",
853 value: function drawWave(peaks, channelIndex, start, end) {}
854
855 /**
856 * Clear the waveform
857 *
858 * @abstract
859 */
860 }, {
861 key: "clearWave",
862 value: function clearWave() {}
863
864 /**
865 * Render the new progress
866 *
867 * @abstract
868 * @param {number} position X-Offset of progress position in pixels
869 */
870 }, {
871 key: "updateProgress",
872 value: function updateProgress(position) {}
873 }]);
874 return Drawer;
875}(util.Observer);
876exports["default"] = Drawer;
877module.exports = exports.default;
878
879/***/ }),
880
881/***/ "./src/drawer.multicanvas.js":
882/*!***********************************!*\
883 !*** ./src/drawer.multicanvas.js ***!
884 \***********************************/
885/***/ ((module, exports, __webpack_require__) => {
886
887"use strict";
888
889
890function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
891Object.defineProperty(exports, "__esModule", ({
892 value: true
893}));
894exports["default"] = void 0;
895var _drawer = _interopRequireDefault(__webpack_require__(/*! ./drawer */ "./src/drawer.js"));
896var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js"));
897var _drawer2 = _interopRequireDefault(__webpack_require__(/*! ./drawer.canvasentry */ "./src/drawer.canvasentry.js"));
898function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
899function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
900function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
901function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
902function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
903function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
904function _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); }
905function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
906function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
907function _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); }
908function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
909function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
910function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
911/**
912 * MultiCanvas renderer for wavesurfer. Is currently the default and sole
913 * builtin renderer.
914 *
915 * A `MultiCanvas` consists of one or more `CanvasEntry` instances, depending
916 * on the zoom level.
917 */
918var MultiCanvas = /*#__PURE__*/function (_Drawer) {
919 _inherits(MultiCanvas, _Drawer);
920 var _super = _createSuper(MultiCanvas);
921 /**
922 * @param {HTMLElement} container The container node of the wavesurfer instance
923 * @param {WavesurferParams} params The wavesurfer initialisation options
924 */
925 function MultiCanvas(container, params) {
926 var _this;
927 _classCallCheck(this, MultiCanvas);
928 _this = _super.call(this, container, params);
929
930 /**
931 * @type {number}
932 */
933 _this.maxCanvasWidth = params.maxCanvasWidth;
934
935 /**
936 * @type {number}
937 */
938 _this.maxCanvasElementWidth = Math.round(params.maxCanvasWidth / params.pixelRatio);
939
940 /**
941 * Whether or not the progress wave is rendered. If the `waveColor`
942 * and `progressColor` are the same color it is not.
943 *
944 * @type {boolean}
945 */
946 _this.hasProgressCanvas = params.waveColor != params.progressColor;
947
948 /**
949 * @type {number}
950 */
951 _this.halfPixel = 0.5 / params.pixelRatio;
952
953 /**
954 * List of `CanvasEntry` instances.
955 *
956 * @type {Array}
957 */
958 _this.canvases = [];
959
960 /**
961 * @type {HTMLElement}
962 */
963 _this.progressWave = null;
964
965 /**
966 * Class used to generate entries.
967 *
968 * @type {function}
969 */
970 _this.EntryClass = _drawer2.default;
971
972 /**
973 * Canvas 2d context attributes.
974 *
975 * @type {object}
976 */
977 _this.canvasContextAttributes = params.drawingContextAttributes;
978
979 /**
980 * Overlap added between entries to prevent vertical white stripes
981 * between `canvas` elements.
982 *
983 * @type {number}
984 */
985 _this.overlap = 2 * Math.ceil(params.pixelRatio / 2);
986
987 /**
988 * The radius of the wave bars. Makes bars rounded
989 *
990 * @type {number}
991 */
992 _this.barRadius = params.barRadius || 0;
993
994 /**
995 * Whether to render the waveform vertically. Defaults to false.
996 *
997 * @type {boolean}
998 */
999 _this.vertical = params.vertical;
1000 return _this;
1001 }
1002
1003 /**
1004 * Initialize the drawer
1005 */
1006 _createClass(MultiCanvas, [{
1007 key: "init",
1008 value: function init() {
1009 this.createWrapper();
1010 this.createElements();
1011 }
1012
1013 /**
1014 * Create the canvas elements and style them
1015 *
1016 */
1017 }, {
1018 key: "createElements",
1019 value: function createElements() {
1020 this.progressWave = util.withOrientation(this.wrapper.appendChild(document.createElement('wave')), this.params.vertical);
1021 this.style(this.progressWave, {
1022 position: 'absolute',
1023 zIndex: 3,
1024 left: 0,
1025 top: 0,
1026 bottom: 0,
1027 overflow: 'hidden',
1028 width: '0',
1029 display: 'none',
1030 boxSizing: 'border-box',
1031 borderRightStyle: 'solid',
1032 pointerEvents: 'none'
1033 });
1034 this.addCanvas();
1035 this.updateCursor();
1036 }
1037
1038 /**
1039 * Update cursor style
1040 */
1041 }, {
1042 key: "updateCursor",
1043 value: function updateCursor() {
1044 this.style(this.progressWave, {
1045 borderRightWidth: this.params.cursorWidth + 'px',
1046 borderRightColor: this.params.cursorColor
1047 });
1048 }
1049
1050 /**
1051 * Adjust to the updated size by adding or removing canvases
1052 */
1053 }, {
1054 key: "updateSize",
1055 value: function updateSize() {
1056 var _this2 = this;
1057 var totalWidth = Math.round(this.width / this.params.pixelRatio);
1058 var requiredCanvases = Math.ceil(totalWidth / (this.maxCanvasElementWidth + this.overlap));
1059
1060 // add required canvases
1061 while (this.canvases.length < requiredCanvases) {
1062 this.addCanvas();
1063 }
1064
1065 // remove older existing canvases, if any
1066 while (this.canvases.length > requiredCanvases) {
1067 this.removeCanvas();
1068 }
1069 var canvasWidth = this.maxCanvasWidth + this.overlap;
1070 var lastCanvas = this.canvases.length - 1;
1071 this.canvases.forEach(function (entry, i) {
1072 if (i == lastCanvas) {
1073 canvasWidth = _this2.width - _this2.maxCanvasWidth * lastCanvas;
1074 }
1075 _this2.updateDimensions(entry, canvasWidth, _this2.height);
1076 entry.clearWave();
1077 });
1078 }
1079
1080 /**
1081 * Add a canvas to the canvas list
1082 *
1083 */
1084 }, {
1085 key: "addCanvas",
1086 value: function addCanvas() {
1087 var entry = new this.EntryClass();
1088 entry.canvasContextAttributes = this.canvasContextAttributes;
1089 entry.hasProgressCanvas = this.hasProgressCanvas;
1090 entry.halfPixel = this.halfPixel;
1091 var leftOffset = this.maxCanvasElementWidth * this.canvases.length;
1092
1093 // wave
1094 var wave = util.withOrientation(this.wrapper.appendChild(document.createElement('canvas')), this.params.vertical);
1095 this.style(wave, {
1096 position: 'absolute',
1097 zIndex: 2,
1098 left: leftOffset + 'px',
1099 top: 0,
1100 bottom: 0,
1101 height: '100%',
1102 pointerEvents: 'none'
1103 });
1104 entry.initWave(wave);
1105
1106 // progress
1107 if (this.hasProgressCanvas) {
1108 var progress = util.withOrientation(this.progressWave.appendChild(document.createElement('canvas')), this.params.vertical);
1109 this.style(progress, {
1110 position: 'absolute',
1111 left: leftOffset + 'px',
1112 top: 0,
1113 bottom: 0,
1114 height: '100%'
1115 });
1116 entry.initProgress(progress);
1117 }
1118 this.canvases.push(entry);
1119 }
1120
1121 /**
1122 * Pop single canvas from the list
1123 *
1124 */
1125 }, {
1126 key: "removeCanvas",
1127 value: function removeCanvas() {
1128 var lastEntry = this.canvases[this.canvases.length - 1];
1129
1130 // wave
1131 lastEntry.wave.parentElement.removeChild(lastEntry.wave.domElement);
1132
1133 // progress
1134 if (this.hasProgressCanvas) {
1135 lastEntry.progress.parentElement.removeChild(lastEntry.progress.domElement);
1136 }
1137
1138 // cleanup
1139 if (lastEntry) {
1140 lastEntry.destroy();
1141 lastEntry = null;
1142 }
1143 this.canvases.pop();
1144 }
1145
1146 /**
1147 * Update the dimensions of a canvas element
1148 *
1149 * @param {CanvasEntry} entry Target entry
1150 * @param {number} width The new width of the element
1151 * @param {number} height The new height of the element
1152 */
1153 }, {
1154 key: "updateDimensions",
1155 value: function updateDimensions(entry, width, height) {
1156 var elementWidth = Math.round(width / this.params.pixelRatio);
1157 var totalWidth = Math.round(this.width / this.params.pixelRatio);
1158
1159 // update canvas dimensions
1160 entry.updateDimensions(elementWidth, totalWidth, width, height);
1161
1162 // style element
1163 this.style(this.progressWave, {
1164 display: 'block'
1165 });
1166 }
1167
1168 /**
1169 * Clear the whole multi-canvas
1170 */
1171 }, {
1172 key: "clearWave",
1173 value: function clearWave() {
1174 var _this3 = this;
1175 util.frame(function () {
1176 _this3.canvases.forEach(function (entry) {
1177 return entry.clearWave();
1178 });
1179 })();
1180 }
1181
1182 /**
1183 * Draw a waveform with bars
1184 *
1185 * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays
1186 * for split channel rendering
1187 * @param {number} channelIndex The index of the current channel. Normally
1188 * should be 0. Must be an integer.
1189 * @param {number} start The x-offset of the beginning of the area that
1190 * should be rendered
1191 * @param {number} end The x-offset of the end of the area that should be
1192 * rendered
1193 * @returns {void}
1194 */
1195 }, {
1196 key: "drawBars",
1197 value: function drawBars(peaks, channelIndex, start, end) {
1198 var _this4 = this;
1199 return this.prepareDraw(peaks, channelIndex, start, end, function (_ref) {
1200 var absmax = _ref.absmax,
1201 hasMinVals = _ref.hasMinVals,
1202 height = _ref.height,
1203 offsetY = _ref.offsetY,
1204 halfH = _ref.halfH,
1205 peaks = _ref.peaks,
1206 ch = _ref.channelIndex;
1207 // if drawBars was called within ws.empty we don't pass a start and
1208 // don't want anything to happen
1209 if (start === undefined) {
1210 return;
1211 }
1212 // Skip every other value if there are negatives.
1213 var peakIndexScale = hasMinVals ? 2 : 1;
1214 var length = peaks.length / peakIndexScale;
1215 var bar = _this4.params.barWidth * _this4.params.pixelRatio;
1216 var gap = _this4.params.barGap === null ? Math.max(_this4.params.pixelRatio, ~~(bar / 2)) : Math.max(_this4.params.pixelRatio, _this4.params.barGap * _this4.params.pixelRatio);
1217 var step = bar + gap;
1218 var scale = length / _this4.width;
1219 var first = start;
1220 var last = end;
1221 var peakIndex = first;
1222 for (peakIndex; peakIndex < last; peakIndex += step) {
1223 // search for the highest peak in the range this bar falls into
1224 var peak = 0;
1225 var peakIndexRange = Math.floor(peakIndex * scale) * peakIndexScale; // start index
1226 var peakIndexEnd = Math.floor((peakIndex + step) * scale) * peakIndexScale;
1227 do {
1228 // do..while makes sure at least one peak is always evaluated
1229 var newPeak = Math.abs(peaks[peakIndexRange]); // for arrays starting with negative values
1230 if (newPeak > peak) {
1231 peak = newPeak; // higher
1232 }
1233
1234 peakIndexRange += peakIndexScale; // skip every other value for negatives
1235 } while (peakIndexRange < peakIndexEnd);
1236
1237 // calculate the height of this bar according to the highest peak found
1238 var h = Math.round(peak / absmax * halfH);
1239
1240 // raise the bar height to the specified minimum height
1241 // Math.max is used to replace any value smaller than barMinHeight (not just 0) with barMinHeight
1242 if (_this4.params.barMinHeight) {
1243 h = Math.max(h, _this4.params.barMinHeight);
1244 }
1245 _this4.fillRect(peakIndex + _this4.halfPixel, halfH - h + offsetY, bar + _this4.halfPixel, h * 2, _this4.barRadius, ch);
1246 }
1247 });
1248 }
1249
1250 /**
1251 * Draw a waveform
1252 *
1253 * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays
1254 * for split channel rendering
1255 * @param {number} channelIndex The index of the current channel. Normally
1256 * should be 0
1257 * @param {number?} start The x-offset of the beginning of the area that
1258 * should be rendered (If this isn't set only a flat line is rendered)
1259 * @param {number?} end The x-offset of the end of the area that should be
1260 * rendered
1261 * @returns {void}
1262 */
1263 }, {
1264 key: "drawWave",
1265 value: function drawWave(peaks, channelIndex, start, end) {
1266 var _this5 = this;
1267 return this.prepareDraw(peaks, channelIndex, start, end, function (_ref2) {
1268 var absmax = _ref2.absmax,
1269 hasMinVals = _ref2.hasMinVals,
1270 height = _ref2.height,
1271 offsetY = _ref2.offsetY,
1272 halfH = _ref2.halfH,
1273 peaks = _ref2.peaks,
1274 channelIndex = _ref2.channelIndex;
1275 if (!hasMinVals) {
1276 var reflectedPeaks = [];
1277 var len = peaks.length;
1278 var i = 0;
1279 for (i; i < len; i++) {
1280 reflectedPeaks[2 * i] = peaks[i];
1281 reflectedPeaks[2 * i + 1] = -peaks[i];
1282 }
1283 peaks = reflectedPeaks;
1284 }
1285
1286 // if drawWave was called within ws.empty we don't pass a start and
1287 // end and simply want a flat line
1288 if (start !== undefined) {
1289 _this5.drawLine(peaks, absmax, halfH, offsetY, start, end, channelIndex);
1290 }
1291
1292 // always draw a median line
1293 _this5.fillRect(0, halfH + offsetY - _this5.halfPixel, _this5.width, _this5.halfPixel, _this5.barRadius, channelIndex);
1294 });
1295 }
1296
1297 /**
1298 * Tell the canvas entries to render their portion of the waveform
1299 *
1300 * @param {number[]} peaks Peaks data
1301 * @param {number} absmax Maximum peak value (absolute)
1302 * @param {number} halfH Half the height of the waveform
1303 * @param {number} offsetY Offset to the top
1304 * @param {number} start The x-offset of the beginning of the area that
1305 * should be rendered
1306 * @param {number} end The x-offset of the end of the area that
1307 * should be rendered
1308 * @param {channelIndex} channelIndex The channel index of the line drawn
1309 */
1310 }, {
1311 key: "drawLine",
1312 value: function drawLine(peaks, absmax, halfH, offsetY, start, end, channelIndex) {
1313 var _this6 = this;
1314 var _ref3 = this.params.splitChannelsOptions.channelColors[channelIndex] || {},
1315 waveColor = _ref3.waveColor,
1316 progressColor = _ref3.progressColor;
1317 this.canvases.forEach(function (entry, i) {
1318 _this6.setFillStyles(entry, waveColor, progressColor);
1319 _this6.applyCanvasTransforms(entry, _this6.params.vertical);
1320 entry.drawLines(peaks, absmax, halfH, offsetY, start, end);
1321 });
1322 }
1323
1324 /**
1325 * Draw a rectangle on the multi-canvas
1326 *
1327 * @param {number} x X-position of the rectangle
1328 * @param {number} y Y-position of the rectangle
1329 * @param {number} width Width of the rectangle
1330 * @param {number} height Height of the rectangle
1331 * @param {number} radius Radius of the rectangle
1332 * @param {channelIndex} channelIndex The channel index of the bar drawn
1333 */
1334 }, {
1335 key: "fillRect",
1336 value: function fillRect(x, y, width, height, radius, channelIndex) {
1337 var startCanvas = Math.floor(x / this.maxCanvasWidth);
1338 var endCanvas = Math.min(Math.ceil((x + width) / this.maxCanvasWidth) + 1, this.canvases.length);
1339 var i = startCanvas;
1340 for (i; i < endCanvas; i++) {
1341 var entry = this.canvases[i];
1342 var leftOffset = i * this.maxCanvasWidth;
1343 var intersection = {
1344 x1: Math.max(x, i * this.maxCanvasWidth),
1345 y1: y,
1346 x2: Math.min(x + width, i * this.maxCanvasWidth + entry.wave.width),
1347 y2: y + height
1348 };
1349 if (intersection.x1 < intersection.x2) {
1350 var _ref4 = this.params.splitChannelsOptions.channelColors[channelIndex] || {},
1351 waveColor = _ref4.waveColor,
1352 progressColor = _ref4.progressColor;
1353 this.setFillStyles(entry, waveColor, progressColor);
1354 this.applyCanvasTransforms(entry, this.params.vertical);
1355 entry.fillRects(intersection.x1 - leftOffset, intersection.y1, intersection.x2 - intersection.x1, intersection.y2 - intersection.y1, radius);
1356 }
1357 }
1358 }
1359
1360 /**
1361 * Returns whether to hide the channel from being drawn based on params.
1362 *
1363 * @param {number} channelIndex The index of the current channel.
1364 * @returns {bool} True to hide the channel, false to draw.
1365 */
1366 }, {
1367 key: "hideChannel",
1368 value: function hideChannel(channelIndex) {
1369 return this.params.splitChannels && this.params.splitChannelsOptions.filterChannels.includes(channelIndex);
1370 }
1371
1372 /**
1373 * Performs preparation tasks and calculations which are shared by `drawBars`
1374 * and `drawWave`
1375 *
1376 * @param {number[]|Number.<Array[]>} peaks Can also be an array of arrays for
1377 * split channel rendering
1378 * @param {number} channelIndex The index of the current channel. Normally
1379 * should be 0
1380 * @param {number?} start The x-offset of the beginning of the area that
1381 * should be rendered. If this isn't set only a flat line is rendered
1382 * @param {number?} end The x-offset of the end of the area that should be
1383 * rendered
1384 * @param {function} fn The render function to call, e.g. `drawWave`
1385 * @param {number} drawIndex The index of the current channel after filtering.
1386 * @param {number?} normalizedMax Maximum modulation value across channels for use with relativeNormalization. Ignored when undefined
1387 * @returns {void}
1388 */
1389 }, {
1390 key: "prepareDraw",
1391 value: function prepareDraw(peaks, channelIndex, start, end, fn, drawIndex, normalizedMax) {
1392 var _this7 = this;
1393 return util.frame(function () {
1394 // Split channels and call this function with the channelIndex set
1395 if (peaks[0] instanceof Array) {
1396 var channels = peaks;
1397 if (_this7.params.splitChannels) {
1398 var filteredChannels = channels.filter(function (c, i) {
1399 return !_this7.hideChannel(i);
1400 });
1401 if (!_this7.params.splitChannelsOptions.overlay) {
1402 _this7.setHeight(Math.max(filteredChannels.length, 1) * _this7.params.height * _this7.params.pixelRatio);
1403 }
1404 var overallAbsMax;
1405 if (_this7.params.splitChannelsOptions && _this7.params.splitChannelsOptions.relativeNormalization) {
1406 // calculate maximum peak across channels to use for normalization
1407 overallAbsMax = util.max(channels.map(function (channelPeaks) {
1408 return util.absMax(channelPeaks);
1409 }));
1410 }
1411 return channels.forEach(function (channelPeaks, i) {
1412 return _this7.prepareDraw(channelPeaks, i, start, end, fn, filteredChannels.indexOf(channelPeaks), overallAbsMax);
1413 });
1414 }
1415 peaks = channels[0];
1416 }
1417
1418 // Return and do not draw channel peaks if hidden.
1419 if (_this7.hideChannel(channelIndex)) {
1420 return;
1421 }
1422
1423 // calculate maximum modulation value, either from the barHeight
1424 // parameter or if normalize=true from the largest value in the peak
1425 // set
1426 var absmax = 1 / _this7.params.barHeight;
1427 if (_this7.params.normalize) {
1428 absmax = normalizedMax === undefined ? util.absMax(peaks) : normalizedMax;
1429 }
1430
1431 // Bar wave draws the bottom only as a reflection of the top,
1432 // so we don't need negative values
1433 var hasMinVals = [].some.call(peaks, function (val) {
1434 return val < 0;
1435 });
1436 var height = _this7.params.height * _this7.params.pixelRatio;
1437 var halfH = height / 2;
1438 var offsetY = height * drawIndex || 0;
1439
1440 // Override offsetY if overlay is true
1441 if (_this7.params.splitChannelsOptions && _this7.params.splitChannelsOptions.overlay) {
1442 offsetY = 0;
1443 }
1444 return fn({
1445 absmax: absmax,
1446 hasMinVals: hasMinVals,
1447 height: height,
1448 offsetY: offsetY,
1449 halfH: halfH,
1450 peaks: peaks,
1451 channelIndex: channelIndex
1452 });
1453 })();
1454 }
1455
1456 /**
1457 * Set the fill styles for a certain entry (wave and progress)
1458 *
1459 * @param {CanvasEntry} entry Target entry
1460 * @param {string} waveColor Wave color to draw this entry
1461 * @param {string} progressColor Progress color to draw this entry
1462 */
1463 }, {
1464 key: "setFillStyles",
1465 value: function setFillStyles(entry) {
1466 var waveColor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.params.waveColor;
1467 var progressColor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.params.progressColor;
1468 entry.setFillStyles(waveColor, progressColor);
1469 }
1470
1471 /**
1472 * Set the canvas transforms for a certain entry (wave and progress)
1473 *
1474 * @param {CanvasEntry} entry Target entry
1475 * @param {boolean} vertical Whether to render the waveform vertically
1476 */
1477 }, {
1478 key: "applyCanvasTransforms",
1479 value: function applyCanvasTransforms(entry) {
1480 var vertical = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
1481 entry.applyCanvasTransforms(vertical);
1482 }
1483
1484 /**
1485 * Return image data of the multi-canvas
1486 *
1487 * When using a `type` of `'blob'`, this will return a `Promise`.
1488 *
1489 * @param {string} format='image/png' An optional value of a format type.
1490 * @param {number} quality=0.92 An optional value between 0 and 1.
1491 * @param {string} type='dataURL' Either 'dataURL' or 'blob'.
1492 * @return {string|string[]|Promise} When using the default `'dataURL'`
1493 * `type` this returns a single data URL or an array of data URLs,
1494 * one for each canvas. When using the `'blob'` `type` this returns a
1495 * `Promise` that resolves with an array of `Blob` instances, one for each
1496 * canvas.
1497 */
1498 }, {
1499 key: "getImage",
1500 value: function getImage(format, quality, type) {
1501 if (type === 'blob') {
1502 return Promise.all(this.canvases.map(function (entry) {
1503 return entry.getImage(format, quality, type);
1504 }));
1505 } else if (type === 'dataURL') {
1506 var images = this.canvases.map(function (entry) {
1507 return entry.getImage(format, quality, type);
1508 });
1509 return images.length > 1 ? images : images[0];
1510 }
1511 }
1512
1513 /**
1514 * Render the new progress
1515 *
1516 * @param {number} position X-offset of progress position in pixels
1517 */
1518 }, {
1519 key: "updateProgress",
1520 value: function updateProgress(position) {
1521 this.style(this.progressWave, {
1522 width: position + 'px'
1523 });
1524 }
1525 }]);
1526 return MultiCanvas;
1527}(_drawer.default);
1528exports["default"] = MultiCanvas;
1529module.exports = exports.default;
1530
1531/***/ }),
1532
1533/***/ "./src/mediaelement-webaudio.js":
1534/*!**************************************!*\
1535 !*** ./src/mediaelement-webaudio.js ***!
1536 \**************************************/
1537/***/ ((module, exports, __webpack_require__) => {
1538
1539"use strict";
1540
1541
1542function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
1543Object.defineProperty(exports, "__esModule", ({
1544 value: true
1545}));
1546exports["default"] = void 0;
1547var _mediaelement = _interopRequireDefault(__webpack_require__(/*! ./mediaelement */ "./src/mediaelement.js"));
1548function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1549function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
1550function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
1551function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
1552function _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); }
1553function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
1554function _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); }
1555function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
1556function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
1557function _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); }
1558function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
1559function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
1560function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
1561/**
1562 * MediaElementWebAudio backend: load audio via an HTML5 audio tag, but playback with the WebAudio API.
1563 * The advantage here is that the html5 <audio> tag can perform range requests on the server and not
1564 * buffer the entire file in one request, and you still get the filtering and scripting functionality
1565 * of the webaudio API.
1566 * Note that in order to use range requests and prevent buffering, you must provide peak data.
1567 *
1568 * @since 3.2.0
1569 */
1570var MediaElementWebAudio = /*#__PURE__*/function (_MediaElement) {
1571 _inherits(MediaElementWebAudio, _MediaElement);
1572 var _super = _createSuper(MediaElementWebAudio);
1573 /**
1574 * Construct the backend
1575 *
1576 * @param {WavesurferParams} params Wavesurfer parameters
1577 */
1578 function MediaElementWebAudio(params) {
1579 var _this;
1580 _classCallCheck(this, MediaElementWebAudio);
1581 _this = _super.call(this, params);
1582 /** @private */
1583 _this.params = params;
1584 /** @private */
1585 _this.sourceMediaElement = null;
1586 return _this;
1587 }
1588
1589 /**
1590 * Initialise the backend, called in `wavesurfer.createBackend()`
1591 */
1592 _createClass(MediaElementWebAudio, [{
1593 key: "init",
1594 value: function init() {
1595 this.setPlaybackRate(this.params.audioRate);
1596 this.createTimer();
1597 this.createVolumeNode();
1598 this.createScriptNode();
1599 this.createAnalyserNode();
1600 }
1601 /**
1602 * Private method called by both `load` (from url)
1603 * and `loadElt` (existing media element) methods.
1604 *
1605 * @param {HTMLMediaElement} media HTML5 Audio or Video element
1606 * @param {number[]|Number.<Array[]>} peaks Array of peak data
1607 * @param {string} preload HTML 5 preload attribute value
1608 * @private
1609 */
1610 }, {
1611 key: "_load",
1612 value: function _load(media, peaks, preload) {
1613 _get(_getPrototypeOf(MediaElementWebAudio.prototype), "_load", this).call(this, media, peaks, preload);
1614 this.createMediaElementSource(media);
1615 }
1616
1617 /**
1618 * Create MediaElementSource node
1619 *
1620 * @since 3.2.0
1621 * @param {HTMLMediaElement} mediaElement HTML5 Audio to load
1622 */
1623 }, {
1624 key: "createMediaElementSource",
1625 value: function createMediaElementSource(mediaElement) {
1626 this.sourceMediaElement = this.ac.createMediaElementSource(mediaElement);
1627 this.sourceMediaElement.connect(this.analyser);
1628 }
1629 }, {
1630 key: "play",
1631 value: function play(start, end) {
1632 this.resumeAudioContext();
1633 return _get(_getPrototypeOf(MediaElementWebAudio.prototype), "play", this).call(this, start, end);
1634 }
1635
1636 /**
1637 * This is called when wavesurfer is destroyed
1638 *
1639 */
1640 }, {
1641 key: "destroy",
1642 value: function destroy() {
1643 _get(_getPrototypeOf(MediaElementWebAudio.prototype), "destroy", this).call(this);
1644 this.destroyWebAudio();
1645 }
1646 }]);
1647 return MediaElementWebAudio;
1648}(_mediaelement.default);
1649exports["default"] = MediaElementWebAudio;
1650module.exports = exports.default;
1651
1652/***/ }),
1653
1654/***/ "./src/mediaelement.js":
1655/*!*****************************!*\
1656 !*** ./src/mediaelement.js ***!
1657 \*****************************/
1658/***/ ((module, exports, __webpack_require__) => {
1659
1660"use strict";
1661
1662
1663function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
1664Object.defineProperty(exports, "__esModule", ({
1665 value: true
1666}));
1667exports["default"] = void 0;
1668var _webaudio = _interopRequireDefault(__webpack_require__(/*! ./webaudio */ "./src/webaudio.js"));
1669var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js"));
1670function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
1671function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
1672function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1673function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
1674function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
1675function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
1676function _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); }
1677function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
1678function _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); }
1679function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
1680function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
1681function _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); }
1682function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
1683function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
1684function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
1685/**
1686 * MediaElement backend
1687 */
1688var MediaElement = /*#__PURE__*/function (_WebAudio) {
1689 _inherits(MediaElement, _WebAudio);
1690 var _super = _createSuper(MediaElement);
1691 /**
1692 * Construct the backend
1693 *
1694 * @param {WavesurferParams} params Wavesurfer parameters
1695 */
1696 function MediaElement(params) {
1697 var _this;
1698 _classCallCheck(this, MediaElement);
1699 _this = _super.call(this, params);
1700 /** @private */
1701 _this.params = params;
1702
1703 /**
1704 * Initially a dummy media element to catch errors. Once `_load` is
1705 * called, this will contain the actual `HTMLMediaElement`.
1706 * @private
1707 */
1708 _this.media = {
1709 currentTime: 0,
1710 duration: 0,
1711 paused: true,
1712 playbackRate: 1,
1713 play: function play() {},
1714 pause: function pause() {},
1715 volume: 0
1716 };
1717
1718 /** @private */
1719 _this.mediaType = params.mediaType.toLowerCase();
1720 /** @private */
1721 _this.elementPosition = params.elementPosition;
1722 /** @private */
1723 _this.peaks = null;
1724 /** @private */
1725 _this.playbackRate = 1;
1726 /** @private */
1727 _this.volume = 1;
1728 /** @private */
1729 _this.isMuted = false;
1730 /** @private */
1731 _this.buffer = null;
1732 /** @private */
1733 _this.onPlayEnd = null;
1734 /** @private */
1735 _this.mediaListeners = {};
1736 return _this;
1737 }
1738
1739 /**
1740 * Initialise the backend, called in `wavesurfer.createBackend()`
1741 */
1742 _createClass(MediaElement, [{
1743 key: "init",
1744 value: function init() {
1745 this.setPlaybackRate(this.params.audioRate);
1746 this.createTimer();
1747 }
1748
1749 /**
1750 * Attach event listeners to media element.
1751 */
1752 }, {
1753 key: "_setupMediaListeners",
1754 value: function _setupMediaListeners() {
1755 var _this2 = this;
1756 this.mediaListeners.error = function () {
1757 _this2.fireEvent('error', 'Error loading media element');
1758 };
1759 this.mediaListeners.canplay = function () {
1760 _this2.fireEvent('canplay');
1761 };
1762 this.mediaListeners.ended = function () {
1763 _this2.fireEvent('finish');
1764 };
1765 // listen to and relay play, pause and seeked events to enable
1766 // playback control from the external media element
1767 this.mediaListeners.play = function () {
1768 _this2.fireEvent('play');
1769 };
1770 this.mediaListeners.pause = function () {
1771 _this2.fireEvent('pause');
1772 };
1773 this.mediaListeners.seeked = function (event) {
1774 _this2.fireEvent('seek');
1775 };
1776 this.mediaListeners.volumechange = function (event) {
1777 _this2.isMuted = _this2.media.muted;
1778 if (_this2.isMuted) {
1779 _this2.volume = 0;
1780 } else {
1781 _this2.volume = _this2.media.volume;
1782 }
1783 _this2.fireEvent('volume');
1784 };
1785
1786 // reset event listeners
1787 Object.keys(this.mediaListeners).forEach(function (id) {
1788 _this2.media.removeEventListener(id, _this2.mediaListeners[id]);
1789 _this2.media.addEventListener(id, _this2.mediaListeners[id]);
1790 });
1791 }
1792
1793 /**
1794 * Create a timer to provide a more precise `audioprocess` event.
1795 */
1796 }, {
1797 key: "createTimer",
1798 value: function createTimer() {
1799 var _this3 = this;
1800 var onAudioProcess = function onAudioProcess() {
1801 if (_this3.isPaused()) {
1802 return;
1803 }
1804 _this3.fireEvent('audioprocess', _this3.getCurrentTime());
1805
1806 // Call again in the next frame
1807 util.frame(onAudioProcess)();
1808 };
1809 this.on('play', onAudioProcess);
1810
1811 // Update the progress one more time to prevent it from being stuck in
1812 // case of lower framerates
1813 this.on('pause', function () {
1814 _this3.fireEvent('audioprocess', _this3.getCurrentTime());
1815 });
1816 }
1817
1818 /**
1819 * Create media element with url as its source,
1820 * and append to container element.
1821 *
1822 * @param {string} url Path to media file
1823 * @param {HTMLElement} container HTML element
1824 * @param {number[]|Number.<Array[]>} peaks Array of peak data
1825 * @param {string} preload HTML 5 preload attribute value
1826 * @throws Will throw an error if the `url` argument is not a valid media
1827 * element.
1828 */
1829 }, {
1830 key: "load",
1831 value: function load(url, container, peaks, preload) {
1832 var media = document.createElement(this.mediaType);
1833 media.controls = this.params.mediaControls;
1834 media.autoplay = this.params.autoplay || false;
1835 media.preload = preload == null ? 'auto' : preload;
1836 media.src = url;
1837 media.style.width = '100%';
1838 var prevMedia = container.querySelector(this.mediaType);
1839 if (prevMedia) {
1840 container.removeChild(prevMedia);
1841 }
1842 container.appendChild(media);
1843 this._load(media, peaks, preload);
1844 }
1845
1846 /**
1847 * Load existing media element.
1848 *
1849 * @param {HTMLMediaElement} elt HTML5 Audio or Video element
1850 * @param {number[]|Number.<Array[]>} peaks Array of peak data
1851 */
1852 }, {
1853 key: "loadElt",
1854 value: function loadElt(elt, peaks) {
1855 elt.controls = this.params.mediaControls;
1856 elt.autoplay = this.params.autoplay || false;
1857 this._load(elt, peaks, elt.preload);
1858 }
1859
1860 /**
1861 * Method called by both `load` (from url)
1862 * and `loadElt` (existing media element) methods.
1863 *
1864 * @param {HTMLMediaElement} media HTML5 Audio or Video element
1865 * @param {number[]|Number.<Array[]>} peaks Array of peak data
1866 * @param {string} preload HTML 5 preload attribute value
1867 * @throws Will throw an error if the `media` argument is not a valid media
1868 * element.
1869 * @private
1870 */
1871 }, {
1872 key: "_load",
1873 value: function _load(media, peaks, preload) {
1874 // verify media element is valid
1875 if (!(media instanceof HTMLMediaElement) || typeof media.addEventListener === 'undefined') {
1876 throw new Error('media parameter is not a valid media element');
1877 }
1878
1879 // load must be called manually on iOS, otherwise peaks won't draw
1880 // until a user interaction triggers load --> 'ready' event
1881 //
1882 // note that we avoid calling media.load here when given peaks and preload == 'none'
1883 // as this almost always triggers some browser fetch of the media.
1884 if (typeof media.load == 'function' && !(peaks && preload == 'none')) {
1885 // Resets the media element and restarts the media resource. Any
1886 // pending events are discarded. How much media data is fetched is
1887 // still affected by the preload attribute.
1888 media.load();
1889 }
1890 this.media = media;
1891 this._setupMediaListeners();
1892 this.peaks = peaks;
1893 this.onPlayEnd = null;
1894 this.buffer = null;
1895 this.isMuted = media.muted;
1896 this.setPlaybackRate(this.playbackRate);
1897 this.setVolume(this.volume);
1898 }
1899
1900 /**
1901 * Used by `wavesurfer.isPlaying()` and `wavesurfer.playPause()`
1902 *
1903 * @return {boolean} Media paused or not
1904 */
1905 }, {
1906 key: "isPaused",
1907 value: function isPaused() {
1908 return !this.media || this.media.paused;
1909 }
1910
1911 /**
1912 * Used by `wavesurfer.getDuration()`
1913 *
1914 * @return {number} Duration
1915 */
1916 }, {
1917 key: "getDuration",
1918 value: function getDuration() {
1919 if (this.explicitDuration) {
1920 return this.explicitDuration;
1921 }
1922 var duration = (this.buffer || this.media).duration;
1923 if (duration >= Infinity) {
1924 // streaming audio
1925 duration = this.media.seekable.end(0);
1926 }
1927 return duration;
1928 }
1929
1930 /**
1931 * Returns the current time in seconds relative to the audio-clip's
1932 * duration.
1933 *
1934 * @return {number} Current time
1935 */
1936 }, {
1937 key: "getCurrentTime",
1938 value: function getCurrentTime() {
1939 return this.media && this.media.currentTime;
1940 }
1941
1942 /**
1943 * Get the position from 0 to 1
1944 *
1945 * @return {number} Current position
1946 */
1947 }, {
1948 key: "getPlayedPercents",
1949 value: function getPlayedPercents() {
1950 return this.getCurrentTime() / this.getDuration() || 0;
1951 }
1952
1953 /**
1954 * Get the audio source playback rate.
1955 *
1956 * @return {number} Playback rate
1957 */
1958 }, {
1959 key: "getPlaybackRate",
1960 value: function getPlaybackRate() {
1961 return this.playbackRate || this.media.playbackRate;
1962 }
1963
1964 /**
1965 * Set the audio source playback rate.
1966 *
1967 * @param {number} value Playback rate
1968 */
1969 }, {
1970 key: "setPlaybackRate",
1971 value: function setPlaybackRate(value) {
1972 this.playbackRate = value || 1;
1973 this.media.playbackRate = this.playbackRate;
1974 }
1975
1976 /**
1977 * Used by `wavesurfer.seekTo()`
1978 *
1979 * @param {number} start Position to start at in seconds
1980 */
1981 }, {
1982 key: "seekTo",
1983 value: function seekTo(start) {
1984 if (start != null && !isNaN(start)) {
1985 this.media.currentTime = start;
1986 }
1987 this.clearPlayEnd();
1988 }
1989
1990 /**
1991 * Plays the loaded audio region.
1992 *
1993 * @param {number} start Start offset in seconds, relative to the beginning
1994 * of a clip.
1995 * @param {number} end When to stop, relative to the beginning of a clip.
1996 * @emits MediaElement#play
1997 * @return {Promise} Result
1998 */
1999 }, {
2000 key: "play",
2001 value: function play(start, end) {
2002 this.seekTo(start);
2003 var promise = this.media.play();
2004 end && this.setPlayEnd(end);
2005 return promise;
2006 }
2007
2008 /**
2009 * Pauses the loaded audio.
2010 *
2011 * @emits MediaElement#pause
2012 * @return {Promise} Result
2013 */
2014 }, {
2015 key: "pause",
2016 value: function pause() {
2017 var promise;
2018 if (this.media) {
2019 promise = this.media.pause();
2020 }
2021 this.clearPlayEnd();
2022 return promise;
2023 }
2024
2025 /**
2026 * Set the play end
2027 *
2028 * @param {number} end Where to end
2029 */
2030 }, {
2031 key: "setPlayEnd",
2032 value: function setPlayEnd(end) {
2033 var _this4 = this;
2034 this.clearPlayEnd();
2035 this._onPlayEnd = function (time) {
2036 if (time >= end) {
2037 _this4.pause();
2038 _this4.seekTo(end);
2039 }
2040 };
2041 this.on('audioprocess', this._onPlayEnd);
2042 }
2043
2044 /** @private */
2045 }, {
2046 key: "clearPlayEnd",
2047 value: function clearPlayEnd() {
2048 if (this._onPlayEnd) {
2049 this.un('audioprocess', this._onPlayEnd);
2050 this._onPlayEnd = null;
2051 }
2052 }
2053
2054 /**
2055 * Compute the max and min value of the waveform when broken into
2056 * <length> subranges.
2057 *
2058 * @param {number} length How many subranges to break the waveform into.
2059 * @param {number} first First sample in the required range.
2060 * @param {number} last Last sample in the required range.
2061 * @return {number[]|Number.<Array[]>} Array of 2*<length> peaks or array of
2062 * arrays of peaks consisting of (max, min) values for each subrange.
2063 */
2064 }, {
2065 key: "getPeaks",
2066 value: function getPeaks(length, first, last) {
2067 if (this.buffer) {
2068 return _get(_getPrototypeOf(MediaElement.prototype), "getPeaks", this).call(this, length, first, last);
2069 }
2070 return this.peaks || [];
2071 }
2072
2073 /**
2074 * Set the sink id for the media player
2075 *
2076 * @param {string} deviceId String value representing audio device id.
2077 * @returns {Promise} A Promise that resolves to `undefined` when there
2078 * are no errors.
2079 */
2080 }, {
2081 key: "setSinkId",
2082 value: function setSinkId(deviceId) {
2083 if (deviceId) {
2084 if (!this.media.setSinkId) {
2085 return Promise.reject(new Error('setSinkId is not supported in your browser'));
2086 }
2087 return this.media.setSinkId(deviceId);
2088 }
2089 return Promise.reject(new Error('Invalid deviceId: ' + deviceId));
2090 }
2091
2092 /**
2093 * Get the current volume
2094 *
2095 * @return {number} value A floating point value between 0 and 1.
2096 */
2097 }, {
2098 key: "getVolume",
2099 value: function getVolume() {
2100 return this.volume;
2101 }
2102
2103 /**
2104 * Set the audio volume
2105 *
2106 * @param {number} value A floating point value between 0 and 1.
2107 */
2108 }, {
2109 key: "setVolume",
2110 value: function setVolume(value) {
2111 this.volume = value;
2112 // no need to change when it's already at that volume
2113 if (this.media.volume !== this.volume) {
2114 this.media.volume = this.volume;
2115 }
2116 }
2117
2118 /**
2119 * Enable or disable muted audio
2120 *
2121 * @since 4.0.0
2122 * @param {boolean} muted Specify `true` to mute audio.
2123 */
2124 }, {
2125 key: "setMute",
2126 value: function setMute(muted) {
2127 // This causes a volume change to be emitted too through the
2128 // volumechange event listener.
2129 this.isMuted = this.media.muted = muted;
2130 }
2131
2132 /**
2133 * This is called when wavesurfer is destroyed
2134 *
2135 */
2136 }, {
2137 key: "destroy",
2138 value: function destroy() {
2139 var _this5 = this;
2140 this.pause();
2141 this.unAll();
2142 this.destroyed = true;
2143
2144 // cleanup media event listeners
2145 Object.keys(this.mediaListeners).forEach(function (id) {
2146 if (_this5.media) {
2147 _this5.media.removeEventListener(id, _this5.mediaListeners[id]);
2148 }
2149 });
2150 if (this.params.removeMediaElementOnDestroy && this.media && this.media.parentNode) {
2151 this.media.parentNode.removeChild(this.media);
2152 }
2153 this.media = null;
2154 }
2155 }]);
2156 return MediaElement;
2157}(_webaudio.default);
2158exports["default"] = MediaElement;
2159module.exports = exports.default;
2160
2161/***/ }),
2162
2163/***/ "./src/peakcache.js":
2164/*!**************************!*\
2165 !*** ./src/peakcache.js ***!
2166 \**************************/
2167/***/ ((module, exports) => {
2168
2169"use strict";
2170
2171
2172Object.defineProperty(exports, "__esModule", ({
2173 value: true
2174}));
2175exports["default"] = void 0;
2176function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2177function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
2178function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
2179/**
2180 * Caches the decoded peaks data to improve rendering speed for large audio
2181 *
2182 * Is used if the option parameter `partialRender` is set to `true`
2183 */
2184var PeakCache = /*#__PURE__*/function () {
2185 /**
2186 * Instantiate cache
2187 */
2188 function PeakCache() {
2189 _classCallCheck(this, PeakCache);
2190 this.clearPeakCache();
2191 }
2192
2193 /**
2194 * Empty the cache
2195 */
2196 _createClass(PeakCache, [{
2197 key: "clearPeakCache",
2198 value: function clearPeakCache() {
2199 /**
2200 * Flat array with entries that are always in pairs to mark the
2201 * beginning and end of each subrange. This is a convenience so we can
2202 * iterate over the pairs for easy set difference operations.
2203 * @private
2204 */
2205 this.peakCacheRanges = [];
2206 /**
2207 * Length of the entire cachable region, used for resetting the cache
2208 * when this changes (zoom events, for instance).
2209 * @private
2210 */
2211 this.peakCacheLength = -1;
2212 }
2213
2214 /**
2215 * Add a range of peaks to the cache
2216 *
2217 * @param {number} length The length of the range
2218 * @param {number} start The x offset of the start of the range
2219 * @param {number} end The x offset of the end of the range
2220 * @return {Number.<Array[]>} Array with arrays of numbers
2221 */
2222 }, {
2223 key: "addRangeToPeakCache",
2224 value: function addRangeToPeakCache(length, start, end) {
2225 if (length != this.peakCacheLength) {
2226 this.clearPeakCache();
2227 this.peakCacheLength = length;
2228 }
2229
2230 // Return ranges that weren't in the cache before the call.
2231 var uncachedRanges = [];
2232 var i = 0;
2233 // Skip ranges before the current start.
2234 while (i < this.peakCacheRanges.length && this.peakCacheRanges[i] < start) {
2235 i++;
2236 }
2237 // If |i| is even, |start| falls after an existing range. Otherwise,
2238 // |start| falls between an existing range, and the uncached region
2239 // starts when we encounter the next node in |peakCacheRanges| or
2240 // |end|, whichever comes first.
2241 if (i % 2 == 0) {
2242 uncachedRanges.push(start);
2243 }
2244 while (i < this.peakCacheRanges.length && this.peakCacheRanges[i] <= end) {
2245 uncachedRanges.push(this.peakCacheRanges[i]);
2246 i++;
2247 }
2248 // If |i| is even, |end| is after all existing ranges.
2249 if (i % 2 == 0) {
2250 uncachedRanges.push(end);
2251 }
2252
2253 // Filter out the 0-length ranges.
2254 uncachedRanges = uncachedRanges.filter(function (item, pos, arr) {
2255 if (pos == 0) {
2256 return item != arr[pos + 1];
2257 } else if (pos == arr.length - 1) {
2258 return item != arr[pos - 1];
2259 }
2260 return item != arr[pos - 1] && item != arr[pos + 1];
2261 });
2262
2263 // Merge the two ranges together, uncachedRanges will either contain
2264 // wholly new points, or duplicates of points in peakCacheRanges. If
2265 // duplicates are detected, remove both and extend the range.
2266 this.peakCacheRanges = this.peakCacheRanges.concat(uncachedRanges);
2267 this.peakCacheRanges = this.peakCacheRanges.sort(function (a, b) {
2268 return a - b;
2269 }).filter(function (item, pos, arr) {
2270 if (pos == 0) {
2271 return item != arr[pos + 1];
2272 } else if (pos == arr.length - 1) {
2273 return item != arr[pos - 1];
2274 }
2275 return item != arr[pos - 1] && item != arr[pos + 1];
2276 });
2277
2278 // Push the uncached ranges into an array of arrays for ease of
2279 // iteration in the functions that call this.
2280 var uncachedRangePairs = [];
2281 for (i = 0; i < uncachedRanges.length; i += 2) {
2282 uncachedRangePairs.push([uncachedRanges[i], uncachedRanges[i + 1]]);
2283 }
2284 return uncachedRangePairs;
2285 }
2286
2287 /**
2288 * For testing
2289 *
2290 * @return {Number.<Array[]>} Array with arrays of numbers
2291 */
2292 }, {
2293 key: "getCacheRanges",
2294 value: function getCacheRanges() {
2295 var peakCacheRangePairs = [];
2296 var i;
2297 for (i = 0; i < this.peakCacheRanges.length; i += 2) {
2298 peakCacheRangePairs.push([this.peakCacheRanges[i], this.peakCacheRanges[i + 1]]);
2299 }
2300 return peakCacheRangePairs;
2301 }
2302 }]);
2303 return PeakCache;
2304}();
2305exports["default"] = PeakCache;
2306module.exports = exports.default;
2307
2308/***/ }),
2309
2310/***/ "./src/util/absMax.js":
2311/*!****************************!*\
2312 !*** ./src/util/absMax.js ***!
2313 \****************************/
2314/***/ ((module, exports, __webpack_require__) => {
2315
2316"use strict";
2317
2318
2319Object.defineProperty(exports, "__esModule", ({
2320 value: true
2321}));
2322exports["default"] = absMax;
2323var _max = _interopRequireDefault(__webpack_require__(/*! ./max */ "./src/util/max.js"));
2324var _min = _interopRequireDefault(__webpack_require__(/*! ./min */ "./src/util/min.js"));
2325function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2326/**
2327 * Get the largest absolute value in an array
2328 *
2329 * @param {Array} values Array of numbers
2330 * @returns {Number} Largest number found
2331 * @example console.log(max([-3, 2, 1]), max([-3, 2, 4])); // logs 3 4
2332 * @since 4.3.0
2333 */
2334function absMax(values) {
2335 var max = (0, _max.default)(values);
2336 var min = (0, _min.default)(values);
2337 return -min > max ? -min : max;
2338}
2339module.exports = exports.default;
2340
2341/***/ }),
2342
2343/***/ "./src/util/clamp.js":
2344/*!***************************!*\
2345 !*** ./src/util/clamp.js ***!
2346 \***************************/
2347/***/ ((module, exports) => {
2348
2349"use strict";
2350
2351
2352Object.defineProperty(exports, "__esModule", ({
2353 value: true
2354}));
2355exports["default"] = clamp;
2356/**
2357 * Returns a number limited to the given range.
2358 *
2359 * @param {number} val The number to be limited to a range
2360 * @param {number} min The lower boundary of the limit range
2361 * @param {number} max The upper boundary of the limit range
2362 * @returns {number} A number in the range [min, max]
2363 */
2364function clamp(val, min, max) {
2365 return Math.min(Math.max(min, val), max);
2366}
2367module.exports = exports.default;
2368
2369/***/ }),
2370
2371/***/ "./src/util/fetch.js":
2372/*!***************************!*\
2373 !*** ./src/util/fetch.js ***!
2374 \***************************/
2375/***/ ((module, exports, __webpack_require__) => {
2376
2377"use strict";
2378
2379
2380Object.defineProperty(exports, "__esModule", ({
2381 value: true
2382}));
2383exports["default"] = fetchFile;
2384var _observer = _interopRequireDefault(__webpack_require__(/*! ./observer */ "./src/util/observer.js"));
2385function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2386function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2387function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
2388function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
2389var ProgressHandler = /*#__PURE__*/function () {
2390 /**
2391 * Instantiate ProgressHandler
2392 *
2393 * @param {Observer} instance The `fetchFile` observer instance.
2394 * @param {Number} contentLength Content length.
2395 * @param {Response} response Response object.
2396 */
2397 function ProgressHandler(instance, contentLength, response) {
2398 _classCallCheck(this, ProgressHandler);
2399 this.instance = instance;
2400 this.instance._reader = response.body.getReader();
2401 this.total = parseInt(contentLength, 10);
2402 this.loaded = 0;
2403 }
2404
2405 /**
2406 * A method that is called once, immediately after the `ReadableStream``
2407 * is constructed.
2408 *
2409 * @param {ReadableStreamDefaultController} controller Controller instance
2410 * used to control the stream.
2411 */
2412 _createClass(ProgressHandler, [{
2413 key: "start",
2414 value: function start(controller) {
2415 var _this = this;
2416 var read = function read() {
2417 // instance._reader.read() returns a promise that resolves
2418 // when a value has been received
2419 _this.instance._reader.read().then(function (_ref) {
2420 var done = _ref.done,
2421 value = _ref.value;
2422 // result objects contain two properties:
2423 // done - true if the stream has already given you all its data.
2424 // value - some data. Always undefined when done is true.
2425 if (done) {
2426 // ensure onProgress called when content-length=0
2427 if (_this.total === 0) {
2428 _this.instance.onProgress.call(_this.instance, {
2429 loaded: _this.loaded,
2430 total: _this.total,
2431 lengthComputable: false
2432 });
2433 }
2434 // no more data needs to be consumed, close the stream
2435 controller.close();
2436 return;
2437 }
2438 _this.loaded += value.byteLength;
2439 _this.instance.onProgress.call(_this.instance, {
2440 loaded: _this.loaded,
2441 total: _this.total,
2442 lengthComputable: !(_this.total === 0)
2443 });
2444 // enqueue the next data chunk into our target stream
2445 controller.enqueue(value);
2446 read();
2447 }).catch(function (error) {
2448 controller.error(error);
2449 });
2450 };
2451 read();
2452 }
2453 }]);
2454 return ProgressHandler;
2455}();
2456/**
2457 * Load a file using `fetch`.
2458 *
2459 * @param {object} options Request options to use. See example below.
2460 * @returns {Observer} Observer instance
2461 * @example
2462 * // default options
2463 * let options = {
2464 * url: undefined,
2465 * method: 'GET',
2466 * mode: 'cors',
2467 * credentials: 'same-origin',
2468 * cache: 'default',
2469 * responseType: 'json',
2470 * requestHeaders: [],
2471 * redirect: 'follow',
2472 * referrer: 'client'
2473 * };
2474 *
2475 * // override some options
2476 * options.url = '../media/demo.wav';
2477
2478 * // available types: 'arraybuffer', 'blob', 'json' or 'text'
2479 * options.responseType = 'arraybuffer';
2480 *
2481 * // make fetch call
2482 * let request = util.fetchFile(options);
2483 *
2484 * // listen for events
2485 * request.on('progress', e => {
2486 * console.log('progress', e);
2487 * });
2488 *
2489 * request.on('success', data => {
2490 * console.log('success!', data);
2491 * });
2492 *
2493 * request.on('error', e => {
2494 * console.warn('fetchFile error: ', e);
2495 * });
2496 */
2497function fetchFile(options) {
2498 if (!options) {
2499 throw new Error('fetch options missing');
2500 } else if (!options.url) {
2501 throw new Error('fetch url missing');
2502 }
2503 var instance = new _observer.default();
2504 var fetchHeaders = new Headers();
2505 var fetchRequest = new Request(options.url);
2506
2507 // add ability to abort
2508 instance.controller = new AbortController();
2509
2510 // check if headers have to be added
2511 if (options && options.requestHeaders) {
2512 // add custom request headers
2513 options.requestHeaders.forEach(function (header) {
2514 fetchHeaders.append(header.key, header.value);
2515 });
2516 }
2517
2518 // parse fetch options
2519 var responseType = options.responseType || 'json';
2520 var fetchOptions = {
2521 method: options.method || 'GET',
2522 headers: fetchHeaders,
2523 mode: options.mode || 'cors',
2524 credentials: options.credentials || 'same-origin',
2525 cache: options.cache || 'default',
2526 redirect: options.redirect || 'follow',
2527 referrer: options.referrer || 'client',
2528 signal: instance.controller.signal
2529 };
2530 fetch(fetchRequest, fetchOptions).then(function (response) {
2531 // store response reference
2532 instance.response = response;
2533 var progressAvailable = true;
2534 if (!response.body) {
2535 // ReadableStream is not yet supported in this browser
2536 // see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
2537 progressAvailable = false;
2538 }
2539
2540 // Server must send CORS header "Access-Control-Expose-Headers: content-length"
2541 var contentLength = response.headers.get('content-length');
2542 if (contentLength === null) {
2543 // Content-Length server response header missing.
2544 // Don't evaluate download progress if we can't compare against a total size
2545 // see https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Access-Control-Expose-Headers
2546 progressAvailable = false;
2547 }
2548 if (!progressAvailable) {
2549 // not able to check download progress so skip it
2550 return response;
2551 }
2552
2553 // fire progress event when during load
2554 instance.onProgress = function (e) {
2555 instance.fireEvent('progress', e);
2556 };
2557 return new Response(new ReadableStream(new ProgressHandler(instance, contentLength, response)), fetchOptions);
2558 }).then(function (response) {
2559 var errMsg;
2560 if (response.ok) {
2561 switch (responseType) {
2562 case 'arraybuffer':
2563 return response.arrayBuffer();
2564 case 'json':
2565 return response.json();
2566 case 'blob':
2567 return response.blob();
2568 case 'text':
2569 return response.text();
2570 default:
2571 errMsg = 'Unknown responseType: ' + responseType;
2572 break;
2573 }
2574 }
2575 if (!errMsg) {
2576 errMsg = 'HTTP error status: ' + response.status;
2577 }
2578 throw new Error(errMsg);
2579 }).then(function (response) {
2580 instance.fireEvent('success', response);
2581 }).catch(function (error) {
2582 instance.fireEvent('error', error);
2583 });
2584
2585 // return the fetch request
2586 instance.fetchRequest = fetchRequest;
2587 return instance;
2588}
2589module.exports = exports.default;
2590
2591/***/ }),
2592
2593/***/ "./src/util/frame.js":
2594/*!***************************!*\
2595 !*** ./src/util/frame.js ***!
2596 \***************************/
2597/***/ ((module, exports, __webpack_require__) => {
2598
2599"use strict";
2600
2601
2602Object.defineProperty(exports, "__esModule", ({
2603 value: true
2604}));
2605exports["default"] = frame;
2606var _requestAnimationFrame = _interopRequireDefault(__webpack_require__(/*! ./request-animation-frame */ "./src/util/request-animation-frame.js"));
2607function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2608/**
2609 * Create a function which will be called at the next requestAnimationFrame
2610 * cycle
2611 *
2612 * @param {function} func The function to call
2613 *
2614 * @return {func} The function wrapped within a requestAnimationFrame
2615 */
2616function frame(func) {
2617 return function () {
2618 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
2619 args[_key] = arguments[_key];
2620 }
2621 return (0, _requestAnimationFrame.default)(function () {
2622 return func.apply(void 0, args);
2623 });
2624 };
2625}
2626module.exports = exports.default;
2627
2628/***/ }),
2629
2630/***/ "./src/util/get-id.js":
2631/*!****************************!*\
2632 !*** ./src/util/get-id.js ***!
2633 \****************************/
2634/***/ ((module, exports) => {
2635
2636"use strict";
2637
2638
2639Object.defineProperty(exports, "__esModule", ({
2640 value: true
2641}));
2642exports["default"] = getId;
2643/**
2644 * Get a random prefixed ID
2645 *
2646 * @param {String} prefix Prefix to use. Default is `'wavesurfer_'`.
2647 * @returns {String} Random prefixed ID
2648 * @example
2649 * console.log(getId()); // logs 'wavesurfer_b5pors4ru6g'
2650 *
2651 * let prefix = 'foo-';
2652 * console.log(getId(prefix)); // logs 'foo-b5pors4ru6g'
2653 */
2654function getId(prefix) {
2655 if (prefix === undefined) {
2656 prefix = 'wavesurfer_';
2657 }
2658 return prefix + Math.random().toString(32).substring(2);
2659}
2660module.exports = exports.default;
2661
2662/***/ }),
2663
2664/***/ "./src/util/index.js":
2665/*!***************************!*\
2666 !*** ./src/util/index.js ***!
2667 \***************************/
2668/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
2669
2670"use strict";
2671
2672
2673Object.defineProperty(exports, "__esModule", ({
2674 value: true
2675}));
2676Object.defineProperty(exports, "Observer", ({
2677 enumerable: true,
2678 get: function get() {
2679 return _observer.default;
2680 }
2681}));
2682Object.defineProperty(exports, "absMax", ({
2683 enumerable: true,
2684 get: function get() {
2685 return _absMax.default;
2686 }
2687}));
2688Object.defineProperty(exports, "clamp", ({
2689 enumerable: true,
2690 get: function get() {
2691 return _clamp.default;
2692 }
2693}));
2694Object.defineProperty(exports, "debounce", ({
2695 enumerable: true,
2696 get: function get() {
2697 return _debounce.default;
2698 }
2699}));
2700Object.defineProperty(exports, "fetchFile", ({
2701 enumerable: true,
2702 get: function get() {
2703 return _fetch.default;
2704 }
2705}));
2706Object.defineProperty(exports, "frame", ({
2707 enumerable: true,
2708 get: function get() {
2709 return _frame.default;
2710 }
2711}));
2712Object.defineProperty(exports, "getId", ({
2713 enumerable: true,
2714 get: function get() {
2715 return _getId.default;
2716 }
2717}));
2718Object.defineProperty(exports, "ignoreSilenceMode", ({
2719 enumerable: true,
2720 get: function get() {
2721 return _silenceMode.default;
2722 }
2723}));
2724Object.defineProperty(exports, "max", ({
2725 enumerable: true,
2726 get: function get() {
2727 return _max.default;
2728 }
2729}));
2730Object.defineProperty(exports, "min", ({
2731 enumerable: true,
2732 get: function get() {
2733 return _min.default;
2734 }
2735}));
2736Object.defineProperty(exports, "preventClick", ({
2737 enumerable: true,
2738 get: function get() {
2739 return _preventClick.default;
2740 }
2741}));
2742Object.defineProperty(exports, "requestAnimationFrame", ({
2743 enumerable: true,
2744 get: function get() {
2745 return _requestAnimationFrame.default;
2746 }
2747}));
2748Object.defineProperty(exports, "style", ({
2749 enumerable: true,
2750 get: function get() {
2751 return _style.default;
2752 }
2753}));
2754Object.defineProperty(exports, "withOrientation", ({
2755 enumerable: true,
2756 get: function get() {
2757 return _orientation.default;
2758 }
2759}));
2760var _getId = _interopRequireDefault(__webpack_require__(/*! ./get-id */ "./src/util/get-id.js"));
2761var _max = _interopRequireDefault(__webpack_require__(/*! ./max */ "./src/util/max.js"));
2762var _min = _interopRequireDefault(__webpack_require__(/*! ./min */ "./src/util/min.js"));
2763var _absMax = _interopRequireDefault(__webpack_require__(/*! ./absMax */ "./src/util/absMax.js"));
2764var _observer = _interopRequireDefault(__webpack_require__(/*! ./observer */ "./src/util/observer.js"));
2765var _style = _interopRequireDefault(__webpack_require__(/*! ./style */ "./src/util/style.js"));
2766var _requestAnimationFrame = _interopRequireDefault(__webpack_require__(/*! ./request-animation-frame */ "./src/util/request-animation-frame.js"));
2767var _frame = _interopRequireDefault(__webpack_require__(/*! ./frame */ "./src/util/frame.js"));
2768var _debounce = _interopRequireDefault(__webpack_require__(/*! debounce */ "./node_modules/debounce/index.js"));
2769var _preventClick = _interopRequireDefault(__webpack_require__(/*! ./prevent-click */ "./src/util/prevent-click.js"));
2770var _fetch = _interopRequireDefault(__webpack_require__(/*! ./fetch */ "./src/util/fetch.js"));
2771var _clamp = _interopRequireDefault(__webpack_require__(/*! ./clamp */ "./src/util/clamp.js"));
2772var _orientation = _interopRequireDefault(__webpack_require__(/*! ./orientation */ "./src/util/orientation.js"));
2773var _silenceMode = _interopRequireDefault(__webpack_require__(/*! ./silence-mode */ "./src/util/silence-mode.js"));
2774function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2775
2776/***/ }),
2777
2778/***/ "./src/util/max.js":
2779/*!*************************!*\
2780 !*** ./src/util/max.js ***!
2781 \*************************/
2782/***/ ((module, exports) => {
2783
2784"use strict";
2785
2786
2787Object.defineProperty(exports, "__esModule", ({
2788 value: true
2789}));
2790exports["default"] = max;
2791/**
2792 * Get the largest value
2793 *
2794 * @param {Array} values Array of numbers
2795 * @returns {Number} Largest number found
2796 * @example console.log(max([1, 2, 3])); // logs 3
2797 */
2798function max(values) {
2799 var largest = -Infinity;
2800 Object.keys(values).forEach(function (i) {
2801 if (values[i] > largest) {
2802 largest = values[i];
2803 }
2804 });
2805 return largest;
2806}
2807module.exports = exports.default;
2808
2809/***/ }),
2810
2811/***/ "./src/util/min.js":
2812/*!*************************!*\
2813 !*** ./src/util/min.js ***!
2814 \*************************/
2815/***/ ((module, exports) => {
2816
2817"use strict";
2818
2819
2820Object.defineProperty(exports, "__esModule", ({
2821 value: true
2822}));
2823exports["default"] = min;
2824/**
2825 * Get the smallest value
2826 *
2827 * @param {Array} values Array of numbers
2828 * @returns {Number} Smallest number found
2829 * @example console.log(min([1, 2, 3])); // logs 1
2830 */
2831function min(values) {
2832 var smallest = Number(Infinity);
2833 Object.keys(values).forEach(function (i) {
2834 if (values[i] < smallest) {
2835 smallest = values[i];
2836 }
2837 });
2838 return smallest;
2839}
2840module.exports = exports.default;
2841
2842/***/ }),
2843
2844/***/ "./src/util/observer.js":
2845/*!******************************!*\
2846 !*** ./src/util/observer.js ***!
2847 \******************************/
2848/***/ ((module, exports) => {
2849
2850"use strict";
2851
2852
2853Object.defineProperty(exports, "__esModule", ({
2854 value: true
2855}));
2856exports["default"] = void 0;
2857function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2858function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
2859function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
2860/**
2861 * @typedef {Object} ListenerDescriptor
2862 * @property {string} name The name of the event
2863 * @property {function} callback The callback
2864 * @property {function} un The function to call to remove the listener
2865 */
2866/**
2867 * Observer class
2868 */
2869var Observer = /*#__PURE__*/function () {
2870 /**
2871 * Instantiate Observer
2872 */
2873 function Observer() {
2874 _classCallCheck(this, Observer);
2875 /**
2876 * @private
2877 * @todo Initialise the handlers here already and remove the conditional
2878 * assignment in `on()`
2879 */
2880 this._disabledEventEmissions = [];
2881 this.handlers = null;
2882 }
2883 /**
2884 * Attach a handler function for an event.
2885 *
2886 * @param {string} event Name of the event to listen to
2887 * @param {function} fn The callback to trigger when the event is fired
2888 * @return {ListenerDescriptor} The event descriptor
2889 */
2890 _createClass(Observer, [{
2891 key: "on",
2892 value: function on(event, fn) {
2893 var _this = this;
2894 if (!this.handlers) {
2895 this.handlers = {};
2896 }
2897 var handlers = this.handlers[event];
2898 if (!handlers) {
2899 handlers = this.handlers[event] = [];
2900 }
2901 handlers.push(fn);
2902
2903 // Return an event descriptor
2904 return {
2905 name: event,
2906 callback: fn,
2907 un: function un(e, fn) {
2908 return _this.un(e, fn);
2909 }
2910 };
2911 }
2912
2913 /**
2914 * Remove an event handler.
2915 *
2916 * @param {string} event Name of the event the listener that should be
2917 * removed listens to
2918 * @param {function} fn The callback that should be removed
2919 */
2920 }, {
2921 key: "un",
2922 value: function un(event, fn) {
2923 if (!this.handlers) {
2924 return;
2925 }
2926 var handlers = this.handlers[event];
2927 var i;
2928 if (handlers) {
2929 if (fn) {
2930 for (i = handlers.length - 1; i >= 0; i--) {
2931 if (handlers[i] == fn) {
2932 handlers.splice(i, 1);
2933 }
2934 }
2935 } else {
2936 handlers.length = 0;
2937 }
2938 }
2939 }
2940
2941 /**
2942 * Remove all event handlers.
2943 */
2944 }, {
2945 key: "unAll",
2946 value: function unAll() {
2947 this.handlers = null;
2948 }
2949
2950 /**
2951 * Attach a handler to an event. The handler is executed at most once per
2952 * event type.
2953 *
2954 * @param {string} event The event to listen to
2955 * @param {function} handler The callback that is only to be called once
2956 * @return {ListenerDescriptor} The event descriptor
2957 */
2958 }, {
2959 key: "once",
2960 value: function once(event, handler) {
2961 var _this2 = this;
2962 var fn = function fn() {
2963 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
2964 args[_key] = arguments[_key];
2965 }
2966 /* eslint-disable no-invalid-this */
2967 handler.apply(_this2, args);
2968 /* eslint-enable no-invalid-this */
2969 setTimeout(function () {
2970 _this2.un(event, fn);
2971 }, 0);
2972 };
2973 return this.on(event, fn);
2974 }
2975
2976 /**
2977 * Disable firing a list of events by name. When specified, event handlers for any event type
2978 * passed in here will not be called.
2979 *
2980 * @since 4.0.0
2981 * @param {string[]} eventNames an array of event names to disable emissions for
2982 * @example
2983 * // disable seek and interaction events
2984 * wavesurfer.setDisabledEventEmissions(['seek', 'interaction']);
2985 */
2986 }, {
2987 key: "setDisabledEventEmissions",
2988 value: function setDisabledEventEmissions(eventNames) {
2989 this._disabledEventEmissions = eventNames;
2990 }
2991
2992 /**
2993 * plugins borrow part of this class without calling the constructor,
2994 * so we have to be careful about _disabledEventEmissions
2995 */
2996 }, {
2997 key: "_isDisabledEventEmission",
2998 value: function _isDisabledEventEmission(event) {
2999 return this._disabledEventEmissions && this._disabledEventEmissions.includes(event);
3000 }
3001
3002 /**
3003 * Manually fire an event
3004 *
3005 * @param {string} event The event to fire manually
3006 * @param {...any} args The arguments with which to call the listeners
3007 */
3008 }, {
3009 key: "fireEvent",
3010 value: function fireEvent(event) {
3011 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
3012 args[_key2 - 1] = arguments[_key2];
3013 }
3014 if (!this.handlers || this._isDisabledEventEmission(event)) {
3015 return;
3016 }
3017 var handlers = this.handlers[event];
3018 handlers && handlers.forEach(function (fn) {
3019 fn.apply(void 0, args);
3020 });
3021 }
3022 }]);
3023 return Observer;
3024}();
3025exports["default"] = Observer;
3026module.exports = exports.default;
3027
3028/***/ }),
3029
3030/***/ "./src/util/orientation.js":
3031/*!*********************************!*\
3032 !*** ./src/util/orientation.js ***!
3033 \*********************************/
3034/***/ ((module, exports) => {
3035
3036"use strict";
3037
3038
3039Object.defineProperty(exports, "__esModule", ({
3040 value: true
3041}));
3042exports["default"] = withOrientation;
3043var verticalPropMap = {
3044 width: 'height',
3045 height: 'width',
3046 overflowX: 'overflowY',
3047 overflowY: 'overflowX',
3048 clientWidth: 'clientHeight',
3049 clientHeight: 'clientWidth',
3050 clientX: 'clientY',
3051 clientY: 'clientX',
3052 scrollWidth: 'scrollHeight',
3053 scrollLeft: 'scrollTop',
3054 offsetLeft: 'offsetTop',
3055 offsetTop: 'offsetLeft',
3056 offsetHeight: 'offsetWidth',
3057 offsetWidth: 'offsetHeight',
3058 left: 'top',
3059 right: 'bottom',
3060 top: 'left',
3061 bottom: 'right',
3062 borderRightStyle: 'borderBottomStyle',
3063 borderRightWidth: 'borderBottomWidth',
3064 borderRightColor: 'borderBottomColor'
3065};
3066
3067/**
3068 * Convert a horizontally-oriented property name to a vertical one.
3069 *
3070 * @param {string} prop A property name
3071 * @param {bool} vertical Whether the element is oriented vertically
3072 * @returns {string} prop, converted appropriately
3073 */
3074function mapProp(prop, vertical) {
3075 if (Object.prototype.hasOwnProperty.call(verticalPropMap, prop)) {
3076 return vertical ? verticalPropMap[prop] : prop;
3077 } else {
3078 return prop;
3079 }
3080}
3081var isProxy = Symbol("isProxy");
3082
3083/**
3084 * Returns an appropriately oriented object based on vertical.
3085 * If vertical is true, attribute getting and setting will be mapped through
3086 * verticalPropMap, so that e.g. getting the object's .width will give its
3087 * .height instead.
3088 * Certain methods of an oriented object will return oriented objects as well.
3089 * Oriented objects can't be added to the DOM directly since they are Proxy objects
3090 * and thus fail typechecks. Use domElement to get the actual element for this.
3091 *
3092 * @param {object} target The object to be wrapped and oriented
3093 * @param {bool} vertical Whether the element is oriented vertically
3094 * @returns {Proxy} An oriented object with attr translation via verticalAttrMap
3095 * @since 5.0.0
3096 */
3097function withOrientation(target, vertical) {
3098 if (target[isProxy]) {
3099 return target;
3100 } else {
3101 return new Proxy(target, {
3102 get: function get(obj, prop, receiver) {
3103 if (prop === isProxy) {
3104 return true;
3105 } else if (prop === 'domElement') {
3106 return obj;
3107 } else if (prop === 'style') {
3108 return withOrientation(obj.style, vertical);
3109 } else if (prop === 'canvas') {
3110 return withOrientation(obj.canvas, vertical);
3111 } else if (prop === 'getBoundingClientRect') {
3112 return function () {
3113 return withOrientation(obj.getBoundingClientRect.apply(obj, arguments), vertical);
3114 };
3115 } else if (prop === 'getContext') {
3116 return function () {
3117 return withOrientation(obj.getContext.apply(obj, arguments), vertical);
3118 };
3119 } else {
3120 var value = obj[mapProp(prop, vertical)];
3121 return typeof value == 'function' ? value.bind(obj) : value;
3122 }
3123 },
3124 set: function set(obj, prop, value) {
3125 obj[mapProp(prop, vertical)] = value;
3126 return true;
3127 }
3128 });
3129 }
3130}
3131module.exports = exports.default;
3132
3133/***/ }),
3134
3135/***/ "./src/util/prevent-click.js":
3136/*!***********************************!*\
3137 !*** ./src/util/prevent-click.js ***!
3138 \***********************************/
3139/***/ ((module, exports) => {
3140
3141"use strict";
3142
3143
3144Object.defineProperty(exports, "__esModule", ({
3145 value: true
3146}));
3147exports["default"] = preventClick;
3148/**
3149 * Stops propagation of click event and removes event listener
3150 *
3151 * @private
3152 * @param {object} event The click event
3153 */
3154function preventClickHandler(event) {
3155 event.stopPropagation();
3156 document.body.removeEventListener('click', preventClickHandler, true);
3157}
3158
3159/**
3160 * Starts listening for click event and prevent propagation
3161 *
3162 * @param {object} values Values
3163 */
3164function preventClick(values) {
3165 document.body.addEventListener('click', preventClickHandler, true);
3166}
3167module.exports = exports.default;
3168
3169/***/ }),
3170
3171/***/ "./src/util/request-animation-frame.js":
3172/*!*********************************************!*\
3173 !*** ./src/util/request-animation-frame.js ***!
3174 \*********************************************/
3175/***/ ((module, exports) => {
3176
3177"use strict";
3178
3179
3180Object.defineProperty(exports, "__esModule", ({
3181 value: true
3182}));
3183exports["default"] = void 0;
3184/* eslint-disable valid-jsdoc */
3185/**
3186 * Returns the `requestAnimationFrame` function for the browser, or a shim with
3187 * `setTimeout` if the function is not found
3188 *
3189 * @return {function} Available `requestAnimationFrame` function for the browser
3190 */
3191var _default = (window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback, element) {
3192 return setTimeout(callback, 1000 / 60);
3193}).bind(window);
3194exports["default"] = _default;
3195module.exports = exports.default;
3196
3197/***/ }),
3198
3199/***/ "./src/util/silence-mode.js":
3200/*!**********************************!*\
3201 !*** ./src/util/silence-mode.js ***!
3202 \**********************************/
3203/***/ ((module, exports) => {
3204
3205"use strict";
3206
3207
3208Object.defineProperty(exports, "__esModule", ({
3209 value: true
3210}));
3211exports["default"] = ignoreSilenceMode;
3212/**
3213 * Ignores device silence mode when using the `WebAudio` backend.
3214 *
3215 * Many mobile devices contain a hardware button to mute the ringtone for incoming
3216 * calls and messages. Unfortunately, on some platforms like iOS, this also mutes
3217 * wavesurfer's audio when using the `WebAudio` backend. This function creates a
3218 * temporary `<audio>` element that makes sure the WebAudio backend keeps playing
3219 * when muting the device ringer.
3220 *
3221 * @since 5.2.0
3222 */
3223function ignoreSilenceMode() {
3224 // Set the src to a short bit of url encoded as a silent mp3
3225 // NOTE The silence MP3 must be high quality, when web audio sounds are played
3226 // in parallel the web audio sound is mixed to match the bitrate of the html sound
3227 // 0.01 seconds of silence VBR220-260 Joint Stereo 859B
3228 var audioData = "data:audio/mpeg;base64,//uQxAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAACAAACcQCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA//////////////////////////////////////////////////////////////////8AAABhTEFNRTMuMTAwA8MAAAAAAAAAABQgJAUHQQAB9AAAAnGMHkkIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//sQxAADgnABGiAAQBCqgCRMAAgEAH///////////////7+n/9FTuQsQH//////2NG0jWUGlio5gLQTOtIoeR2WX////X4s9Atb/JRVCbBUpeRUq//////////////////9RUi0f2jn/+xDECgPCjAEQAABN4AAANIAAAAQVTEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVQ==";
3229
3230 // disable iOS Airplay (setting the attribute in js doesn't work)
3231 var tmp = document.createElement("div");
3232 tmp.innerHTML = '<audio x-webkit-airplay="deny"></audio>';
3233 var audioSilentMode = tmp.children.item(0);
3234 audioSilentMode.src = audioData;
3235 audioSilentMode.preload = "auto";
3236 audioSilentMode.type = "audio/mpeg";
3237 audioSilentMode.disableRemotePlayback = true;
3238
3239 // play
3240 audioSilentMode.play();
3241
3242 // cleanup
3243 audioSilentMode.remove();
3244 tmp.remove();
3245}
3246module.exports = exports.default;
3247
3248/***/ }),
3249
3250/***/ "./src/util/style.js":
3251/*!***************************!*\
3252 !*** ./src/util/style.js ***!
3253 \***************************/
3254/***/ ((module, exports) => {
3255
3256"use strict";
3257
3258
3259Object.defineProperty(exports, "__esModule", ({
3260 value: true
3261}));
3262exports["default"] = style;
3263/**
3264 * Apply a map of styles to an element
3265 *
3266 * @param {HTMLElement} el The element that the styles will be applied to
3267 * @param {Object} styles The map of propName: attribute, both are used as-is
3268 *
3269 * @return {HTMLElement} el
3270 */
3271function style(el, styles) {
3272 Object.keys(styles).forEach(function (prop) {
3273 if (el.style[prop] !== styles[prop]) {
3274 el.style[prop] = styles[prop];
3275 }
3276 });
3277 return el;
3278}
3279module.exports = exports.default;
3280
3281/***/ }),
3282
3283/***/ "./src/wavesurfer.js":
3284/*!***************************!*\
3285 !*** ./src/wavesurfer.js ***!
3286 \***************************/
3287/***/ ((module, exports, __webpack_require__) => {
3288
3289"use strict";
3290
3291
3292function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
3293Object.defineProperty(exports, "__esModule", ({
3294 value: true
3295}));
3296exports["default"] = void 0;
3297var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js"));
3298var _drawer = _interopRequireDefault(__webpack_require__(/*! ./drawer.multicanvas */ "./src/drawer.multicanvas.js"));
3299var _webaudio = _interopRequireDefault(__webpack_require__(/*! ./webaudio */ "./src/webaudio.js"));
3300var _mediaelement = _interopRequireDefault(__webpack_require__(/*! ./mediaelement */ "./src/mediaelement.js"));
3301var _peakcache = _interopRequireDefault(__webpack_require__(/*! ./peakcache */ "./src/peakcache.js"));
3302var _mediaelementWebaudio = _interopRequireDefault(__webpack_require__(/*! ./mediaelement-webaudio */ "./src/mediaelement-webaudio.js"));
3303function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
3304function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
3305function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
3306function _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); }
3307function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
3308function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
3309function _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); }
3310function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
3311function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
3312function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
3313function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
3314function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
3315function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
3316function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
3317/*
3318 * This work is licensed under a BSD-3-Clause License.
3319 */
3320/** @external {HTMLElement} https://developer.mozilla.org/en/docs/Web/API/HTMLElement */
3321/** @external {OfflineAudioContext} https://developer.mozilla.org/en-US/docs/Web/API/OfflineAudioContext */
3322/** @external {File} https://developer.mozilla.org/en-US/docs/Web/API/File */
3323/** @external {Blob} https://developer.mozilla.org/en-US/docs/Web/API/Blob */
3324/** @external {CanvasRenderingContext2D} https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D */
3325/** @external {MediaStreamConstraints} https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints */
3326/** @external {AudioNode} https://developer.mozilla.org/de/docs/Web/API/AudioNode */
3327/**
3328 * @typedef {Object} WavesurferParams
3329 * @property {AudioContext} audioContext=null Use your own previously
3330 * initialized AudioContext or leave blank.
3331 * @property {number} audioRate=1 Speed at which to play audio. Lower number is
3332 * slower.
3333 * @property {ScriptProcessorNode} audioScriptProcessor=null Use your own previously
3334 * initialized ScriptProcessorNode or leave blank.
3335 * @property {boolean} autoCenter=true If a scrollbar is present, center the
3336 * waveform on current progress
3337 * @property {number} autoCenterRate=5 If autoCenter is active, rate at which the
3338 * waveform is centered
3339 * @property {boolean} autoCenterImmediately=false If autoCenter is active, immediately
3340 * center waveform on current progress
3341 * @property {string} backend='WebAudio' `'WebAudio'|'MediaElement'|'MediaElementWebAudio'` In most cases
3342 * you don't have to set this manually. MediaElement is a fallback for unsupported browsers.
3343 * MediaElementWebAudio allows to use WebAudio API also with big audio files, loading audio like with
3344 * MediaElement backend (HTML5 audio tag). You have to use the same methods of MediaElement backend for loading and
3345 * playback, giving also peaks, so the audio data are not decoded. In this way you can use WebAudio features, like filters,
3346 * also with audio with big duration. For example:
3347 * ` wavesurfer.load(url | HTMLMediaElement, peaks, preload, duration);
3348 * wavesurfer.play();
3349 * wavesurfer.setFilter(customFilter);
3350 * `
3351 * @property {string} backgroundColor=null Change background color of the
3352 * waveform container.
3353 * @property {number} barHeight=1 The height of the wave bars.
3354 * @property {number} barRadius=0 The radius of the wave bars. Makes bars rounded
3355 * @property {number} barGap=null The optional spacing between bars of the wave,
3356 * if not provided will be calculated in legacy format.
3357 * @property {number} barWidth=null Draw the waveform using bars.
3358 * @property {number} barMinHeight=null If specified, draw at least a bar of this height,
3359 * eliminating waveform gaps
3360 * @property {boolean} closeAudioContext=false Close and nullify all audio
3361 * contexts when the destroy method is called.
3362 * @property {!string|HTMLElement} container CSS selector or HTML element where
3363 * the waveform should be drawn. This is the only required parameter.
3364 * @property {string} cursorColor='#333' The fill color of the cursor indicating
3365 * the playhead position.
3366 * @property {number} cursorWidth=1 Measured in pixels.
3367 * @property {object} drawingContextAttributes={desynchronized: false} Drawing context
3368 * attributes.
3369 * @property {number} duration=null Optional audio length so pre-rendered peaks
3370 * can be display immediately for example.
3371 * @property {boolean} fillParent=true Whether to fill the entire container or
3372 * draw only according to `minPxPerSec`.
3373 * @property {boolean} forceDecode=false Force decoding of audio using web audio
3374 * when zooming to get a more detailed waveform.
3375 * @property {number} height=128 The height of the waveform. Measured in
3376 * pixels.
3377 * @property {boolean} hideScrollbar=false Whether to hide the horizontal
3378 * scrollbar when one would normally be shown.
3379 * @property {boolean} hideCursor=false Whether to hide the mouse cursor
3380 * when one would normally be shown by default.
3381 * @property {boolean} ignoreSilenceMode=false If true, ignores device silence mode
3382 * when using the `WebAudio` backend.
3383 * @property {boolean} interact=true Whether the mouse interaction will be
3384 * enabled at initialization. You can switch this parameter at any time later
3385 * on.
3386 * @property {boolean} loopSelection=true (Use with regions plugin) Enable
3387 * looping of selected regions
3388 * @property {number} maxCanvasWidth=4000 Maximum width of a single canvas in
3389 * pixels, excluding a small overlap (2 * `pixelRatio`, rounded up to the next
3390 * even integer). If the waveform is longer than this value, additional canvases
3391 * will be used to render the waveform, which is useful for very large waveforms
3392 * that may be too wide for browsers to draw on a single canvas.
3393 * @property {boolean} mediaControls=false (Use with backend `MediaElement` or `MediaElementWebAudio`)
3394 * this enables the native controls for the media element
3395 * @property {string} mediaType='audio' (Use with backend `MediaElement` or `MediaElementWebAudio`)
3396 * `'audio'|'video'` ('video' only for `MediaElement`)
3397 * @property {number} minPxPerSec=20 Minimum number of pixels per second of
3398 * audio.
3399 * @property {boolean} normalize=false If true, normalize by the maximum peak
3400 * instead of 1.0.
3401 * @property {boolean} partialRender=false Use the PeakCache to improve
3402 * rendering speed of large waveforms
3403 * @property {number} pixelRatio=window.devicePixelRatio The pixel ratio used to
3404 * calculate display
3405 * @property {PluginDefinition[]} plugins=[] An array of plugin definitions to
3406 * register during instantiation, they will be directly initialised unless they
3407 * are added with the `deferInit` property set to true.
3408 * @property {string} progressColor='#555' The fill color of the part of the
3409 * waveform behind the cursor. When `progressColor` and `waveColor` are the same
3410 * the progress wave is not rendered at all.
3411 * @property {boolean} removeMediaElementOnDestroy=true Set to false to keep the
3412 * media element in the DOM when the player is destroyed. This is useful when
3413 * reusing an existing media element via the `loadMediaElement` method.
3414 * @property {Object} renderer=MultiCanvas Can be used to inject a custom
3415 * renderer.
3416 * @property {boolean|number} responsive=false If set to `true` resize the
3417 * waveform, when the window is resized. This is debounced with a `100ms`
3418 * timeout by default. If this parameter is a number it represents that timeout.
3419 * @property {boolean} rtl=false If set to `true`, renders waveform from
3420 * right-to-left.
3421 * @property {boolean} scrollParent=false Whether to scroll the container with a
3422 * lengthy waveform. Otherwise the waveform is shrunk to the container width
3423 * (see fillParent).
3424 * @property {number} skipLength=2 Number of seconds to skip with the
3425 * skipForward() and skipBackward() methods.
3426 * @property {boolean} splitChannels=false Render with separate waveforms for
3427 * the channels of the audio
3428 * @property {SplitChannelOptions} splitChannelsOptions={} Options for splitChannel rendering
3429 * @property {boolean} vertical=false Render the waveform vertically instead of horizontally.
3430 * @property {string} waveColor='#999' The fill color of the waveform after the
3431 * cursor.
3432 * @property {object} xhr={} XHR options. For example:
3433 * `let xhr = {
3434 * cache: 'default',
3435 * mode: 'cors',
3436 * method: 'GET',
3437 * credentials: 'same-origin',
3438 * redirect: 'follow',
3439 * referrer: 'client',
3440 * requestHeaders: [
3441 * {
3442 * key: 'Authorization',
3443 * value: 'my-token'
3444 * }
3445 * ]
3446 * };`
3447 */
3448/**
3449 * @typedef {Object} PluginDefinition
3450 * @desc The Object used to describe a plugin
3451 * @example wavesurfer.addPlugin(pluginDefinition);
3452 * @property {string} name The name of the plugin, the plugin instance will be
3453 * added as a property to the wavesurfer instance under this name
3454 * @property {?Object} staticProps The properties that should be added to the
3455 * wavesurfer instance as static properties
3456 * @property {?boolean} deferInit Don't initialise plugin
3457 * automatically
3458 * @property {Object} params={} The plugin parameters, they are the first parameter
3459 * passed to the plugin class constructor function
3460 * @property {PluginClass} instance The plugin instance factory, is called with
3461 * the dependency specified in extends. Returns the plugin class.
3462 */
3463/**
3464 * @typedef {Object} SplitChannelOptions
3465 * @desc parameters applied when splitChannels option is true
3466 * @property {boolean} overlay=false determines whether channels are rendered on top of each other or on separate tracks
3467 * @property {object} channelColors={} object describing color for each channel. Example:
3468 * {
3469 * 0: {
3470 * progressColor: 'green',
3471 * waveColor: 'pink'
3472 * },
3473 * 1: {
3474 * progressColor: 'orange',
3475 * waveColor: 'purple'
3476 * }
3477 * }
3478 * @property {number[]} filterChannels=[] indexes of channels to be hidden from rendering
3479 * @property {boolean} relativeNormalization=false determines whether
3480 * normalization is done per channel or maintains proportionality between
3481 * channels. Only applied when normalize and splitChannels are both true.
3482 * @property {boolean} splitDragSelection=false determines if drag selection in regions
3483 * plugin works separately on each channel or only one selection for all channels
3484 * @since 4.3.0
3485 */
3486/**
3487 * @interface PluginClass
3488 *
3489 * @desc This is the interface which is implemented by all plugin classes. Note
3490 * that this only turns into an observer after being passed through
3491 * `wavesurfer.addPlugin`.
3492 *
3493 * @extends {Observer}
3494 */
3495var PluginClass = /*#__PURE__*/function () {
3496 /**
3497 * Construct the plugin
3498 *
3499 * @param {Object} params={} The plugin params (specific to the plugin)
3500 * @param {Object} ws The wavesurfer instance
3501 */
3502 function PluginClass(params, ws) {
3503 _classCallCheck(this, PluginClass);
3504 }
3505 /**
3506 * Initialise the plugin
3507 *
3508 * Start doing something. This is called by
3509 * `wavesurfer.initPlugin(pluginName)`
3510 */
3511 _createClass(PluginClass, [{
3512 key: "create",
3513 value:
3514 /**
3515 * Plugin definition factory
3516 *
3517 * This function must be used to create a plugin definition which can be
3518 * used by wavesurfer to correctly instantiate the plugin.
3519 *
3520 * It returns a `PluginDefinition` object representing the plugin.
3521 *
3522 * @param {Object} params={} The plugin params (specific to the plugin)
3523 */
3524 function create(params) {}
3525 }, {
3526 key: "init",
3527 value: function init() {}
3528 /**
3529 * Destroy the plugin instance
3530 *
3531 * Stop doing something. This is called by
3532 * `wavesurfer.destroyPlugin(pluginName)`
3533 */
3534 }, {
3535 key: "destroy",
3536 value: function destroy() {}
3537 }]);
3538 return PluginClass;
3539}();
3540/**
3541 * WaveSurfer core library class
3542 *
3543 * @extends {Observer}
3544 * @example
3545 * const params = {
3546 * container: '#waveform',
3547 * waveColor: 'violet',
3548 * progressColor: 'purple'
3549 * };
3550 *
3551 * // initialise like this
3552 * const wavesurfer = WaveSurfer.create(params);
3553 *
3554 * // or like this ...
3555 * const wavesurfer = new WaveSurfer(params);
3556 * wavesurfer.init();
3557 *
3558 * // load audio file
3559 * wavesurfer.load('example/media/demo.wav');
3560 */
3561var WaveSurfer = /*#__PURE__*/function (_util$Observer) {
3562 _inherits(WaveSurfer, _util$Observer);
3563 var _super = _createSuper(WaveSurfer);
3564 /**
3565 * Initialise wavesurfer instance
3566 *
3567 * @param {WavesurferParams} params Instantiation options for wavesurfer
3568 * @example
3569 * const wavesurfer = new WaveSurfer(params);
3570 * @returns {this} Wavesurfer instance
3571 */
3572 function WaveSurfer(params) {
3573 var _this;
3574 _classCallCheck(this, WaveSurfer);
3575 _this = _super.call(this);
3576 /**
3577 * Extract relevant parameters (or defaults)
3578 * @private
3579 */
3580 _defineProperty(_assertThisInitialized(_this), "defaultParams", {
3581 audioContext: null,
3582 audioScriptProcessor: null,
3583 audioRate: 1,
3584 autoCenter: true,
3585 autoCenterRate: 5,
3586 autoCenterImmediately: false,
3587 backend: 'WebAudio',
3588 backgroundColor: null,
3589 barHeight: 1,
3590 barRadius: 0,
3591 barGap: null,
3592 barMinHeight: null,
3593 container: null,
3594 cursorColor: '#333',
3595 cursorWidth: 1,
3596 dragSelection: true,
3597 drawingContextAttributes: {
3598 // Boolean that hints the user agent to reduce the latency
3599 // by desynchronizing the canvas paint cycle from the event
3600 // loop
3601 desynchronized: false
3602 },
3603 duration: null,
3604 fillParent: true,
3605 forceDecode: false,
3606 height: 128,
3607 hideScrollbar: false,
3608 hideCursor: false,
3609 ignoreSilenceMode: false,
3610 interact: true,
3611 loopSelection: true,
3612 maxCanvasWidth: 4000,
3613 mediaContainer: null,
3614 mediaControls: false,
3615 mediaType: 'audio',
3616 minPxPerSec: 20,
3617 normalize: false,
3618 partialRender: false,
3619 pixelRatio: window.devicePixelRatio || screen.deviceXDPI / screen.logicalXDPI,
3620 plugins: [],
3621 progressColor: '#555',
3622 removeMediaElementOnDestroy: true,
3623 renderer: _drawer.default,
3624 responsive: false,
3625 rtl: false,
3626 scrollParent: false,
3627 skipLength: 2,
3628 splitChannels: false,
3629 splitChannelsOptions: {
3630 overlay: false,
3631 channelColors: {},
3632 filterChannels: [],
3633 relativeNormalization: false,
3634 splitDragSelection: false
3635 },
3636 vertical: false,
3637 waveColor: '#999',
3638 xhr: {}
3639 });
3640 _defineProperty(_assertThisInitialized(_this), "backends", {
3641 MediaElement: _mediaelement.default,
3642 WebAudio: _webaudio.default,
3643 MediaElementWebAudio: _mediaelementWebaudio.default
3644 });
3645 _defineProperty(_assertThisInitialized(_this), "util", util);
3646 _this.params = Object.assign({}, _this.defaultParams, params);
3647 _this.params.splitChannelsOptions = Object.assign({}, _this.defaultParams.splitChannelsOptions, params.splitChannelsOptions);
3648 /** @private */
3649 _this.container = 'string' == typeof params.container ? document.querySelector(_this.params.container) : _this.params.container;
3650 if (!_this.container) {
3651 throw new Error('Container element not found');
3652 }
3653 if (_this.params.mediaContainer == null) {
3654 /** @private */
3655 _this.mediaContainer = _this.container;
3656 } else if (typeof _this.params.mediaContainer == 'string') {
3657 /** @private */
3658 _this.mediaContainer = document.querySelector(_this.params.mediaContainer);
3659 } else {
3660 /** @private */
3661 _this.mediaContainer = _this.params.mediaContainer;
3662 }
3663 if (!_this.mediaContainer) {
3664 throw new Error('Media Container element not found');
3665 }
3666 if (_this.params.maxCanvasWidth <= 1) {
3667 throw new Error('maxCanvasWidth must be greater than 1');
3668 } else if (_this.params.maxCanvasWidth % 2 == 1) {
3669 throw new Error('maxCanvasWidth must be an even number');
3670 }
3671 if (_this.params.rtl === true) {
3672 if (_this.params.vertical === true) {
3673 util.style(_this.container, {
3674 transform: 'rotateX(180deg)'
3675 });
3676 } else {
3677 util.style(_this.container, {
3678 transform: 'rotateY(180deg)'
3679 });
3680 }
3681 }
3682 if (_this.params.backgroundColor) {
3683 _this.setBackgroundColor(_this.params.backgroundColor);
3684 }
3685
3686 /**
3687 * @private Used to save the current volume when muting so we can
3688 * restore once unmuted
3689 * @type {number}
3690 */
3691 _this.savedVolume = 0;
3692
3693 /**
3694 * @private The current muted state
3695 * @type {boolean}
3696 */
3697 _this.isMuted = false;
3698
3699 /**
3700 * @private Will hold a list of event descriptors that need to be
3701 * canceled on subsequent loads of audio
3702 * @type {Object[]}
3703 */
3704 _this.tmpEvents = [];
3705
3706 /**
3707 * @private Holds any running audio downloads
3708 * @type {Observer}
3709 */
3710 _this.currentRequest = null;
3711 /** @private */
3712 _this.arraybuffer = null;
3713 /** @private */
3714 _this.drawer = null;
3715 /** @private */
3716 _this.backend = null;
3717 /** @private */
3718 _this.peakCache = null;
3719
3720 // cache constructor objects
3721 if (typeof _this.params.renderer !== 'function') {
3722 throw new Error('Renderer parameter is invalid');
3723 }
3724 /**
3725 * @private The uninitialised Drawer class
3726 */
3727 _this.Drawer = _this.params.renderer;
3728 /**
3729 * @private The uninitialised Backend class
3730 */
3731 // Back compat
3732 if (_this.params.backend == 'AudioElement') {
3733 _this.params.backend = 'MediaElement';
3734 }
3735 if ((_this.params.backend == 'WebAudio' || _this.params.backend === 'MediaElementWebAudio') && !_webaudio.default.prototype.supportsWebAudio.call(null)) {
3736 _this.params.backend = 'MediaElement';
3737 }
3738 _this.Backend = _this.backends[_this.params.backend];
3739
3740 /**
3741 * @private map of plugin names that are currently initialised
3742 */
3743 _this.initialisedPluginList = {};
3744 /** @private */
3745 _this.isDestroyed = false;
3746
3747 /**
3748 * Get the current ready status.
3749 *
3750 * @example const isReady = wavesurfer.isReady;
3751 * @return {boolean}
3752 */
3753 _this.isReady = false;
3754
3755 // responsive debounced event listener. If this.params.responsive is not
3756 // set, this is never called. Use 100ms or this.params.responsive as
3757 // timeout for the debounce function.
3758 var prevWidth = 0;
3759 _this._onResize = util.debounce(function () {
3760 if (_this.drawer.wrapper && prevWidth != _this.drawer.wrapper.clientWidth && !_this.params.scrollParent) {
3761 prevWidth = _this.drawer.wrapper.clientWidth;
3762 if (prevWidth) {
3763 // redraw only if waveform container is rendered and has a width
3764 _this.drawer.fireEvent('redraw');
3765 }
3766 }
3767 }, typeof _this.params.responsive === 'number' ? _this.params.responsive : 100);
3768 return _possibleConstructorReturn(_this, _assertThisInitialized(_this));
3769 }
3770
3771 /**
3772 * Initialise the wave
3773 *
3774 * @example
3775 * var wavesurfer = new WaveSurfer(params);
3776 * wavesurfer.init();
3777 * @return {this} The wavesurfer instance
3778 */
3779 _createClass(WaveSurfer, [{
3780 key: "init",
3781 value: function init() {
3782 this.registerPlugins(this.params.plugins);
3783 this.createDrawer();
3784 this.createBackend();
3785 this.createPeakCache();
3786 return this;
3787 }
3788
3789 /**
3790 * Add and initialise array of plugins (if `plugin.deferInit` is falsey),
3791 * this function is called in the init function of wavesurfer
3792 *
3793 * @param {PluginDefinition[]} plugins An array of plugin definitions
3794 * @emits {WaveSurfer#plugins-registered} Called with the array of plugin definitions
3795 * @return {this} The wavesurfer instance
3796 */
3797 }, {
3798 key: "registerPlugins",
3799 value: function registerPlugins(plugins) {
3800 var _this2 = this;
3801 // first instantiate all the plugins
3802 plugins.forEach(function (plugin) {
3803 return _this2.addPlugin(plugin);
3804 });
3805
3806 // now run the init functions
3807 plugins.forEach(function (plugin) {
3808 // call init function of the plugin if deferInit is falsey
3809 // in that case you would manually use initPlugins()
3810 if (!plugin.deferInit) {
3811 _this2.initPlugin(plugin.name);
3812 }
3813 });
3814 this.fireEvent('plugins-registered', plugins);
3815 return this;
3816 }
3817
3818 /**
3819 * Get a map of plugin names that are currently initialised
3820 *
3821 * @example wavesurfer.getPlugins();
3822 * @return {Object} Object with plugin names
3823 */
3824 }, {
3825 key: "getActivePlugins",
3826 value: function getActivePlugins() {
3827 return this.initialisedPluginList;
3828 }
3829
3830 /**
3831 * Add a plugin object to wavesurfer
3832 *
3833 * @param {PluginDefinition} plugin A plugin definition
3834 * @emits {WaveSurfer#plugin-added} Called with the name of the plugin that was added
3835 * @example wavesurfer.addPlugin(WaveSurfer.minimap());
3836 * @return {this} The wavesurfer instance
3837 */
3838 }, {
3839 key: "addPlugin",
3840 value: function addPlugin(plugin) {
3841 var _this3 = this;
3842 if (!plugin.name) {
3843 throw new Error('Plugin does not have a name!');
3844 }
3845 if (!plugin.instance) {
3846 throw new Error("Plugin ".concat(plugin.name, " does not have an instance property!"));
3847 }
3848
3849 // staticProps properties are applied to wavesurfer instance
3850 if (plugin.staticProps) {
3851 Object.keys(plugin.staticProps).forEach(function (pluginStaticProp) {
3852 /**
3853 * Properties defined in a plugin definition's `staticProps` property are added as
3854 * staticProps properties of the WaveSurfer instance
3855 */
3856 _this3[pluginStaticProp] = plugin.staticProps[pluginStaticProp];
3857 });
3858 }
3859 var Instance = plugin.instance;
3860
3861 // turn the plugin instance into an observer
3862 var observerPrototypeKeys = Object.getOwnPropertyNames(util.Observer.prototype);
3863 observerPrototypeKeys.forEach(function (key) {
3864 Instance.prototype[key] = util.Observer.prototype[key];
3865 });
3866
3867 /**
3868 * Instantiated plugin classes are added as a property of the wavesurfer
3869 * instance
3870 * @type {Object}
3871 */
3872 this[plugin.name] = new Instance(plugin.params || {}, this);
3873 this.fireEvent('plugin-added', plugin.name);
3874 return this;
3875 }
3876
3877 /**
3878 * Initialise a plugin
3879 *
3880 * @param {string} name A plugin name
3881 * @emits WaveSurfer#plugin-initialised
3882 * @example wavesurfer.initPlugin('minimap');
3883 * @return {this} The wavesurfer instance
3884 */
3885 }, {
3886 key: "initPlugin",
3887 value: function initPlugin(name) {
3888 if (!this[name]) {
3889 throw new Error("Plugin ".concat(name, " has not been added yet!"));
3890 }
3891 if (this.initialisedPluginList[name]) {
3892 // destroy any already initialised plugins
3893 this.destroyPlugin(name);
3894 }
3895 this[name].init();
3896 this.initialisedPluginList[name] = true;
3897 this.fireEvent('plugin-initialised', name);
3898 return this;
3899 }
3900
3901 /**
3902 * Destroy a plugin
3903 *
3904 * @param {string} name A plugin name
3905 * @emits WaveSurfer#plugin-destroyed
3906 * @example wavesurfer.destroyPlugin('minimap');
3907 * @returns {this} The wavesurfer instance
3908 */
3909 }, {
3910 key: "destroyPlugin",
3911 value: function destroyPlugin(name) {
3912 if (!this[name]) {
3913 throw new Error("Plugin ".concat(name, " has not been added yet and cannot be destroyed!"));
3914 }
3915 if (!this.initialisedPluginList[name]) {
3916 throw new Error("Plugin ".concat(name, " is not active and cannot be destroyed!"));
3917 }
3918 if (typeof this[name].destroy !== 'function') {
3919 throw new Error("Plugin ".concat(name, " does not have a destroy function!"));
3920 }
3921 this[name].destroy();
3922 delete this.initialisedPluginList[name];
3923 this.fireEvent('plugin-destroyed', name);
3924 return this;
3925 }
3926
3927 /**
3928 * Destroy all initialised plugins. Convenience function to use when
3929 * wavesurfer is removed
3930 *
3931 * @private
3932 */
3933 }, {
3934 key: "destroyAllPlugins",
3935 value: function destroyAllPlugins() {
3936 var _this4 = this;
3937 Object.keys(this.initialisedPluginList).forEach(function (name) {
3938 return _this4.destroyPlugin(name);
3939 });
3940 }
3941
3942 /**
3943 * Create the drawer and draw the waveform
3944 *
3945 * @private
3946 * @emits WaveSurfer#drawer-created
3947 */
3948 }, {
3949 key: "createDrawer",
3950 value: function createDrawer() {
3951 var _this5 = this;
3952 this.drawer = new this.Drawer(this.container, this.params);
3953 this.drawer.init();
3954 this.fireEvent('drawer-created', this.drawer);
3955 if (this.params.responsive !== false) {
3956 window.addEventListener('resize', this._onResize, true);
3957 window.addEventListener('orientationchange', this._onResize, true);
3958 }
3959 this.drawer.on('redraw', function () {
3960 _this5.drawBuffer();
3961 _this5.drawer.progress(_this5.backend.getPlayedPercents());
3962 });
3963
3964 // Click-to-seek
3965 this.drawer.on('click', function (e, progress) {
3966 setTimeout(function () {
3967 return _this5.seekTo(progress);
3968 }, 0);
3969 });
3970
3971 // Relay the scroll event from the drawer
3972 this.drawer.on('scroll', function (e) {
3973 if (_this5.params.partialRender) {
3974 _this5.drawBuffer();
3975 }
3976 _this5.fireEvent('scroll', e);
3977 });
3978 }
3979
3980 /**
3981 * Create the backend
3982 *
3983 * @private
3984 * @emits WaveSurfer#backend-created
3985 */
3986 }, {
3987 key: "createBackend",
3988 value: function createBackend() {
3989 var _this6 = this;
3990 if (this.backend) {
3991 this.backend.destroy();
3992 }
3993 this.backend = new this.Backend(this.params);
3994 this.backend.init();
3995 this.fireEvent('backend-created', this.backend);
3996 this.backend.on('finish', function () {
3997 _this6.drawer.progress(_this6.backend.getPlayedPercents());
3998 _this6.fireEvent('finish');
3999 });
4000 this.backend.on('play', function () {
4001 return _this6.fireEvent('play');
4002 });
4003 this.backend.on('pause', function () {
4004 return _this6.fireEvent('pause');
4005 });
4006 this.backend.on('audioprocess', function (time) {
4007 _this6.drawer.progress(_this6.backend.getPlayedPercents());
4008 _this6.fireEvent('audioprocess', time);
4009 });
4010
4011 // only needed for MediaElement and MediaElementWebAudio backend
4012 if (this.params.backend === 'MediaElement' || this.params.backend === 'MediaElementWebAudio') {
4013 this.backend.on('seek', function () {
4014 _this6.drawer.progress(_this6.backend.getPlayedPercents());
4015 });
4016 this.backend.on('volume', function () {
4017 var newVolume = _this6.getVolume();
4018 _this6.fireEvent('volume', newVolume);
4019 if (_this6.backend.isMuted !== _this6.isMuted) {
4020 _this6.isMuted = _this6.backend.isMuted;
4021 _this6.fireEvent('mute', _this6.isMuted);
4022 }
4023 });
4024 }
4025 }
4026
4027 /**
4028 * Create the peak cache
4029 *
4030 * @private
4031 */
4032 }, {
4033 key: "createPeakCache",
4034 value: function createPeakCache() {
4035 if (this.params.partialRender) {
4036 this.peakCache = new _peakcache.default();
4037 }
4038 }
4039
4040 /**
4041 * Get the duration of the audio clip
4042 *
4043 * @example const duration = wavesurfer.getDuration();
4044 * @return {number} Duration in seconds
4045 */
4046 }, {
4047 key: "getDuration",
4048 value: function getDuration() {
4049 return this.backend.getDuration();
4050 }
4051
4052 /**
4053 * Get the current playback position
4054 *
4055 * @example const currentTime = wavesurfer.getCurrentTime();
4056 * @return {number} Playback position in seconds
4057 */
4058 }, {
4059 key: "getCurrentTime",
4060 value: function getCurrentTime() {
4061 return this.backend.getCurrentTime();
4062 }
4063
4064 /**
4065 * Set the current play time in seconds.
4066 *
4067 * @param {number} seconds A positive number in seconds. E.g. 10 means 10
4068 * seconds, 60 means 1 minute
4069 */
4070 }, {
4071 key: "setCurrentTime",
4072 value: function setCurrentTime(seconds) {
4073 if (seconds >= this.getDuration()) {
4074 this.seekTo(1);
4075 } else {
4076 this.seekTo(seconds / this.getDuration());
4077 }
4078 }
4079
4080 /**
4081 * Starts playback from the current position. Optional start and end
4082 * measured in seconds can be used to set the range of audio to play.
4083 *
4084 * @param {?number} start Position to start at
4085 * @param {?number} end Position to end at
4086 * @emits WaveSurfer#interaction
4087 * @return {Promise} Result of the backend play method
4088 * @example
4089 * // play from second 1 to 5
4090 * wavesurfer.play(1, 5);
4091 */
4092 }, {
4093 key: "play",
4094 value: function play(start, end) {
4095 var _this7 = this;
4096 if (this.params.ignoreSilenceMode) {
4097 // ignores device hardware silence mode
4098 util.ignoreSilenceMode();
4099 }
4100 this.fireEvent('interaction', function () {
4101 return _this7.play(start, end);
4102 });
4103 return this.backend.play(start, end);
4104 }
4105
4106 /**
4107 * Set a point in seconds for playback to stop at.
4108 *
4109 * @param {number} position Position (in seconds) to stop at
4110 * @version 3.3.0
4111 */
4112 }, {
4113 key: "setPlayEnd",
4114 value: function setPlayEnd(position) {
4115 this.backend.setPlayEnd(position);
4116 }
4117
4118 /**
4119 * Stops and pauses playback
4120 *
4121 * @example wavesurfer.pause();
4122 * @return {Promise} Result of the backend pause method
4123 */
4124 }, {
4125 key: "pause",
4126 value: function pause() {
4127 if (!this.backend.isPaused()) {
4128 return this.backend.pause();
4129 }
4130 }
4131
4132 /**
4133 * Toggle playback
4134 *
4135 * @example wavesurfer.playPause();
4136 * @return {Promise} Result of the backend play or pause method
4137 */
4138 }, {
4139 key: "playPause",
4140 value: function playPause() {
4141 return this.backend.isPaused() ? this.play() : this.pause();
4142 }
4143
4144 /**
4145 * Get the current playback state
4146 *
4147 * @example const isPlaying = wavesurfer.isPlaying();
4148 * @return {boolean} False if paused, true if playing
4149 */
4150 }, {
4151 key: "isPlaying",
4152 value: function isPlaying() {
4153 return !this.backend.isPaused();
4154 }
4155
4156 /**
4157 * Skip backward
4158 *
4159 * @param {?number} seconds Amount to skip back, if not specified `skipLength`
4160 * is used
4161 * @example wavesurfer.skipBackward();
4162 */
4163 }, {
4164 key: "skipBackward",
4165 value: function skipBackward(seconds) {
4166 this.skip(-seconds || -this.params.skipLength);
4167 }
4168
4169 /**
4170 * Skip forward
4171 *
4172 * @param {?number} seconds Amount to skip back, if not specified `skipLength`
4173 * is used
4174 * @example wavesurfer.skipForward();
4175 */
4176 }, {
4177 key: "skipForward",
4178 value: function skipForward(seconds) {
4179 this.skip(seconds || this.params.skipLength);
4180 }
4181
4182 /**
4183 * Skip a number of seconds from the current position (use a negative value
4184 * to go backwards).
4185 *
4186 * @param {number} offset Amount to skip back or forwards
4187 * @example
4188 * // go back 2 seconds
4189 * wavesurfer.skip(-2);
4190 */
4191 }, {
4192 key: "skip",
4193 value: function skip(offset) {
4194 var duration = this.getDuration() || 1;
4195 var position = this.getCurrentTime() || 0;
4196 position = Math.max(0, Math.min(duration, position + (offset || 0)));
4197 this.seekAndCenter(position / duration);
4198 }
4199
4200 /**
4201 * Seeks to a position and centers the view
4202 *
4203 * @param {number} progress Between 0 (=beginning) and 1 (=end)
4204 * @example
4205 * // seek and go to the middle of the audio
4206 * wavesurfer.seekTo(0.5);
4207 */
4208 }, {
4209 key: "seekAndCenter",
4210 value: function seekAndCenter(progress) {
4211 this.seekTo(progress);
4212 this.drawer.recenter(progress);
4213 }
4214
4215 /**
4216 * Seeks to a position
4217 *
4218 * @param {number} progress Between 0 (=beginning) and 1 (=end)
4219 * @emits WaveSurfer#interaction
4220 * @emits WaveSurfer#seek
4221 * @example
4222 * // seek to the middle of the audio
4223 * wavesurfer.seekTo(0.5);
4224 */
4225 }, {
4226 key: "seekTo",
4227 value: function seekTo(progress) {
4228 var _this8 = this;
4229 // return an error if progress is not a number between 0 and 1
4230 if (typeof progress !== 'number' || !isFinite(progress) || progress < 0 || progress > 1) {
4231 throw new Error('Error calling wavesurfer.seekTo, parameter must be a number between 0 and 1!');
4232 }
4233 this.fireEvent('interaction', function () {
4234 return _this8.seekTo(progress);
4235 });
4236 var isWebAudioBackend = this.params.backend === 'WebAudio';
4237 var paused = this.backend.isPaused();
4238 if (isWebAudioBackend && !paused) {
4239 this.backend.pause();
4240 }
4241
4242 // avoid small scrolls while paused seeking
4243 var oldScrollParent = this.params.scrollParent;
4244 this.params.scrollParent = false;
4245 this.backend.seekTo(progress * this.getDuration());
4246 this.drawer.progress(progress);
4247 if (isWebAudioBackend && !paused) {
4248 this.backend.play();
4249 }
4250 this.params.scrollParent = oldScrollParent;
4251 this.fireEvent('seek', progress);
4252 }
4253
4254 /**
4255 * Stops and goes to the beginning.
4256 *
4257 * @example wavesurfer.stop();
4258 */
4259 }, {
4260 key: "stop",
4261 value: function stop() {
4262 this.pause();
4263 this.seekTo(0);
4264 this.drawer.progress(0);
4265 }
4266
4267 /**
4268 * Sets the ID of the audio device to use for output and returns a Promise.
4269 *
4270 * @param {string} deviceId String value representing underlying output
4271 * device
4272 * @returns {Promise} `Promise` that resolves to `undefined` when there are
4273 * no errors detected.
4274 */
4275 }, {
4276 key: "setSinkId",
4277 value: function setSinkId(deviceId) {
4278 return this.backend.setSinkId(deviceId);
4279 }
4280
4281 /**
4282 * Set the playback volume.
4283 *
4284 * @param {number} newVolume A value between 0 and 1, 0 being no
4285 * volume and 1 being full volume.
4286 * @emits WaveSurfer#volume
4287 */
4288 }, {
4289 key: "setVolume",
4290 value: function setVolume(newVolume) {
4291 this.backend.setVolume(newVolume);
4292 this.fireEvent('volume', newVolume);
4293 }
4294
4295 /**
4296 * Get the playback volume.
4297 *
4298 * @return {number} A value between 0 and 1, 0 being no
4299 * volume and 1 being full volume.
4300 */
4301 }, {
4302 key: "getVolume",
4303 value: function getVolume() {
4304 return this.backend.getVolume();
4305 }
4306
4307 /**
4308 * Set the playback rate.
4309 *
4310 * @param {number} rate A positive number. E.g. 0.5 means half the normal
4311 * speed, 2 means double speed and so on.
4312 * @example wavesurfer.setPlaybackRate(2);
4313 */
4314 }, {
4315 key: "setPlaybackRate",
4316 value: function setPlaybackRate(rate) {
4317 this.backend.setPlaybackRate(rate);
4318 }
4319
4320 /**
4321 * Get the playback rate.
4322 *
4323 * @return {number} The current playback rate.
4324 */
4325 }, {
4326 key: "getPlaybackRate",
4327 value: function getPlaybackRate() {
4328 return this.backend.getPlaybackRate();
4329 }
4330
4331 /**
4332 * Toggle the volume on and off. If not currently muted it will save the
4333 * current volume value and turn the volume off. If currently muted then it
4334 * will restore the volume to the saved value, and then rest the saved
4335 * value.
4336 *
4337 * @example wavesurfer.toggleMute();
4338 */
4339 }, {
4340 key: "toggleMute",
4341 value: function toggleMute() {
4342 this.setMute(!this.isMuted);
4343 }
4344
4345 /**
4346 * Enable or disable muted audio
4347 *
4348 * @param {boolean} mute Specify `true` to mute audio.
4349 * @emits WaveSurfer#volume
4350 * @emits WaveSurfer#mute
4351 * @example
4352 * // unmute
4353 * wavesurfer.setMute(false);
4354 * console.log(wavesurfer.getMute()) // logs false
4355 */
4356 }, {
4357 key: "setMute",
4358 value: function setMute(mute) {
4359 // ignore all muting requests if the audio is already in that state
4360 if (mute === this.isMuted) {
4361 this.fireEvent('mute', this.isMuted);
4362 return;
4363 }
4364 if (this.backend.setMute) {
4365 // Backends such as the MediaElement backend have their own handling
4366 // of mute, let them handle it.
4367 this.backend.setMute(mute);
4368 this.isMuted = mute;
4369 } else {
4370 if (mute) {
4371 // If currently not muted then save current volume,
4372 // turn off the volume and update the mute properties
4373 this.savedVolume = this.backend.getVolume();
4374 this.backend.setVolume(0);
4375 this.isMuted = true;
4376 this.fireEvent('volume', 0);
4377 } else {
4378 // If currently muted then restore to the saved volume
4379 // and update the mute properties
4380 this.backend.setVolume(this.savedVolume);
4381 this.isMuted = false;
4382 this.fireEvent('volume', this.savedVolume);
4383 }
4384 }
4385 this.fireEvent('mute', this.isMuted);
4386 }
4387
4388 /**
4389 * Get the current mute status.
4390 *
4391 * @example const isMuted = wavesurfer.getMute();
4392 * @return {boolean} Current mute status
4393 */
4394 }, {
4395 key: "getMute",
4396 value: function getMute() {
4397 return this.isMuted;
4398 }
4399
4400 /**
4401 * Get the list of current set filters as an array.
4402 *
4403 * Filters must be set with setFilters method first
4404 *
4405 * @return {array} List of enabled filters
4406 */
4407 }, {
4408 key: "getFilters",
4409 value: function getFilters() {
4410 return this.backend.filters || [];
4411 }
4412
4413 /**
4414 * Toggles `scrollParent` and redraws
4415 *
4416 * @example wavesurfer.toggleScroll();
4417 */
4418 }, {
4419 key: "toggleScroll",
4420 value: function toggleScroll() {
4421 this.params.scrollParent = !this.params.scrollParent;
4422 this.drawBuffer();
4423 }
4424
4425 /**
4426 * Toggle mouse interaction
4427 *
4428 * @example wavesurfer.toggleInteraction();
4429 */
4430 }, {
4431 key: "toggleInteraction",
4432 value: function toggleInteraction() {
4433 this.params.interact = !this.params.interact;
4434 }
4435
4436 /**
4437 * Get the fill color of the waveform after the cursor.
4438 *
4439 * @param {?number} channelIdx Optional index of the channel to get its wave color if splitChannels is true
4440 * @return {string|object} A CSS color string, or an array of CSS color strings.
4441 */
4442 }, {
4443 key: "getWaveColor",
4444 value: function getWaveColor() {
4445 var channelIdx = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
4446 if (this.params.splitChannelsOptions.channelColors[channelIdx]) {
4447 return this.params.splitChannelsOptions.channelColors[channelIdx].waveColor;
4448 }
4449 return this.params.waveColor;
4450 }
4451
4452 /**
4453 * Set the fill color of the waveform after the cursor.
4454 *
4455 * @param {string|object} color A CSS color string, or an array of CSS color strings.
4456 * @param {?number} channelIdx Optional index of the channel to set its wave color if splitChannels is true
4457 * @example wavesurfer.setWaveColor('#ddd');
4458 */
4459 }, {
4460 key: "setWaveColor",
4461 value: function setWaveColor(color) {
4462 var channelIdx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
4463 if (this.params.splitChannelsOptions.channelColors[channelIdx]) {
4464 this.params.splitChannelsOptions.channelColors[channelIdx].waveColor = color;
4465 } else {
4466 this.params.waveColor = color;
4467 }
4468 this.drawBuffer();
4469 }
4470
4471 /**
4472 * Get the fill color of the waveform behind the cursor.
4473 *
4474 * @param {?number} channelIdx Optional index of the channel to get its progress color if splitChannels is true
4475 * @return {string|object} A CSS color string, or an array of CSS color strings.
4476 */
4477 }, {
4478 key: "getProgressColor",
4479 value: function getProgressColor() {
4480 var channelIdx = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
4481 if (this.params.splitChannelsOptions.channelColors[channelIdx]) {
4482 return this.params.splitChannelsOptions.channelColors[channelIdx].progressColor;
4483 }
4484 return this.params.progressColor;
4485 }
4486
4487 /**
4488 * Set the fill color of the waveform behind the cursor.
4489 *
4490 * @param {string|object} color A CSS color string, or an array of CSS color strings.
4491 * @param {?number} channelIdx Optional index of the channel to set its progress color if splitChannels is true
4492 * @example wavesurfer.setProgressColor('#400');
4493 */
4494 }, {
4495 key: "setProgressColor",
4496 value: function setProgressColor(color, channelIdx) {
4497 if (this.params.splitChannelsOptions.channelColors[channelIdx]) {
4498 this.params.splitChannelsOptions.channelColors[channelIdx].progressColor = color;
4499 } else {
4500 this.params.progressColor = color;
4501 }
4502 this.drawBuffer();
4503 }
4504
4505 /**
4506 * Get the background color of the waveform container.
4507 *
4508 * @return {string} A CSS color string.
4509 */
4510 }, {
4511 key: "getBackgroundColor",
4512 value: function getBackgroundColor() {
4513 return this.params.backgroundColor;
4514 }
4515
4516 /**
4517 * Set the background color of the waveform container.
4518 *
4519 * @param {string} color A CSS color string.
4520 * @example wavesurfer.setBackgroundColor('#FF00FF');
4521 */
4522 }, {
4523 key: "setBackgroundColor",
4524 value: function setBackgroundColor(color) {
4525 this.params.backgroundColor = color;
4526 util.style(this.container, {
4527 background: this.params.backgroundColor
4528 });
4529 }
4530
4531 /**
4532 * Get the fill color of the cursor indicating the playhead
4533 * position.
4534 *
4535 * @return {string} A CSS color string.
4536 */
4537 }, {
4538 key: "getCursorColor",
4539 value: function getCursorColor() {
4540 return this.params.cursorColor;
4541 }
4542
4543 /**
4544 * Set the fill color of the cursor indicating the playhead
4545 * position.
4546 *
4547 * @param {string} color A CSS color string.
4548 * @example wavesurfer.setCursorColor('#222');
4549 */
4550 }, {
4551 key: "setCursorColor",
4552 value: function setCursorColor(color) {
4553 this.params.cursorColor = color;
4554 this.drawer.updateCursor();
4555 }
4556
4557 /**
4558 * Get the height of the waveform.
4559 *
4560 * @return {number} Height measured in pixels.
4561 */
4562 }, {
4563 key: "getHeight",
4564 value: function getHeight() {
4565 return this.params.height;
4566 }
4567
4568 /**
4569 * Set the height of the waveform.
4570 *
4571 * @param {number} height Height measured in pixels.
4572 * @example wavesurfer.setHeight(200);
4573 */
4574 }, {
4575 key: "setHeight",
4576 value: function setHeight(height) {
4577 this.params.height = height;
4578 this.drawer.setHeight(height * this.params.pixelRatio);
4579 this.drawBuffer();
4580 }
4581
4582 /**
4583 * Hide channels from being drawn on the waveform if splitting channels.
4584 *
4585 * For example, if we want to draw only the peaks for the right stereo channel:
4586 *
4587 * const wavesurfer = new WaveSurfer.create({...splitChannels: true});
4588 * wavesurfer.load('stereo_audio.mp3');
4589 *
4590 * wavesurfer.setFilteredChannel([0]); <-- hide left channel peaks.
4591 *
4592 * @param {array} channelIndices Channels to be filtered out from drawing.
4593 * @version 4.0.0
4594 */
4595 }, {
4596 key: "setFilteredChannels",
4597 value: function setFilteredChannels(channelIndices) {
4598 this.params.splitChannelsOptions.filterChannels = channelIndices;
4599 this.drawBuffer();
4600 }
4601
4602 /**
4603 * Get the correct peaks for current wave view-port and render wave
4604 *
4605 * @private
4606 * @emits WaveSurfer#redraw
4607 */
4608 }, {
4609 key: "drawBuffer",
4610 value: function drawBuffer() {
4611 var nominalWidth = Math.round(this.getDuration() * this.params.minPxPerSec * this.params.pixelRatio);
4612 var parentWidth = this.drawer.getWidth();
4613 var width = nominalWidth;
4614 // always start at 0 after zooming for scrolling : issue redraw left part
4615 var start = 0;
4616 var end = Math.max(start + parentWidth, width);
4617 // Fill container
4618 if (this.params.fillParent && (!this.params.scrollParent || nominalWidth < parentWidth)) {
4619 width = parentWidth;
4620 start = 0;
4621 end = width;
4622 }
4623 var peaks;
4624 if (this.params.partialRender) {
4625 var newRanges = this.peakCache.addRangeToPeakCache(width, start, end);
4626 var i;
4627 for (i = 0; i < newRanges.length; i++) {
4628 peaks = this.backend.getPeaks(width, newRanges[i][0], newRanges[i][1]);
4629 this.drawer.drawPeaks(peaks, width, newRanges[i][0], newRanges[i][1]);
4630 }
4631 } else {
4632 peaks = this.backend.getPeaks(width, start, end);
4633 this.drawer.drawPeaks(peaks, width, start, end);
4634 }
4635 this.fireEvent('redraw', peaks, width);
4636 }
4637
4638 /**
4639 * Horizontally zooms the waveform in and out. It also changes the parameter
4640 * `minPxPerSec` and enables the `scrollParent` option. Calling the function
4641 * with a falsey parameter will reset the zoom state.
4642 *
4643 * @param {?number} pxPerSec Number of horizontal pixels per second of
4644 * audio, if none is set the waveform returns to unzoomed state
4645 * @emits WaveSurfer#zoom
4646 * @example wavesurfer.zoom(20);
4647 */
4648 }, {
4649 key: "zoom",
4650 value: function zoom(pxPerSec) {
4651 if (!pxPerSec) {
4652 this.params.minPxPerSec = this.defaultParams.minPxPerSec;
4653 this.params.scrollParent = false;
4654 } else {
4655 this.params.minPxPerSec = pxPerSec;
4656 this.params.scrollParent = true;
4657 }
4658 this.drawBuffer();
4659 this.drawer.progress(this.backend.getPlayedPercents());
4660 this.drawer.recenter(this.getCurrentTime() / this.getDuration());
4661 this.fireEvent('zoom', pxPerSec);
4662 }
4663
4664 /**
4665 * Decode buffer and load
4666 *
4667 * @private
4668 * @param {ArrayBuffer} arraybuffer Buffer to process
4669 */
4670 }, {
4671 key: "loadArrayBuffer",
4672 value: function loadArrayBuffer(arraybuffer) {
4673 var _this9 = this;
4674 this.decodeArrayBuffer(arraybuffer, function (data) {
4675 if (!_this9.isDestroyed) {
4676 _this9.loadDecodedBuffer(data);
4677 }
4678 });
4679 }
4680
4681 /**
4682 * Directly load an externally decoded AudioBuffer
4683 *
4684 * @private
4685 * @param {AudioBuffer} buffer Buffer to process
4686 * @emits WaveSurfer#ready
4687 */
4688 }, {
4689 key: "loadDecodedBuffer",
4690 value: function loadDecodedBuffer(buffer) {
4691 this.backend.load(buffer);
4692 this.drawBuffer();
4693 this.isReady = true;
4694 this.fireEvent('ready');
4695 }
4696
4697 /**
4698 * Loads audio data from a Blob or File object
4699 *
4700 * @param {Blob|File} blob Audio data
4701 * @example
4702 */
4703 }, {
4704 key: "loadBlob",
4705 value: function loadBlob(blob) {
4706 var _this10 = this;
4707 // Create file reader
4708 var reader = new FileReader();
4709 reader.addEventListener('progress', function (e) {
4710 return _this10.onProgress(e);
4711 });
4712 reader.addEventListener('load', function (e) {
4713 return _this10.loadArrayBuffer(e.target.result);
4714 });
4715 reader.addEventListener('error', function () {
4716 return _this10.fireEvent('error', 'Error reading file');
4717 });
4718 reader.readAsArrayBuffer(blob);
4719 this.empty();
4720 }
4721
4722 /**
4723 * Loads audio and re-renders the waveform.
4724 *
4725 * @param {string|HTMLMediaElement} url The url of the audio file or the
4726 * audio element with the audio
4727 * @param {number[]|Number.<Array[]>} peaks Wavesurfer does not have to decode
4728 * the audio to render the waveform if this is specified
4729 * @param {?string} preload (Use with backend `MediaElement` and `MediaElementWebAudio`)
4730 * `'none'|'metadata'|'auto'` Preload attribute for the media element
4731 * @param {?number} duration The duration of the audio. This is used to
4732 * render the peaks data in the correct size for the audio duration (as
4733 * befits the current `minPxPerSec` and zoom value) without having to decode
4734 * the audio.
4735 * @returns {void}
4736 * @throws Will throw an error if the `url` argument is empty.
4737 * @example
4738 * // uses fetch or media element to load file (depending on backend)
4739 * wavesurfer.load('http://example.com/demo.wav');
4740 *
4741 * // setting preload attribute with media element backend and supplying
4742 * // peaks
4743 * wavesurfer.load(
4744 * 'http://example.com/demo.wav',
4745 * [0.0218, 0.0183, 0.0165, 0.0198, 0.2137, 0.2888],
4746 * true
4747 * );
4748 */
4749 }, {
4750 key: "load",
4751 value: function load(url, peaks, preload, duration) {
4752 if (!url) {
4753 throw new Error('url parameter cannot be empty');
4754 }
4755 this.empty();
4756 if (preload) {
4757 // check whether the preload attribute will be usable and if not log
4758 // a warning listing the reasons why not and nullify the variable
4759 var preloadIgnoreReasons = {
4760 "Preload is not 'auto', 'none' or 'metadata'": ['auto', 'metadata', 'none'].indexOf(preload) === -1,
4761 'Peaks are not provided': !peaks,
4762 "Backend is not of type 'MediaElement' or 'MediaElementWebAudio'": ['MediaElement', 'MediaElementWebAudio'].indexOf(this.params.backend) === -1,
4763 'Url is not of type string': typeof url !== 'string'
4764 };
4765 var activeReasons = Object.keys(preloadIgnoreReasons).filter(function (reason) {
4766 return preloadIgnoreReasons[reason];
4767 });
4768 if (activeReasons.length) {
4769 // eslint-disable-next-line no-console
4770 console.warn('Preload parameter of wavesurfer.load will be ignored because:\n\t- ' + activeReasons.join('\n\t- '));
4771 // stop invalid values from being used
4772 preload = null;
4773 }
4774 }
4775
4776 // loadBuffer(url, peaks, duration) requires that url is a string
4777 // but users can pass in a HTMLMediaElement to WaveSurfer
4778 if (this.params.backend === 'WebAudio' && url instanceof HTMLMediaElement) {
4779 url = url.src;
4780 }
4781 switch (this.params.backend) {
4782 case 'WebAudio':
4783 return this.loadBuffer(url, peaks, duration);
4784 case 'MediaElement':
4785 case 'MediaElementWebAudio':
4786 return this.loadMediaElement(url, peaks, preload, duration);
4787 }
4788 }
4789
4790 /**
4791 * Loads audio using Web Audio buffer backend.
4792 *
4793 * @private
4794 * @emits WaveSurfer#waveform-ready
4795 * @param {string} url URL of audio file
4796 * @param {number[]|Number.<Array[]>} peaks Peaks data
4797 * @param {?number} duration Optional duration of audio file
4798 * @returns {void}
4799 */
4800 }, {
4801 key: "loadBuffer",
4802 value: function loadBuffer(url, peaks, duration) {
4803 var _this11 = this;
4804 var load = function load(action) {
4805 if (action) {
4806 _this11.tmpEvents.push(_this11.once('ready', action));
4807 }
4808 return _this11.getArrayBuffer(url, function (data) {
4809 return _this11.loadArrayBuffer(data);
4810 });
4811 };
4812 if (peaks) {
4813 this.backend.setPeaks(peaks, duration);
4814 this.drawBuffer();
4815 this.fireEvent('waveform-ready');
4816 this.tmpEvents.push(this.once('interaction', load));
4817 } else {
4818 return load();
4819 }
4820 }
4821
4822 /**
4823 * Either create a media element, or load an existing media element.
4824 *
4825 * @private
4826 * @emits WaveSurfer#waveform-ready
4827 * @param {string|HTMLMediaElement} urlOrElt Either a path to a media file, or an
4828 * existing HTML5 Audio/Video Element
4829 * @param {number[]|Number.<Array[]>} peaks Array of peaks. Required to bypass web audio
4830 * dependency
4831 * @param {?boolean} preload Set to true if the preload attribute of the
4832 * audio element should be enabled
4833 * @param {?number} duration Optional duration of audio file
4834 */
4835 }, {
4836 key: "loadMediaElement",
4837 value: function loadMediaElement(urlOrElt, peaks, preload, duration) {
4838 var _this12 = this;
4839 var url = urlOrElt;
4840 if (typeof urlOrElt === 'string') {
4841 this.backend.load(url, this.mediaContainer, peaks, preload);
4842 } else {
4843 var elt = urlOrElt;
4844 this.backend.loadElt(elt, peaks);
4845
4846 // If peaks are not provided,
4847 // url = element.src so we can get peaks with web audio
4848 url = elt.src;
4849 }
4850 this.tmpEvents.push(this.backend.once('canplay', function () {
4851 // ignore when backend was already destroyed
4852 if (!_this12.backend.destroyed) {
4853 _this12.drawBuffer();
4854 _this12.isReady = true;
4855 _this12.fireEvent('ready');
4856 }
4857 }), this.backend.once('error', function (err) {
4858 return _this12.fireEvent('error', err);
4859 }));
4860
4861 // If peaks are provided, render them and fire the `waveform-ready` event.
4862 if (peaks) {
4863 this.backend.setPeaks(peaks, duration);
4864 this.drawBuffer();
4865 this.fireEvent('waveform-ready');
4866 }
4867
4868 // If no pre-decoded peaks are provided, or are provided with
4869 // forceDecode flag, attempt to download the audio file and decode it
4870 // with Web Audio.
4871 if ((!peaks || this.params.forceDecode) && this.backend.supportsWebAudio()) {
4872 this.getArrayBuffer(url, function (arraybuffer) {
4873 _this12.decodeArrayBuffer(arraybuffer, function (buffer) {
4874 _this12.backend.buffer = buffer;
4875 _this12.backend.setPeaks(null);
4876 _this12.drawBuffer();
4877 _this12.fireEvent('waveform-ready');
4878 });
4879 });
4880 }
4881 }
4882
4883 /**
4884 * Decode an array buffer and pass data to a callback
4885 *
4886 * @private
4887 * @param {Object} arraybuffer The array buffer to decode
4888 * @param {function} callback The function to call on complete
4889 */
4890 }, {
4891 key: "decodeArrayBuffer",
4892 value: function decodeArrayBuffer(arraybuffer, callback) {
4893 var _this13 = this;
4894 if (!this.isDestroyed) {
4895 this.arraybuffer = arraybuffer;
4896 this.backend.decodeArrayBuffer(arraybuffer, function (data) {
4897 // Only use the decoded data if we haven't been destroyed or
4898 // another decode started in the meantime
4899 if (!_this13.isDestroyed && _this13.arraybuffer == arraybuffer) {
4900 callback(data);
4901 _this13.arraybuffer = null;
4902 }
4903 }, function () {
4904 return _this13.fireEvent('error', 'Error decoding audiobuffer');
4905 });
4906 }
4907 }
4908
4909 /**
4910 * Load an array buffer using fetch and pass the result to a callback
4911 *
4912 * @param {string} url The URL of the file object
4913 * @param {function} callback The function to call on complete
4914 * @returns {util.fetchFile} fetch call
4915 * @private
4916 */
4917 }, {
4918 key: "getArrayBuffer",
4919 value: function getArrayBuffer(url, callback) {
4920 var _this14 = this;
4921 var options = Object.assign({
4922 url: url,
4923 responseType: 'arraybuffer'
4924 }, this.params.xhr);
4925 var request = util.fetchFile(options);
4926 this.currentRequest = request;
4927 this.tmpEvents.push(request.on('progress', function (e) {
4928 _this14.onProgress(e);
4929 }), request.on('success', function (data) {
4930 callback(data);
4931 _this14.currentRequest = null;
4932 }), request.on('error', function (e) {
4933 _this14.fireEvent('error', e);
4934 _this14.currentRequest = null;
4935 }));
4936 return request;
4937 }
4938
4939 /**
4940 * Called while the audio file is loading
4941 *
4942 * @private
4943 * @param {Event} e Progress event
4944 * @emits WaveSurfer#loading
4945 */
4946 }, {
4947 key: "onProgress",
4948 value: function onProgress(e) {
4949 var percentComplete;
4950 if (e.lengthComputable) {
4951 percentComplete = e.loaded / e.total;
4952 } else {
4953 // Approximate progress with an asymptotic
4954 // function, and assume downloads in the 1-3 MB range.
4955 percentComplete = e.loaded / (e.loaded + 1000000);
4956 }
4957 this.fireEvent('loading', Math.round(percentComplete * 100), e.target);
4958 }
4959
4960 /**
4961 * Exports PCM data into a JSON array and optionally opens in a new window
4962 * as valid JSON Blob instance.
4963 *
4964 * @param {number} length=1024 The scale in which to export the peaks
4965 * @param {number} accuracy=10000
4966 * @param {?boolean} noWindow Set to true to disable opening a new
4967 * window with the JSON
4968 * @param {number} start Start index
4969 * @param {number} end End index
4970 * @return {Promise} Promise that resolves with array of peaks
4971 */
4972 }, {
4973 key: "exportPCM",
4974 value: function exportPCM(length, accuracy, noWindow, start, end) {
4975 length = length || 1024;
4976 start = start || 0;
4977 accuracy = accuracy || 10000;
4978 noWindow = noWindow || false;
4979 var peaks = this.backend.getPeaks(length, start, end);
4980 var arr = [].map.call(peaks, function (val) {
4981 return Math.round(val * accuracy) / accuracy;
4982 });
4983 return new Promise(function (resolve, reject) {
4984 if (!noWindow) {
4985 var blobJSON = new Blob([JSON.stringify(arr)], {
4986 type: 'application/json;charset=utf-8'
4987 });
4988 var objURL = URL.createObjectURL(blobJSON);
4989 window.open(objURL);
4990 URL.revokeObjectURL(objURL);
4991 }
4992 resolve(arr);
4993 });
4994 }
4995
4996 /**
4997 * Save waveform image as data URI.
4998 *
4999 * The default format is `image/png`. Other supported types are
5000 * `image/jpeg` and `image/webp`.
5001 *
5002 * @param {string} format='image/png' A string indicating the image format.
5003 * The default format type is `image/png`.
5004 * @param {number} quality=1 A number between 0 and 1 indicating the image
5005 * quality to use for image formats that use lossy compression such as
5006 * `image/jpeg` and `image/webp`.
5007 * @param {string} type Image data type to return. Either `dataURL` (default)
5008 * or `blob`.
5009 * @return {string|string[]|Promise} When using `dataURL` type this returns
5010 * a single data URL or an array of data URLs, one for each canvas. When using
5011 * `blob` type this returns a `Promise` resolving with an array of `Blob`
5012 * instances, one for each canvas.
5013 */
5014 }, {
5015 key: "exportImage",
5016 value: function exportImage(format, quality, type) {
5017 if (!format) {
5018 format = 'image/png';
5019 }
5020 if (!quality) {
5021 quality = 1;
5022 }
5023 if (!type) {
5024 type = 'dataURL';
5025 }
5026 return this.drawer.getImage(format, quality, type);
5027 }
5028
5029 /**
5030 * Cancel any fetch request currently in progress
5031 */
5032 }, {
5033 key: "cancelAjax",
5034 value: function cancelAjax() {
5035 if (this.currentRequest && this.currentRequest.controller) {
5036 // If the current request has a ProgressHandler, then its ReadableStream might need to be cancelled too
5037 // See: Wavesurfer issue #2042
5038 // See Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1583815
5039 if (this.currentRequest._reader) {
5040 // Ignoring exceptions thrown by call to cancel()
5041 this.currentRequest._reader.cancel().catch(function (err) {});
5042 }
5043 this.currentRequest.controller.abort();
5044 this.currentRequest = null;
5045 }
5046 }
5047
5048 /**
5049 * @private
5050 */
5051 }, {
5052 key: "clearTmpEvents",
5053 value: function clearTmpEvents() {
5054 this.tmpEvents.forEach(function (e) {
5055 return e.un();
5056 });
5057 }
5058
5059 /**
5060 * Display empty waveform.
5061 */
5062 }, {
5063 key: "empty",
5064 value: function empty() {
5065 if (!this.backend.isPaused()) {
5066 this.stop();
5067 this.backend.disconnectSource();
5068 }
5069 this.isReady = false;
5070 this.cancelAjax();
5071 this.clearTmpEvents();
5072
5073 // empty drawer
5074 this.drawer.progress(0);
5075 this.drawer.setWidth(0);
5076 this.drawer.drawPeaks({
5077 length: this.drawer.getWidth()
5078 }, 0);
5079 }
5080
5081 /**
5082 * Remove events, elements and disconnect WebAudio nodes.
5083 *
5084 * @emits WaveSurfer#destroy
5085 */
5086 }, {
5087 key: "destroy",
5088 value: function destroy() {
5089 this.destroyAllPlugins();
5090 this.fireEvent('destroy');
5091 this.cancelAjax();
5092 this.clearTmpEvents();
5093 this.unAll();
5094 if (this.params.responsive !== false) {
5095 window.removeEventListener('resize', this._onResize, true);
5096 window.removeEventListener('orientationchange', this._onResize, true);
5097 }
5098 if (this.backend) {
5099 this.backend.destroy();
5100 // clears memory usage
5101 this.backend = null;
5102 }
5103 if (this.drawer) {
5104 this.drawer.destroy();
5105 }
5106 this.isDestroyed = true;
5107 this.isReady = false;
5108 this.arraybuffer = null;
5109 }
5110 }], [{
5111 key: "create",
5112 value: /** @private */
5113
5114 /** @private */
5115
5116 /**
5117 * Instantiate this class, call its `init` function and returns it
5118 *
5119 * @param {WavesurferParams} params The wavesurfer parameters
5120 * @return {Object} WaveSurfer instance
5121 * @example const wavesurfer = WaveSurfer.create(params);
5122 */
5123 function create(params) {
5124 var wavesurfer = new WaveSurfer(params);
5125 return wavesurfer.init();
5126 }
5127
5128 /**
5129 * The library version number is available as a static property of the
5130 * WaveSurfer class
5131 *
5132 * @type {String}
5133 * @example
5134 * console.log('Using wavesurfer.js ' + WaveSurfer.VERSION);
5135 */
5136 }]);
5137 return WaveSurfer;
5138}(util.Observer);
5139exports["default"] = WaveSurfer;
5140_defineProperty(WaveSurfer, "VERSION", "6.4.0");
5141_defineProperty(WaveSurfer, "util", util);
5142module.exports = exports.default;
5143
5144/***/ }),
5145
5146/***/ "./src/webaudio.js":
5147/*!*************************!*\
5148 !*** ./src/webaudio.js ***!
5149 \*************************/
5150/***/ ((module, exports, __webpack_require__) => {
5151
5152"use strict";
5153
5154
5155function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
5156Object.defineProperty(exports, "__esModule", ({
5157 value: true
5158}));
5159exports["default"] = void 0;
5160var util = _interopRequireWildcard(__webpack_require__(/*! ./util */ "./src/util/index.js"));
5161function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
5162function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
5163function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
5164function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
5165function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
5166function _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); }
5167function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
5168function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
5169function _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); }
5170function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
5171function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
5172function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
5173function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
5174// using constants to prevent someone writing the string wrong
5175var PLAYING = 'playing';
5176var PAUSED = 'paused';
5177var FINISHED = 'finished';
5178
5179/**
5180 * WebAudio backend
5181 *
5182 * @extends {Observer}
5183 */
5184var WebAudio = /*#__PURE__*/function (_util$Observer) {
5185 _inherits(WebAudio, _util$Observer);
5186 var _super = _createSuper(WebAudio);
5187 /**
5188 * Construct the backend
5189 *
5190 * @param {WavesurferParams} params Wavesurfer parameters
5191 */
5192 function WebAudio(params) {
5193 var _defineProperty2, _this$states;
5194 var _this;
5195 _classCallCheck(this, WebAudio);
5196 _this = _super.call(this);
5197 /** @private */
5198 _defineProperty(_assertThisInitialized(_this), "audioContext", null);
5199 _defineProperty(_assertThisInitialized(_this), "offlineAudioContext", null);
5200 _defineProperty(_assertThisInitialized(_this), "stateBehaviors", (_defineProperty2 = {}, _defineProperty(_defineProperty2, PLAYING, {
5201 init: function init() {
5202 this.addOnAudioProcess();
5203 },
5204 getPlayedPercents: function getPlayedPercents() {
5205 var duration = this.getDuration();
5206 return this.getCurrentTime() / duration || 0;
5207 },
5208 getCurrentTime: function getCurrentTime() {
5209 return this.startPosition + this.getPlayedTime();
5210 }
5211 }), _defineProperty(_defineProperty2, PAUSED, {
5212 init: function init() {
5213 this.removeOnAudioProcess();
5214 },
5215 getPlayedPercents: function getPlayedPercents() {
5216 var duration = this.getDuration();
5217 return this.getCurrentTime() / duration || 0;
5218 },
5219 getCurrentTime: function getCurrentTime() {
5220 return this.startPosition;
5221 }
5222 }), _defineProperty(_defineProperty2, FINISHED, {
5223 init: function init() {
5224 this.removeOnAudioProcess();
5225 this.fireEvent('finish');
5226 },
5227 getPlayedPercents: function getPlayedPercents() {
5228 return 1;
5229 },
5230 getCurrentTime: function getCurrentTime() {
5231 return this.getDuration();
5232 }
5233 }), _defineProperty2));
5234 _this.params = params;
5235 /** ac: Audio Context instance */
5236 _this.ac = params.audioContext || (_this.supportsWebAudio() ? _this.getAudioContext() : {});
5237 /**@private */
5238 _this.lastPlay = _this.ac.currentTime;
5239 /** @private */
5240 _this.startPosition = 0;
5241 /** @private */
5242 _this.scheduledPause = null;
5243 /** @private */
5244 _this.states = (_this$states = {}, _defineProperty(_this$states, PLAYING, Object.create(_this.stateBehaviors[PLAYING])), _defineProperty(_this$states, PAUSED, Object.create(_this.stateBehaviors[PAUSED])), _defineProperty(_this$states, FINISHED, Object.create(_this.stateBehaviors[FINISHED])), _this$states);
5245 /** @private */
5246 _this.buffer = null;
5247 /** @private */
5248 _this.filters = [];
5249 /** gainNode: allows to control audio volume */
5250 _this.gainNode = null;
5251 /** @private */
5252 _this.mergedPeaks = null;
5253 /** @private */
5254 _this.offlineAc = null;
5255 /** @private */
5256 _this.peaks = null;
5257 /** @private */
5258 _this.playbackRate = 1;
5259 /** analyser: provides audio analysis information */
5260 _this.analyser = null;
5261 /** scriptNode: allows processing audio */
5262 _this.scriptNode = null;
5263 /** @private */
5264 _this.source = null;
5265 /** @private */
5266 _this.splitPeaks = [];
5267 /** @private */
5268 _this.state = null;
5269 /** @private */
5270 _this.explicitDuration = params.duration;
5271 /** @private */
5272 _this.sinkStreamDestination = null;
5273 /** @private */
5274 _this.sinkAudioElement = null;
5275 /**
5276 * Boolean indicating if the backend was destroyed.
5277 */
5278 _this.destroyed = false;
5279 return _this;
5280 }
5281
5282 /**
5283 * Initialise the backend, called in `wavesurfer.createBackend()`
5284 */
5285 _createClass(WebAudio, [{
5286 key: "supportsWebAudio",
5287 value: /** scriptBufferSize: size of the processing buffer */
5288
5289 /** audioContext: allows to process audio with WebAudio API */
5290
5291 /** @private */
5292
5293 /** @private */
5294
5295 /**
5296 * Does the browser support this backend
5297 *
5298 * @return {boolean} Whether or not this browser supports this backend
5299 */
5300 function supportsWebAudio() {
5301 return !!(window.AudioContext || window.webkitAudioContext);
5302 }
5303
5304 /**
5305 * Get the audio context used by this backend or create one
5306 *
5307 * @return {AudioContext} Existing audio context, or creates a new one
5308 */
5309 }, {
5310 key: "getAudioContext",
5311 value: function getAudioContext() {
5312 if (!window.WaveSurferAudioContext) {
5313 window.WaveSurferAudioContext = new (window.AudioContext || window.webkitAudioContext)();
5314 }
5315 return window.WaveSurferAudioContext;
5316 }
5317
5318 /**
5319 * Get the offline audio context used by this backend or create one
5320 *
5321 * @param {number} sampleRate The sample rate to use
5322 * @return {OfflineAudioContext} Existing offline audio context, or creates
5323 * a new one
5324 */
5325 }, {
5326 key: "getOfflineAudioContext",
5327 value: function getOfflineAudioContext(sampleRate) {
5328 if (!window.WaveSurferOfflineAudioContext) {
5329 window.WaveSurferOfflineAudioContext = new (window.OfflineAudioContext || window.webkitOfflineAudioContext)(1, 2, sampleRate);
5330 }
5331 return window.WaveSurferOfflineAudioContext;
5332 }
5333 }, {
5334 key: "init",
5335 value: function init() {
5336 this.createVolumeNode();
5337 this.createScriptNode();
5338 this.createAnalyserNode();
5339 this.setState(PAUSED);
5340 this.setPlaybackRate(this.params.audioRate);
5341 this.setLength(0);
5342 }
5343
5344 /** @private */
5345 }, {
5346 key: "disconnectFilters",
5347 value: function disconnectFilters() {
5348 if (this.filters) {
5349 this.filters.forEach(function (filter) {
5350 filter && filter.disconnect();
5351 });
5352 this.filters = null;
5353 // Reconnect direct path
5354 this.analyser.connect(this.gainNode);
5355 }
5356 }
5357
5358 /**
5359 * @private
5360 *
5361 * @param {string} state The new state
5362 */
5363 }, {
5364 key: "setState",
5365 value: function setState(state) {
5366 if (this.state !== this.states[state]) {
5367 this.state = this.states[state];
5368 this.state.init.call(this);
5369 }
5370 }
5371
5372 /**
5373 * Unpacked `setFilters()`
5374 *
5375 * @param {...AudioNode} filters One or more filters to set
5376 */
5377 }, {
5378 key: "setFilter",
5379 value: function setFilter() {
5380 for (var _len = arguments.length, filters = new Array(_len), _key = 0; _key < _len; _key++) {
5381 filters[_key] = arguments[_key];
5382 }
5383 this.setFilters(filters);
5384 }
5385
5386 /**
5387 * Insert custom Web Audio nodes into the graph
5388 *
5389 * @param {AudioNode[]} filters Packed filters array
5390 * @example
5391 * const lowpass = wavesurfer.backend.ac.createBiquadFilter();
5392 * wavesurfer.backend.setFilter(lowpass);
5393 */
5394 }, {
5395 key: "setFilters",
5396 value: function setFilters(filters) {
5397 // Remove existing filters
5398 this.disconnectFilters();
5399
5400 // Insert filters if filter array not empty
5401 if (filters && filters.length) {
5402 this.filters = filters;
5403
5404 // Disconnect direct path before inserting filters
5405 this.analyser.disconnect();
5406
5407 // Connect each filter in turn
5408 filters.reduce(function (prev, curr) {
5409 prev.connect(curr);
5410 return curr;
5411 }, this.analyser).connect(this.gainNode);
5412 }
5413 }
5414 /** Create ScriptProcessorNode to process audio */
5415 }, {
5416 key: "createScriptNode",
5417 value: function createScriptNode() {
5418 if (this.params.audioScriptProcessor) {
5419 this.scriptNode = this.params.audioScriptProcessor;
5420 } else {
5421 if (this.ac.createScriptProcessor) {
5422 this.scriptNode = this.ac.createScriptProcessor(WebAudio.scriptBufferSize);
5423 } else {
5424 this.scriptNode = this.ac.createJavaScriptNode(WebAudio.scriptBufferSize);
5425 }
5426 }
5427 this.scriptNode.connect(this.ac.destination);
5428 }
5429
5430 /** @private */
5431 }, {
5432 key: "addOnAudioProcess",
5433 value: function addOnAudioProcess() {
5434 var _this2 = this;
5435 this.scriptNode.onaudioprocess = function () {
5436 var time = _this2.getCurrentTime();
5437 if (time >= _this2.getDuration()) {
5438 _this2.setState(FINISHED);
5439 _this2.fireEvent('pause');
5440 } else if (time >= _this2.scheduledPause) {
5441 _this2.pause();
5442 } else if (_this2.state === _this2.states[PLAYING]) {
5443 _this2.fireEvent('audioprocess', time);
5444 }
5445 };
5446 }
5447
5448 /** @private */
5449 }, {
5450 key: "removeOnAudioProcess",
5451 value: function removeOnAudioProcess() {
5452 this.scriptNode.onaudioprocess = null;
5453 }
5454 /** Create analyser node to perform audio analysis */
5455 }, {
5456 key: "createAnalyserNode",
5457 value: function createAnalyserNode() {
5458 this.analyser = this.ac.createAnalyser();
5459 this.analyser.connect(this.gainNode);
5460 }
5461
5462 /**
5463 * Create the gain node needed to control the playback volume.
5464 *
5465 */
5466 }, {
5467 key: "createVolumeNode",
5468 value: function createVolumeNode() {
5469 // Create gain node using the AudioContext
5470 if (this.ac.createGain) {
5471 this.gainNode = this.ac.createGain();
5472 } else {
5473 this.gainNode = this.ac.createGainNode();
5474 }
5475 // Add the gain node to the graph
5476 this.gainNode.connect(this.ac.destination);
5477 }
5478
5479 /**
5480 * Set the sink id for the media player
5481 *
5482 * @param {string} deviceId String value representing audio device id.
5483 * @returns {Promise} A Promise that resolves to `undefined` when there
5484 * are no errors.
5485 */
5486 }, {
5487 key: "setSinkId",
5488 value: function setSinkId(deviceId) {
5489 if (deviceId) {
5490 /**
5491 * The webaudio API doesn't currently support setting the device
5492 * output. Here we create an HTMLAudioElement, connect the
5493 * webaudio stream to that element and setSinkId there.
5494 */
5495 if (!this.sinkAudioElement) {
5496 this.sinkAudioElement = new window.Audio();
5497 // autoplay is necessary since we're not invoking .play()
5498 this.sinkAudioElement.autoplay = true;
5499 }
5500 if (!this.sinkAudioElement.setSinkId) {
5501 return Promise.reject(new Error('setSinkId is not supported in your browser'));
5502 }
5503 if (!this.sinkStreamDestination) {
5504 this.sinkStreamDestination = this.ac.createMediaStreamDestination();
5505 }
5506 this.gainNode.disconnect();
5507 this.gainNode.connect(this.sinkStreamDestination);
5508 this.sinkAudioElement.srcObject = this.sinkStreamDestination.stream;
5509 return this.sinkAudioElement.setSinkId(deviceId);
5510 } else {
5511 return Promise.reject(new Error('Invalid deviceId: ' + deviceId));
5512 }
5513 }
5514
5515 /**
5516 * Set the audio volume
5517 *
5518 * @param {number} value A floating point value between 0 and 1.
5519 */
5520 }, {
5521 key: "setVolume",
5522 value: function setVolume(value) {
5523 this.gainNode.gain.setValueAtTime(value, this.ac.currentTime);
5524 }
5525
5526 /**
5527 * Get the current volume
5528 *
5529 * @return {number} value A floating point value between 0 and 1.
5530 */
5531 }, {
5532 key: "getVolume",
5533 value: function getVolume() {
5534 return this.gainNode.gain.value;
5535 }
5536
5537 /**
5538 * Decode an array buffer and pass data to a callback
5539 *
5540 * @private
5541 * @param {ArrayBuffer} arraybuffer The array buffer to decode
5542 * @param {function} callback The function to call on complete.
5543 * @param {function} errback The function to call on error.
5544 */
5545 }, {
5546 key: "decodeArrayBuffer",
5547 value: function decodeArrayBuffer(arraybuffer, callback, errback) {
5548 if (!this.offlineAc) {
5549 this.offlineAc = this.getOfflineAudioContext(this.ac && this.ac.sampleRate ? this.ac.sampleRate : 44100);
5550 }
5551 if ('webkitAudioContext' in window) {
5552 // Safari: no support for Promise-based decodeAudioData enabled
5553 // Enable it in Safari using the Experimental Features > Modern WebAudio API option
5554 this.offlineAc.decodeAudioData(arraybuffer, function (data) {
5555 return callback(data);
5556 }, errback);
5557 } else {
5558 this.offlineAc.decodeAudioData(arraybuffer).then(function (data) {
5559 return callback(data);
5560 }).catch(function (err) {
5561 return errback(err);
5562 });
5563 }
5564 }
5565
5566 /**
5567 * Set pre-decoded peaks
5568 *
5569 * @param {number[]|Number.<Array[]>} peaks Peaks data
5570 * @param {?number} duration Explicit duration
5571 */
5572 }, {
5573 key: "setPeaks",
5574 value: function setPeaks(peaks, duration) {
5575 if (duration != null) {
5576 this.explicitDuration = duration;
5577 }
5578 this.peaks = peaks;
5579 }
5580
5581 /**
5582 * Set the rendered length (different from the length of the audio)
5583 *
5584 * @param {number} length The rendered length
5585 */
5586 }, {
5587 key: "setLength",
5588 value: function setLength(length) {
5589 // No resize, we can preserve the cached peaks.
5590 if (this.mergedPeaks && length == 2 * this.mergedPeaks.length - 1 + 2) {
5591 return;
5592 }
5593 this.splitPeaks = [];
5594 this.mergedPeaks = [];
5595 // Set the last element of the sparse array so the peak arrays are
5596 // appropriately sized for other calculations.
5597 var channels = this.buffer ? this.buffer.numberOfChannels : 1;
5598 var c;
5599 for (c = 0; c < channels; c++) {
5600 this.splitPeaks[c] = [];
5601 this.splitPeaks[c][2 * (length - 1)] = 0;
5602 this.splitPeaks[c][2 * (length - 1) + 1] = 0;
5603 }
5604 this.mergedPeaks[2 * (length - 1)] = 0;
5605 this.mergedPeaks[2 * (length - 1) + 1] = 0;
5606 }
5607
5608 /**
5609 * Compute the max and min value of the waveform when broken into <length> subranges.
5610 *
5611 * @param {number} length How many subranges to break the waveform into.
5612 * @param {number} first First sample in the required range.
5613 * @param {number} last Last sample in the required range.
5614 * @return {number[]|Number.<Array[]>} Array of 2*<length> peaks or array of arrays of
5615 * peaks consisting of (max, min) values for each subrange.
5616 */
5617 }, {
5618 key: "getPeaks",
5619 value: function getPeaks(length, first, last) {
5620 if (this.peaks) {
5621 return this.peaks;
5622 }
5623 if (!this.buffer) {
5624 return [];
5625 }
5626 first = first || 0;
5627 last = last || length - 1;
5628 this.setLength(length);
5629 if (!this.buffer) {
5630 return this.params.splitChannels ? this.splitPeaks : this.mergedPeaks;
5631 }
5632
5633 /**
5634 * The following snippet fixes a buffering data issue on the Safari
5635 * browser which returned undefined It creates the missing buffer based
5636 * on 1 channel, 4096 samples and the sampleRate from the current
5637 * webaudio context 4096 samples seemed to be the best fit for rendering
5638 * will review this code once a stable version of Safari TP is out
5639 */
5640 if (!this.buffer.length) {
5641 var newBuffer = this.createBuffer(1, 4096, this.sampleRate);
5642 this.buffer = newBuffer.buffer;
5643 }
5644 var sampleSize = this.buffer.length / length;
5645 var sampleStep = ~~(sampleSize / 10) || 1;
5646 var channels = this.buffer.numberOfChannels;
5647 var c;
5648 for (c = 0; c < channels; c++) {
5649 var peaks = this.splitPeaks[c];
5650 var chan = this.buffer.getChannelData(c);
5651 var i = void 0;
5652 for (i = first; i <= last; i++) {
5653 var start = ~~(i * sampleSize);
5654 var end = ~~(start + sampleSize);
5655 /**
5656 * Initialize the max and min to the first sample of this
5657 * subrange, so that even if the samples are entirely
5658 * on one side of zero, we still return the true max and
5659 * min values in the subrange.
5660 */
5661 var min = chan[start];
5662 var max = min;
5663 var j = void 0;
5664 for (j = start; j < end; j += sampleStep) {
5665 var value = chan[j];
5666 if (value > max) {
5667 max = value;
5668 }
5669 if (value < min) {
5670 min = value;
5671 }
5672 }
5673 peaks[2 * i] = max;
5674 peaks[2 * i + 1] = min;
5675 if (c == 0 || max > this.mergedPeaks[2 * i]) {
5676 this.mergedPeaks[2 * i] = max;
5677 }
5678 if (c == 0 || min < this.mergedPeaks[2 * i + 1]) {
5679 this.mergedPeaks[2 * i + 1] = min;
5680 }
5681 }
5682 }
5683 return this.params.splitChannels ? this.splitPeaks : this.mergedPeaks;
5684 }
5685
5686 /**
5687 * Get the position from 0 to 1
5688 *
5689 * @return {number} Position
5690 */
5691 }, {
5692 key: "getPlayedPercents",
5693 value: function getPlayedPercents() {
5694 return this.state.getPlayedPercents.call(this);
5695 }
5696
5697 /** @private */
5698 }, {
5699 key: "disconnectSource",
5700 value: function disconnectSource() {
5701 if (this.source) {
5702 this.source.disconnect();
5703 }
5704 }
5705 /**
5706 * Destroy all references with WebAudio, disconnecting audio nodes and closing Audio Context
5707 */
5708 }, {
5709 key: "destroyWebAudio",
5710 value: function destroyWebAudio() {
5711 this.disconnectFilters();
5712 this.disconnectSource();
5713 this.gainNode.disconnect();
5714 this.scriptNode.disconnect();
5715 this.analyser.disconnect();
5716
5717 // close the audioContext if closeAudioContext option is set to true
5718 if (this.params.closeAudioContext) {
5719 // check if browser supports AudioContext.close()
5720 if (typeof this.ac.close === 'function' && this.ac.state != 'closed') {
5721 this.ac.close();
5722 }
5723 // clear the reference to the audiocontext
5724 this.ac = null;
5725 // clear the actual audiocontext, either passed as param or the
5726 // global singleton
5727 if (!this.params.audioContext) {
5728 window.WaveSurferAudioContext = null;
5729 } else {
5730 this.params.audioContext = null;
5731 }
5732 // clear the offlineAudioContext
5733 window.WaveSurferOfflineAudioContext = null;
5734 }
5735
5736 // disconnect resources used by setSinkId
5737 if (this.sinkStreamDestination) {
5738 this.sinkAudioElement.pause();
5739 this.sinkAudioElement.srcObject = null;
5740 this.sinkStreamDestination.disconnect();
5741 this.sinkStreamDestination = null;
5742 }
5743 }
5744 /**
5745 * This is called when wavesurfer is destroyed
5746 */
5747 }, {
5748 key: "destroy",
5749 value: function destroy() {
5750 if (!this.isPaused()) {
5751 this.pause();
5752 }
5753 this.unAll();
5754 this.buffer = null;
5755 this.destroyed = true;
5756 this.destroyWebAudio();
5757 }
5758
5759 /**
5760 * Loaded a decoded audio buffer
5761 *
5762 * @param {Object} buffer Decoded audio buffer to load
5763 */
5764 }, {
5765 key: "load",
5766 value: function load(buffer) {
5767 this.startPosition = 0;
5768 this.lastPlay = this.ac.currentTime;
5769 this.buffer = buffer;
5770 this.createSource();
5771 }
5772
5773 /** @private */
5774 }, {
5775 key: "createSource",
5776 value: function createSource() {
5777 this.disconnectSource();
5778 this.source = this.ac.createBufferSource();
5779
5780 // adjust for old browsers
5781 this.source.start = this.source.start || this.source.noteGrainOn;
5782 this.source.stop = this.source.stop || this.source.noteOff;
5783 this.setPlaybackRate(this.playbackRate);
5784 this.source.buffer = this.buffer;
5785 this.source.connect(this.analyser);
5786 }
5787
5788 /**
5789 * @private
5790 *
5791 * some browsers require an explicit call to #resume before they will play back audio
5792 */
5793 }, {
5794 key: "resumeAudioContext",
5795 value: function resumeAudioContext() {
5796 if (this.ac.state == 'suspended') {
5797 this.ac.resume && this.ac.resume();
5798 }
5799 }
5800
5801 /**
5802 * Used by `wavesurfer.isPlaying()` and `wavesurfer.playPause()`
5803 *
5804 * @return {boolean} Whether or not this backend is currently paused
5805 */
5806 }, {
5807 key: "isPaused",
5808 value: function isPaused() {
5809 return this.state !== this.states[PLAYING];
5810 }
5811
5812 /**
5813 * Used by `wavesurfer.getDuration()`
5814 *
5815 * @return {number} Duration of loaded buffer
5816 */
5817 }, {
5818 key: "getDuration",
5819 value: function getDuration() {
5820 if (this.explicitDuration) {
5821 return this.explicitDuration;
5822 }
5823 if (!this.buffer) {
5824 return 0;
5825 }
5826 return this.buffer.duration;
5827 }
5828
5829 /**
5830 * Used by `wavesurfer.seekTo()`
5831 *
5832 * @param {number} start Position to start at in seconds
5833 * @param {number} end Position to end at in seconds
5834 * @return {{start: number, end: number}} Object containing start and end
5835 * positions
5836 */
5837 }, {
5838 key: "seekTo",
5839 value: function seekTo(start, end) {
5840 if (!this.buffer) {
5841 return;
5842 }
5843 this.scheduledPause = null;
5844 if (start == null) {
5845 start = this.getCurrentTime();
5846 if (start >= this.getDuration()) {
5847 start = 0;
5848 }
5849 }
5850 if (end == null) {
5851 end = this.getDuration();
5852 }
5853 this.startPosition = start;
5854 this.lastPlay = this.ac.currentTime;
5855 if (this.state === this.states[FINISHED]) {
5856 this.setState(PAUSED);
5857 }
5858 return {
5859 start: start,
5860 end: end
5861 };
5862 }
5863
5864 /**
5865 * Get the playback position in seconds
5866 *
5867 * @return {number} The playback position in seconds
5868 */
5869 }, {
5870 key: "getPlayedTime",
5871 value: function getPlayedTime() {
5872 return (this.ac.currentTime - this.lastPlay) * this.playbackRate;
5873 }
5874
5875 /**
5876 * Plays the loaded audio region.
5877 *
5878 * @param {number} start Start offset in seconds, relative to the beginning
5879 * of a clip.
5880 * @param {number} end When to stop relative to the beginning of a clip.
5881 */
5882 }, {
5883 key: "play",
5884 value: function play(start, end) {
5885 if (!this.buffer) {
5886 return;
5887 }
5888
5889 // need to re-create source on each playback
5890 this.createSource();
5891 var adjustedTime = this.seekTo(start, end);
5892 start = adjustedTime.start;
5893 end = adjustedTime.end;
5894 this.scheduledPause = end;
5895 this.source.start(0, start);
5896 this.resumeAudioContext();
5897 this.setState(PLAYING);
5898 this.fireEvent('play');
5899 }
5900
5901 /**
5902 * Pauses the loaded audio.
5903 */
5904 }, {
5905 key: "pause",
5906 value: function pause() {
5907 this.scheduledPause = null;
5908 this.startPosition += this.getPlayedTime();
5909 try {
5910 this.source && this.source.stop(0);
5911 } catch (err) {
5912 // Calling stop can throw the following 2 errors:
5913 // - RangeError (The value specified for when is negative.)
5914 // - InvalidStateNode (The node has not been started by calling start().)
5915 // We can safely ignore both errors, because:
5916 // - The range is surely correct
5917 // - The node might not have been started yet, in which case we just want to carry on without causing any trouble.
5918 }
5919 this.setState(PAUSED);
5920 this.fireEvent('pause');
5921 }
5922
5923 /**
5924 * Returns the current time in seconds relative to the audio-clip's
5925 * duration.
5926 *
5927 * @return {number} The current time in seconds
5928 */
5929 }, {
5930 key: "getCurrentTime",
5931 value: function getCurrentTime() {
5932 return this.state.getCurrentTime.call(this);
5933 }
5934
5935 /**
5936 * Returns the current playback rate. (0=no playback, 1=normal playback)
5937 *
5938 * @return {number} The current playback rate
5939 */
5940 }, {
5941 key: "getPlaybackRate",
5942 value: function getPlaybackRate() {
5943 return this.playbackRate;
5944 }
5945
5946 /**
5947 * Set the audio source playback rate.
5948 *
5949 * @param {number} value The playback rate to use
5950 */
5951 }, {
5952 key: "setPlaybackRate",
5953 value: function setPlaybackRate(value) {
5954 this.playbackRate = value || 1;
5955 this.source && this.source.playbackRate.setValueAtTime(this.playbackRate, this.ac.currentTime);
5956 }
5957
5958 /**
5959 * Set a point in seconds for playback to stop at.
5960 *
5961 * @param {number} end Position to end at
5962 * @version 3.3.0
5963 */
5964 }, {
5965 key: "setPlayEnd",
5966 value: function setPlayEnd(end) {
5967 this.scheduledPause = end;
5968 }
5969 }]);
5970 return WebAudio;
5971}(util.Observer);
5972exports["default"] = WebAudio;
5973_defineProperty(WebAudio, "scriptBufferSize", 256);
5974module.exports = exports.default;
5975
5976/***/ }),
5977
5978/***/ "./node_modules/debounce/index.js":
5979/*!****************************************!*\
5980 !*** ./node_modules/debounce/index.js ***!
5981 \****************************************/
5982/***/ ((module) => {
5983
5984/**
5985 * Returns a function, that, as long as it continues to be invoked, will not
5986 * be triggered. The function will be called after it stops being called for
5987 * N milliseconds. If `immediate` is passed, trigger the function on the
5988 * leading edge, instead of the trailing. The function also has a property 'clear'
5989 * that is a function which will clear the timer to prevent previously scheduled executions.
5990 *
5991 * @source underscore.js
5992 * @see http://unscriptable.com/2009/03/20/debouncing-javascript-methods/
5993 * @param {Function} function to wrap
5994 * @param {Number} timeout in ms (`100`)
5995 * @param {Boolean} whether to execute at the beginning (`false`)
5996 * @api public
5997 */
5998function debounce(func, wait, immediate){
5999 var timeout, args, context, timestamp, result;
6000 if (null == wait) wait = 100;
6001
6002 function later() {
6003 var last = Date.now() - timestamp;
6004
6005 if (last < wait && last >= 0) {
6006 timeout = setTimeout(later, wait - last);
6007 } else {
6008 timeout = null;
6009 if (!immediate) {
6010 result = func.apply(context, args);
6011 context = args = null;
6012 }
6013 }
6014 };
6015
6016 var debounced = function(){
6017 context = this;
6018 args = arguments;
6019 timestamp = Date.now();
6020 var callNow = immediate && !timeout;
6021 if (!timeout) timeout = setTimeout(later, wait);
6022 if (callNow) {
6023 result = func.apply(context, args);
6024 context = args = null;
6025 }
6026
6027 return result;
6028 };
6029
6030 debounced.clear = function() {
6031 if (timeout) {
6032 clearTimeout(timeout);
6033 timeout = null;
6034 }
6035 };
6036
6037 debounced.flush = function() {
6038 if (timeout) {
6039 result = func.apply(context, args);
6040 context = args = null;
6041
6042 clearTimeout(timeout);
6043 timeout = null;
6044 }
6045 };
6046
6047 return debounced;
6048};
6049
6050// Adds compatibility for ES modules
6051debounce.debounce = debounce;
6052
6053module.exports = debounce;
6054
6055
6056/***/ })
6057
6058/******/ });
6059/************************************************************************/
6060/******/ // The module cache
6061/******/ var __webpack_module_cache__ = {};
6062/******/
6063/******/ // The require function
6064/******/ function __webpack_require__(moduleId) {
6065/******/ // Check if module is in cache
6066/******/ var cachedModule = __webpack_module_cache__[moduleId];
6067/******/ if (cachedModule !== undefined) {
6068/******/ return cachedModule.exports;
6069/******/ }
6070/******/ // Create a new module (and put it into the cache)
6071/******/ var module = __webpack_module_cache__[moduleId] = {
6072/******/ // no module.id needed
6073/******/ // no module.loaded needed
6074/******/ exports: {}
6075/******/ };
6076/******/
6077/******/ // Execute the module function
6078/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
6079/******/
6080/******/ // Return the exports of the module
6081/******/ return module.exports;
6082/******/ }
6083/******/
6084/************************************************************************/
6085/******/
6086/******/ // startup
6087/******/ // Load entry module and return exports
6088/******/ // This entry module is referenced by other modules so it can't be inlined
6089/******/ var __webpack_exports__ = __webpack_require__("./src/wavesurfer.js");
6090/******/
6091/******/ return __webpack_exports__;
6092/******/ })()
6093;
6094});
6095//# sourceMappingURL=wavesurfer.js.map
\No newline at end of file