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