diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..ee13034f1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..4634e3bac --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,26 @@ +# Hey, thank you for testing and contributing to wavesurfer.js! + +## Please make sure you can check all of these points below before opening an issue: + +(You don't have to post this section) + +- [ ] I have checked [the FAQ](https://wavesurfer-js.org/faq/) and it doesn't solve my problem. +- [ ] I have checked [the documentation](https://wavesurfer-js.org/docs/) and it doesn't solve my problem +- [ ] I have searched for [already open issues](https://github.com/katspaugh/wavesurfer.js/issues) which desribe my problem. +- [ ] The issue I'm having is related to and caused by wavesurfer.js, not by other software (which maybe packages and uses wavesurfer incorrectly) – In that case you should open the issue on the respective project pages. + +## Please make sure you provide the following information (if applicable): + +### Wavesurfer.js version(s): + + +### Browser version(s): + + +### Code needed to reproduce the issue: + +(Please reduce your code as much as possible and only post the minimum code needed to reproduce the issue. [A Code pen](http://codepen.io/) is an excellent way to share such code) + + +### Use behaviour needed to reproduce the issue: + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..f9002b067 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,21 @@ +# Hey, thank you for contributing to wavesurfer.js! + +To review/merge open PRs it is very helpful to know as much as possible about the changes which are being introduced. Reviewing PRs is very time consuming, please be patient, it can take some time to do properly. + +**Title:** Please make sure the name of your PR is as descriptive as possible (Describe the feature that is introduced or the bug that is being fixed). + +## Please make sure you provide the information below: + +### Short description of changes: + + +### Breaking in the external API: + + +### Breaking changes in the internal API: + + +### Todos/Notes: + + +### Related Issues and other PRs: diff --git a/.gitignore b/.gitignore index 561f4da4f..8d493c83d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ dist/plugin/*.js /_SpecRunner.html .DS_Store .idea +.project diff --git a/.npmignore b/.npmignore index 8e8929995..2419d3986 100644 --- a/.npmignore +++ b/.npmignore @@ -1,3 +1,6 @@ -/dist/wavesurfer.min.js.map -/dist/wavesurfer.amd.js -/dist/wavesurfer.min.js +# exclude everything except dist, plugin and src +**/* +!dist/* +!dist/plugin/*.min.js +!plugin/** +!src/** diff --git a/CHANGES.md b/CHANGES.md index aad51d344..142c4965e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,76 @@ wavesurfer.js changelog ======================= +1.4.0 (10.04.2017) +------------------ +- A "breaking" change: a new license. We switched to BSD 3 (#1060). +– Reset the mute state on subsequent file loads (#1045) +– A fix for vertical scroll (#1054) +– A new plugin for linguists: `elan-wave-segment` (#1052) + + +1.3.7 (19.03.2017) +------------------ +- A fix for the timeline plugin (#1049) + +1.3.6 (19.03.2017) +------------------ +- New renderer: `SplitWavePointPlot` (#1048) + +1.3.5 (10.03.2017) +------------------ +- Add a `getImage` method for MultiCanvas (#1012) +- Add Playlist plugin (#1018) +- Add Playlist plugin sample (#1019) +- Add a new parameter `barHeight` (#1020) +- Add a `playBackRate` method for MediaElement (#1022) + +1.3.4 (25.02.2017) +------------------ +- Add a new parameter `forceDecode` (#1009) + +1.3.3 (23.02.2017) +------------------ +- Add Media Session plugin (#996) +- Fix check for AudioContext.close (#998) + +1.3.2 (12.02.2017) +------------------ +- Add a `getVolume` method (#979) +- Add a `getMute` method (#980) +- Add a `getFilters` method (#982) +- New feature for spectrogram plugin to display frequency labels + +1.3.1 (07.02.2017) +------------------ +- Add a `getPlaybackRate` method (#936) +- Add a `debounce` utility (#964) +- Fixes for MultiCanvas & peaks cache, a fix the minimap plugin – thanks @entonbiba! + +1.3.0 (19.01.2017) +------------------ +- MultiCanvas is now the default renderer. Single Canvas will be soon removed. +- Fix backward seeking with Media Element (#918) +- Fix normalize + bar-style waveform (#916) +- New option `partialRender` for better performance with high zoom (#909) + +1.2.8 (09.12.2016) +------------------ +- PhantomJS support with the MediaElement backend (#875) + +1.2.7 (03.12.2016) +------------------ +- Timeline bugfixes: correctly unsubscribe on destroy and re-render on zoom (#825, #848) + +1.2.6 (19.11.2016) +------------------ +- Solve the disappearing canvas problem on zoom (#825) + +1.2.5 (19.11.2016) +------------------ +- WebAudio backend closes the AudioContext when it is destroyed, unless the AudioContext was passed in as a parameter (params.audioContext) +- The AudioContext is no longer cached in WebAudio.audioContext, use the getter-function WebAudio.getAudioContext. (#862) + 1.2.4 (11.11.2016) ------------------ - Fix a problem of Web Audio not playing in Safari on initial load (#749) diff --git a/Gruntfile.js b/Gruntfile.js index e34ff2ecd..6dc37f870 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -25,6 +25,7 @@ module.exports = function (grunt) { 'src/mediaelement.js', 'src/drawer.js', 'src/drawer.*.js', + 'src/peakcache.js', 'src/html-init.js' ], dest: 'dist/wavesurfer.js' @@ -102,7 +103,8 @@ module.exports = function (grunt) { strict: false, globals: { WaveSurfer: true, - Promise: true + Promise: true, + MediaMetadata: true } }, ignore_warning: { diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..6749f6eab --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2012-2017, katspaugh and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 4373b23d5..426e3f5d5 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,9 @@ Yes, if you use the `backend: 'MediaElement'` option. See here: https://wavesurf ### Can drawing be done as file loads? No. Web Audio needs the whole file to decode it in the browser. You can however load pre-decoded waveform data to draw the waveform immediately. See here: https://wavesurfer-js.org/example/audio-element/ (the "Pre-recoded Peaks" section). +### Is wavesurfer.js ES6-compatible? +ES6 refactoring is going on in the `next` branch. In the meanwhile, you can import wavesurfer.js as an ES6 module via WebPack as described [here](https://github.com/katspaugh/wavesurfer.js/issues/767#issuecomment-267768714). + ## API in examples Choose a container: @@ -102,7 +105,7 @@ grunt coverage The HTML report can be found in `coverage/html/index.html`. ## Editing documentation -The homepage and the documentation are in the [`gh-pages` branch](https://github.com/katspaugh/wavesurfer.js/tree/gh-pages). Contributions to the documentation are especially welcome. +The homepage and documentation files are maintained in the [`gh-pages` branch](https://github.com/katspaugh/wavesurfer.js/tree/gh-pages). Contributions to the documentation are especially welcome. ## Credits @@ -112,7 +115,7 @@ thanks to ## License -![cc-by](https://i.creativecommons.org/l/by/3.0/88x31.png) +[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) This work is licensed under a -[Creative Commons Attribution 3.0 Unported License](https://creativecommons.org/licenses/by/3.0/deed.en_US). +[BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause). diff --git a/bower.json b/bower.json index 165ab018d..cfda80a36 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "wavesurfer.js", - "version": "1.2.4", + "version": "1.4.0", "homepage": "http://wavesurfer-js.org", "authors": [ "katspaugh " @@ -10,5 +10,5 @@ "keywords": [ "waveform", "audio", "music", "player" ], - "license": "CC BY 3.0" + "license": "BSD-3-Clause" } diff --git a/dist/plugin/wavesurfer.cursor.min.js b/dist/plugin/wavesurfer.cursor.min.js index 13850b14e..03ab7c508 100644 --- a/dist/plugin/wavesurfer.cursor.min.js +++ b/dist/plugin/wavesurfer.cursor.min.js @@ -1,3 +1,3 @@ -/*! wavesurfer.js 1.2.4 (Fri, 11 Nov 2016 15:43:23 GMT) +/*! wavesurfer.js 1.4.0 (Mon, 10 Apr 2017 08:55:36 GMT) * https://github.com/katspaugh/wavesurfer.js -* @license CC-BY-3.0 */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.Cursor={init:function(a){var b=this;this.wavesurfer=a,this.drawer=this.wavesurfer.drawer,this.wrapper=this.drawer.wrapper,this.wrapper.addEventListener("mousemove",function(a){b.updateCursorPosition(b.drawer.handleEvent(a))}),this.wrapper.addEventListener("mouseenter",function(a){b.showCursor()}),this.wrapper.addEventListener("mouseleave",function(a){b.hideCursor()}),this.cursor=this.wrapper.appendChild(this.drawer.style(document.createElement("wave"),{position:"absolute",zIndex:3,left:0,top:0,bottom:0,width:"0",display:"block",borderRightStyle:"solid",borderRightWidth:"1px",borderRightColor:"black",opacity:".25",pointerEvents:"none"}))},updateCursorPosition:function(a){var b=Math.round(this.drawer.width*a)/this.drawer.params.pixelRatio-1;this.drawer.style(this.cursor,{left:b+"px"})},showCursor:function(){this.drawer.style(this.cursor,{display:"block"})},hideCursor:function(){this.drawer.style(this.cursor,{display:"none"})}},a.util.extend(a.Cursor,a.Observer),a.enableCursor=function(){this.cursor||(this.cursor=Object.create(a.Cursor),this.cursor.init(this))}}); \ No newline at end of file +* @license BSD-3-Clause */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.Cursor={init:function(a){var b=this;this.wavesurfer=a,this.drawer=this.wavesurfer.drawer,this.wrapper=this.drawer.wrapper,this.wrapper.addEventListener("mousemove",function(a){b.updateCursorPosition(b.drawer.handleEvent(a))}),this.wrapper.addEventListener("mouseenter",function(a){b.showCursor()}),this.wrapper.addEventListener("mouseleave",function(a){b.hideCursor()}),this.cursor=this.wrapper.appendChild(this.drawer.style(document.createElement("wave"),{position:"absolute",zIndex:3,left:0,top:0,bottom:0,width:"0",display:"block",borderRightStyle:"solid",borderRightWidth:"1px",borderRightColor:"black",opacity:".25",pointerEvents:"none"}))},updateCursorPosition:function(a){var b=Math.round(this.drawer.width*a)/this.drawer.params.pixelRatio-1;this.drawer.style(this.cursor,{left:b+"px"})},showCursor:function(){this.drawer.style(this.cursor,{display:"block"})},hideCursor:function(){this.drawer.style(this.cursor,{display:"none"})}},a.util.extend(a.Cursor,a.Observer),a.enableCursor=function(){this.cursor||(this.cursor=Object.create(a.Cursor),this.cursor.init(this))}}); \ No newline at end of file diff --git a/dist/plugin/wavesurfer.elan-wave-segment.min.js b/dist/plugin/wavesurfer.elan-wave-segment.min.js new file mode 100644 index 000000000..7e7947ccd --- /dev/null +++ b/dist/plugin/wavesurfer.elan-wave-segment.min.js @@ -0,0 +1,3 @@ +/*! wavesurfer.js 1.4.0 (Mon, 10 Apr 2017 08:55:36 GMT) +* https://github.com/katspaugh/wavesurfer.js +* @license BSD-3-Clause */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.ELANWaveSegment={defaultParams:{waveSegmentWidth:200,waveSegmentPeaksPerSegment:200,waveSegmentHeight:30,waveSegmentRenderer:"Canvas",waveSegmentNormalizeTo:"whole",waveSegmentBarHeight:1,waveSegmentBorderWidth:1,pixelRatio:window.devicePixelRatio||screen.deviceXDPI/screen.logicalXDPI},ELAN:null,wavesurfer:null,waveSegments:[],maxPeak:0,init:function(b){switch(this.params=a.util.extend(this.defaultParams,b),this.ELAN=b.ELAN,this.wavesurfer=b.wavesurfer,this.waveSegments=[],this.maxPeak=0,this.params.waveSegmentNormalizeTo){case"segment":this.params.normalize=!0;break;case"whole":this.calculateMaxPeak(),this.params.noramlize=!1;break;default:this.params.normalize=!1}this.addSegmentColumn()},calculateMaxPeak:function(){var b=this.ELAN.renderedAlignable.length*this.params.waveSegmentWidth,c=this.wavesurfer.backend.getPeaks(b,0,b),d=a.util.max(c),e=a.util.min(c);this.maxPeak=-e>d?-e:d},addSegmentColumn:function(){var a=this.ELAN.container.getElementsByTagName("tr"),b=document.createElement("th");b.textContent="Wave",b.className="wavesurfer-wave",b.setAttribute("style","width: "+this.params.waveSegmentWidth+"px"),a[0].insertBefore(b,a[0].firstChild.nextSibling);for(var c=0;ca)c=0;else if(e=a&&(b=c,!0)}),b},getAnnotationNode:function(a){return document.getElementById("wavesurfer-alignable-"+a.id)}},a.util.extend(a.ELAN,a.Observer)}); \ No newline at end of file +* @license BSD-3-Clause */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.ELAN={Types:{ALIGNABLE_ANNOTATION:"ALIGNABLE_ANNOTATION",REF_ANNOTATION:"REF_ANNOTATION"},init:function(a){if(this.data=null,this.params=a,this.container="string"==typeof a.container?document.querySelector(a.container):a.container,!this.container)throw Error("No container for ELAN");this.bindClick(),a.url&&this.load(a.url)},load:function(a){var b=this;this.loadXML(a,function(a){b.data=b.parseElan(a),b.render(),b.fireEvent("ready",b.data)})},loadXML:function(a,b){var c=new XMLHttpRequest;c.open("GET",a,!0),c.responseType="document",c.send(),c.addEventListener("load",function(a){b&&b(a.target.responseXML)})},parseElan:function(a){var b=Array.prototype.forEach,c=Array.prototype.map,d={media:{},timeOrder:{},tiers:[],annotations:{},alignableAnnotations:[]},e=a.querySelector("HEADER"),f="milliseconds"==e.getAttribute("TIME_UNITS"),g=e.querySelector("MEDIA_DESCRIPTOR");d.media.url=g.getAttribute("MEDIA_URL"),d.media.type=g.getAttribute("MIME_TYPE");var h=a.querySelectorAll("TIME_ORDER TIME_SLOT"),i={};return b.call(h,function(a){var b=parseFloat(a.getAttribute("TIME_VALUE"));f&&(b=Math.round(100*b)/1e5),i[a.getAttribute("TIME_SLOT_ID")]=b}),d.tiers=c.call(a.querySelectorAll("TIER"),function(a){return{id:a.getAttribute("TIER_ID"),linguisticTypeRef:a.getAttribute("LINGUISTIC_TYPE_REF"),defaultLocale:a.getAttribute("DEFAULT_LOCALE"),annotations:c.call(a.querySelectorAll("REF_ANNOTATION, ALIGNABLE_ANNOTATION"),function(a){var b={type:a.nodeName,id:a.getAttribute("ANNOTATION_ID"),ref:a.getAttribute("ANNOTATION_REF"),value:a.querySelector("ANNOTATION_VALUE").textContent.trim()};return this.Types.ALIGNABLE_ANNOTATION==b.type&&(b.start=i[a.getAttribute("TIME_SLOT_REF1")],b.end=i[a.getAttribute("TIME_SLOT_REF2")],d.alignableAnnotations.push(b)),d.annotations[b.id]=b,b},this)}},this),d.tiers.forEach(function(a){a.annotations.forEach(function(a){null!=a.ref&&(a.reference=d.annotations[a.ref])},this)},this),d.alignableAnnotations.sort(function(a,b){var c=a.start-b.start;return 0==c&&(c=b.end-a.end),c}),d.length=d.alignableAnnotations.length,d},render:function(){var a=this.data.tiers;this.params.tiers&&(a=a.filter(function(a){return a.id in this.params.tiers},this));var b={},c={};a.forEach(function(a,d){a.annotations.forEach(function(a){a.reference&&a.reference.type==this.Types.ALIGNABLE_ANNOTATION&&(a.reference.id in b||(b[a.ref]={}),b[a.ref][d]=a,c[d]=!0)},this)},this),c=Object.keys(c).sort(),this.renderedAlignable=this.data.alignableAnnotations.filter(function(a){return b[a.id]});var d=document.createElement("table");d.className="wavesurfer-annotations";var e=document.createElement("thead"),f=document.createElement("tr");e.appendChild(f),d.appendChild(e);var g=document.createElement("th");g.textContent="Time",g.className="wavesurfer-time",f.appendChild(g),c.forEach(function(b){var c=a[b],d=document.createElement("th");d.className="wavesurfer-tier-"+c.id,d.textContent=c.id,d.style.width=this.params.tiers[c.id],f.appendChild(d)},this);var h=document.createElement("tbody");d.appendChild(h),this.renderedAlignable.forEach(function(d){var e=document.createElement("tr");e.id="wavesurfer-alignable-"+d.id,h.appendChild(e);var f=document.createElement("td");f.className="wavesurfer-time",f.textContent=d.start.toFixed(1)+"–"+d.end.toFixed(1),e.appendChild(f);var g=b[d.id];c.forEach(function(b){var c=a[b],f=document.createElement("td"),h=g[b];h&&(f.id="wavesurfer-annotation-"+h.id,f.dataset.ref=d.id,f.dataset.start=d.start,f.dataset.end=d.end,f.textContent=h.value),f.className="wavesurfer-tier-"+c.id,e.appendChild(f)},this)},this),this.container.innerHTML="",this.container.appendChild(d)},bindClick:function(){var a=this;this.container.addEventListener("click",function(b){var c=b.target.dataset.ref;if(null!=c){var d=a.data.annotations[c];d&&a.fireEvent("select",d.start,d.end)}})},getRenderedAnnotation:function(a){var b;return this.renderedAlignable.some(function(c){return c.start<=a&&c.end>=a&&(b=c,!0)}),b},getAnnotationNode:function(a){return document.getElementById("wavesurfer-alignable-"+a.id)}},a.util.extend(a.ELAN,a.Observer)}); \ No newline at end of file diff --git a/dist/plugin/wavesurfer.mediasession.min.js b/dist/plugin/wavesurfer.mediasession.min.js new file mode 100644 index 000000000..dbe677102 --- /dev/null +++ b/dist/plugin/wavesurfer.mediasession.min.js @@ -0,0 +1,3 @@ +/*! wavesurfer.js 1.4.0 (Mon, 10 Apr 2017 08:55:36 GMT) +* https://github.com/katspaugh/wavesurfer.js +* @license BSD-3-Clause */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.MediaSession={init:function(a){this.params=a;var b=this.wavesurfer=a.wavesurfer;if(!this.wavesurfer)throw new Error("No WaveSurfer instance provided");if("mediaSession"in navigator){this.metadata=this.params.metadata,this.update();var c=this;b.on("play",function(){c.update()}),navigator.mediaSession.setActionHandler("play",function(){b.play()}),navigator.mediaSession.setActionHandler("pause",function(){b.playPause()}),navigator.mediaSession.setActionHandler("seekbackward",function(){b.skipBackward()}),navigator.mediaSession.setActionHandler("seekforward",function(){b.skipForward()})}},update:function(){typeof MediaMetadata==typeof Function&&(navigator.mediaSession.metadata=new MediaMetadata(this.metadata))}},a.util.extend(a.MediaSession,a.Observer)}); \ No newline at end of file diff --git a/dist/plugin/wavesurfer.microphone.min.js b/dist/plugin/wavesurfer.microphone.min.js index b9bd745a9..9e868b0ed 100644 --- a/dist/plugin/wavesurfer.microphone.min.js +++ b/dist/plugin/wavesurfer.microphone.min.js @@ -1,3 +1,3 @@ -/*! wavesurfer.js 1.2.4 (Fri, 11 Nov 2016 15:43:23 GMT) +/*! wavesurfer.js 1.4.0 (Mon, 10 Apr 2017 08:55:36 GMT) * https://github.com/katspaugh/wavesurfer.js -* @license CC-BY-3.0 */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.Microphone={init:function(a){this.params=a;this.wavesurfer=a.wavesurfer;if(!this.wavesurfer)throw new Error("No WaveSurfer instance provided");this.active=!1,this.paused=!1,this.reloadBufferFunction=this.reloadBuffer.bind(this);var b=function(a,b,c){var d=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia;return d?new Promise(function(b,c){d.call(navigator,a,b,c)}):Promise.reject(new Error("getUserMedia is not implemented in this browser"))};void 0===navigator.mediaDevices&&(navigator.mediaDevices={}),void 0===navigator.mediaDevices.getUserMedia&&(navigator.mediaDevices.getUserMedia=b),this.constraints=this.params.constraints||{video:!1,audio:!0},this.bufferSize=this.params.bufferSize||4096,this.numberOfInputChannels=this.params.numberOfInputChannels||1,this.numberOfOutputChannels=this.params.numberOfOutputChannels||1,this.micContext=this.wavesurfer.backend.getAudioContext()},start:function(){navigator.mediaDevices.getUserMedia(this.constraints).then(this.gotStream.bind(this))["catch"](this.deviceError.bind(this))},togglePlay:function(){this.active?(this.paused=!this.paused,this.paused?this.pause():this.play()):this.start()},play:function(){this.paused=!1,this.connect()},pause:function(){this.paused=!0,this.disconnect()},stop:function(){this.active&&(this.stopDevice(),this.wavesurfer.empty())},stopDevice:function(){if(this.active=!1,this.disconnect(),this.stream){var a=this.detectBrowser();if(("chrome"===a.browser&&a.version>=45||"firefox"===a.browser&&a.version>=44||"edge"===a.browser)&&this.stream.getTracks)return void this.stream.getTracks().forEach(function(a){a.stop()});this.stream.stop()}},connect:function(){void 0!==this.stream&&(this.mediaStreamSource=this.micContext.createMediaStreamSource(this.stream),this.levelChecker=this.micContext.createScriptProcessor(this.bufferSize,this.numberOfInputChannels,this.numberOfOutputChannels),this.mediaStreamSource.connect(this.levelChecker),this.levelChecker.connect(this.micContext.destination),this.levelChecker.onaudioprocess=this.reloadBufferFunction)},disconnect:function(){void 0!==this.mediaStreamSource&&this.mediaStreamSource.disconnect(),void 0!==this.levelChecker&&(this.levelChecker.disconnect(),this.levelChecker.onaudioprocess=void 0)},reloadBuffer:function(a){this.paused||(this.wavesurfer.empty(),this.wavesurfer.loadDecodedBuffer(a.inputBuffer))},gotStream:function(a){this.stream=a,this.active=!0,this.play(),this.fireEvent("deviceReady",a)},destroy:function(a){this.paused=!0,this.stop()},deviceError:function(a){this.fireEvent("deviceError",a)},extractVersion:function(a,b,c){var d=a.match(b);return d&&d.length>=c&&parseInt(d[c],10)},detectBrowser:function(){var a={};return a.browser=null,a.version=null,a.minVersion=null,"undefined"!=typeof window&&window.navigator?navigator.mozGetUserMedia?(a.browser="firefox",a.version=this.extractVersion(navigator.userAgent,/Firefox\/([0-9]+)\./,1),a.minVersion=31,a):navigator.webkitGetUserMedia&&window.webkitRTCPeerConnection?(a.browser="chrome",a.version=this.extractVersion(navigator.userAgent,/Chrom(e|ium)\/([0-9]+)\./,2),a.minVersion=38,a):navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)?(a.browser="edge",a.version=this.extractVersion(navigator.userAgent,/Edge\/(\d+).(\d+)$/,2),a.minVersion=10547,a):(a.browser="Not a supported browser.",a):(a.browser="Not a supported browser.",a)}},a.util.extend(a.Microphone,a.Observer)}); \ No newline at end of file +* @license BSD-3-Clause */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.Microphone={init:function(a){this.params=a;this.wavesurfer=a.wavesurfer;if(!this.wavesurfer)throw new Error("No WaveSurfer instance provided");this.active=!1,this.paused=!1,this.reloadBufferFunction=this.reloadBuffer.bind(this);var b=function(a,b,c){var d=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia;return d?new Promise(function(b,c){d.call(navigator,a,b,c)}):Promise.reject(new Error("getUserMedia is not implemented in this browser"))};void 0===navigator.mediaDevices&&(navigator.mediaDevices={}),void 0===navigator.mediaDevices.getUserMedia&&(navigator.mediaDevices.getUserMedia=b),this.constraints=this.params.constraints||{video:!1,audio:!0},this.bufferSize=this.params.bufferSize||4096,this.numberOfInputChannels=this.params.numberOfInputChannels||1,this.numberOfOutputChannels=this.params.numberOfOutputChannels||1,this.micContext=this.wavesurfer.backend.getAudioContext()},start:function(){navigator.mediaDevices.getUserMedia(this.constraints).then(this.gotStream.bind(this))["catch"](this.deviceError.bind(this))},togglePlay:function(){this.active?(this.paused=!this.paused,this.paused?this.pause():this.play()):this.start()},play:function(){this.paused=!1,this.connect()},pause:function(){this.paused=!0,this.disconnect()},stop:function(){this.active&&(this.stopDevice(),this.wavesurfer.empty())},stopDevice:function(){if(this.active=!1,this.disconnect(),this.stream){var a=this.detectBrowser();if(("chrome"===a.browser&&a.version>=45||"firefox"===a.browser&&a.version>=44||"edge"===a.browser)&&this.stream.getTracks)return void this.stream.getTracks().forEach(function(a){a.stop()});this.stream.stop()}},connect:function(){void 0!==this.stream&&(this.mediaStreamSource=this.micContext.createMediaStreamSource(this.stream),this.levelChecker=this.micContext.createScriptProcessor(this.bufferSize,this.numberOfInputChannels,this.numberOfOutputChannels),this.mediaStreamSource.connect(this.levelChecker),this.levelChecker.connect(this.micContext.destination),this.levelChecker.onaudioprocess=this.reloadBufferFunction)},disconnect:function(){void 0!==this.mediaStreamSource&&this.mediaStreamSource.disconnect(),void 0!==this.levelChecker&&(this.levelChecker.disconnect(),this.levelChecker.onaudioprocess=void 0)},reloadBuffer:function(a){this.paused||(this.wavesurfer.empty(),this.wavesurfer.loadDecodedBuffer(a.inputBuffer))},gotStream:function(a){this.stream=a,this.active=!0,this.play(),this.fireEvent("deviceReady",a)},destroy:function(a){this.paused=!0,this.stop()},deviceError:function(a){this.fireEvent("deviceError",a)},extractVersion:function(a,b,c){var d=a.match(b);return d&&d.length>=c&&parseInt(d[c],10)},detectBrowser:function(){var a={};return a.browser=null,a.version=null,a.minVersion=null,"undefined"!=typeof window&&window.navigator?navigator.mozGetUserMedia?(a.browser="firefox",a.version=this.extractVersion(navigator.userAgent,/Firefox\/([0-9]+)\./,1),a.minVersion=31,a):navigator.webkitGetUserMedia&&window.webkitRTCPeerConnection?(a.browser="chrome",a.version=this.extractVersion(navigator.userAgent,/Chrom(e|ium)\/([0-9]+)\./,2),a.minVersion=38,a):navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)?(a.browser="edge",a.version=this.extractVersion(navigator.userAgent,/Edge\/(\d+).(\d+)$/,2),a.minVersion=10547,a):(a.browser="Not a supported browser.",a):(a.browser="Not a supported browser.",a)}},a.util.extend(a.Microphone,a.Observer)}); \ No newline at end of file diff --git a/dist/plugin/wavesurfer.minimap.min.js b/dist/plugin/wavesurfer.minimap.min.js index d31ea14c7..25eff70b5 100644 --- a/dist/plugin/wavesurfer.minimap.min.js +++ b/dist/plugin/wavesurfer.minimap.min.js @@ -1,3 +1,3 @@ -/*! wavesurfer.js 1.2.4 (Fri, 11 Nov 2016 15:43:23 GMT) +/*! wavesurfer.js 1.4.0 (Mon, 10 Apr 2017 08:55:36 GMT) * https://github.com/katspaugh/wavesurfer.js -* @license CC-BY-3.0 */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.Minimap=a.util.extend({},a.Drawer,a.Drawer.Canvas,{init:function(b,c){this.wavesurfer=b,this.container=this.wavesurfer.drawer.container,this.lastPos=this.wavesurfer.drawer.lastPos,this.params=b.util.extend({},this.wavesurfer.drawer.params,{showRegions:!1,showOverview:!1,overviewBorderColor:"green",overviewBorderSize:2},c,{scrollParent:!1,fillParent:!0}),this.width=0,this.height=this.params.height*this.params.pixelRatio,this.createWrapper(),this.createElements(),a.Regions&&this.params.showRegions&&this.regions(),this.bindWaveSurferEvents(),this.bindMinimapEvents()},regions:function(){var a=this;this.regions={},this.wavesurfer.on("region-created",function(b){a.regions[b.id]=b,a.renderRegions()}),this.wavesurfer.on("region-updated",function(b){a.regions[b.id]=b,a.renderRegions()}),this.wavesurfer.on("region-removed",function(b){delete a.regions[b.id],a.renderRegions()})},renderRegions:function(){for(var a=this,b=this.wrapper.querySelectorAll("region"),c=0;c1||a.target.childElementCount>0||(e=a.targetTouches?a.targetTouches[0].identifier:null,b=!0,c=f.wavesurfer.drawer.handleEvent(a,!0),d=null)};this.wrapper.addEventListener("mousedown",i),this.wrapper.addEventListener("touchstart",i),this.on("disable-drag-selection",function(){f.wrapper.removeEventListener("touchstart",i),f.wrapper.removeEventListener("mousedown",i)});var j=function(a){a.touches&&a.touches.length>1||(b=!1,h=0,d&&(d.fireEvent("update-end",a),f.wavesurfer.fireEvent("region-update-end",d,a)),d=null)};this.wrapper.addEventListener("mouseup",j),this.wrapper.addEventListener("touchend",j),this.on("disable-drag-selection",function(){f.wrapper.removeEventListener("touchend",j),f.wrapper.removeEventListener("mouseup",j)});var k=function(i){if(b&&!(++h<=g||i.touches&&i.touches.length>1||i.targetTouches&&i.targetTouches[0].identifier!=e)){d||(d=f.add(a||{}));var j=f.wavesurfer.getDuration(),k=f.wavesurfer.drawer.handleEvent(i);d.update({start:Math.min(k*j,c*j),end:Math.max(k*j,c*j)})}};this.wrapper.addEventListener("mousemove",k),this.wrapper.addEventListener("touchmove",k),this.on("disable-drag-selection",function(){f.wrapper.removeEventListener("touchmove",k),f.wrapper.removeEventListener("mousemove",k)})},disableDragSelection:function(){this.fireEvent("disable-drag-selection")}},a.util.extend(a.Regions,a.Observer),a.Region={style:a.Drawer.style,init:function(b,c){this.wavesurfer=c,this.wrapper=c.drawer.wrapper,this.id=null==b.id?a.util.getId():b.id,this.start=Number(b.start)||0,this.end=null==b.end?this.start+4/this.wrapper.scrollWidth*this.wavesurfer.getDuration():Number(b.end),this.resize=void 0===b.resize||Boolean(b.resize),this.drag=void 0===b.drag||Boolean(b.drag),this.loop=Boolean(b.loop),this.color=b.color||"rgba(0, 0, 0, 0.1)",this.data=b.data||{},this.attributes=b.attributes||{},this.maxLength=b.maxLength,this.minLength=b.minLength,this.bindInOut(),this.render(),this.wavesurfer.on("zoom",this.updateRender.bind(this)),this.wavesurfer.fireEvent("region-created",this)},update:function(a){null!=a.start&&(this.start=Number(a.start)),null!=a.end&&(this.end=Number(a.end)),null!=a.loop&&(this.loop=Boolean(a.loop)),null!=a.color&&(this.color=a.color),null!=a.data&&(this.data=a.data),null!=a.resize&&(this.resize=Boolean(a.resize)),null!=a.drag&&(this.drag=Boolean(a.drag)),null!=a.maxLength&&(this.maxLength=Number(a.maxLength)),null!=a.minLength&&(this.minLength=Number(a.minLength)),null!=a.attributes&&(this.attributes=a.attributes),this.updateRender(),this.fireEvent("update"),this.wavesurfer.fireEvent("region-updated",this)},remove:function(){this.element&&(this.wrapper.removeChild(this.element),this.element=null,this.fireEvent("remove"),this.wavesurfer.un("zoom",this.updateRender.bind(this)),this.wavesurfer.fireEvent("region-removed",this))},play:function(){this.wavesurfer.play(this.start,this.end),this.fireEvent("play"),this.wavesurfer.fireEvent("region-play",this)},playLoop:function(){this.play(),this.once("out",this.playLoop.bind(this))},render:function(){var a=document.createElement("region");a.className="wavesurfer-region",a.title=this.formatTime(this.start,this.end),a.setAttribute("data-id",this.id);for(var b in this.attributes)a.setAttribute("data-region-"+b,this.attributes[b]);this.wrapper.scrollWidth;if(this.style(a,{position:"absolute",zIndex:2,height:"100%",top:"0px"}),this.resize){var c=a.appendChild(document.createElement("handle")),d=a.appendChild(document.createElement("handle"));c.className="wavesurfer-handle wavesurfer-handle-start",d.className="wavesurfer-handle wavesurfer-handle-end";var e={cursor:"col-resize",position:"absolute",left:"0px",top:"0px",width:"1%",maxWidth:"4px",height:"100%"};this.style(c,e),this.style(d,e),this.style(d,{left:"100%"})}this.element=this.wrapper.appendChild(a),this.updateRender(),this.bindEvents(a)},formatTime:function(a,b){return(a==b?[a]:[a,b]).map(function(a){return[Math.floor(a%3600/60),("00"+Math.floor(a%60)).slice(-2)].join(":")}).join("-")},updateRender:function(a){var b,c=this.wavesurfer.getDuration();if(b=a?Math.round(this.wavesurfer.getDuration()*a):this.wrapper.scrollWidth,this.start<0&&(this.start=0,this.end=this.end-this.start),this.end>c&&(this.end=c,this.start=c-(this.end-this.start)),null!=this.minLength&&(this.end=Math.max(this.start+this.minLength,this.end)),null!=this.maxLength&&(this.end=Math.min(this.start+this.maxLength,this.end)),null!=this.element){var d=Math.round(this.start/c*b),e=Math.round(this.end/c*b)-d;this.style(this.element,{left:d+"px",width:e+"px",backgroundColor:this.color,cursor:this.drag?"move":"default"});for(var f in this.attributes)this.element.setAttribute("data-region-"+f,this.attributes[f]);this.element.title=this.formatTime(this.start,this.end)}},bindInOut:function(){var a=this;a.firedIn=!1,a.firedOut=!1;var b=function(b){!a.firedOut&&a.firedIn&&(a.start>=Math.round(100*b)/100||a.end<=Math.round(100*b)/100)&&(a.firedOut=!0,a.firedIn=!1,a.fireEvent("out"),a.wavesurfer.fireEvent("region-out",a)),!a.firedIn&&a.start<=b&&a.end>b&&(a.firedIn=!0,a.firedOut=!1,a.fireEvent("in"),a.wavesurfer.fireEvent("region-in",a))};this.wavesurfer.backend.on("audioprocess",b),this.on("remove",function(){a.wavesurfer.backend.un("audioprocess",b)}),this.on("out",function(){a.loop&&a.wavesurfer.play(a.start)})},bindEvents:function(){var a=this;this.element.addEventListener("mouseenter",function(b){a.fireEvent("mouseenter",b),a.wavesurfer.fireEvent("region-mouseenter",a,b)}),this.element.addEventListener("mouseleave",function(b){a.fireEvent("mouseleave",b),a.wavesurfer.fireEvent("region-mouseleave",a,b)}),this.element.addEventListener("click",function(b){b.preventDefault(),a.fireEvent("click",b),a.wavesurfer.fireEvent("region-click",a,b)}),this.element.addEventListener("dblclick",function(b){b.stopPropagation(),b.preventDefault(),a.fireEvent("dblclick",b),a.wavesurfer.fireEvent("region-dblclick",a,b)}),(this.drag||this.resize)&&function(){var b,c,d,e,f=a.wavesurfer.getDuration(),g=function(g){g.touches&&g.touches.length>1||(e=g.targetTouches?g.targetTouches[0].identifier:null,g.stopPropagation(),d=a.wavesurfer.drawer.handleEvent(g,!0)*f,"handle"==g.target.tagName.toLowerCase()?c=g.target.classList.contains("wavesurfer-handle-start")?"start":"end":(b=!0,c=!1))},h=function(d){d.touches&&d.touches.length>1||(b||c)&&(b=!1,c=!1,a.fireEvent("update-end",d),a.wavesurfer.fireEvent("region-update-end",a,d))},i=function(g){if(!(g.touches&&g.touches.length>1)&&(!g.targetTouches||g.targetTouches[0].identifier==e)&&(b||c)){var h=a.wavesurfer.drawer.handleEvent(g)*f,i=h-d;d=h,a.drag&&b&&a.onDrag(i),a.resize&&c&&a.onResize(i,c)}};a.element.addEventListener("mousedown",g),a.element.addEventListener("touchstart",g),a.wrapper.addEventListener("mousemove",i),a.wrapper.addEventListener("touchmove",i),document.body.addEventListener("mouseup",h),document.body.addEventListener("touchend",h),a.on("remove",function(){document.body.removeEventListener("mouseup",h),document.body.removeEventListener("touchend",h),a.wrapper.removeEventListener("mousemove",i),a.wrapper.removeEventListener("touchmove",i)}),a.wavesurfer.on("destroy",function(){document.body.removeEventListener("mouseup",h),document.body.removeEventListener("touchend",h)})}()},onDrag:function(a){var b=this.wavesurfer.getDuration();this.end+a>b||this.start+a<0||this.update({start:this.start+a,end:this.end+a})},onResize:function(a,b){"start"==b?this.update({start:Math.min(this.start+a,this.end),end:Math.max(this.start+a,this.end)}):this.update({start:Math.min(this.end+a,this.start),end:Math.max(this.end+a,this.start)})}},a.util.extend(a.Region,a.Observer),a.initRegions=function(){this.regions||(this.regions=Object.create(a.Regions),this.regions.init(this))},a.addRegion=function(a){return this.initRegions(),this.regions.add(a)},a.clearRegions=function(){this.regions&&this.regions.clear()},a.enableDragSelection=function(a){this.initRegions(),this.regions.enableDragSelection(a)},a.disableDragSelection=function(){this.regions.disableDragSelection()}}); \ No newline at end of file +* @license BSD-3-Clause */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.Regions={init:function(a){this.wavesurfer=a,this.wrapper=this.wavesurfer.drawer.wrapper,this.list={}},add:function(b){var c=Object.create(a.Region);return c.init(b,this.wavesurfer),this.list[c.id]=c,c.on("remove",function(){delete this.list[c.id]}.bind(this)),c},clear:function(){Object.keys(this.list).forEach(function(a){this.list[a].remove()},this)},enableDragSelection:function(a){var b,c,d,e,f=this,g=a.slop||2,h=0,i=function(a){a.touches&&a.touches.length>1||a.target.childElementCount>0||(e=a.targetTouches?a.targetTouches[0].identifier:null,b=!0,c=f.wavesurfer.drawer.handleEvent(a,!0),d=null)};this.wrapper.addEventListener("mousedown",i),this.wrapper.addEventListener("touchstart",i),this.on("disable-drag-selection",function(){f.wrapper.removeEventListener("touchstart",i),f.wrapper.removeEventListener("mousedown",i)});var j=function(a){a.touches&&a.touches.length>1||(b=!1,h=0,d&&(d.fireEvent("update-end",a),f.wavesurfer.fireEvent("region-update-end",d,a)),d=null)};this.wrapper.addEventListener("mouseup",j),this.wrapper.addEventListener("touchend",j),this.on("disable-drag-selection",function(){f.wrapper.removeEventListener("touchend",j),f.wrapper.removeEventListener("mouseup",j)});var k=function(i){if(b&&!(++h<=g||i.touches&&i.touches.length>1||i.targetTouches&&i.targetTouches[0].identifier!=e)){d||(d=f.add(a||{}));var j=f.wavesurfer.getDuration(),k=f.wavesurfer.drawer.handleEvent(i);d.update({start:Math.min(k*j,c*j),end:Math.max(k*j,c*j)})}};this.wrapper.addEventListener("mousemove",k),this.wrapper.addEventListener("touchmove",k),this.on("disable-drag-selection",function(){f.wrapper.removeEventListener("touchmove",k),f.wrapper.removeEventListener("mousemove",k)})},disableDragSelection:function(){this.fireEvent("disable-drag-selection")}},a.util.extend(a.Regions,a.Observer),a.Region={style:a.Drawer.style,init:function(b,c){this.wavesurfer=c,this.wrapper=c.drawer.wrapper,this.id=null==b.id?a.util.getId():b.id,this.start=Number(b.start)||0,this.end=null==b.end?this.start+4/this.wrapper.scrollWidth*this.wavesurfer.getDuration():Number(b.end),this.resize=void 0===b.resize||Boolean(b.resize),this.drag=void 0===b.drag||Boolean(b.drag),this.loop=Boolean(b.loop),this.color=b.color||"rgba(0, 0, 0, 0.1)",this.data=b.data||{},this.attributes=b.attributes||{},this.maxLength=b.maxLength,this.minLength=b.minLength,this.bindInOut(),this.render(),this.onZoom=this.updateRender.bind(this),this.wavesurfer.on("zoom",this.onZoom),this.wavesurfer.fireEvent("region-created",this)},update:function(a){null!=a.start&&(this.start=Number(a.start)),null!=a.end&&(this.end=Number(a.end)),null!=a.loop&&(this.loop=Boolean(a.loop)),null!=a.color&&(this.color=a.color),null!=a.data&&(this.data=a.data),null!=a.resize&&(this.resize=Boolean(a.resize)),null!=a.drag&&(this.drag=Boolean(a.drag)),null!=a.maxLength&&(this.maxLength=Number(a.maxLength)),null!=a.minLength&&(this.minLength=Number(a.minLength)),null!=a.attributes&&(this.attributes=a.attributes),this.updateRender(),this.fireEvent("update"),this.wavesurfer.fireEvent("region-updated",this)},remove:function(){this.element&&(this.wrapper.removeChild(this.element),this.element=null,this.wavesurfer.un("zoom",this.onZoom),this.fireEvent("remove"),this.wavesurfer.fireEvent("region-removed",this))},play:function(){this.wavesurfer.play(this.start,this.end),this.fireEvent("play"),this.wavesurfer.fireEvent("region-play",this)},playLoop:function(){this.play(),this.once("out",this.playLoop.bind(this))},render:function(){var a=document.createElement("region");a.className="wavesurfer-region",a.title=this.formatTime(this.start,this.end),a.setAttribute("data-id",this.id);for(var b in this.attributes)a.setAttribute("data-region-"+b,this.attributes[b]);this.wrapper.scrollWidth;if(this.style(a,{position:"absolute",zIndex:2,height:"100%",top:"0px"}),this.resize){var c=a.appendChild(document.createElement("handle")),d=a.appendChild(document.createElement("handle"));c.className="wavesurfer-handle wavesurfer-handle-start",d.className="wavesurfer-handle wavesurfer-handle-end";var e={cursor:"col-resize",position:"absolute",left:"0px",top:"0px",width:"1%",maxWidth:"4px",height:"100%"};this.style(c,e),this.style(d,e),this.style(d,{left:"100%"})}this.element=this.wrapper.appendChild(a),this.updateRender(),this.bindEvents(a)},formatTime:function(a,b){return(a==b?[a]:[a,b]).map(function(a){return[Math.floor(a%3600/60),("00"+Math.floor(a%60)).slice(-2)].join(":")}).join("-")},getWidth:function(){return this.wavesurfer.drawer.width/this.wavesurfer.params.pixelRatio},updateRender:function(){var a=this.wavesurfer.getDuration(),b=this.getWidth();if(this.start<0&&(this.start=0,this.end=this.end-this.start),this.end>a&&(this.end=a,this.start=a-(this.end-this.start)),null!=this.minLength&&(this.end=Math.max(this.start+this.minLength,this.end)),null!=this.maxLength&&(this.end=Math.min(this.start+this.maxLength,this.end)),null!=this.element){var c=Math.round(this.start/a*b),d=Math.round(this.end/a*b)-c;this.style(this.element,{left:c+"px",width:d+"px",backgroundColor:this.color,cursor:this.drag?"move":"default"});for(var e in this.attributes)this.element.setAttribute("data-region-"+e,this.attributes[e]);this.element.title=this.formatTime(this.start,this.end)}},bindInOut:function(){var a=this;a.firedIn=!1,a.firedOut=!1;var b=function(b){!a.firedOut&&a.firedIn&&(a.start>=Math.round(100*b)/100||a.end<=Math.round(100*b)/100)&&(a.firedOut=!0,a.firedIn=!1,a.fireEvent("out"),a.wavesurfer.fireEvent("region-out",a)),!a.firedIn&&a.start<=b&&a.end>b&&(a.firedIn=!0,a.firedOut=!1,a.fireEvent("in"),a.wavesurfer.fireEvent("region-in",a))};this.wavesurfer.backend.on("audioprocess",b),this.on("remove",function(){a.wavesurfer.backend.un("audioprocess",b)}),this.on("out",function(){a.loop&&a.wavesurfer.play(a.start)})},bindEvents:function(){var a=this;this.element.addEventListener("mouseenter",function(b){a.fireEvent("mouseenter",b),a.wavesurfer.fireEvent("region-mouseenter",a,b)}),this.element.addEventListener("mouseleave",function(b){a.fireEvent("mouseleave",b),a.wavesurfer.fireEvent("region-mouseleave",a,b)}),this.element.addEventListener("click",function(b){b.preventDefault(),a.fireEvent("click",b),a.wavesurfer.fireEvent("region-click",a,b)}),this.element.addEventListener("dblclick",function(b){b.stopPropagation(),b.preventDefault(),a.fireEvent("dblclick",b),a.wavesurfer.fireEvent("region-dblclick",a,b)}),(this.drag||this.resize)&&function(){var b,c,d,e,f=a.wavesurfer.getDuration(),g=function(g){g.touches&&g.touches.length>1||(e=g.targetTouches?g.targetTouches[0].identifier:null,g.stopPropagation(),d=a.wavesurfer.drawer.handleEvent(g,!0)*f,"handle"==g.target.tagName.toLowerCase()?c=g.target.classList.contains("wavesurfer-handle-start")?"start":"end":(b=!0,c=!1))},h=function(d){d.touches&&d.touches.length>1||(b||c)&&(b=!1,c=!1,a.fireEvent("update-end",d),a.wavesurfer.fireEvent("region-update-end",a,d))},i=function(g){if(!(g.touches&&g.touches.length>1)&&(!g.targetTouches||g.targetTouches[0].identifier==e)&&(b||c)){var h=a.wavesurfer.drawer.handleEvent(g)*f,i=h-d;d=h,a.drag&&b&&a.onDrag(i),a.resize&&c&&a.onResize(i,c)}};a.element.addEventListener("mousedown",g),a.element.addEventListener("touchstart",g),a.wrapper.addEventListener("mousemove",i),a.wrapper.addEventListener("touchmove",i),document.body.addEventListener("mouseup",h),document.body.addEventListener("touchend",h),a.on("remove",function(){document.body.removeEventListener("mouseup",h),document.body.removeEventListener("touchend",h),a.wrapper.removeEventListener("mousemove",i),a.wrapper.removeEventListener("touchmove",i)}),a.wavesurfer.on("destroy",function(){document.body.removeEventListener("mouseup",h),document.body.removeEventListener("touchend",h)})}()},onDrag:function(a){var b=this.wavesurfer.getDuration();this.end+a>b||this.start+a<0||this.update({start:this.start+a,end:this.end+a})},onResize:function(a,b){"start"==b?this.update({start:Math.min(this.start+a,this.end),end:Math.max(this.start+a,this.end)}):this.update({start:Math.min(this.end+a,this.start),end:Math.max(this.end+a,this.start)})}},a.util.extend(a.Region,a.Observer),a.initRegions=function(){this.regions||(this.regions=Object.create(a.Regions),this.regions.init(this))},a.addRegion=function(a){return this.initRegions(),this.regions.add(a)},a.clearRegions=function(){this.regions&&this.regions.clear()},a.enableDragSelection=function(a){this.initRegions(),this.regions.enableDragSelection(a)},a.disableDragSelection=function(){this.regions.disableDragSelection()}}); \ No newline at end of file diff --git a/dist/plugin/wavesurfer.spectrogram.min.js b/dist/plugin/wavesurfer.spectrogram.min.js index 990bde300..55d2c64ec 100644 --- a/dist/plugin/wavesurfer.spectrogram.min.js +++ b/dist/plugin/wavesurfer.spectrogram.min.js @@ -1,3 +1,3 @@ -/*! wavesurfer.js 1.2.4 (Fri, 11 Nov 2016 15:43:23 GMT) +/*! wavesurfer.js 1.4.0 (Mon, 10 Apr 2017 08:55:36 GMT) * https://github.com/katspaugh/wavesurfer.js -* @license CC-BY-3.0 */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.Spectrogram={init:function(a){this.params=a;var b=this.wavesurfer=a.wavesurfer;if(!this.wavesurfer)throw Error("No WaveSurfer instance provided");this.frequenciesDataUrl=a.frequenciesDataUrl;var c=this.drawer=this.wavesurfer.drawer;if(this.container="string"==typeof a.container?document.querySelector(a.container):a.container,!this.container)throw Error("No container for WaveSurfer spectrogram");this.width=c.width,this.pixelRatio=this.params.pixelRatio||b.params.pixelRatio,this.fftSamples=this.params.fftSamples||b.params.fftSamples||512,this.height=this.fftSamples/2,this.noverlap=a.noverlap,this.windowFunc=a.windowFunc,this.alpha=a.alpha,this.createWrapper(),this.createCanvas(),this.render(),c.wrapper.addEventListener("scroll",function(a){this.updateScroll(a)}.bind(this)),b.on("redraw",this.render.bind(this)),b.on("destroy",this.destroy.bind(this))},destroy:function(){this.unAll(),this.wrapper&&(this.wrapper.parentNode.removeChild(this.wrapper),this.wrapper=null)},createWrapper:function(){var a=this.container.querySelector("spectrogram");a&&this.container.removeChild(a);var b=this.wavesurfer.params;this.wrapper=this.container.appendChild(document.createElement("spectrogram")),this.drawer.style(this.wrapper,{display:"block",position:"relative",userSelect:"none",webkitUserSelect:"none",height:this.height+"px"}),(b.fillParent||b.scrollParent)&&this.drawer.style(this.wrapper,{width:"100%",overflowX:"hidden",overflowY:"hidden"});var c=this;this.wrapper.addEventListener("click",function(a){a.preventDefault();var b="offsetX"in a?a.offsetX:a.layerX;c.fireEvent("click",b/c.scrollWidth||0)})},createCanvas:function(){var a=this.canvas=this.wrapper.appendChild(document.createElement("canvas"));this.spectrCc=a.getContext("2d"),this.wavesurfer.drawer.style(a,{position:"absolute",zIndex:4})},render:function(){this.updateCanvasStyle(),this.frequenciesDataUrl?this.loadFrequenciesData(this.frequenciesDataUrl):this.getFrequencies(this.drawSpectrogram)},updateCanvasStyle:function(){var a=Math.round(this.width/this.pixelRatio)+"px";this.canvas.width=this.width,this.canvas.height=this.height,this.canvas.style.width=a},drawSpectrogram:function(a,b){for(var c=(b.spectrCc,b.wavesurfer.backend.getDuration(),b.height),d=b.resample(a),e=b.buffer?2/b.buffer.numberOfChannels:1,f=0;f0)for(var n=0;n>1;f>=1}for(e=0;ethis.peak&&(this.peakBand=x,this.peak=d),m[x]=d;return m}},a.util.extend(a.Spectrogram,a.Observer,a.FFT)}); \ No newline at end of file +* @license BSD-3-Clause */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.Spectrogram={init:function(a){this.params=a;var b=this.wavesurfer=a.wavesurfer;if(!this.wavesurfer)throw Error("No WaveSurfer instance provided");this.frequenciesDataUrl=a.frequenciesDataUrl;var c=this.drawer=this.wavesurfer.drawer;if(this.container="string"==typeof a.container?document.querySelector(a.container):a.container,!this.container)throw Error("No container for WaveSurfer spectrogram");this.width=c.width,this.pixelRatio=this.params.pixelRatio||b.params.pixelRatio,this.fftSamples=this.params.fftSamples||b.params.fftSamples||512,this.height=this.fftSamples/2,this.noverlap=a.noverlap,this.windowFunc=a.windowFunc,this.alpha=a.alpha,this.createWrapper(),this.createCanvas(),this.render(),c.wrapper.addEventListener("scroll",function(a){this.updateScroll(a)}.bind(this)),b.on("redraw",this.render.bind(this)),b.on("destroy",this.destroy.bind(this))},destroy:function(){this.unAll(),this.wrapper&&(this.wrapper.parentNode.removeChild(this.wrapper),this.wrapper=null)},createWrapper:function(){var a=this.container.querySelector("spectrogram");if(a&&this.container.removeChild(a),this.params.labels){var b=document.createElement("div");b.setAttribute("id","specLabels"),this.drawer.style(b,{left:0,position:"relative",zIndex:9}),b.innerHTML="",this.wrapper=this.container.appendChild(b),this.loadLabels("rgba(68,68,68,0.5)","12px","10px","","#fff","#f7f7f7","center","#specLabels")}var c=this.wavesurfer.params,d=document.createElement("spectrogram");this.params.labels&&this.drawer.style(d,{left:0,position:"relative"}),this.wrapper=this.container.appendChild(d),this.drawer.style(this.wrapper,{display:"block",position:"relative",userSelect:"none",webkitUserSelect:"none",height:this.height+"px"}),(c.fillParent||c.scrollParent)&&this.drawer.style(this.wrapper,{width:"100%",overflowX:"hidden",overflowY:"hidden"});var e=this;this.wrapper.addEventListener("click",function(a){a.preventDefault();var b="offsetX"in a?a.offsetX:a.layerX;e.fireEvent("click",b/e.scrollWidth||0)})},createCanvas:function(){var a=this.canvas=this.wrapper.appendChild(document.createElement("canvas"));this.spectrCc=a.getContext("2d"),this.wavesurfer.drawer.style(a,{position:"absolute",zIndex:4})},render:function(){this.updateCanvasStyle(),this.frequenciesDataUrl?this.loadFrequenciesData(this.frequenciesDataUrl):this.getFrequencies(this.drawSpectrogram)},updateCanvasStyle:function(){var a=Math.round(this.width/this.pixelRatio)+"px";this.canvas.width=this.width,this.canvas.height=this.height,this.canvas.style.width=a},drawSpectrogram:function(a,b){for(var c=(b.spectrCc,b.wavesurfer.backend.getDuration(),b.height),d=b.resample(a),e=b.buffer?2/b.buffer.numberOfChannels:1,f=0;f=1e3?(a/1e3).toFixed(1):Math.round(a)},unitType:function(a){return a>=1e3?"KHz":"Hz"},loadLabels:function(a,b,c,d,e,f,g,h){var i=this.height,a=a||"rgba(68,68,68,0.5)",b=b||"12px",c=c||"10px",d=d||"Helvetica",e=e||"#fff",f=f||"#fff",g=g||"center",h=h||"#specLabels",j=i||512,k=5*(j/256),l=0,m=(this.wavesurfer.backend.ac.sampleRate/2-l)/k,n=document.querySelectorAll(h+" canvas")[0].getContext("2d");document.querySelectorAll(h+" canvas")[0].height=this.height,document.querySelectorAll(h+" canvas")[0].width=55,n.fillStyle=a,n.fillRect(0,0,55,j),n.fill();for(var o=0;o<=k;o++){n.textAlign=g,n.textBaseline="middle";var p=l+m*o,q=Math.round(p/this.sampleRate/2*this.fftSamples),q=Math.round(p/(this.fftSamples/2)*this.fftSamples),r=q/this.fftSamples/2,s=((1-r)*this.height,this.freqType(p)),t=this.unitType(p),u=16,v=2;0==o?(n.fillStyle=f,n.font=c+" "+d,n.fillText(t,u+24,j+o-10),n.fillStyle=e,n.font=b+" "+d,n.fillText(s,u,j+o-10)):(n.fillStyle=f,n.font=c+" "+d,n.fillText(t,u+24,j-50*o+v),n.fillStyle=e,n.font=b+" "+d,n.fillText(s,u,j-50*o+v))}},updateScroll:function(a){this.wrapper.scrollLeft=a.target.scrollLeft},resample:function(a,b){for(var b=this.width,c=[],d=1/a.length,e=1/b,f=0;f0)for(var n=0;n>1;f>=1}for(e=0;ethis.peak&&(this.peakBand=x,this.peak=d),m[x]=d;return m}},a.util.extend(a.Spectrogram,a.Observer,a.FFT)}); \ No newline at end of file diff --git a/dist/plugin/wavesurfer.timeline.min.js b/dist/plugin/wavesurfer.timeline.min.js index c6d2f7b95..5977a579e 100644 --- a/dist/plugin/wavesurfer.timeline.min.js +++ b/dist/plugin/wavesurfer.timeline.min.js @@ -1,3 +1,3 @@ -/*! wavesurfer.js 1.2.4 (Fri, 11 Nov 2016 15:43:23 GMT) +/*! wavesurfer.js 1.4.0 (Mon, 10 Apr 2017 08:55:36 GMT) * https://github.com/katspaugh/wavesurfer.js -* @license CC-BY-3.0 */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.Timeline={init:function(a){this.params=a;var b=this.wavesurfer=a.wavesurfer;if(!this.wavesurfer)throw Error("No WaveSurfer intance provided");var c=this.drawer=this.wavesurfer.drawer;if(this.container="string"==typeof a.container?document.querySelector(a.container):a.container,!this.container)throw Error("No container for WaveSurfer timeline");this.width=c.width,this.pixelRatio=this.drawer.params.pixelRatio,this.maxCanvasWidth=c.maxCanvasWidth||this.width,this.maxCanvasElementWidth=c.maxCanvasElementWidth||Math.round(this.maxCanvasWidth/this.pixelRatio),this.height=this.params.height||20,this.notchPercentHeight=this.params.notchPercentHeight||90,this.primaryColor=this.params.primaryColor||"#000",this.secondaryColor=this.params.secondaryColor||"#c0c0c0",this.primaryFontColor=this.params.primaryFontColor||"#000",this.secondaryFontColor=this.params.secondaryFontColor||"#000",this.fontFamily=this.params.fontFamily||"Arial",this.fontSize=this.params.fontSize||10,this.timeInterval=this.params.timeInterval,this.primaryLabelInterval=this.params.primaryLabelInterval,this.secondaryLabelInterval=this.params.secondaryLabelInterval,this.formatTimeCallback=this.params.formatTimeCallback,this.canvases=[],this.createWrapper(),this.render(),c.wrapper.addEventListener("scroll",function(a){this.updateScroll(a)}.bind(this)),b.on("redraw",this.render.bind(this)),b.on("destroy",this.destroy.bind(this))},destroy:function(){this.unAll(),this.wrapper&&this.wrapper.parentNode&&(this.wrapper.parentNode.removeChild(this.wrapper),this.wrapper=null)},createWrapper:function(){var a=this.container.querySelector("timeline");a&&this.container.removeChild(a);var b=this.wavesurfer.params;this.wrapper=this.container.appendChild(document.createElement("timeline")),this.drawer.style(this.wrapper,{display:"block",position:"relative",userSelect:"none",webkitUserSelect:"none",height:this.height+"px"}),(b.fillParent||b.scrollParent)&&this.drawer.style(this.wrapper,{width:"100%",overflowX:"hidden",overflowY:"hidden"});var c=this;this.wrapper.addEventListener("click",function(a){a.preventDefault();var b="offsetX"in a?a.offsetX:a.layerX;c.fireEvent("click",b/c.wrapper.scrollWidth||0)})},removeOldCanvases:function(){for(;this.canvases.length>0;){var a=this.canvases.pop();a.parentElement.removeChild(a)}},createCanvases:function(){this.removeOldCanvases();for(var a,b=Math.round(this.drawer.wrapper.scrollWidth),c=Math.ceil(b/this.maxCanvasElementWidth),d=0;d1){var b=parseInt(a/60),a=parseInt(a%60);return a=a<10?"0"+a:a,""+b+":"+a}return a};if(1*f>=25)var k=1,l=10,m=5;else if(5*f>=25)var k=5,l=6,m=2;else if(15*f>=25)var k=15,l=4,m=2;else var k=60,l=4,m=2;k=this.timeInterval||k,l=this.primaryLabelInterval||l,m=this.secondaryLabelInterval||m;for(var n=this.height-4,o=this.height*(this.notchPercentHeight/100)-4,p=this.fontSize*b.pixelRatio,q=0;qb+d)break;e+h>b&&(d=g.measureText(a).width,g.fillText(a,b-e,c)),e+=h}},updateScroll:function(){this.wrapper.scrollLeft=this.drawer.wrapper.scrollLeft}},a.util.extend(a.Timeline,a.Observer)}); \ No newline at end of file +* @license BSD-3-Clause */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("wavesurfer.js")):b(WaveSurfer)}(this,function(a){"use strict";a.Timeline={init:function(a){this.params=a;var b=this.wavesurfer=a.wavesurfer;if(!this.wavesurfer)throw Error("No WaveSurfer instance provided");var c=this.drawer=this.wavesurfer.drawer;if(this.container="string"==typeof a.container?document.querySelector(a.container):a.container,!this.container)throw Error("No container for WaveSurfer timeline");this.width=c.width,this.pixelRatio=this.drawer.params.pixelRatio,this.maxCanvasWidth=c.maxCanvasWidth||this.width,this.maxCanvasElementWidth=c.maxCanvasElementWidth||Math.round(this.maxCanvasWidth/this.pixelRatio),this.height=this.params.height||20,this.notchPercentHeight=this.params.notchPercentHeight||90,this.primaryColor=this.params.primaryColor||"#000",this.secondaryColor=this.params.secondaryColor||"#c0c0c0",this.primaryFontColor=this.params.primaryFontColor||"#000",this.secondaryFontColor=this.params.secondaryFontColor||"#000",this.fontFamily=this.params.fontFamily||"Arial",this.fontSize=this.params.fontSize||10,this.timeInterval=this.params.timeInterval,this.primaryLabelInterval=this.params.primaryLabelInterval,this.secondaryLabelInterval=this.params.secondaryLabelInterval,this.formatTimeCallback=this.params.formatTimeCallback,this.canvases=[],this.createWrapper(),this.render(),c.wrapper.addEventListener("scroll",function(a){this.updateScroll(a)}.bind(this)),this._onRedraw=b.on("redraw",this.render.bind(this)),this._onZoom=b.on("zoom",this.render.bind(this)),this._onDestroy=b.on("destroy",this.destroy.bind(this))},destroy:function(){this._onRedraw.un(),this._onZoom.un(),this._onDestroy.un(),this.unAll(),this.wrapper&&this.wrapper.parentNode&&(this.wrapper.parentNode.removeChild(this.wrapper),this.wrapper=null)},createWrapper:function(){var a=this.container.querySelector("timeline");a&&this.container.removeChild(a);var b=this.wavesurfer.params;this.wrapper=this.container.appendChild(document.createElement("timeline")),this.drawer.style(this.wrapper,{display:"block",position:"relative",userSelect:"none",webkitUserSelect:"none",height:this.height+"px"}),(b.fillParent||b.scrollParent)&&this.drawer.style(this.wrapper,{width:"100%",overflowX:"hidden",overflowY:"hidden"});var c=this;this.wrapper.addEventListener("click",function(a){a.preventDefault();var b="offsetX"in a?a.offsetX:a.layerX;c.fireEvent("click",b/c.wrapper.scrollWidth||0)})},removeOldCanvases:function(){for(;this.canvases.length>0;){var a=this.canvases.pop();a.parentElement.removeChild(a)}},createCanvases:function(){this.removeOldCanvases();for(var a,b=Math.round(this.drawer.wrapper.scrollWidth),c=Math.ceil(b/this.maxCanvasElementWidth),d=0;d1){var b=parseInt(a/60),a=parseInt(a%60);return a=a<10?"0"+a:a,""+b+":"+a}return a};if(1*f>=25)var k=1,l=10,m=5;else if(5*f>=25)var k=5,l=6,m=2;else if(15*f>=25)var k=15,l=4,m=2;else var k=60*Math.ceil(.5/f),l=4,m=2;k=this.timeInterval||k,l=this.primaryLabelInterval||l,m=this.secondaryLabelInterval||m;for(var n=this.height-4,o=this.height*(this.notchPercentHeight/100)-4,p=this.fontSize*b.pixelRatio,q=0;qb+d)break;e+h>b&&(d=g.measureText(a).width,g.fillText(a,b-e,c)),e+=h}},updateScroll:function(){this.wrapper.scrollLeft=this.drawer.wrapper.scrollLeft}},a.util.extend(a.Timeline,a.Observer)}); \ No newline at end of file diff --git a/dist/wavesurfer.js b/dist/wavesurfer.js index 8bc8d8db5..09ebad4fc 100644 --- a/dist/wavesurfer.js +++ b/dist/wavesurfer.js @@ -18,31 +18,35 @@ var WaveSurfer = { defaultParams: { - height : 128, - waveColor : '#999', - progressColor : '#555', + audioContext : null, + audioRate : 1, + autoCenter : true, + backend : 'WebAudio', + barHeight : 1, + closeAudioContext: false, + container : null, cursorColor : '#333', cursorWidth : 1, - skipLength : 2, - minPxPerSec : 20, - pixelRatio : window.devicePixelRatio || screen.deviceXDPI / screen.logicalXDPI, + dragSelection : true, fillParent : true, - scrollParent : false, + forceDecode : false, + height : 128, hideScrollbar : false, - normalize : false, - audioContext : null, - container : null, - dragSelection : true, - loopSelection : true, - audioRate : 1, interact : true, - splitChannels : false, + loopSelection : true, mediaContainer: null, mediaControls : false, - renderer : 'Canvas', - backend : 'WebAudio', mediaType : 'audio', - autoCenter : true + minPxPerSec : 20, + partialRender : false, + pixelRatio : window.devicePixelRatio || screen.deviceXDPI / screen.logicalXDPI, + progressColor : '#555', + normalize : false, + renderer : 'MultiCanvas', + scrollParent : false, + skipLength : 2, + splitChannels : false, + waveColor : '#999', }, init: function (params) { @@ -85,6 +89,7 @@ var WaveSurfer = { this.createDrawer(); this.createBackend(); + this.createPeakCache(); this.isDestroyed = false; }, @@ -109,6 +114,9 @@ var WaveSurfer = { // Relay the scroll event from the drawer this.drawer.on('scroll', function (e) { + if (my.params.partialRender) { + my.drawBuffer(); + } my.fireEvent('scroll', e); }); }, @@ -142,6 +150,13 @@ var WaveSurfer = { }); }, + createPeakCache: function() { + if (this.params.partialRender) { + this.peakCache = Object.create(WaveSurfer.PeakCache); + this.peakCache.init(); + } + }, + getDuration: function () { return this.backend.getDuration(); }, @@ -156,7 +171,7 @@ var WaveSurfer = { }, pause: function () { - this.backend.pause(); + this.backend.isPaused() || this.backend.pause(); }, playPause: function () { @@ -191,16 +206,17 @@ var WaveSurfer = { this.fireEvent('interaction', this.seekTo.bind(this, progress)); var paused = this.backend.isPaused(); + // avoid draw wrong position while playing backward seeking + if (!paused) { + this.backend.pause(); + } // avoid small scrolls while paused seeking var oldScrollParent = this.params.scrollParent; - if (paused) { - this.params.scrollParent = false; - } + this.params.scrollParent = false; this.backend.seekTo(progress * this.getDuration()); this.drawer.progress(this.backend.getPlayedPercents()); if (!paused) { - this.backend.pause(); this.backend.play(); } this.params.scrollParent = oldScrollParent; @@ -223,6 +239,13 @@ var WaveSurfer = { this.backend.setVolume(newVolume); }, + /** + * Get the playback volume. + */ + getVolume: function () { + return this.backend.getVolume(); + }, + /** * Set the playback rate. * @@ -233,6 +256,13 @@ var WaveSurfer = { this.backend.setPlaybackRate(rate); }, + /** + * Get the playback rate. + */ + getPlaybackRate: function () { + return this.backend.getPlaybackRate(); + }, + /** * Toggle the volume on and off. It not currenly muted it will * save the current volume value and turn the volume off. @@ -263,6 +293,22 @@ var WaveSurfer = { } }, + /** + * Get the current mute status. + */ + getMute: function () { + return this.isMuted; + }, + + /** + * Get the list of current set filters as an array. + * + * Filters must be set with setFilters method first + */ + getFilters: function() { + return this.backend.filters || []; + }, + toggleScroll: function () { this.params.scrollParent = !this.params.scrollParent; this.drawBuffer(); @@ -278,14 +324,28 @@ var WaveSurfer = { ); var parentWidth = this.drawer.getWidth(); var width = nominalWidth; + var start = this.drawer.getScrollX(); + var end = Math.min(start + parentWidth, width); // Fill container if (this.params.fillParent && (!this.params.scrollParent || nominalWidth < parentWidth)) { width = parentWidth; + start = 0; + end = width; } - var peaks = this.backend.getPeaks(width); - this.drawer.drawPeaks(peaks, width); + if (this.params.partialRender) { + var newRanges = this.peakCache.addRangeToPeakCache(width, start, end); + for (var i = 0; i < newRanges.length; i++) { + var peaks = this.backend.getPeaks(width, newRanges[i][0], newRanges[i][1]); + this.drawer.drawPeaks(peaks, width, newRanges[i][0], newRanges[i][1]); + } + } else { + start = 0; + end = width; + var peaks = this.backend.getPeaks(width, start, end); + this.drawer.drawPeaks(peaks, width, start, end); + } this.fireEvent('redraw', peaks, width); }, @@ -350,6 +410,7 @@ var WaveSurfer = { */ load: function (url, peaks, preload) { this.empty(); + this.isMuted = false; switch (this.params.backend) { case 'WebAudio': return this.loadBuffer(url, peaks); @@ -411,14 +472,16 @@ var WaveSurfer = { }).bind(this)) ); - // If no pre-decoded peaks provided, attempt to download the + // If no pre-decoded peaks provided or pre-decoded peaks are + // provided with forceDecode flag, attempt to download the // audio file and decode it with Web Audio. - if (peaks) { - this.backend.setPeaks(peaks); - } else if (this.backend.supportsWebAudio()) { + if (peaks) { this.backend.setPeaks(peaks); } + + if ((!peaks || this.params.forceDecode) && this.backend.supportsWebAudio()) { this.getArrayBuffer(url, (function (arraybuffer) { this.decodeArrayBuffer(arraybuffer, (function (buffer) { this.backend.buffer = buffer; + this.backend.setPeaks(null); this.drawBuffer(); this.fireEvent('waveform-ready'); }).bind(this)); @@ -573,7 +636,30 @@ WaveSurfer.util = { return dest; }, - min: function(values) { + debounce: function (func, wait, immediate) { + var args, context, timeout; + var later = function() { + timeout = null; + if (!immediate) { + func.apply(context, args); + } + }; + return function() { + context = this; + args = arguments; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (!timeout) { + timeout = setTimeout(later, wait); + } + if (callNow) { + func.apply(context, args); + } + }; + }, + + min: function (values) { var min = +Infinity; for (var i in values) { if (values[i] < min) { @@ -584,7 +670,7 @@ WaveSurfer.util = { return min; }, - max: function(values) { + max: function (values) { var max = -Infinity; for (var i in values) { if (values[i] > max) { @@ -764,6 +850,7 @@ WaveSurfer.WebAudio = { this.setState(this.PAUSED_STATE); this.setPlaybackRate(this.params.audioRate); + this.setLength(0); }, disconnectFilters: function () { @@ -898,27 +985,52 @@ WaveSurfer.WebAudio = { this.peaks = peaks; }, + /** + * Set the rendered length (different from the length of the audio). + */ + setLength: function (length) { + // No resize, we can preserve the cached peaks. + if (this.mergedPeaks && length == ((2 * this.mergedPeaks.length - 1) + 2)) { + return; + } + + this.splitPeaks = []; + this.mergedPeaks = []; + // Set the last element of the sparse array so the peak arrays are + // appropriately sized for other calculations. + var channels = this.buffer ? this.buffer.numberOfChannels : 1; + for (var c = 0; c < channels; c++) { + this.splitPeaks[c] = []; + this.splitPeaks[c][2 * (length - 1)] = 0; + this.splitPeaks[c][2 * (length - 1) + 1] = 0; + } + this.mergedPeaks[2 * (length - 1)] = 0; + this.mergedPeaks[2 * (length - 1) + 1] = 0; + }, + /** * Compute the max and min value of the waveform when broken into * subranges. - * @param {Number} How many subranges to break the waveform into. + * @param {Number} length How many subranges to break the waveform into. + * @param {Number} first First sample in the required range. + * @param {Number} last Last sample in the required range. * @returns {Array} Array of 2* peaks or array of arrays * of peaks consisting of (max, min) values for each subrange. */ - getPeaks: function (length) { + getPeaks: function (length, first, last) { if (this.peaks) { return this.peaks; } + this.setLength(length); + var sampleSize = this.buffer.length / length; var sampleStep = ~~(sampleSize / 10) || 1; var channels = this.buffer.numberOfChannels; - var splitPeaks = []; - var mergedPeaks = []; for (var c = 0; c < channels; c++) { - var peaks = splitPeaks[c] = []; + var peaks = this.splitPeaks[c]; var chan = this.buffer.getChannelData(c); - for (var i = 0; i < length; i++) { + for (var i = first; i <= last; i++) { var start = ~~(i * sampleSize); var end = ~~(start + sampleSize); var min = 0; @@ -939,17 +1051,17 @@ WaveSurfer.WebAudio = { peaks[2 * i] = max; peaks[2 * i + 1] = min; - if (c == 0 || max > mergedPeaks[2 * i]) { - mergedPeaks[2 * i] = max; + if (c == 0 || max > this.mergedPeaks[2 * i]) { + this.mergedPeaks[2 * i] = max; } - if (c == 0 || min < mergedPeaks[2 * i + 1]) { - mergedPeaks[2 * i + 1] = min; + if (c == 0 || min < this.mergedPeaks[2 * i + 1]) { + this.mergedPeaks[2 * i + 1] = min; } } } - return this.params.splitChannels ? splitPeaks : mergedPeaks; + return this.params.splitChannels ? this.splitPeaks : this.mergedPeaks; }, getPlayedPercents: function () { @@ -973,6 +1085,24 @@ WaveSurfer.WebAudio = { this.gainNode.disconnect(); this.scriptNode.disconnect(); this.analyser.disconnect(); + // close the audioContext if closeAudioContext option is set to true + if (this.params.closeAudioContext) { + // check if browser supports AudioContext.close() + if (typeof this.ac.close === 'function' && this.ac.state != 'closed') { + this.ac.close(); + } + // clear the reference to the audiocontext + this.ac = null; + // clear the actual audiocontext, either passed as param or the + // global singleton + if (!this.params.audioContext) { + WaveSurfer.WebAudio.audioContext = null; + } else { + this.params.audioContext = null; + } + // clear the offlineAudioContext + WaveSurfer.WebAudio.offlineAudioContext = null; + } }, load: function (buffer) { @@ -1057,7 +1187,10 @@ WaveSurfer.WebAudio = { this.scheduledPause = end; this.source.start(0, start, end - start); - this.ac.resume(); + + if (this.ac.state == 'suspended') { + this.ac.resume && this.ac.resume(); + } this.setState(this.PLAYING_STATE); @@ -1085,6 +1218,13 @@ WaveSurfer.WebAudio = { return this.state.getCurrentTime.call(this); }, + /** + * Returns the current playback rate. + */ + getPlaybackRate: function () { + return this.playbackRate; + }, + /** * Set the audio source playback rate. */ @@ -1242,7 +1382,9 @@ WaveSurfer.util.extend(WaveSurfer.MediaElement, { // load must be called manually on iOS, otherwise peaks won't draw // until a user interaction triggers load --> 'ready' event - media.load(); + if (typeof media.load == 'function') { + media.load(); + } media.addEventListener('error', function () { my.fireEvent('error', 'Error loading media element'); @@ -1268,7 +1410,7 @@ WaveSurfer.util.extend(WaveSurfer.MediaElement, { }, getDuration: function () { - var duration = this.media.duration; + var duration = (this.buffer || this.media).duration; if (duration >= Infinity) { // streaming audio duration = this.media.seekable.end(0); } @@ -1283,6 +1425,10 @@ WaveSurfer.util.extend(WaveSurfer.MediaElement, { return (this.getCurrentTime() / this.getDuration()) || 0; }, + getPlaybackRate: function () { + return this.playbackRate || this.media.playbackRate; + }, + /** * Set the audio source playback rate. */ @@ -1340,9 +1486,9 @@ WaveSurfer.util.extend(WaveSurfer.MediaElement, { } }, - getPeaks: function (length) { + getPeaks: function (length, start, end) { if (this.buffer) { - return WaveSurfer.WebAudio.getPeaks.call(this, length); + return WaveSurfer.WebAudio.getPeaks.call(this, length, start, end); } return this.peaks || []; }, @@ -1455,13 +1601,12 @@ WaveSurfer.Drawer = { }); }, - drawPeaks: function (peaks, length) { - this.resetScroll(); + drawPeaks: function (peaks, length, start, end) { this.setWidth(length); this.params.barWidth ? - this.drawBars(peaks) : - this.drawWave(peaks); + this.drawBars(peaks, 0, start, end) : + this.drawWave(peaks, 0, start, end); }, style: function (el, styles) { @@ -1513,11 +1658,19 @@ WaveSurfer.Drawer = { }, + getScrollX: function() { + return Math.round(this.wrapper.scrollLeft * this.params.pixelRatio); + }, + getWidth: function () { return Math.round(this.container.clientWidth * this.params.pixelRatio); }, setWidth: function (width) { + if (this.width == width) { + return; + } + this.width = width; if (this.params.fillParent || this.params.scrollParent) { @@ -1554,7 +1707,7 @@ WaveSurfer.Drawer = { this.recenterOnPosition(newPos); } - this.updateProgress(progress); + this.updateProgress(pos); } }, @@ -1649,13 +1802,16 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.Canvas, { } }, - drawBars: function (peaks, channelIndex) { + drawBars: function (peaks, channelIndex, start, end) { + var my = this; // Split channels if (peaks[0] instanceof Array) { var channels = peaks; if (this.params.splitChannels) { this.setHeight(channels.length * this.params.height * this.params.pixelRatio); - channels.forEach(this.drawBars, this); + channels.forEach(function(channelPeaks, i) { + my.drawBars(channelPeaks, i, start, end); + }); return; } else { peaks = channels[0]; @@ -1665,8 +1821,10 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.Canvas, { // Bar wave draws the bottom only as a reflection of the top, // so we don't need negative values var hasMinVals = [].some.call(peaks, function (val) { return val < 0; }); + // Skip every other value if there are negatives. + var peakIndexScale = 1; if (hasMinVals) { - peaks = [].filter.call(peaks, function (_, index) { return index % 2 == 0; }); + peakIndexScale = 2; } // A half-pixel offset makes lines crisp @@ -1675,14 +1833,16 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.Canvas, { var height = this.params.height * this.params.pixelRatio; var offsetY = height * channelIndex || 0; var halfH = height / 2; - var length = peaks.length; + var length = peaks.length / peakIndexScale; var bar = this.params.barWidth * this.params.pixelRatio; var gap = Math.max(this.params.pixelRatio, ~~(bar / 2)); var step = bar + gap; - var absmax = 1; + var absmax = 1 / this.params.barHeight; if (this.params.normalize) { - absmax = WaveSurfer.util.max(peaks); + var max = WaveSurfer.util.max(peaks); + var min = WaveSurfer.util.min(peaks); + absmax = -min > max ? -min : max; } var scale = length / width; @@ -1695,20 +1855,24 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.Canvas, { [ this.waveCc, this.progressCc ].forEach(function (cc) { if (!cc) { return; } - for (var i = 0; i < width; i += step) { - var h = Math.round(peaks[Math.floor(i * scale)] / absmax * halfH); + for (var i = (start / scale); i < (end / scale); i += step) { + var peak = peaks[Math.floor(i * scale * peakIndexScale)] || 0; + var h = Math.round(peak / absmax * halfH); cc.fillRect(i + $, halfH - h + offsetY, bar + $, h * 2); } }, this); }, - drawWave: function (peaks, channelIndex) { + drawWave: function (peaks, channelIndex, start, end) { + var my = this; // Split channels if (peaks[0] instanceof Array) { var channels = peaks; if (this.params.splitChannels) { this.setHeight(channels.length * this.params.height * this.params.pixelRatio); - channels.forEach(this.drawWave, this); + channels.forEach(function(channelPeaks, i) { + my.drawWave(channelPeaks, i, start, end); + }); return; } else { peaks = channels[0]; @@ -1738,7 +1902,7 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.Canvas, { scale = this.width / length; } - var absmax = 1; + var absmax = 1 / this.params.barHeight; if (this.params.normalize) { var max = WaveSurfer.util.max(peaks); var min = WaveSurfer.util.min(peaks); @@ -1754,16 +1918,16 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.Canvas, { if (!cc) { return; } cc.beginPath(); - cc.moveTo($, halfH + offsetY); + cc.moveTo(start * scale + $, halfH + offsetY); - for (var i = 0; i < length; i++) { + for (var i = start; i < end; i++) { var h = Math.round(peaks[2 * i] / absmax * halfH); cc.lineTo(i * scale + $, halfH - h + offsetY); } // Draw the bottom edge going backwards, to make a single // closed hull to fill. - for (var i = length - 1; i >= 0; i--) { + for (var i = end - 1; i >= start; i--) { var h = Math.round(peaks[2 * i + 1] / absmax * halfH); cc.lineTo(i * scale + $, halfH - h + offsetY); } @@ -1776,10 +1940,7 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.Canvas, { }, this); }, - updateProgress: function (progress) { - var pos = Math.round( - this.width * progress - ) / this.params.pixelRatio; + updateProgress: function (pos) { this.style(this.progressWave, { width: pos + 'px' }); }, @@ -1855,7 +2016,7 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, { } }, - addCanvas: function () { + addCanvas: function () { var entry = {}, leftOffset = this.maxCanvasElementWidth * this.canvases.length; @@ -1865,7 +2026,8 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, { zIndex: 1, left: leftOffset + 'px', top: 0, - bottom: 0 + bottom: 0, + height: '100%' }) ); entry.waveCtx = entry.wave.getContext('2d'); @@ -1876,7 +2038,8 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, { position: 'absolute', left: leftOffset + 'px', top: 0, - bottom: 0 + bottom: 0, + height: '100%' }) ); entry.progressCtx = entry.progress.getContext('2d'); @@ -1927,13 +2090,16 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, { } }, - drawBars: function (peaks, channelIndex) { + drawBars: function (peaks, channelIndex, start, end) { + var my = this; // Split channels if (peaks[0] instanceof Array) { var channels = peaks; if (this.params.splitChannels) { this.setHeight(channels.length * this.params.height * this.params.pixelRatio); - channels.forEach(this.drawBars, this); + channels.forEach(function(channelPeaks, i) { + my.drawBars(channelPeaks, i, start, end); + }); return; } else { peaks = channels[0]; @@ -1943,8 +2109,10 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, { // Bar wave draws the bottom only as a reflection of the top, // so we don't need negative values var hasMinVals = [].some.call(peaks, function (val) { return val < 0; }); + // Skip every other value if there are negatives. + var peakIndexScale = 1; if (hasMinVals) { - peaks = [].filter.call(peaks, function (_, index) { return index % 2 == 0; }); + peakIndexScale = 2; } // A half-pixel offset makes lines crisp @@ -1952,31 +2120,37 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, { var height = this.params.height * this.params.pixelRatio; var offsetY = height * channelIndex || 0; var halfH = height / 2; - var length = peaks.length; + var length = peaks.length / peakIndexScale; var bar = this.params.barWidth * this.params.pixelRatio; var gap = Math.max(this.params.pixelRatio, ~~(bar / 2)); var step = bar + gap; - var absmax = 1; + var absmax = 1 / this.params.barHeight; if (this.params.normalize) { - absmax = WaveSurfer.util.max(peaks); + var max = WaveSurfer.util.max(peaks); + var min = WaveSurfer.util.min(peaks); + absmax = -min > max ? -min : max; } var scale = length / width; - for (var i = 0; i < width; i += step) { - var h = Math.round(peaks[Math.floor(i * scale)] / absmax * halfH); + for (var i = (start / scale); i < (end / scale); i += step) { + var peak = peaks[Math.floor(i * scale * peakIndexScale)] || 0; + var h = Math.round(peak / absmax * halfH); this.fillRect(i + this.halfPixel, halfH - h + offsetY, bar + this.halfPixel, h * 2); } }, - drawWave: function (peaks, channelIndex) { + drawWave: function (peaks, channelIndex, start, end) { + var my = this; // Split channels if (peaks[0] instanceof Array) { var channels = peaks; if (this.params.splitChannels) { this.setHeight(channels.length * this.params.height * this.params.pixelRatio); - channels.forEach(this.drawWave, this); + channels.forEach(function(channelPeaks, i) { + my.drawWave(channelPeaks, i, start, end); + }); return; } else { peaks = channels[0]; @@ -1999,31 +2173,31 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, { var offsetY = height * channelIndex || 0; var halfH = height / 2; - var absmax = 1; + var absmax = 1 / this.params.barHeight; if (this.params.normalize) { var max = WaveSurfer.util.max(peaks); var min = WaveSurfer.util.min(peaks); absmax = -min > max ? -min : max; } - this.drawLine(peaks, absmax, halfH, offsetY); + this.drawLine(peaks, absmax, halfH, offsetY, start, end); // Always draw a median line this.fillRect(0, halfH + offsetY - this.halfPixel, this.width, this.halfPixel); }, - drawLine: function (peaks, absmax, halfH, offsetY) { + drawLine: function (peaks, absmax, halfH, offsetY, start, end) { for (var index in this.canvases) { var entry = this.canvases[index]; this.setFillStyles(entry); - this.drawLineToContext(entry, entry.waveCtx, peaks, absmax, halfH, offsetY); - this.drawLineToContext(entry, entry.progressCtx, peaks, absmax, halfH, offsetY); + this.drawLineToContext(entry, entry.waveCtx, peaks, absmax, halfH, offsetY, start, end); + this.drawLineToContext(entry, entry.progressCtx, peaks, absmax, halfH, offsetY, start, end); } }, - drawLineToContext: function (entry, ctx, peaks, absmax, halfH, offsetY) { + drawLineToContext: function (entry, ctx, peaks, absmax, halfH, offsetY, start, end) { if (!ctx) { return; } var length = peaks.length / 2; @@ -2035,19 +2209,24 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, { var first = Math.round(length * entry.start), last = Math.round(length * entry.end); + if (first > end || last < start) { return; } + var canvasStart = Math.max(first, start); + var canvasEnd = Math.min(last, end); ctx.beginPath(); - ctx.moveTo(this.halfPixel, halfH + offsetY); + ctx.moveTo((canvasStart - first) * scale + this.halfPixel, halfH + offsetY); - for (var i = first; i < last; i++) { - var h = Math.round(peaks[2 * i] / absmax * halfH); + for (var i = canvasStart; i < canvasEnd; i++) { + var peak = peaks[2 * i] || 0; + var h = Math.round(peak / absmax * halfH); ctx.lineTo((i - first) * scale + this.halfPixel, halfH - h + offsetY); } // Draw the bottom edge going backwards, to make a single // closed hull to fill. - for (var i = last - 1; i >= first; i--) { - var h = Math.round(peaks[2 * i + 1] / absmax * halfH); + for (var i = canvasEnd - 1; i >= canvasStart; i--) { + var peak = peaks[2 * i + 1] || 0; + var h = Math.round(peak / absmax * halfH); ctx.lineTo((i - first) * scale + this.halfPixel, halfH - h + offsetY); } @@ -2056,7 +2235,10 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, { }, fillRect: function (x, y, width, height) { - for (var i in this.canvases) { + var startCanvas = Math.floor(x / this.maxCanvasWidth); + var endCanvas = Math.min(Math.ceil((x + width) / this.maxCanvasWidth) + 1, + this.canvases.length); + for (var i = startCanvas; i < endCanvas; i++) { var entry = this.canvases[i], leftOffset = i * this.maxCanvasWidth; @@ -2097,16 +2279,459 @@ WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, { } }, - updateProgress: function (progress) { - var pos = Math.round( - this.width * progress - ) / this.params.pixelRatio; + updateProgress: function (pos) { this.style(this.progressWave, { width: pos + 'px' }); + }, + + /** + * Combine all available canvasses together. + * + * @param {String} type - an optional value of a format type. Default is image/png. + * @param {Number} quality - an optional value between 0 and 1. Default is 0.92. + * + */ + getImage: function(type, quality) { + var availableCanvas = []; + this.canvases.forEach(function (entry) { + availableCanvas.push(entry.wave.toDataURL(type, quality)); + }); + return availableCanvas.length > 1 ? availableCanvas : availableCanvas[0]; } }); 'use strict'; +WaveSurfer.Drawer.SplitWavePointPlot = Object.create(WaveSurfer.Drawer.Canvas); + +WaveSurfer.util.extend(WaveSurfer.Drawer.SplitWavePointPlot, { + + defaultPlotParams: { + plotNormalizeTo: 'whole', + plotTimeStart: 0, + plotMin: 0, + plotMax: 1, + plotColor : '#f63', + plotProgressColor : '#F00', + plotPointHeight: 2, + plotPointWidth: 2, + plotSeparator: true, + plotSeparatorColor: 'black', + plotRangeDisplay: false, + plotRangeUnits: '', + plotRangePrecision: 4, + plotRangeIgnoreOutliers: false, + plotRangeFontSize: 12, + plotRangeFontType: 'Ariel', + waveDrawMedianLine: true, + plotFileDelimiter: '\t' + }, + + //object variables that get manipulated by various object functions + plotTimeStart: 0, //the start time of our wave according to plot data + plotTimeEnd: -1, //the end of our wave according to plot data + plotArrayLoaded: false, + plotArray: [], //array of plot data objects containing time and plot value + plotPoints: [], //calculated average plot points corresponding to value of our wave + plotMin: 0, + plotMax: 1, + + /** + * Initializes the plot array. If params.plotFileUrl is provided an ajax call will be + * executed and drawing of the wave is delayed until plot info is retrieved + * @param params + */ + initDrawer: function (params) { + var my = this; + + //set defaults if not passed in + for(var paramName in this.defaultPlotParams) { + if(this.params[paramName] === undefined) { + this.params[paramName] = this.defaultPlotParams[paramName]; + } + } + + //set the plotTimeStart + this.plotTimeStart = this.params.plotTimeStart; + + //check to see if plotTimeEnd + if(this.params.plotTimeEnd !== undefined) { + this.plotTimeEnd = this.params.plotTimeEnd; + } + + //set the plot array + if (Array.isArray(params.plotArray)) { + this.plotArray = params.plotArray; + this.plotArrayLoaded = true; + } + //Need to load the plot array from ajax with our callback + else { + var onPlotArrayLoaded = function (plotArray) { + my.plotArray = plotArray; + my.plotArrayLoaded = true; + my.fireEvent('plot_array_loaded'); + }; + this.loadPlotArrayFromFile(params.plotFileUrl, onPlotArrayLoaded, this.params.plotFileDelimiter); + } + }, + + /** + * Draw the peaks - this overrides the drawer.js function and does the following additional steps + * - ensures that the plotArray has already been loaded, if not it loads via ajax + * - moves the wave form to where channel 1 would normally be + * @param peaks + * @param length + * @param start + * @param end + */ + drawPeaks: function (peaks, length, start, end) { + //make sure that the plot array is already loaded + if (this.plotArrayLoaded == true) { + + this.setWidth(length); + + //fake that we are splitting channels + this.splitChannels = true; + this.params.height = this.params.height/2; + if (peaks[0] instanceof Array) { + peaks = peaks[0]; + } + + this.params.barWidth ? + this.drawBars(peaks, 1, start, end) : + this.drawWave(peaks, 1, start, end); + + //set the height back to the original + this.params.height = this.params.height*2; + + this.calculatePlots(); + this.drawPlots(); + + } + //otherwise wait for the plot array to be loaded and then draw again + else { + var my = this; + my.on('plot-array-loaded', function () { + my.drawPeaks(peaks, length, start, end); + }); + } + }, + + + + + /** + * Loop through the calculated plot values and actually draw them + */ + drawPlots: function() { + var height = this.params.height * this.params.pixelRatio / 2; + + var $ = 0.5 / this.params.pixelRatio; + + this.waveCc.fillStyle = this.params.plotColor; + if(this.progressCc) { + this.progressCc.fillStyle = this.params.plotProgressColor; + } + for(var i in this.plotPoints) { + var x = parseInt(i); + var y = height - this.params.plotPointHeight - (this.plotPoints[i] * (height - this.params.plotPointHeight)); + var pointHeight = this.params.plotPointHeight; + + this.waveCc.fillRect(x, y, this.params.plotPointWidth, pointHeight); + + if(this.progressCc) { + this.progressCc.fillRect(x, y, this.params.plotPointWidth, pointHeight); + } + } + + //draw line to separate the two waves + if(this.params.plotSeparator) { + this.waveCc.fillStyle = this.params.plotSeparatorColor; + this.waveCc.fillRect(0, height, this.width, $); + } + + if(this.params.plotRangeDisplay) { + this.displayPlotRange(); + } + }, + + + /** + * Display the range for the plot graph + */ + displayPlotRange: function() + { + var fontSize = this.params.plotRangeFontSize * this.params.pixelRatio; + var maxRange = this.plotMax.toPrecision(this.params.plotRangePrecision) + ' ' + this.params.plotRangeUnits; + var minRange = this.plotMin.toPrecision(this.params.plotRangePrecision) + ' ' + this.params.plotRangeUnits; + this.waveCc.font = fontSize.toString() + 'px ' + this.params.plotRangeFontType; + this.waveCc.fillText(maxRange, 3, fontSize); + this.waveCc.fillText(minRange, 3, this.height/2); + + }, + /** + * This function loops through the plotArray and converts it to the plot points + * to be drawn on the canvas keyed by their position + */ + calculatePlots: function() { + //reset plots array + this.plotPoints = {}; + + //make sure we have our plotTimeEnd + this.calculatePlotTimeEnd(); + + var pointsForAverage = []; + var previousWaveIndex = -1; + var maxPlot = 0; + var minPlot = 99999999999999; + var maxSegmentPlot = 0; + var minSegmentPlot = 99999999999999; + var duration = this.plotTimeEnd - this.plotTimeStart; + + //loop through our plotArray and map values to wave indexes and take the average values for each wave index + for(var i = 0; i < this.plotArray.length; i++) { + var dataPoint = this.plotArray[i]; + if(dataPoint.value > maxPlot) {maxPlot = dataPoint.value;} + if(dataPoint.value < minPlot) {minPlot = dataPoint.value;} + + //make sure we are in the specified range + if(dataPoint.time >= this.plotTimeStart && dataPoint.time <= this.plotTimeEnd) { + //get the wave index corresponding to the data point + var waveIndex = Math.round(this.width * (dataPoint.time - this.plotTimeStart) / duration); + + pointsForAverage.push(dataPoint.value); + + //if we have moved on to a new position in our wave record average and reset previousWaveIndex + if(waveIndex !== previousWaveIndex) { + if(pointsForAverage.length > 0) { + //get the average plot for this point + var avgPlot = this.avg(pointsForAverage); + + //check for min max + if(avgPlot > maxSegmentPlot) {maxSegmentPlot = avgPlot;} + if(avgPlot < minSegmentPlot) {minSegmentPlot = avgPlot;} + + //add plot to the position + this.plotPoints[previousWaveIndex] = avgPlot; + pointsForAverage = []; + } + } + previousWaveIndex = waveIndex; + } + } + + //normalize the plots points + if(this.params.plotNormalizeTo == 'whole') { + this.plotMin = minPlot; + this.plotMax = maxPlot; + } + else if(this.params.plotNormalizeTo == 'values') { + this.plotMin = this.params.plotMin; + this.plotMax = this.params.plotMax; + } + else { + this.plotMin = minSegmentPlot; + this.plotMax = maxSegmentPlot; + } + this.normalizeValues(); + }, + + /** + * Function to take all of the plots in this.plots and normalize them from 0 to one + * depending on this.plotMin and this.plotMax values + */ + normalizeValues: function() { + var normalizedValues = {}; + + //check to make sure we should be normalizing + if(this.params.plotNormalizeTo === 'none') {return;} + + for(var i in this.plotPoints) { + //get the normalized value between 0 and 1 + var normalizedValue = (this.plotPoints[i] - this.plotMin) / (this.plotMax - this.plotMin); + + //check if the value is above our specified range max + if(normalizedValue > 1) { + if(!this.params.plotRangeIgnoreOutliers) { + normalizedValues[i] = 1; + } + } + //check if hte value is below our specified rant + else if(normalizedValue < 0) { + if(!this.params.plotRangeIgnoreOutliers) { + normalizedValues[i] = 0; + } + } + //in our range add the normalized value + else { + normalizedValues[i] = normalizedValue; + } + } + this.plotPoints = normalizedValues; + }, + /** + * + */ + + /** + * Function to load the plot array from a external file + * + * The text file should contain a series of lines. + * Each line should contain [audio time] [delimiter character] [plot value] + * e.g. "1.2355 [tab] 124.2321" + * + * @param plotFileUrl url of the file containing time and value information + * @param onSuccess function to run on success + * @param delimiter the delimiter that separates the time and values on each line + */ + loadPlotArrayFromFile: function(plotFileUrl, onSuccess, delimiter) { + //default delimiter to tab character + if (delimiter === undefined) {delimiter = '\t';} + + var plotArray = []; + + var options = { + url: plotFileUrl, + responseType: 'text' + }; + var fileAjax = WaveSurfer.util.ajax(options); + + fileAjax.on('load', function (data) { + if (data.currentTarget.status == 200) { + //split the file by line endings + var plotLines = data.currentTarget.responseText.split('\n'); + //loop through each line and find the time and plot values (delimited by tab) + for (var i = 0; i < plotLines.length; i++) { + var plotParts = plotLines[i].split(delimiter); + if(plotParts.length == 2) { + plotArray.push({time: parseFloat(plotParts[0]), value: parseFloat(plotParts[1])}); + } + } + //run success function + onSuccess(plotArray); + } + }); + }, + + /*** + * Calculate the end time of the plot + */ + calculatePlotTimeEnd: function() { + if(this.params.plotTimeEnd !== undefined) { + this.plotTimeEnd = this.params.plotTimeEnd; + } + else { + this.plotTimeEnd = this.plotArray[this.plotArray.length -1].time; + } + }, + + /** + * Quick convenience function to average numbers in an array + * @param array of values + * @returns {number} + */ + avg: function(values) { + var sum = values.reduce(function(a, b) {return a+b;}); + return sum/values.length; + } +}); + +WaveSurfer.util.extend(WaveSurfer.Drawer.SplitWavePointPlot, WaveSurfer.Observer); + +'use strict'; + +WaveSurfer.PeakCache = { + init: function() { + this.clearPeakCache(); + }, + + clearPeakCache: function() { + // Flat array with entries that are always in pairs to mark the + // beginning and end of each subrange. This is a convenience so we can + // iterate over the pairs for easy set difference operations. + this.peakCacheRanges = []; + // Length of the entire cachable region, used for resetting the cache + // when this changes (zoom events, for instance). + this.peakCacheLength = -1; + }, + + addRangeToPeakCache: function(length, start, end) { + if (length != this.peakCacheLength) { + this.clearPeakCache(); + this.peakCacheLength = length; + } + + // Return ranges that weren't in the cache before the call. + var uncachedRanges = []; + var i = 0; + // Skip ranges before the current start. + while (i < this.peakCacheRanges.length && this.peakCacheRanges[i] < start) { + i++; + } + // If |i| is even, |start| falls after an existing range. Otherwise, + // |start| falls between an existing range, and the uncached region + // starts when we encounter the next node in |peakCacheRanges| or + // |end|, whichever comes first. + if (i % 2 == 0) { + uncachedRanges.push(start); + } + while (i < this.peakCacheRanges.length && this.peakCacheRanges[i] <= end) { + uncachedRanges.push(this.peakCacheRanges[i]); + i++; + } + // If |i| is even, |end| is after all existing ranges. + if (i % 2 == 0) { + uncachedRanges.push(end); + } + + // Filter out the 0-length ranges. + uncachedRanges = uncachedRanges.filter(function(item, pos, arr) { + if (pos == 0) { + return item != arr[pos + 1]; + } else if (pos == arr.length - 1) { + return item != arr[pos - 1]; + } else { + return item != arr[pos - 1] && item != arr[pos + 1]; + } + }); + + // Merge the two ranges together, uncachedRanges will either contain + // wholly new points, or duplicates of points in peakCacheRanges. If + // duplicates are detected, remove both and extend the range. + this.peakCacheRanges = this.peakCacheRanges.concat(uncachedRanges); + this.peakCacheRanges = this.peakCacheRanges.sort(function(a, b) { + return a - b; + }).filter(function(item, pos, arr) { + if (pos == 0) { + return item != arr[pos + 1]; + } else if (pos == arr.length - 1) { + return item != arr[pos - 1]; + } else { + return item != arr[pos - 1] && item != arr[pos + 1]; + } + }); + + // Push the uncached ranges into an array of arrays for ease of + // iteration in the functions that call this. + var uncachedRangePairs = []; + for (i = 0; i < uncachedRanges.length; i += 2) { + uncachedRangePairs.push([uncachedRanges[i], uncachedRanges[i+1]]); + } + + return uncachedRangePairs; + }, + + // For testing + getCacheRanges: function() { + var peakCacheRangePairs = []; + for (var i = 0; i < this.peakCacheRanges.length; i += 2) { + peakCacheRangePairs.push([this.peakCacheRanges[i], this.peakCacheRanges[i+1]]); + } + return peakCacheRangePairs; + } +}; + +'use strict'; + /* Init from HTML */ (function () { var init = function () { diff --git a/dist/wavesurfer.min.js b/dist/wavesurfer.min.js index 020cda9bc..8f222fc83 100644 --- a/dist/wavesurfer.min.js +++ b/dist/wavesurfer.min.js @@ -1,5 +1,6 @@ -/*! wavesurfer.js 1.2.4 (Fri, 11 Nov 2016 15:43:23 GMT) +/*! wavesurfer.js 1.4.0 (Mon, 10 Apr 2017 08:55:35 GMT) * https://github.com/katspaugh/wavesurfer.js -* @license CC-BY-3.0 */ -!function(a,b){"function"==typeof define&&define.amd?define("wavesurfer",[],function(){return a.WaveSurfer=b()}):"object"==typeof exports?module.exports=b():a.WaveSurfer=b()}(this,function(){"use strict";var a={defaultParams:{height:128,waveColor:"#999",progressColor:"#555",cursorColor:"#333",cursorWidth:1,skipLength:2,minPxPerSec:20,pixelRatio:window.devicePixelRatio||screen.deviceXDPI/screen.logicalXDPI,fillParent:!0,scrollParent:!1,hideScrollbar:!1,normalize:!1,audioContext:null,container:null,dragSelection:!0,loopSelection:!0,audioRate:1,interact:!0,splitChannels:!1,mediaContainer:null,mediaControls:!1,renderer:"Canvas",backend:"WebAudio",mediaType:"audio",autoCenter:!0},init:function(b){if(this.params=a.util.extend({},this.defaultParams,b),this.container="string"==typeof b.container?document.querySelector(this.params.container):this.params.container,!this.container)throw new Error("Container element not found");if(null==this.params.mediaContainer?this.mediaContainer=this.container:"string"==typeof this.params.mediaContainer?this.mediaContainer=document.querySelector(this.params.mediaContainer):this.mediaContainer=this.params.mediaContainer,!this.mediaContainer)throw new Error("Media Container element not found");this.savedVolume=0,this.isMuted=!1,this.tmpEvents=[],this.currentAjax=null,this.createDrawer(),this.createBackend(),this.isDestroyed=!1},createDrawer:function(){var b=this;this.drawer=Object.create(a.Drawer[this.params.renderer]),this.drawer.init(this.container,this.params),this.drawer.on("redraw",function(){b.drawBuffer(),b.drawer.progress(b.backend.getPlayedPercents())}),this.drawer.on("click",function(a,c){setTimeout(function(){b.seekTo(c)},0)}),this.drawer.on("scroll",function(a){b.fireEvent("scroll",a)})},createBackend:function(){var b=this;this.backend&&this.backend.destroy(),"AudioElement"==this.params.backend&&(this.params.backend="MediaElement"),"WebAudio"!=this.params.backend||a.WebAudio.supportsWebAudio()||(this.params.backend="MediaElement"),this.backend=Object.create(a[this.params.backend]),this.backend.init(this.params),this.backend.on("finish",function(){b.fireEvent("finish")}),this.backend.on("play",function(){b.fireEvent("play")}),this.backend.on("pause",function(){b.fireEvent("pause")}),this.backend.on("audioprocess",function(a){b.drawer.progress(b.backend.getPlayedPercents()),b.fireEvent("audioprocess",a)})},getDuration:function(){return this.backend.getDuration()},getCurrentTime:function(){return this.backend.getCurrentTime()},play:function(a,b){this.fireEvent("interaction",this.play.bind(this,a,b)),this.backend.play(a,b)},pause:function(){this.backend.pause()},playPause:function(){this.backend.isPaused()?this.play():this.pause()},isPlaying:function(){return!this.backend.isPaused()},skipBackward:function(a){this.skip(-a||-this.params.skipLength)},skipForward:function(a){this.skip(a||this.params.skipLength)},skip:function(a){var b=this.getCurrentTime()||0,c=this.getDuration()||1;b=Math.max(0,Math.min(c,b+(a||0))),this.seekAndCenter(b/c)},seekAndCenter:function(a){this.seekTo(a),this.drawer.recenter(a)},seekTo:function(a){this.fireEvent("interaction",this.seekTo.bind(this,a));var b=this.backend.isPaused(),c=this.params.scrollParent;b&&(this.params.scrollParent=!1),this.backend.seekTo(a*this.getDuration()),this.drawer.progress(this.backend.getPlayedPercents()),b||(this.backend.pause(),this.backend.play()),this.params.scrollParent=c,this.fireEvent("seek",a)},stop:function(){this.pause(),this.seekTo(0),this.drawer.progress(0)},setVolume:function(a){this.backend.setVolume(a)},setPlaybackRate:function(a){this.backend.setPlaybackRate(a)},toggleMute:function(){this.setMute(!this.isMuted)},setMute:function(a){a!==this.isMuted&&(a?(this.savedVolume=this.backend.getVolume(),this.backend.setVolume(0),this.isMuted=!0):(this.backend.setVolume(this.savedVolume),this.isMuted=!1))},toggleScroll:function(){this.params.scrollParent=!this.params.scrollParent,this.drawBuffer()},toggleInteraction:function(){this.params.interact=!this.params.interact},drawBuffer:function(){var a=Math.round(this.getDuration()*this.params.minPxPerSec*this.params.pixelRatio),b=this.drawer.getWidth(),c=a;this.params.fillParent&&(!this.params.scrollParent||ab&&(b=a[c]);return b},getId:function(){return"wavesurfer_"+Math.random().toString(32).substring(2)},ajax:function(b){var c=Object.create(a.Observer),d=new XMLHttpRequest,e=!1;return d.open(b.method||"GET",b.url,!0),d.responseType=b.responseType||"json",d.addEventListener("progress",function(a){c.fireEvent("progress",a),a.lengthComputable&&a.loaded==a.total&&(e=!0)}),d.addEventListener("load",function(a){e||c.fireEvent("progress",a),c.fireEvent("load",a),200==d.status||206==d.status?c.fireEvent("success",d.response,a):c.fireEvent("error",a)}),d.addEventListener("error",function(a){c.fireEvent("error",a)}),d.send(),c.xhr=d,c}},a.Observer={on:function(a,b){this.handlers||(this.handlers={});var c=this.handlers[a];return c||(c=this.handlers[a]=[]),c.push(b),{name:a,callback:b,un:this.un.bind(this,a,b)}},un:function(a,b){if(this.handlers){var c=this.handlers[a];if(c)if(b)for(var d=c.length-1;d>=0;d--)c[d]==b&&c.splice(d,1);else c.length=0}},unAll:function(){this.handlers=null},once:function(a,b){var c=this,d=function(){b.apply(this,arguments),setTimeout(function(){c.un(a,d)},0)};return this.on(a,d)},fireEvent:function(a){if(this.handlers){var b=this.handlers[a],c=Array.prototype.slice.call(arguments,1);b&&b.forEach(function(a){a.apply(null,c)})}}},a.util.extend(a,a.Observer),a.WebAudio={scriptBufferSize:256,PLAYING_STATE:0,PAUSED_STATE:1,FINISHED_STATE:2,supportsWebAudio:function(){return!(!window.AudioContext&&!window.webkitAudioContext)},getAudioContext:function(){return a.WebAudio.audioContext||(a.WebAudio.audioContext=new(window.AudioContext||window.webkitAudioContext)),a.WebAudio.audioContext},getOfflineAudioContext:function(b){return a.WebAudio.offlineAudioContext||(a.WebAudio.offlineAudioContext=new(window.OfflineAudioContext||window.webkitOfflineAudioContext)(1,2,b)),a.WebAudio.offlineAudioContext},init:function(b){this.params=b,this.ac=b.audioContext||this.getAudioContext(),this.lastPlay=this.ac.currentTime,this.startPosition=0,this.scheduledPause=null,this.states=[Object.create(a.WebAudio.state.playing),Object.create(a.WebAudio.state.paused),Object.create(a.WebAudio.state.finished)],this.createVolumeNode(),this.createScriptNode(),this.createAnalyserNode(),this.setState(this.PAUSED_STATE),this.setPlaybackRate(this.params.audioRate)},disconnectFilters:function(){this.filters&&(this.filters.forEach(function(a){a&&a.disconnect()}),this.filters=null,this.analyser.connect(this.gainNode))},setState:function(a){this.state!==this.states[a]&&(this.state=this.states[a],this.state.init.call(this))},setFilter:function(){this.setFilters([].slice.call(arguments))},setFilters:function(a){this.disconnectFilters(),a&&a.length&&(this.filters=a,this.analyser.disconnect(),a.reduce(function(a,b){return a.connect(b),b},this.analyser).connect(this.gainNode))},createScriptNode:function(){this.ac.createScriptProcessor?this.scriptNode=this.ac.createScriptProcessor(this.scriptBufferSize):this.scriptNode=this.ac.createJavaScriptNode(this.scriptBufferSize),this.scriptNode.connect(this.ac.destination)},addOnAudioProcess:function(){var a=this;this.scriptNode.onaudioprocess=function(){var b=a.getCurrentTime();b>=a.getDuration()?(a.setState(a.FINISHED_STATE),a.fireEvent("pause")):b>=a.scheduledPause?a.pause():a.state===a.states[a.PLAYING_STATE]&&a.fireEvent("audioprocess",b)}},removeOnAudioProcess:function(){this.scriptNode.onaudioprocess=null},createAnalyserNode:function(){this.analyser=this.ac.createAnalyser(),this.analyser.connect(this.gainNode)},createVolumeNode:function(){this.ac.createGain?this.gainNode=this.ac.createGain():this.gainNode=this.ac.createGainNode(),this.gainNode.connect(this.ac.destination)},setVolume:function(a){this.gainNode.gain.value=a},getVolume:function(){return this.gainNode.gain.value},decodeArrayBuffer:function(a,b,c){this.offlineAc||(this.offlineAc=this.getOfflineAudioContext(this.ac?this.ac.sampleRate:44100)),this.offlineAc.decodeAudioData(a,function(a){b(a)}.bind(this),c)},setPeaks:function(a){this.peaks=a},getPeaks:function(a){if(this.peaks)return this.peaks;for(var b=this.buffer.length/a,c=~~(b/10)||1,d=this.buffer.numberOfChannels,e=[],f=[],g=0;gn&&(n=p),pf[2*j])&&(f[2*j]=n),(0==g||m=this.getDuration()&&(a=0)),null==b&&(b=this.getDuration()),this.startPosition=a,this.lastPlay=this.ac.currentTime,this.state===this.states[this.FINISHED_STATE]&&this.setState(this.PAUSED_STATE),{start:a,end:b}},getPlayedTime:function(){return(this.ac.currentTime-this.lastPlay)*this.playbackRate},play:function(a,b){if(this.buffer){this.createSource();var c=this.seekTo(a,b);a=c.start,b=c.end,this.scheduledPause=b,this.source.start(0,a,b-a),this.ac.resume(),this.setState(this.PLAYING_STATE),this.fireEvent("play")}},pause:function(){this.scheduledPause=null,this.startPosition+=this.getPlayedTime(),this.source&&this.source.stop(0),this.setState(this.PAUSED_STATE),this.fireEvent("pause")},getCurrentTime:function(){return this.state.getCurrentTime.call(this)},setPlaybackRate:function(a){a=a||1,this.isPaused()?this.playbackRate=a:(this.pause(),this.playbackRate=a,this.play())}},a.WebAudio.state={},a.WebAudio.state.playing={init:function(){this.addOnAudioProcess()},getPlayedPercents:function(){var a=this.getDuration();return this.getCurrentTime()/a||0},getCurrentTime:function(){return this.startPosition+this.getPlayedTime()}},a.WebAudio.state.paused={init:function(){this.removeOnAudioProcess()},getPlayedPercents:function(){var a=this.getDuration();return this.getCurrentTime()/a||0},getCurrentTime:function(){return this.startPosition}},a.WebAudio.state.finished={init:function(){this.removeOnAudioProcess(),this.fireEvent("finish")},getPlayedPercents:function(){return 1},getCurrentTime:function(){return this.getDuration()}},a.util.extend(a.WebAudio,a.Observer),a.MediaElement=Object.create(a.WebAudio),a.util.extend(a.MediaElement,{init:function(a){this.params=a,this.media={currentTime:0,duration:0,paused:!0,playbackRate:1,play:function(){},pause:function(){}},this.mediaType=a.mediaType.toLowerCase(),this.elementPosition=a.elementPosition,this.setPlaybackRate(this.params.audioRate),this.createTimer()},createTimer:function(){var a=this,b=function(){if(!a.isPaused()){a.fireEvent("audioprocess",a.getCurrentTime());var c=window.requestAnimationFrame||window.webkitRequestAnimationFrame;c(b)}};this.on("play",b)},load:function(a,b,c,d){var e=document.createElement(this.mediaType);e.controls=this.params.mediaControls,e.autoplay=this.params.autoplay||!1,e.preload=null==d?"auto":d,e.src=a,e.style.width="100%";var f=b.querySelector(this.mediaType);f&&b.removeChild(f),b.appendChild(e),this._load(e,c)},loadElt:function(a,b){var c=a;c.controls=this.params.mediaControls,c.autoplay=this.params.autoplay||!1,this._load(c,b)},_load:function(a,b){var c=this;a.load(),a.addEventListener("error",function(){c.fireEvent("error","Error loading media element")}),a.addEventListener("canplay",function(){c.fireEvent("canplay")}),a.addEventListener("ended",function(){c.fireEvent("finish")}),this.media=a,this.peaks=b,this.onPlayEnd=null,this.buffer=null,this.setPlaybackRate(this.playbackRate)},isPaused:function(){return!this.media||this.media.paused},getDuration:function(){var a=this.media.duration;return a>=1/0&&(a=this.media.seekable.end(0)),a},getCurrentTime:function(){return this.media&&this.media.currentTime},getPlayedPercents:function(){return this.getCurrentTime()/this.getDuration()||0},setPlaybackRate:function(a){this.playbackRate=a||1,this.media.playbackRate=this.playbackRate},seekTo:function(a){null!=a&&(this.media.currentTime=a),this.clearPlayEnd()},play:function(a,b){this.seekTo(a),this.media.play(),b&&this.setPlayEnd(b),this.fireEvent("play")},pause:function(){this.media&&this.media.pause(),this.clearPlayEnd(),this.fireEvent("pause")},setPlayEnd:function(a){var b=this;this.onPlayEnd=function(c){c>=a&&(b.pause(),b.seekTo(a))},this.on("audioprocess",this.onPlayEnd)},clearPlayEnd:function(){this.onPlayEnd&&(this.un("audioprocess",this.onPlayEnd),this.onPlayEnd=null)},getPeaks:function(b){return this.buffer?a.WebAudio.getPeaks.call(this,b):this.peaks||[]},getVolume:function(){return this.media.volume},setVolume:function(a){this.media.volume=a},destroy:function(){this.pause(),this.unAll(),this.media&&this.media.parentNode&&this.media.parentNode.removeChild(this.media),this.media=null}}),a.AudioElement=a.MediaElement,a.Drawer={init:function(a,b){this.container=a,this.params=b,this.width=0,this.height=b.height*this.params.pixelRatio,this.lastPos=0,this.initDrawer(b),this.createWrapper(),this.createElements()},createWrapper:function(){this.wrapper=this.container.appendChild(document.createElement("wave")),this.style(this.wrapper,{display:"block",position:"relative",userSelect:"none",webkitUserSelect:"none",height:this.params.height+"px"}),(this.params.fillParent||this.params.scrollParent)&&this.style(this.wrapper,{width:"100%",overflowX:this.params.hideScrollbar?"hidden":"auto",overflowY:"hidden"}),this.setupWrapperEvents()},handleEvent:function(a,b){!b&&a.preventDefault();var c,d=a.targetTouches?a.targetTouches[0].clientX:a.clientX,e=this.wrapper.getBoundingClientRect(),f=this.width,g=this.getWidth();return!this.params.fillParent&&f1&&(c=1)):c=(d-e.left+this.wrapper.scrollLeft)/this.wrapper.scrollWidth||0,c},setupWrapperEvents:function(){var a=this;this.wrapper.addEventListener("click",function(b){var c=a.wrapper.offsetHeight-a.wrapper.clientHeight;if(0!=c){var d=a.wrapper.getBoundingClientRect();if(b.clientY>=d.bottom-c)return}a.params.interact&&a.fireEvent("click",b,a.handleEvent(b))}),this.wrapper.addEventListener("scroll",function(b){a.fireEvent("scroll",b)})},drawPeaks:function(a,b){this.resetScroll(),this.setWidth(b),this.params.barWidth?this.drawBars(a):this.drawWave(a)},style:function(a,b){return Object.keys(b).forEach(function(c){a.style[c]!==b[c]&&(a.style[c]=b[c])}),a},resetScroll:function(){null!==this.wrapper&&(this.wrapper.scrollLeft=0)},recenter:function(a){var b=this.wrapper.scrollWidth*a;this.recenterOnPosition(b,!0)},recenterOnPosition:function(a,b){var c=this.wrapper.scrollLeft,d=~~(this.wrapper.clientWidth/2),e=a-d,f=e-c,g=this.wrapper.scrollWidth-this.wrapper.clientWidth;if(0!=g){if(!b&&-d<=f&&f=b){if(this.lastPos=c,this.params.scrollParent&&this.params.autoCenter){var d=~~(this.wrapper.scrollWidth*a);this.recenterOnPosition(d)}this.updateProgress(a)}},destroy:function(){this.unAll(),this.wrapper&&(this.container.removeChild(this.wrapper),this.wrapper=null)},initDrawer:function(){},createElements:function(){},updateSize:function(){},drawWave:function(a,b){},clearWave:function(){},updateProgress:function(a){}},a.util.extend(a.Drawer,a.Observer),a.Drawer.Canvas=Object.create(a.Drawer),a.util.extend(a.Drawer.Canvas,{createElements:function(){var a=this.wrapper.appendChild(this.style(document.createElement("canvas"),{position:"absolute",zIndex:1,left:0,top:0,bottom:0}));if(this.waveCc=a.getContext("2d"),this.progressWave=this.wrapper.appendChild(this.style(document.createElement("wave"),{position:"absolute",zIndex:2,left:0,top:0,bottom:0,overflow:"hidden",width:"0",display:"none",boxSizing:"border-box",borderRightStyle:"solid",borderRightWidth:this.params.cursorWidth+"px",borderRightColor:this.params.cursorColor})),this.params.waveColor!=this.params.progressColor){var b=this.progressWave.appendChild(document.createElement("canvas"));this.progressCc=b.getContext("2d")}},updateSize:function(){var a=Math.round(this.width/this.params.pixelRatio);this.waveCc.canvas.width=this.width,this.waveCc.canvas.height=this.height,this.style(this.waveCc.canvas,{width:a+"px"}),this.style(this.progressWave,{display:"block"}),this.progressCc&&(this.progressCc.canvas.width=this.width,this.progressCc.canvas.height=this.height,this.style(this.progressCc.canvas,{width:a+"px"})),this.clearWave()},clearWave:function(){this.waveCc.clearRect(0,0,this.width,this.height),this.progressCc&&this.progressCc.clearRect(0,0,this.width,this.height)},drawBars:function(b,c){if(b[0]instanceof Array){var d=b;if(this.params.splitChannels)return this.setHeight(d.length*this.params.height*this.params.pixelRatio),void d.forEach(this.drawBars,this);b=d[0]}var e=[].some.call(b,function(a){return a<0});e&&(b=[].filter.call(b,function(a,b){return b%2==0}));var f=.5/this.params.pixelRatio,g=this.width,h=this.params.height*this.params.pixelRatio,i=h*c||0,j=h/2,k=b.length,l=this.params.barWidth*this.params.pixelRatio,m=Math.max(this.params.pixelRatio,~~(l/2)),n=l+m,o=1;this.params.normalize&&(o=a.util.max(b));var p=k/g;this.waveCc.fillStyle=this.params.waveColor,this.progressCc&&(this.progressCc.fillStyle=this.params.progressColor),[this.waveCc,this.progressCc].forEach(function(a){if(a)for(var c=0;cp?-q:p}this.waveCc.fillStyle=this.params.waveColor,this.progressCc&&(this.progressCc.fillStyle=this.params.progressColor),[this.waveCc,this.progressCc].forEach(function(a){if(a){a.beginPath(),a.moveTo(i,l+k);for(var c=0;c=0;c--){var d=Math.round(b[2*c+1]/o*l);a.lineTo(c*n+i,l-d+k)}a.closePath(),a.fill(),a.fillRect(0,l+k-i,this.width,i)}},this)},updateProgress:function(a){var b=Math.round(this.width*a)/this.params.pixelRatio;this.style(this.progressWave,{width:b+"px"})},getImage:function(a,b){return this.waveCc.canvas.toDataURL(a,b)}}),a.Drawer.MultiCanvas=Object.create(a.Drawer),a.util.extend(a.Drawer.MultiCanvas,{initDrawer:function(a){if(this.maxCanvasWidth=null!=a.maxCanvasWidth?a.maxCanvasWidth:4e3,this.maxCanvasElementWidth=Math.round(this.maxCanvasWidth/this.params.pixelRatio),this.maxCanvasWidth<=1)throw"maxCanvasWidth must be greater than 1.";if(this.maxCanvasWidth%2==1)throw"maxCanvasWidth must be an even number.";this.hasProgressCanvas=this.params.waveColor!=this.params.progressColor,this.halfPixel=.5/this.params.pixelRatio,this.canvases=[]},createElements:function(){this.progressWave=this.wrapper.appendChild(this.style(document.createElement("wave"),{position:"absolute",zIndex:2,left:0,top:0,bottom:0,overflow:"hidden",width:"0",display:"none",boxSizing:"border-box",borderRightStyle:"solid",borderRightWidth:this.params.cursorWidth+"px",borderRightColor:this.params.cursorColor})),this.addCanvas()},updateSize:function(){for(var a=Math.round(this.width/this.params.pixelRatio),b=Math.ceil(a/this.maxCanvasElementWidth);this.canvases.lengthb;)this.removeCanvas();for(var c in this.canvases){var d=this.maxCanvasWidth+2*Math.ceil(this.params.pixelRatio/2);c==this.canvases.length-1&&(d=this.width-this.maxCanvasWidth*(this.canvases.length-1)),this.updateDimensions(this.canvases[c],d,this.height),this.clearWaveForEntry(this.canvases[c])}},addCanvas:function(){var a={},b=this.maxCanvasElementWidth*this.canvases.length;a.wave=this.wrapper.appendChild(this.style(document.createElement("canvas"),{position:"absolute",zIndex:1,left:b+"px",top:0,bottom:0})),a.waveCtx=a.wave.getContext("2d"),this.hasProgressCanvas&&(a.progress=this.progressWave.appendChild(this.style(document.createElement("canvas"),{position:"absolute",left:b+"px",top:0,bottom:0})),a.progressCtx=a.progress.getContext("2d")),this.canvases.push(a)},removeCanvas:function(){var a=this.canvases.pop();a.wave.parentElement.removeChild(a.wave),this.hasProgressCanvas&&a.progress.parentElement.removeChild(a.progress)},updateDimensions:function(a,b,c){var d=Math.round(b/this.params.pixelRatio),e=Math.round(this.width/this.params.pixelRatio);a.start=a.waveCtx.canvas.offsetLeft/e||0,a.end=a.start+d/e,a.waveCtx.canvas.width=b,a.waveCtx.canvas.height=c,this.style(a.waveCtx.canvas,{width:d+"px"}),this.style(this.progressWave,{display:"block"}),this.hasProgressCanvas&&(a.progressCtx.canvas.width=b,a.progressCtx.canvas.height=c,this.style(a.progressCtx.canvas,{width:d+"px"}))},clearWave:function(){for(var a in this.canvases)this.clearWaveForEntry(this.canvases[a])},clearWaveForEntry:function(a){a.waveCtx.clearRect(0,0,a.waveCtx.canvas.width,a.waveCtx.canvas.height),this.hasProgressCanvas&&a.progressCtx.clearRect(0,0,a.progressCtx.canvas.width,a.progressCtx.canvas.height)},drawBars:function(b,c){if(b[0]instanceof Array){var d=b;if(this.params.splitChannels)return this.setHeight(d.length*this.params.height*this.params.pixelRatio),void d.forEach(this.drawBars,this);b=d[0]}var e=[].some.call(b,function(a){return a<0});e&&(b=[].filter.call(b,function(a,b){return b%2==0}));var f=this.width,g=this.params.height*this.params.pixelRatio,h=g*c||0,i=g/2,j=b.length,k=this.params.barWidth*this.params.pixelRatio,l=Math.max(this.params.pixelRatio,~~(k/2)),m=k+l,n=1;this.params.normalize&&(n=a.util.max(b));for(var o=j/f,p=0;pm?-n:m}this.drawLine(b,l,k,j),this.fillRect(0,k+j-this.halfPixel,this.width,this.halfPixel)},drawLine:function(a,b,c,d){for(var e in this.canvases){var f=this.canvases[e];this.setFillStyles(f),this.drawLineToContext(f,f.waveCtx,a,b,c,d),this.drawLineToContext(f,f.progressCtx,a,b,c,d)}},drawLineToContext:function(a,b,c,d,e,f){if(b){var g=c.length/2,h=1;this.params.fillParent&&this.width!=g&&(h=this.width/g);var i=Math.round(g*a.start),j=Math.round(g*a.end);b.beginPath(),b.moveTo(this.halfPixel,e+f);for(var k=i;k=i;k--){var l=Math.round(c[2*k+1]/d*e);b.lineTo((k-i)*h+this.halfPixel,e-l+f)}b.closePath(),b.fill()}},fillRect:function(a,b,c,d){for(var e in this.canvases){var f=this.canvases[e],g=e*this.maxCanvasWidth,h={x1:Math.max(a,e*this.maxCanvasWidth),y1:b,x2:Math.min(a+c,e*this.maxCanvasWidth+f.waveCtx.canvas.width),y2:b+d};h.x1b&&(b=a[c]);return b},getId:function(){return"wavesurfer_"+Math.random().toString(32).substring(2)},ajax:function(b){var c=Object.create(a.Observer),d=new XMLHttpRequest,e=!1;return d.open(b.method||"GET",b.url,!0),d.responseType=b.responseType||"json",d.addEventListener("progress",function(a){c.fireEvent("progress",a),a.lengthComputable&&a.loaded==a.total&&(e=!0)}),d.addEventListener("load",function(a){e||c.fireEvent("progress",a),c.fireEvent("load",a),200==d.status||206==d.status?c.fireEvent("success",d.response,a):c.fireEvent("error",a)}),d.addEventListener("error",function(a){c.fireEvent("error",a)}),d.send(),c.xhr=d,c}},a.Observer={on:function(a,b){this.handlers||(this.handlers={});var c=this.handlers[a];return c||(c=this.handlers[a]=[]),c.push(b),{name:a,callback:b,un:this.un.bind(this,a,b)}},un:function(a,b){if(this.handlers){var c=this.handlers[a];if(c)if(b)for(var d=c.length-1;d>=0;d--)c[d]==b&&c.splice(d,1);else c.length=0}},unAll:function(){this.handlers=null},once:function(a,b){var c=this,d=function(){b.apply(this,arguments),setTimeout(function(){c.un(a,d)},0)};return this.on(a,d)},fireEvent:function(a){if(this.handlers){var b=this.handlers[a],c=Array.prototype.slice.call(arguments,1);b&&b.forEach(function(a){a.apply(null,c)})}}},a.util.extend(a,a.Observer),a.WebAudio={scriptBufferSize:256,PLAYING_STATE:0,PAUSED_STATE:1,FINISHED_STATE:2,supportsWebAudio:function(){return!(!window.AudioContext&&!window.webkitAudioContext)},getAudioContext:function(){return a.WebAudio.audioContext||(a.WebAudio.audioContext=new(window.AudioContext||window.webkitAudioContext)),a.WebAudio.audioContext},getOfflineAudioContext:function(b){return a.WebAudio.offlineAudioContext||(a.WebAudio.offlineAudioContext=new(window.OfflineAudioContext||window.webkitOfflineAudioContext)(1,2,b)),a.WebAudio.offlineAudioContext},init:function(b){this.params=b,this.ac=b.audioContext||this.getAudioContext(),this.lastPlay=this.ac.currentTime,this.startPosition=0,this.scheduledPause=null,this.states=[Object.create(a.WebAudio.state.playing),Object.create(a.WebAudio.state.paused),Object.create(a.WebAudio.state.finished)],this.createVolumeNode(),this.createScriptNode(),this.createAnalyserNode(),this.setState(this.PAUSED_STATE),this.setPlaybackRate(this.params.audioRate),this.setLength(0)},disconnectFilters:function(){this.filters&&(this.filters.forEach(function(a){a&&a.disconnect()}),this.filters=null,this.analyser.connect(this.gainNode))},setState:function(a){this.state!==this.states[a]&&(this.state=this.states[a],this.state.init.call(this))},setFilter:function(){this.setFilters([].slice.call(arguments))},setFilters:function(a){this.disconnectFilters(),a&&a.length&&(this.filters=a,this.analyser.disconnect(),a.reduce(function(a,b){return a.connect(b),b},this.analyser).connect(this.gainNode))},createScriptNode:function(){this.ac.createScriptProcessor?this.scriptNode=this.ac.createScriptProcessor(this.scriptBufferSize):this.scriptNode=this.ac.createJavaScriptNode(this.scriptBufferSize),this.scriptNode.connect(this.ac.destination)},addOnAudioProcess:function(){var a=this;this.scriptNode.onaudioprocess=function(){var b=a.getCurrentTime();b>=a.getDuration()?(a.setState(a.FINISHED_STATE),a.fireEvent("pause")):b>=a.scheduledPause?a.pause():a.state===a.states[a.PLAYING_STATE]&&a.fireEvent("audioprocess",b)}},removeOnAudioProcess:function(){this.scriptNode.onaudioprocess=null},createAnalyserNode:function(){this.analyser=this.ac.createAnalyser(),this.analyser.connect(this.gainNode)},createVolumeNode:function(){this.ac.createGain?this.gainNode=this.ac.createGain():this.gainNode=this.ac.createGainNode(),this.gainNode.connect(this.ac.destination)},setVolume:function(a){this.gainNode.gain.value=a},getVolume:function(){return this.gainNode.gain.value},decodeArrayBuffer:function(a,b,c){this.offlineAc||(this.offlineAc=this.getOfflineAudioContext(this.ac?this.ac.sampleRate:44100)),this.offlineAc.decodeAudioData(a,function(a){b(a)}.bind(this),c)},setPeaks:function(a){this.peaks=a},setLength:function(a){if(!this.mergedPeaks||a!=2*this.mergedPeaks.length-1+2){this.splitPeaks=[],this.mergedPeaks=[];for(var b=this.buffer?this.buffer.numberOfChannels:1,c=0;cn&&(n=p),pthis.mergedPeaks[2*j])&&(this.mergedPeaks[2*j]=n),(0==g||m=this.getDuration()&&(a=0)),null==b&&(b=this.getDuration()),this.startPosition=a,this.lastPlay=this.ac.currentTime,this.state===this.states[this.FINISHED_STATE]&&this.setState(this.PAUSED_STATE),{start:a,end:b}},getPlayedTime:function(){return(this.ac.currentTime-this.lastPlay)*this.playbackRate},play:function(a,b){if(this.buffer){this.createSource();var c=this.seekTo(a,b);a=c.start,b=c.end,this.scheduledPause=b,this.source.start(0,a,b-a),"suspended"==this.ac.state&&this.ac.resume&&this.ac.resume(),this.setState(this.PLAYING_STATE),this.fireEvent("play")}},pause:function(){this.scheduledPause=null,this.startPosition+=this.getPlayedTime(),this.source&&this.source.stop(0),this.setState(this.PAUSED_STATE),this.fireEvent("pause")},getCurrentTime:function(){return this.state.getCurrentTime.call(this)},getPlaybackRate:function(){return this.playbackRate},setPlaybackRate:function(a){a=a||1,this.isPaused()?this.playbackRate=a:(this.pause(),this.playbackRate=a,this.play())}},a.WebAudio.state={},a.WebAudio.state.playing={init:function(){this.addOnAudioProcess()},getPlayedPercents:function(){var a=this.getDuration();return this.getCurrentTime()/a||0},getCurrentTime:function(){return this.startPosition+this.getPlayedTime()}},a.WebAudio.state.paused={init:function(){this.removeOnAudioProcess()},getPlayedPercents:function(){var a=this.getDuration();return this.getCurrentTime()/a||0},getCurrentTime:function(){return this.startPosition}},a.WebAudio.state.finished={init:function(){this.removeOnAudioProcess(),this.fireEvent("finish")},getPlayedPercents:function(){return 1},getCurrentTime:function(){return this.getDuration()}},a.util.extend(a.WebAudio,a.Observer),a.MediaElement=Object.create(a.WebAudio),a.util.extend(a.MediaElement,{init:function(a){this.params=a,this.media={currentTime:0,duration:0,paused:!0,playbackRate:1,play:function(){},pause:function(){}},this.mediaType=a.mediaType.toLowerCase(),this.elementPosition=a.elementPosition,this.setPlaybackRate(this.params.audioRate),this.createTimer()},createTimer:function(){var a=this,b=function(){if(!a.isPaused()){a.fireEvent("audioprocess",a.getCurrentTime());var c=window.requestAnimationFrame||window.webkitRequestAnimationFrame;c(b)}};this.on("play",b)},load:function(a,b,c,d){var e=document.createElement(this.mediaType);e.controls=this.params.mediaControls,e.autoplay=this.params.autoplay||!1,e.preload=null==d?"auto":d,e.src=a,e.style.width="100%";var f=b.querySelector(this.mediaType);f&&b.removeChild(f),b.appendChild(e),this._load(e,c)},loadElt:function(a,b){var c=a;c.controls=this.params.mediaControls,c.autoplay=this.params.autoplay||!1,this._load(c,b)},_load:function(a,b){var c=this;"function"==typeof a.load&&a.load(),a.addEventListener("error",function(){c.fireEvent("error","Error loading media element")}),a.addEventListener("canplay",function(){c.fireEvent("canplay")}),a.addEventListener("ended",function(){c.fireEvent("finish")}),this.media=a,this.peaks=b,this.onPlayEnd=null,this.buffer=null,this.setPlaybackRate(this.playbackRate)},isPaused:function(){return!this.media||this.media.paused},getDuration:function(){var a=(this.buffer||this.media).duration;return a>=1/0&&(a=this.media.seekable.end(0)),a},getCurrentTime:function(){return this.media&&this.media.currentTime},getPlayedPercents:function(){return this.getCurrentTime()/this.getDuration()||0},getPlaybackRate:function(){return this.playbackRate||this.media.playbackRate},setPlaybackRate:function(a){this.playbackRate=a||1,this.media.playbackRate=this.playbackRate},seekTo:function(a){null!=a&&(this.media.currentTime=a),this.clearPlayEnd()},play:function(a,b){this.seekTo(a),this.media.play(),b&&this.setPlayEnd(b),this.fireEvent("play")},pause:function(){this.media&&this.media.pause(),this.clearPlayEnd(),this.fireEvent("pause")},setPlayEnd:function(a){var b=this;this.onPlayEnd=function(c){c>=a&&(b.pause(),b.seekTo(a))},this.on("audioprocess",this.onPlayEnd)},clearPlayEnd:function(){this.onPlayEnd&&(this.un("audioprocess",this.onPlayEnd),this.onPlayEnd=null)},getPeaks:function(b,c,d){return this.buffer?a.WebAudio.getPeaks.call(this,b,c,d):this.peaks||[]},getVolume:function(){return this.media.volume},setVolume:function(a){this.media.volume=a},destroy:function(){this.pause(),this.unAll(),this.media&&this.media.parentNode&&this.media.parentNode.removeChild(this.media),this.media=null}}),a.AudioElement=a.MediaElement,a.Drawer={init:function(a,b){this.container=a,this.params=b,this.width=0,this.height=b.height*this.params.pixelRatio,this.lastPos=0,this.initDrawer(b),this.createWrapper(),this.createElements()},createWrapper:function(){this.wrapper=this.container.appendChild(document.createElement("wave")),this.style(this.wrapper,{display:"block",position:"relative",userSelect:"none",webkitUserSelect:"none",height:this.params.height+"px"}),(this.params.fillParent||this.params.scrollParent)&&this.style(this.wrapper,{width:"100%",overflowX:this.params.hideScrollbar?"hidden":"auto",overflowY:"hidden"}),this.setupWrapperEvents()},handleEvent:function(a,b){!b&&a.preventDefault();var c,d=a.targetTouches?a.targetTouches[0].clientX:a.clientX,e=this.wrapper.getBoundingClientRect(),f=this.width,g=this.getWidth();return!this.params.fillParent&&f1&&(c=1)):c=(d-e.left+this.wrapper.scrollLeft)/this.wrapper.scrollWidth||0,c},setupWrapperEvents:function(){var a=this;this.wrapper.addEventListener("click",function(b){var c=a.wrapper.offsetHeight-a.wrapper.clientHeight;if(0!=c){var d=a.wrapper.getBoundingClientRect();if(b.clientY>=d.bottom-c)return}a.params.interact&&a.fireEvent("click",b,a.handleEvent(b))}),this.wrapper.addEventListener("scroll",function(b){a.fireEvent("scroll",b)})},drawPeaks:function(a,b,c,d){this.setWidth(b),this.params.barWidth?this.drawBars(a,0,c,d):this.drawWave(a,0,c,d)},style:function(a,b){return Object.keys(b).forEach(function(c){a.style[c]!==b[c]&&(a.style[c]=b[c])}),a},resetScroll:function(){null!==this.wrapper&&(this.wrapper.scrollLeft=0)},recenter:function(a){var b=this.wrapper.scrollWidth*a;this.recenterOnPosition(b,!0)},recenterOnPosition:function(a,b){var c=this.wrapper.scrollLeft,d=~~(this.wrapper.clientWidth/2),e=a-d,f=e-c,g=this.wrapper.scrollWidth-this.wrapper.clientWidth;if(0!=g){if(!b&&-d<=f&&f=b){if(this.lastPos=c,this.params.scrollParent&&this.params.autoCenter){var d=~~(this.wrapper.scrollWidth*a);this.recenterOnPosition(d)}this.updateProgress(c)}},destroy:function(){this.unAll(),this.wrapper&&(this.container.removeChild(this.wrapper),this.wrapper=null)},initDrawer:function(){},createElements:function(){},updateSize:function(){},drawWave:function(a,b){},clearWave:function(){},updateProgress:function(a){}},a.util.extend(a.Drawer,a.Observer),a.Drawer.Canvas=Object.create(a.Drawer),a.util.extend(a.Drawer.Canvas,{createElements:function(){var a=this.wrapper.appendChild(this.style(document.createElement("canvas"),{position:"absolute",zIndex:1,left:0,top:0,bottom:0}));if(this.waveCc=a.getContext("2d"),this.progressWave=this.wrapper.appendChild(this.style(document.createElement("wave"),{position:"absolute",zIndex:2,left:0,top:0,bottom:0,overflow:"hidden",width:"0",display:"none",boxSizing:"border-box",borderRightStyle:"solid",borderRightWidth:this.params.cursorWidth+"px",borderRightColor:this.params.cursorColor})),this.params.waveColor!=this.params.progressColor){var b=this.progressWave.appendChild(document.createElement("canvas"));this.progressCc=b.getContext("2d")}},updateSize:function(){var a=Math.round(this.width/this.params.pixelRatio);this.waveCc.canvas.width=this.width,this.waveCc.canvas.height=this.height,this.style(this.waveCc.canvas,{width:a+"px"}),this.style(this.progressWave,{display:"block"}),this.progressCc&&(this.progressCc.canvas.width=this.width,this.progressCc.canvas.height=this.height,this.style(this.progressCc.canvas,{width:a+"px"})),this.clearWave()},clearWave:function(){this.waveCc.clearRect(0,0,this.width,this.height),this.progressCc&&this.progressCc.clearRect(0,0,this.width,this.height)},drawBars:function(b,c,d,e){var f=this;if(b[0]instanceof Array){var g=b;if(this.params.splitChannels)return this.setHeight(g.length*this.params.height*this.params.pixelRatio),void g.forEach(function(a,b){f.drawBars(a,b,d,e)});b=g[0]}var h=[].some.call(b,function(a){return a<0}),i=1;h&&(i=2);var j=.5/this.params.pixelRatio,k=this.width,l=this.params.height*this.params.pixelRatio,m=l*c||0,n=l/2,o=b.length/i,p=this.params.barWidth*this.params.pixelRatio,q=Math.max(this.params.pixelRatio,~~(p/2)),r=p+q,s=1/this.params.barHeight;if(this.params.normalize){var t=a.util.max(b),u=a.util.min(b);s=-u>t?-u:t}var v=o/k;this.waveCc.fillStyle=this.params.waveColor,this.progressCc&&(this.progressCc.fillStyle=this.params.progressColor),[this.waveCc,this.progressCc].forEach(function(a){if(a)for(var c=d/v;cs?-t:s}this.waveCc.fillStyle=this.params.waveColor,this.progressCc&&(this.progressCc.fillStyle=this.params.progressColor),[this.waveCc,this.progressCc].forEach(function(a){if(a){a.beginPath(),a.moveTo(d*q+l,o+n);for(var c=d;c=d;c--){var f=Math.round(b[2*c+1]/r*o);a.lineTo(c*q+l,o-f+n)}a.closePath(),a.fill(),a.fillRect(0,o+n-l,this.width,l)}},this)},updateProgress:function(a){this.style(this.progressWave,{width:a+"px"})},getImage:function(a,b){return this.waveCc.canvas.toDataURL(a,b)}}),a.Drawer.MultiCanvas=Object.create(a.Drawer),a.util.extend(a.Drawer.MultiCanvas,{initDrawer:function(a){if(this.maxCanvasWidth=null!=a.maxCanvasWidth?a.maxCanvasWidth:4e3,this.maxCanvasElementWidth=Math.round(this.maxCanvasWidth/this.params.pixelRatio),this.maxCanvasWidth<=1)throw"maxCanvasWidth must be greater than 1.";if(this.maxCanvasWidth%2==1)throw"maxCanvasWidth must be an even number.";this.hasProgressCanvas=this.params.waveColor!=this.params.progressColor,this.halfPixel=.5/this.params.pixelRatio,this.canvases=[]},createElements:function(){this.progressWave=this.wrapper.appendChild(this.style(document.createElement("wave"),{position:"absolute",zIndex:2,left:0,top:0,bottom:0,overflow:"hidden",width:"0",display:"none",boxSizing:"border-box",borderRightStyle:"solid",borderRightWidth:this.params.cursorWidth+"px",borderRightColor:this.params.cursorColor})),this.addCanvas()},updateSize:function(){for(var a=Math.round(this.width/this.params.pixelRatio),b=Math.ceil(a/this.maxCanvasElementWidth);this.canvases.lengthb;)this.removeCanvas();for(var c in this.canvases){var d=this.maxCanvasWidth+2*Math.ceil(this.params.pixelRatio/2);c==this.canvases.length-1&&(d=this.width-this.maxCanvasWidth*(this.canvases.length-1)),this.updateDimensions(this.canvases[c],d,this.height),this.clearWaveForEntry(this.canvases[c])}},addCanvas:function(){var a={},b=this.maxCanvasElementWidth*this.canvases.length;a.wave=this.wrapper.appendChild(this.style(document.createElement("canvas"),{position:"absolute",zIndex:1,left:b+"px",top:0,bottom:0,height:"100%"})),a.waveCtx=a.wave.getContext("2d"),this.hasProgressCanvas&&(a.progress=this.progressWave.appendChild(this.style(document.createElement("canvas"),{position:"absolute",left:b+"px",top:0,bottom:0,height:"100%"})),a.progressCtx=a.progress.getContext("2d")),this.canvases.push(a)},removeCanvas:function(){var a=this.canvases.pop();a.wave.parentElement.removeChild(a.wave),this.hasProgressCanvas&&a.progress.parentElement.removeChild(a.progress)},updateDimensions:function(a,b,c){var d=Math.round(b/this.params.pixelRatio),e=Math.round(this.width/this.params.pixelRatio);a.start=a.waveCtx.canvas.offsetLeft/e||0,a.end=a.start+d/e,a.waveCtx.canvas.width=b,a.waveCtx.canvas.height=c,this.style(a.waveCtx.canvas,{width:d+"px"}),this.style(this.progressWave,{display:"block"}),this.hasProgressCanvas&&(a.progressCtx.canvas.width=b,a.progressCtx.canvas.height=c,this.style(a.progressCtx.canvas,{width:d+"px"}))},clearWave:function(){for(var a in this.canvases)this.clearWaveForEntry(this.canvases[a])},clearWaveForEntry:function(a){a.waveCtx.clearRect(0,0,a.waveCtx.canvas.width,a.waveCtx.canvas.height),this.hasProgressCanvas&&a.progressCtx.clearRect(0,0,a.progressCtx.canvas.width,a.progressCtx.canvas.height)},drawBars:function(b,c,d,e){var f=this;if(b[0]instanceof Array){var g=b;if(this.params.splitChannels)return this.setHeight(g.length*this.params.height*this.params.pixelRatio),void g.forEach(function(a,b){f.drawBars(a,b,d,e)});b=g[0]}var h=[].some.call(b,function(a){return a<0}),i=1;h&&(i=2);var j=this.width,k=this.params.height*this.params.pixelRatio,l=k*c||0,m=k/2,n=b.length/i,o=this.params.barWidth*this.params.pixelRatio,p=Math.max(this.params.pixelRatio,~~(o/2)),q=o+p,r=1/this.params.barHeight;if(this.params.normalize){var s=a.util.max(b),t=a.util.min(b);r=-t>s?-t:s}for(var u=n/j,v=d/u;vp?-q:p}this.drawLine(b,o,n,m,d,e),this.fillRect(0,n+m-this.halfPixel,this.width,this.halfPixel)},drawLine:function(a,b,c,d,e,f){for(var g in this.canvases){var h=this.canvases[g];this.setFillStyles(h),this.drawLineToContext(h,h.waveCtx,a,b,c,d,e,f),this.drawLineToContext(h,h.progressCtx,a,b,c,d,e,f)}},drawLineToContext:function(a,b,c,d,e,f,g,h){if(b){var i=c.length/2,j=1;this.params.fillParent&&this.width!=i&&(j=this.width/i);var k=Math.round(i*a.start),l=Math.round(i*a.end);if(!(k>h||l=m;o--){var p=c[2*o+1]||0,q=Math.round(p/d*e);b.lineTo((o-k)*j+this.halfPixel,e-q+f)}b.closePath(),b.fill()}}},fillRect:function(a,b,c,d){for(var e=Math.floor(a/this.maxCanvasWidth),f=Math.min(Math.ceil((a+c)/this.maxCanvasWidth)+1,this.canvases.length),g=e;g1?c:c[0]}}),a.Drawer.SplitWavePointPlot=Object.create(a.Drawer.Canvas),a.util.extend(a.Drawer.SplitWavePointPlot,{defaultPlotParams:{plotNormalizeTo:"whole",plotTimeStart:0,plotMin:0, +plotMax:1,plotColor:"#f63",plotProgressColor:"#F00",plotPointHeight:2,plotPointWidth:2,plotSeparator:!0,plotSeparatorColor:"black",plotRangeDisplay:!1,plotRangeUnits:"",plotRangePrecision:4,plotRangeIgnoreOutliers:!1,plotRangeFontSize:12,plotRangeFontType:"Ariel",waveDrawMedianLine:!0,plotFileDelimiter:"\t"},plotTimeStart:0,plotTimeEnd:-1,plotArrayLoaded:!1,plotArray:[],plotPoints:[],plotMin:0,plotMax:1,initDrawer:function(a){var b=this;for(var c in this.defaultPlotParams)void 0===this.params[c]&&(this.params[c]=this.defaultPlotParams[c]);if(this.plotTimeStart=this.params.plotTimeStart,void 0!==this.params.plotTimeEnd&&(this.plotTimeEnd=this.params.plotTimeEnd),Array.isArray(a.plotArray))this.plotArray=a.plotArray,this.plotArrayLoaded=!0;else{var d=function(a){b.plotArray=a,b.plotArrayLoaded=!0,b.fireEvent("plot_array_loaded")};this.loadPlotArrayFromFile(a.plotFileUrl,d,this.params.plotFileDelimiter)}},drawPeaks:function(a,b,c,d){if(1==this.plotArrayLoaded)this.setWidth(b),this.splitChannels=!0,this.params.height=this.params.height/2,a[0]instanceof Array&&(a=a[0]),this.params.barWidth?this.drawBars(a,1,c,d):this.drawWave(a,1,c,d),this.params.height=2*this.params.height,this.calculatePlots(),this.drawPlots();else{var e=this;e.on("plot-array-loaded",function(){e.drawPeaks(a,b,c,d)})}},drawPlots:function(){var a=this.params.height*this.params.pixelRatio/2,b=.5/this.params.pixelRatio;this.waveCc.fillStyle=this.params.plotColor,this.progressCc&&(this.progressCc.fillStyle=this.params.plotProgressColor);for(var c in this.plotPoints){var d=parseInt(c),e=a-this.params.plotPointHeight-this.plotPoints[c]*(a-this.params.plotPointHeight),f=this.params.plotPointHeight;this.waveCc.fillRect(d,e,this.params.plotPointWidth,f),this.progressCc&&this.progressCc.fillRect(d,e,this.params.plotPointWidth,f)}this.params.plotSeparator&&(this.waveCc.fillStyle=this.params.plotSeparatorColor,this.waveCc.fillRect(0,a,this.width,b)),this.params.plotRangeDisplay&&this.displayPlotRange()},displayPlotRange:function(){var a=this.params.plotRangeFontSize*this.params.pixelRatio,b=this.plotMax.toPrecision(this.params.plotRangePrecision)+" "+this.params.plotRangeUnits,c=this.plotMin.toPrecision(this.params.plotRangePrecision)+" "+this.params.plotRangeUnits;this.waveCc.font=a.toString()+"px "+this.params.plotRangeFontType,this.waveCc.fillText(b,3,a),this.waveCc.fillText(c,3,this.height/2)},calculatePlots:function(){this.plotPoints={},this.calculatePlotTimeEnd();for(var a=[],b=-1,c=0,d=99999999999999,e=0,f=99999999999999,g=this.plotTimeEnd-this.plotTimeStart,h=0;hc&&(c=i.value),i.value=this.plotTimeStart&&i.time<=this.plotTimeEnd){var j=Math.round(this.width*(i.time-this.plotTimeStart)/g);if(a.push(i.value),j!==b&&a.length>0){var k=this.avg(a);k>e&&(e=k),k1?this.params.plotRangeIgnoreOutliers||(a[b]=1):c<0?this.params.plotRangeIgnoreOutliers||(a[b]=0):a[b]=c}this.plotPoints=a}},loadPlotArrayFromFile:function(b,c,d){void 0===d&&(d="\t");var e=[],f={url:b,responseType:"text"},g=a.util.ajax(f);g.on("load",function(a){if(200==a.currentTarget.status){for(var b=a.currentTarget.responseText.split("\n"),f=0;fwavesurfer.js Angular Demo
- wavesurfer.js by katspaugh is licensed under a Creative Commons Attribution 3.0 Unported License. + wavesurfer.js by katspaugh is licensed under a BSD-3-Clause License.
diff --git a/example/annotation/index.html b/example/annotation/index.html index 8eb95634a..853ad52e1 100644 --- a/example/annotation/index.html +++ b/example/annotation/index.html @@ -226,11 +226,11 @@
Region Events