├── .gitattributes ├── .gitignore ├── BeachBlazer.wav ├── LISCENSE ├── README.html ├── README.md ├── Rock_With_You.ogg ├── batchstretcher └── BatchStretcher.ipynb ├── gunplay.ttf ├── index.html ├── main.css ├── scripts └── plugins │ ├── wavesurfer.elan.min.js │ ├── wavesurfer.microphone.min.js │ ├── wavesurfer.min.js │ ├── wavesurfer.min.js.map │ ├── wavesurfer.minimap.min.js │ ├── wavesurfer.regions.min.js │ ├── wavesurfer.spectrogram.min.js │ ├── wavesurfer.timeline.min.js │ └── wavesurfer.umd.js ├── soundtouch.js ├── stretcher.jpg ├── stretcher.js └── test.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | *.mp3 49 | -------------------------------------------------------------------------------- /BeachBlazer.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZVK/stretcher/b75efb589ca275dabe22f642a8d3f6a06ae01eca/BeachBlazer.wav -------------------------------------------------------------------------------- /LISCENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2015 Zachary Zukowski of Soundbomb Arts LLC http://soundbomb.media 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.html: -------------------------------------------------------------------------------- 1 | README

Stretcher

1018 |

A time stretching web app using soundtouch-js and wavesurfer-js.

1019 |

WaveSurfer Documentation:

1020 | 1023 |

SoundTouch Basics:

1024 |

SoundTouch

1025 |

The following code exposes soundtouch objects to the browser window:

1026 |

1027 | window.soundtouch = {
1028 |     'RateTransposer': RateTransposer,
1029 |     'Stretch': Stretch,
1030 |     'SimpleFilter': SimpleFilter,
1031 |     'SoundTouch': SoundTouch,
1032 |     'WebAudioBufferSource': WebAudioBufferSource,
1033 |     'getWebAudioNode': getWebAudioNode
1034 | };
1035 | 
1036 | 1037 |

Here are the required argument types each of these constructors will take:

1038 | 1046 |

Browser Requirements

1047 |

MP3 Support:
1048 | Chrome >= 3.0
1049 | Safari >= 3.1
1050 | Safari Mobile >= 3.2
1051 | Opera >= 10.50
1052 | Firefox >= 3.5
1053 | A note from: Mozilla’s Browser Compatibility Table
1054 |


1055 | [5]To avoid patent issues, support for MP3 is not built directly into Firefox. Instead it relies on support from the OS. Firefox supports this format on the following platforms: Windows Vista+ since Firefox 22.0, Android since Firefox 20.0, Firefox OS since Firefox 15.0, Linux since Firefox 26.0 (relies on GStreamer codecs) and OS X 10.7 since Firefox 35.0.
1056 |

-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stretcher 2 | 3 | A time stretching web app using [soundtouch-js](https://github.com/ZVK/soundtouch-js) and [wavesurfer-js](https://github.com/katspaugh/wavesurfer.js). 4 | 5 | ## WaveSurfer Documentation: 6 | - [wavesurfer-js.org](http://wavesurfer-js.org/) 7 | 8 | this.states = [playing, paused, finished] 9 | this.backend.source.buffer.extract() 10 | 11 | ## SoundTouch Basics: 12 | 13 | The following code exposes soundtouch objects to the browser window: 14 | 15 |

16 | window.soundtouch = {
17 |     'RateTransposer': RateTransposer,
18 |     'Stretch': Stretch,
19 |     'SimpleFilter': SimpleFilter,
20 |     'SoundTouch': SoundTouch,
21 |     'WebAudioBufferSource': WebAudioBufferSource,
22 |     'getWebAudioNode': getWebAudioNode
23 | };
24 | 
25 | 26 | Here are the required argument types each of these constructors will take: 27 | 28 | - RateTransposer createBuffer=bool 29 | - Stretch createBuffers=bool, sampleRate=int 30 | - SimpleFilter sourceSound=obj, pipe=obj 31 | - SoundTouch sampleRate=int 32 | - WebAudioBufferSource buffer=obj 33 | - getWebAudioNode context=obj, filter=obj 34 | 35 | ## Browser Requirements 36 | MP3 Support: 37 | Chrome >= 3.0 38 | Safari >= 3.1 39 | Safari Mobile >= 3.2 40 | Opera >= 10.50 41 | Firefox >= 3.5 42 | A note from: [Mozilla's Browser Compatibility Table](https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats#Browser_compatibility) 43 |
44 | [5]To avoid patent issues, support for MP3 is not built directly into Firefox. Instead it relies on support from the OS. Firefox supports this format on the following platforms: Windows Vista+ since Firefox 22.0, Android since Firefox 20.0, Firefox OS since Firefox 15.0, Linux since Firefox 26.0 (relies on GStreamer codecs) and OS X 10.7 since Firefox 35.0. 45 |
46 | -------------------------------------------------------------------------------- /Rock_With_You.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZVK/stretcher/b75efb589ca275dabe22f642a8d3f6a06ae01eca/Rock_With_You.ogg -------------------------------------------------------------------------------- /batchstretcher/BatchStretcher.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false, 8 | "scrolled": true 9 | }, 10 | "outputs": [ 11 | { 12 | "name": "stderr", 13 | "output_type": "stream", 14 | "text": [ 15 | "C:\\Anaconda\\envs\\dadabots\\lib\\site-packages\\librosa\\core\\audio.py:37: UserWarning: Could not import scikits.samplerate. Falling back to scipy.signal\n", 16 | " warnings.warn('Could not import scikits.samplerate. '\n" 17 | ] 18 | }, 19 | { 20 | "data": { 21 | "text/plain": [ 22 | "\"\\nif __name__ == '__main__':\\n if len(sys.argv) > 0:\\n # get the parameters\\n parameters = process_arguments(sys.argv[1:])\\n # Run the HPSS code\\n batch_stretch(parameters['input_directory'],\\n parameters['output_file_prefix'],\\n parameters['speed'])\\n else:\\n\"" 23 | ] 24 | }, 25 | "execution_count": 1, 26 | "metadata": {}, 27 | "output_type": "execute_result" 28 | } 29 | ], 30 | "source": [ 31 | "#!/usr/bin/env python\n", 32 | "'''CREATED:2013-12-08 14:28:34 by Brian McFee \n", 33 | "Demonstration of phase vocoder time stretching.\n", 34 | "\n", 35 | "Batch processing functionality added 2/16/2016 by Zack Zukowski\n", 36 | "'''\n", 37 | "from __future__ import print_function\n", 38 | "\n", 39 | "import argparse\n", 40 | "import sys\n", 41 | "import librosa\n", 42 | "import os\n", 43 | "\n", 44 | "def get_all_audio_in(directory):\n", 45 | " files = [];\n", 46 | " for file in os.listdir(directory):\n", 47 | " if file.endswith(\".wav\"):\n", 48 | " print('found wav :', file)\n", 49 | " files.append(file)\n", 50 | " if file.endswith(\".mp3\"):\n", 51 | " print('found mp3 :', file)\n", 52 | " files.append(file)\n", 53 | " return files\n", 54 | "\n", 55 | "def stretch(input_file, output_file, speed):\n", 56 | " '''Phase-vocoder time stretch demo function.\n", 57 | " :parameters:\n", 58 | " - input_file : str\n", 59 | " path to input audio\n", 60 | " - output_file : str\n", 61 | " path to save output (wav)\n", 62 | " - speed : float > 0\n", 63 | " speed up by this factor\n", 64 | " '''\n", 65 | " \n", 66 | " # 1. Load the wav file, resample\n", 67 | " print('Loading ', input_file)\n", 68 | "\n", 69 | " y, sr = librosa.load(input_file)\n", 70 | "\n", 71 | " # 2. Time-stretch through effects module\n", 72 | " print('Playing back at speed', (speed * 100))\n", 73 | "\n", 74 | " y_stretch = librosa.effects.time_stretch(y, speed)\n", 75 | "\n", 76 | " print('Saving stretched audio to: ', output_file)\n", 77 | " librosa.output.write_wav(output_file, y_stretch, sr)\n", 78 | "\n", 79 | "def batch_stretch(input_directory, output_file_prefix, speed):\n", 80 | " files = get_all_audio_in(input_directory)\n", 81 | " print(\"found these files:\", files)\n", 82 | " for f in files:\n", 83 | " print('stretching :', f)\n", 84 | " stretch(f, output_file_prefix+f, speed)\n", 85 | " \n", 86 | "def process_arguments(args):\n", 87 | " '''Argparse function to get the program parameters'''\n", 88 | "\n", 89 | " parser = argparse.ArgumentParser(description='Time stretching example')\n", 90 | "\n", 91 | " parser.add_argument('input_file',\n", 92 | " action='store',\n", 93 | " help='path to the input file (wav, mp3, etc)')\n", 94 | "\n", 95 | " parser.add_argument('output_file',\n", 96 | " action='store',\n", 97 | " help='path to the stretched output (wav)')\n", 98 | "\n", 99 | " parser.add_argument('-s', '--speed',\n", 100 | " action='store',\n", 101 | " type=float,\n", 102 | " default=2.0,\n", 103 | " required=False,\n", 104 | " help='speed')\n", 105 | "\n", 106 | " return vars(parser.parse_args(args))\n", 107 | "'''\n", 108 | "if __name__ == '__main__':\n", 109 | " if len(sys.argv) > 0:\n", 110 | " # get the parameters\n", 111 | " parameters = process_arguments(sys.argv[1:])\n", 112 | " # Run the HPSS code\n", 113 | " batch_stretch(parameters['input_directory'],\n", 114 | " parameters['output_file_prefix'],\n", 115 | " parameters['speed'])\n", 116 | " else:\n", 117 | "'''" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 3, 123 | "metadata": { 124 | "collapsed": false, 125 | "scrolled": true 126 | }, 127 | "outputs": [ 128 | { 129 | "name": "stdout", 130 | "output_type": "stream", 131 | "text": [ 132 | "found mp3 : All_Blues.mp3\n", 133 | "found mp3 : A_Night_In_Tunisia.mp3\n", 134 | "found mp3 : Crazy.mp3\n", 135 | "found mp3 : Crazy_In_Love.mp3\n", 136 | "found mp3 : C_Jam_Blues.mp3\n", 137 | "found mp3 : Fighter.mp3\n", 138 | "found mp3 : Genius_of_Love.mp3\n", 139 | "found mp3 : Good_Times.mp3\n", 140 | "found mp3 : I_Wanna_Be_Sedated.mp3\n", 141 | "found mp3 : I_Want_You_Back.mp3\n", 142 | "found mp3 : Knock_On_Wood.mp3\n", 143 | "found mp3 : Mas_Que_Nada.mp3\n", 144 | "found mp3 : Rock_With_You.mp3\n", 145 | "found these files: ['All_Blues.mp3', 'A_Night_In_Tunisia.mp3', 'Crazy.mp3', 'Crazy_In_Love.mp3', 'C_Jam_Blues.mp3', 'Fighter.mp3', 'Genius_of_Love.mp3', 'Good_Times.mp3', 'I_Wanna_Be_Sedated.mp3', 'I_Want_You_Back.mp3', 'Knock_On_Wood.mp3', 'Mas_Que_Nada.mp3', 'Rock_With_You.mp3']\n", 146 | "stretching : All_Blues.mp3\n", 147 | "Loading All_Blues.mp3\n", 148 | "Playing back at speed 50.0\n", 149 | "Saving stretched audio to: pv0.5All_Blues.mp3\n", 150 | "stretching : A_Night_In_Tunisia.mp3\n", 151 | "Loading A_Night_In_Tunisia.mp3\n", 152 | "Playing back at speed 50.0\n", 153 | "Saving stretched audio to: pv0.5A_Night_In_Tunisia.mp3\n", 154 | "stretching : Crazy.mp3\n", 155 | "Loading Crazy.mp3\n", 156 | "Playing back at speed 50.0\n", 157 | "Saving stretched audio to: pv0.5Crazy.mp3\n", 158 | "stretching : Crazy_In_Love.mp3\n", 159 | "Loading Crazy_In_Love.mp3\n", 160 | "Playing back at speed 50.0\n", 161 | "Saving stretched audio to: pv0.5Crazy_In_Love.mp3\n", 162 | "stretching : C_Jam_Blues.mp3\n", 163 | "Loading C_Jam_Blues.mp3\n", 164 | "Playing back at speed 50.0\n", 165 | "Saving stretched audio to: pv0.5C_Jam_Blues.mp3\n", 166 | "stretching : Fighter.mp3\n", 167 | "Loading Fighter.mp3\n", 168 | "Playing back at speed 50.0\n", 169 | "Saving stretched audio to: pv0.5Fighter.mp3\n", 170 | "stretching : Genius_of_Love.mp3\n", 171 | "Loading Genius_of_Love.mp3\n", 172 | "Playing back at speed 50.0\n", 173 | "Saving stretched audio to: pv0.5Genius_of_Love.mp3\n", 174 | "stretching : Good_Times.mp3\n", 175 | "Loading Good_Times.mp3\n", 176 | "Playing back at speed 50.0\n", 177 | "Saving stretched audio to: pv0.5Good_Times.mp3\n", 178 | "stretching : I_Wanna_Be_Sedated.mp3\n", 179 | "Loading I_Wanna_Be_Sedated.mp3\n", 180 | "Playing back at speed 50.0\n", 181 | "Saving stretched audio to: pv0.5I_Wanna_Be_Sedated.mp3\n", 182 | "stretching : I_Want_You_Back.mp3\n", 183 | "Loading I_Want_You_Back.mp3\n", 184 | "Playing back at speed 50.0\n", 185 | "Saving stretched audio to: pv0.5I_Want_You_Back.mp3\n", 186 | "stretching : Knock_On_Wood.mp3\n", 187 | "Loading Knock_On_Wood.mp3\n", 188 | "Playing back at speed 50.0\n", 189 | "Saving stretched audio to: pv0.5Knock_On_Wood.mp3\n", 190 | "stretching : Mas_Que_Nada.mp3\n", 191 | "Loading Mas_Que_Nada.mp3\n", 192 | "Playing back at speed 50.0\n", 193 | "Saving stretched audio to: pv0.5Mas_Que_Nada.mp3\n", 194 | "stretching : Rock_With_You.mp3\n", 195 | "Loading Rock_With_You.mp3\n", 196 | "Playing back at speed 50.0\n", 197 | "Saving stretched audio to: pv0.5Rock_With_You.mp3\n" 198 | ] 199 | } 200 | ], 201 | "source": [ 202 | "batch_stretch('./','pv0.5', 0.5)" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": null, 208 | "metadata": { 209 | "collapsed": true 210 | }, 211 | "outputs": [], 212 | "source": [] 213 | } 214 | ], 215 | "metadata": { 216 | "kernelspec": { 217 | "display_name": "Python 2", 218 | "language": "python", 219 | "name": "python2" 220 | }, 221 | "language_info": { 222 | "codemirror_mode": { 223 | "name": "ipython", 224 | "version": 2 225 | }, 226 | "file_extension": ".py", 227 | "mimetype": "text/x-python", 228 | "name": "python", 229 | "nbconvert_exporter": "python", 230 | "pygments_lexer": "ipython2", 231 | "version": "2.7.11" 232 | } 233 | }, 234 | "nbformat": 4, 235 | "nbformat_minor": 0 236 | } 237 | -------------------------------------------------------------------------------- /gunplay.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZVK/stretcher/b75efb589ca275dabe22f642a8d3f6a06ae01eca/gunplay.ttf -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Stretcher 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 |
33 |
34 | 35 | SOUND STRETCHER 36 |
37 |
38 |
39 | 40 |
41 |
42 |
43 |
44 | 48 | 52 | 56 | 61 |
62 |
63 |
64 |
65 |

TEMPO

100% 66 |

67 |
68 |
69 |

PITCH

0st 70 |

71 |
72 |
73 |
DRAG N DROP AN MP3
74 |
75 | 76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /main.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: gunplay; 3 | src: url('./gunplay.ttf'); 4 | } 5 | body { 6 | background-color: #BDCCD4; 7 | font-family: gunplay; 8 | color: #7AC943; 9 | } 10 | #tempo_slider .slider-selection { 11 | background: #BABABA; 12 | } 13 | #main_wrapper { 14 | background-color: #FFF; 15 | } 16 | #heading { 17 | color: #BDCCD4; 18 | } 19 | #drop { 20 | color: #025; 21 | border: 3px dashed #ddd; 22 | padding: 30px; 23 | } 24 | .lead { 25 | text-align: center; 26 | padding: 20px; 27 | } -------------------------------------------------------------------------------- /scripts/plugins/wavesurfer.elan.min.js: -------------------------------------------------------------------------------- 1 | /*! wavesurfer.js 1.0.52 2 | * https://github.com/katspaugh/wavesurfer.js 3 | * @license CC-BY-3.0 */"use strict";WaveSurfer.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):!1}),b},getAnnotationNode:function(a){return document.getElementById("wavesurfer-alignable-"+a.id)}},WaveSurfer.util.extend(WaveSurfer.ELAN,WaveSurfer.Observer); -------------------------------------------------------------------------------- /scripts/plugins/wavesurfer.microphone.min.js: -------------------------------------------------------------------------------- 1 | /*! wavesurfer.js 1.0.52 2 | * https://github.com/katspaugh/wavesurfer.js 3 | * @license CC-BY-3.0 */!function(a,b){"function"==typeof define&&define.amd?define(["wavesurfer"],b):a.WaveSurfer.Microphone=b(a.WaveSurfer)}(this,function(a){"use strict";return 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=navigator.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.msGetUserMedia;b?this.getUserMedia=b.bind(navigator):this.getUserMedia=function(a,b,c){c(new Error("getUserMedia is not supported"))},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(){this.getUserMedia({video:!1,audio:!0},this.gotStream.bind(this),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(){this.active=!1,this.disconnect(),this.stream&&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)}},a.util.extend(a.Microphone,a.Observer),a.Microphone}); -------------------------------------------------------------------------------- /scripts/plugins/wavesurfer.min.js: -------------------------------------------------------------------------------- 1 | /*! wavesurfer.js 1.0.52 2 | * https://github.com/katspaugh/wavesurfer.js 3 | * @license CC-BY-3.0 */ 4 | "use strict";var WaveSurfer={defaultParams:{height:128,waveColor:"#999",progressColor:"#555",cursorColor:"#333",cursorWidth:1,skipLength:2,minPxPerSec:20,pixelRatio:window.devicePixelRatio,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(a){if(this.params=WaveSurfer.util.extend({},this.defaultParams,a),this.container="string"==typeof a.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.createDrawer(),this.createBackend()},createDrawer:function(){var a=this;this.drawer=Object.create(WaveSurfer.Drawer[this.params.renderer]),this.drawer.init(this.container,this.params),this.drawer.on("redraw",function(){a.drawBuffer(),a.drawer.progress(a.backend.getPlayedPercents())}),this.drawer.on("click",function(b,c){setTimeout(function(){a.seekTo(c)},0)}),this.drawer.on("scroll",function(b){a.fireEvent("scroll",b)})},createBackend:function(){var a=this;this.backend&&this.backend.destroy(),"AudioElement"==this.params.backend&&(this.params.backend="MediaElement"),"WebAudio"!=this.params.backend||WaveSurfer.WebAudio.supportsWebAudio()||(this.params.backend="MediaElement"),this.backend=Object.create(WaveSurfer[this.params.backend]),this.backend.init(this.params),this.backend.on("finish",function(){a.fireEvent("finish")}),this.backend.on("play",function(){a.fireEvent("play")}),this.backend.on("pause",function(){a.fireEvent("pause")}),this.backend.on("audioprocess",function(b){a.fireEvent("audioprocess",b)})},startAnimationLoop:function(){var a=this,b=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame,c=function(){if(!a.backend.isPaused()){var d=a.backend.getPlayedPercents();a.drawer.progress(d),a.fireEvent("audioprocess",d),b(c)}};c()},getDuration:function(){return this.backend.getDuration()},getCurrentTime:function(){return this.backend.getCurrentTime()},play:function(a,b){this.backend.play(a,b),this.startAnimationLoop()},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){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.isMuted?(this.backend.setVolume(this.savedVolume),this.isMuted=!1):(this.savedVolume=this.backend.getVolume(),this.backend.setVolume(0),this.isMuted=!0)},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||b>a)&&(c=b);var d=this.backend.getPeaks(c);this.drawer.drawPeaks(d,c),this.fireEvent("redraw",d,c)},zoom:function(a){this.params.minPxPerSec=a,this.params.scrollParent=!0,this.drawBuffer(),this.seekAndCenter(this.getCurrentTime()/this.getDuration()),this.fireEvent("zoom",a)},loadArrayBuffer:function(a){this.decodeArrayBuffer(a,function(a){this.loadDecodedBuffer(a)}.bind(this))},loadDecodedBuffer:function(a){this.backend.load(a),this.drawBuffer(),this.fireEvent("ready")},loadBlob:function(a){var b=this,c=new FileReader;c.addEventListener("progress",function(a){b.onProgress(a)}),c.addEventListener("load",function(a){b.loadArrayBuffer(a.target.result)}),c.addEventListener("error",function(){b.fireEvent("error","Error reading file")}),c.readAsArrayBuffer(a),this.empty()},load:function(a,b){switch(this.params.backend){case"WebAudio":return this.loadBuffer(a);case"MediaElement":return this.loadMediaElement(a,b)}},loadBuffer:function(a){return this.empty(),this.getArrayBuffer(a,this.loadArrayBuffer.bind(this))},loadMediaElement:function(a,b){this.empty(),this.backend.load(a,this.mediaContainer,b),this.tmpEvents.push(this.backend.once("canplay",function(){this.drawBuffer(),this.fireEvent("ready")}.bind(this)),this.backend.once("error",function(a){this.fireEvent("error",a)}.bind(this))),!b&&this.backend.supportsWebAudio()&&this.getArrayBuffer(a,function(a){this.decodeArrayBuffer(a,function(a){this.backend.buffer=a,this.drawBuffer()}.bind(this))}.bind(this))},decodeArrayBuffer:function(a,b){this.backend.decodeArrayBuffer(a,this.fireEvent.bind(this,"decoded"),this.fireEvent.bind(this,"error","Error decoding audiobuffer")),this.tmpEvents.push(this.once("decoded",b))},getArrayBuffer:function(a,b){var c=this,d=WaveSurfer.util.ajax({url:a,responseType:"arraybuffer"});return this.tmpEvents.push(d.on("progress",function(a){c.onProgress(a)}),d.on("success",b),d.on("error",function(a){c.fireEvent("error","XHR error: "+a.target.statusText)})),d},onProgress:function(a){if(a.lengthComputable)var b=a.loaded/a.total;else b=a.loaded/(a.loaded+1e6);this.fireEvent("loading",Math.round(100*b),a.target)},exportPCM:function(a,b,c){a=a||1024,b=b||1e4,c=c||!1;var d=this.backend.getPeaks(a,b),e=[].map.call(d,function(a){return Math.round(a*b)/b}),f=JSON.stringify(e);return c||window.open("data:application/json;charset=utf-8,"+encodeURIComponent(f)),f},clearTmpEvents:function(){this.tmpEvents.forEach(function(a){a.un()})},empty:function(){this.backend.isPaused()||(this.stop(),this.backend.disconnectSource()),this.clearTmpEvents(),this.drawer.progress(0),this.drawer.setWidth(0),this.drawer.drawPeaks({length:this.drawer.getWidth()},0)},destroy:function(){this.fireEvent("destroy"),this.clearTmpEvents(),this.unAll(),this.backend.destroy(),this.drawer.destroy()}};WaveSurfer.create=function(a){var b=Object.create(WaveSurfer);return b.init(a),b},WaveSurfer.util={extend:function(a){var b=Array.prototype.slice.call(arguments,1);return b.forEach(function(b){Object.keys(b).forEach(function(c){a[c]=b[c]})}),a},getId:function(){return"wavesurfer_"+Math.random().toString(32).substring(2)},ajax:function(a){var b=Object.create(WaveSurfer.Observer),c=new XMLHttpRequest,d=!1;return c.open(a.method||"GET",a.url,!0),c.responseType=a.responseType||"json",c.addEventListener("progress",function(a){b.fireEvent("progress",a),a.lengthComputable&&a.loaded==a.total&&(d=!0)}),c.addEventListener("load",function(a){d||b.fireEvent("progress",a),b.fireEvent("load",a),200==c.status||206==c.status?b.fireEvent("success",c.response,a):b.fireEvent("error",a)}),c.addEventListener("error",function(a){b.fireEvent("error",a)}),c.send(),b.xhr=c,b}},WaveSurfer.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)})}}},WaveSurfer.util.extend(WaveSurfer,WaveSurfer.Observer),WaveSurfer.WebAudio={scriptBufferSize:256,PLAYING_STATE:0,PAUSED_STATE:1,FINISHED_STATE:2,supportsWebAudio:function(){return!(!window.AudioContext&&!window.webkitAudioContext)},getAudioContext:function(){return WaveSurfer.WebAudio.audioContext||(WaveSurfer.WebAudio.audioContext=new(window.AudioContext||window.webkitAudioContext)),WaveSurfer.WebAudio.audioContext},getOfflineAudioContext:function(a){return WaveSurfer.WebAudio.offlineAudioContext||(WaveSurfer.WebAudio.offlineAudioContext=new(window.OfflineAudioContext||window.webkitOfflineAudioContext)(1,2,a)),WaveSurfer.WebAudio.offlineAudioContext},init:function(a){this.params=a,this.ac=a.audioContext||this.getAudioContext(),this.lastPlay=this.ac.currentTime,this.startPosition=0,this.scheduledPause=null,this.states=[Object.create(WaveSurfer.WebAudio.state.playing),Object.create(WaveSurfer.WebAudio.state.paused),Object.create(WaveSurfer.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.setState(a.PAUSED_STATE),a.fireEvent("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)},getPeaks:function(a){for(var b=this.buffer.length/a,c=~~(b/10)||1,d=this.buffer.numberOfChannels,e=[],f=[],g=0;d>g;g++)for(var h=e[g]=[],i=this.buffer.getChannelData(g),j=0;a>j;j++){for(var k=~~(j*b),l=~~(k+b),m=i[0],n=i[0],o=k;l>o;o+=c){var p=i[o];p>n&&(n=p),m>p&&(m=p)}h[2*j]=n,h[2*j+1]=m,(0==g||n>f[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){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.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())}},WaveSurfer.WebAudio.state={},WaveSurfer.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()}},WaveSurfer.WebAudio.state.paused={init:function(){this.removeOnAudioProcess()},getPlayedPercents:function(){var a=this.getDuration();return this.getCurrentTime()/a||0},getCurrentTime:function(){return this.startPosition}},WaveSurfer.WebAudio.state.finished={init:function(){this.removeOnAudioProcess(),this.fireEvent("finish")},getPlayedPercents:function(){return 1},getCurrentTime:function(){return this.getDuration()}},WaveSurfer.util.extend(WaveSurfer.WebAudio,WaveSurfer.Observer),WaveSurfer.MediaElement=Object.create(WaveSurfer.WebAudio),WaveSurfer.util.extend(WaveSurfer.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},load:function(a,b,c){var d=this,e=document.createElement(this.mediaType);e.controls=this.params.mediaControls,e.autoplay=this.params.autoplay||!1,e.preload="auto",e.src=a,e.style.width="100%",e.addEventListener("error",function(){d.fireEvent("error","Error loading media element")}),e.addEventListener("canplay",function(){d.fireEvent("canplay")}),e.addEventListener("ended",function(){d.fireEvent("finish")}),e.addEventListener("timeupdate",function(){d.fireEvent("audioprocess",d.getCurrentTime())});var f=b.querySelector(this.mediaType);f&&b.removeChild(f),b.appendChild(e),this.media=e,this.peaks=c,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()),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(a){return this.buffer?WaveSurfer.WebAudio.getPeaks.call(this,a):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}}),WaveSurfer.AudioElement=WaveSurfer.MediaElement,WaveSurfer.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.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){a.preventDefault();var b=this.wrapper.getBoundingClientRect();return(a.clientX-b.left+this.wrapper.scrollLeft)/this.wrapper.scrollWidth||0},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&&f>=-d&&d>f){var h=5;f=Math.max(-h,Math.min(h,f)),e=c+f}e=Math.max(0,Math.min(g,e)),e!=c&&(this.wrapper.scrollLeft=e)}},getWidth:function(){return Math.round(this.container.clientWidth*this.params.pixelRatio)},setWidth:function(a){a!=this.width&&(this.width=a,this.params.fillParent||this.params.scrollParent?this.style(this.wrapper,{width:""}):this.style(this.wrapper,{width:~~(this.width/this.params.pixelRatio)+"px"}),this.updateSize())},setHeight:function(a){a!=this.height&&(this.height=a,this.style(this.wrapper,{height:~~(this.height/this.params.pixelRatio)+"px"}),this.updateSize())},progress:function(a){var b=1/this.params.pixelRatio,c=Math.round(a*this.width)*b;if(c=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)},createElements:function(){},updateSize:function(){},drawWave:function(a,b){},clearWave:function(){},updateProgress:function(a){}},WaveSurfer.util.extend(WaveSurfer.Drawer,WaveSurfer.Observer),WaveSurfer.Drawer.Canvas=Object.create(WaveSurfer.Drawer),WaveSurfer.util.extend(WaveSurfer.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(a,b){if(a[0]instanceof Array){var c=a;if(this.params.splitChannels)return this.setHeight(c.length*this.params.height*this.params.pixelRatio),void c.forEach(this.drawBars,this);a=c[0]}var d=[].some.call(a,function(a){return 0>a});d&&(a=[].filter.call(a,function(a,b){return b%2==0}));var e=.5/this.params.pixelRatio,f=this.width,g=this.params.height*this.params.pixelRatio,h=g*b||0,i=g/2,j=a.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=Math.max.apply(Math,a));var o=j/f;this.waveCc.fillStyle=this.params.waveColor,this.progressCc&&(this.progressCc.fillStyle=this.params.progressColor),[this.waveCc,this.progressCc].forEach(function(b){if(b)for(var c=0;f>c;c+=m){var d=Math.round(a[Math.floor(c*o)]/n*i);b.fillRect(c+e,i-d+h,k+e,2*d)}},this)},drawWave:function(a,b){if(a[0]instanceof Array){var c=a;if(this.params.splitChannels)return this.setHeight(c.length*this.params.height*this.params.pixelRatio),void c.forEach(this.drawWave,this);a=c[0]}var d=[].some.call(a,function(a){return 0>a});if(!d){for(var e=[],f=0,g=a.length;g>f;f++)e[2*f]=a[f],e[2*f+1]=-a[f];a=e}var h=.5/this.params.pixelRatio,i=this.params.height*this.params.pixelRatio,j=i*b||0,k=i/2,l=~~(a.length/2),m=1;this.params.fillParent&&this.width!=l&&(m=this.width/l);var n=1;if(this.params.normalize){var o=Math.max.apply(Math,a),p=Math.min.apply(Math,a);n=-p>o?-p:o}this.waveCc.fillStyle=this.params.waveColor,this.progressCc&&(this.progressCc.fillStyle=this.params.progressColor),[this.waveCc,this.progressCc].forEach(function(b){if(b){b.beginPath(),b.moveTo(h,k+j);for(var c=0;l>c;c++){var d=Math.round(a[2*c]/n*k);b.lineTo(c*m+h,k-d+j)}for(var c=l-1;c>=0;c--){var d=Math.round(a[2*c+1]/n*k);b.lineTo(c*m+h,k-d+j)}b.closePath(),b.fill(),b.fillRect(0,k+j-h,this.width,h)}},this)},updateProgress:function(a){var b=Math.round(this.width*a)/this.params.pixelRatio;this.style(this.progressWave,{width:b+"px"})}}),function(){var a=function(){var a=document.querySelectorAll("wavesurfer");Array.prototype.forEach.call(a,function(a){var b=WaveSurfer.util.extend({container:a,backend:"MediaElement",mediaControls:!0},a.dataset);a.style.display="block";var c=WaveSurfer.create(b);if(a.dataset.peaks)var d=JSON.parse(a.dataset.peaks);c.load(a.dataset.url,d)})};"complete"===document.readyState?a():window.addEventListener("load",a)}(); 5 | //# sourceMappingURL=wavesurfer.min.js.map -------------------------------------------------------------------------------- /scripts/plugins/wavesurfer.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"wavesurfer.min.js","sources":["../src/wavesurfer.js","../src/util.js","../src/webaudio.js","../src/mediaelement.js","../src/drawer.js","../src/drawer.canvas.js","../src/html-init.js"],"names":["WaveSurfer","defaultParams","height","waveColor","progressColor","cursorColor","cursorWidth","skipLength","minPxPerSec","pixelRatio","window","devicePixelRatio","fillParent","scrollParent","hideScrollbar","normalize","audioContext","container","dragSelection","loopSelection","audioRate","interact","splitChannels","mediaContainer","mediaControls","renderer","backend","mediaType","autoCenter","init","params","this","util","extend","document","querySelector","Error","savedVolume","isMuted","tmpEvents","createDrawer","createBackend","my","drawer","Object","create","Drawer","on","drawBuffer","progress","getPlayedPercents","e","setTimeout","seekTo","fireEvent","destroy","WebAudio","supportsWebAudio","time","startAnimationLoop","requestFrame","requestAnimationFrame","webkitRequestAnimationFrame","mozRequestAnimationFrame","frame","isPaused","percent","getDuration","getCurrentTime","play","start","end","pause","playPause","isPlaying","skipBackward","seconds","skip","skipForward","offset","position","duration","Math","max","min","seekAndCenter","recenter","paused","oldScrollParent","stop","setVolume","newVolume","setPlaybackRate","rate","toggleMute","getVolume","toggleScroll","toggleInteraction","nominalWidth","round","parentWidth","getWidth","width","peaks","getPeaks","drawPeaks","zoom","pxPerSec","loadArrayBuffer","arraybuffer","decodeArrayBuffer","data","loadDecodedBuffer","bind","buffer","load","loadBlob","blob","reader","FileReader","addEventListener","onProgress","target","result","readAsArrayBuffer","empty","url","loadBuffer","loadMediaElement","getArrayBuffer","push","once","err","callback","ajax","responseType","statusText","lengthComputable","percentComplete","loaded","total","exportPCM","length","accuracy","noWindow","arr","map","call","val","json","JSON","stringify","open","encodeURIComponent","clearTmpEvents","forEach","un","disconnectSource","setWidth","unAll","wavesurfer","dest","sources","Array","prototype","slice","arguments","source","keys","key","getId","random","toString","substring","options","Observer","xhr","XMLHttpRequest","fired100","method","status","response","send","event","fn","handlers","name","i","splice","handler","apply","args","scriptBufferSize","PLAYING_STATE","PAUSED_STATE","FINISHED_STATE","AudioContext","webkitAudioContext","getAudioContext","getOfflineAudioContext","sampleRate","offlineAudioContext","OfflineAudioContext","webkitOfflineAudioContext","ac","lastPlay","currentTime","startPosition","scheduledPause","states","state","playing","finished","createVolumeNode","createScriptNode","createAnalyserNode","setState","disconnectFilters","filters","filter","disconnect","analyser","connect","gainNode","setFilter","setFilters","reduce","prev","curr","createScriptProcessor","scriptNode","createJavaScriptNode","destination","addOnAudioProcess","onaudioprocess","removeOnAudioProcess","createAnalyser","createGain","createGainNode","newGain","gain","value","errback","offlineAc","decodeAudioData","sampleSize","sampleStep","channels","numberOfChannels","splitPeaks","mergedPeaks","c","chan","getChannelData","j","createSource","createBufferSource","noteGrainOn","noteOff","playbackRate","getPlayedTime","adjustedTime","MediaElement","media","toLowerCase","elementPosition","createElement","controls","autoplay","preload","src","style","prevMedia","removeChild","appendChild","onPlayEnd","Infinity","seekable","clearPlayEnd","setPlayEnd","volume","parentNode","AudioElement","lastPos","createWrapper","createElements","wrapper","display","userSelect","webkitUserSelect","overflowX","overflowY","setupWrapperEvents","handleEvent","preventDefault","bbox","getBoundingClientRect","clientX","left","scrollLeft","scrollWidth","scrollbarHeight","offsetHeight","clientHeight","clientY","bottom","resetScroll","barWidth","drawBars","drawWave","el","styles","prop","recenterOnPosition","immediate","half","clientWidth","maxScroll","updateSize","setHeight","minPxDelta","pos","newPos","updateProgress","clearWave","Canvas","waveCanvas","zIndex","top","waveCc","getContext","progressWave","overflow","boxSizing","borderRightStyle","borderRightWidth","borderRightColor","progressCanvas","progressCc","canvas","clearRect","channelIndex","hasMinVals","some","_","index","$","offsetY","halfH","bar","gap","step","absmax","scale","fillStyle","cc","h","floor","fillRect","hasMinValues","reflectedPeaks","len","beginPath","moveTo","lineTo","closePath","fill","containers","querySelectorAll","dataset","parse","readyState"],"mappings":"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","sourceRoot":"/"} -------------------------------------------------------------------------------- /scripts/plugins/wavesurfer.minimap.min.js: -------------------------------------------------------------------------------- 1 | /*! wavesurfer.js 1.0.52 2 | * https://github.com/katspaugh/wavesurfer.js 3 | * @license CC-BY-3.0 */"use strict";WaveSurfer.Minimap=WaveSurfer.util.extend({},WaveSurfer.Drawer,WaveSurfer.Drawer.Canvas,{init:function(a,b){this.wavesurfer=a,this.container=this.wavesurfer.drawer.container,this.lastPos=this.wavesurfer.drawer.lastPos,this.params=a.util.extend({},this.wavesurfer.drawer.params,{showRegions:!1,showOverview:!1,overviewBorderColor:"green",overviewBorderSize:2},b,{scrollParent:!1,fillParent:!0}),this.width=0,this.height=this.params.height*this.params.pixelRatio,this.createWrapper(),this.createElements(),WaveSurfer.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;ca?this.overviewPosition=0:a+this.overviewWidtha&&(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){this.style(this.element,{left:~~(this.start/a*b)+"px",width:~~((this.end-this.start)/a*b)+"px",backgroundColor:this.color,cursor:this.drag?"move":"default"});for(var c in this.attributes)this.element.setAttribute("data-region-"+c,this.attributes[c]);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.firedIn&&a.start<=b&&a.end>b&&(a.firedIn=!0,a.firedOut=!1,a.fireEvent("in"),a.wavesurfer.fireEvent("region-in",a)),!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))};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=a.wavesurfer.getDuration(),f=function(f){f.stopPropagation(),d=a.wavesurfer.drawer.handleEvent(f)*e,"handle"==f.target.tagName.toLowerCase()?c=f.target.classList.contains("wavesurfer-handle-start")?"start":"end":b=!0},g=function(d){(b||c)&&(b=!1,c=!1,d.stopPropagation(),d.preventDefault(),a.fireEvent("update-end",d),a.wavesurfer.fireEvent("region-update-end",a,d))},h=function(f){if(b||c){var g=a.wavesurfer.drawer.handleEvent(f)*e,h=g-d;d=g,a.drag&&b&&a.onDrag(h),a.resize&&c&&a.onResize(h,c)}};a.element.addEventListener("mousedown",f),a.wrapper.addEventListener("mousemove",h),document.body.addEventListener("mouseup",g),a.on("remove",function(){document.body.removeEventListener("mouseup",g),a.wrapper.removeEventListener("mousemove",h)}),a.wavesurfer.on("destroy",function(){document.body.removeEventListener("mouseup",g)})}()},onDrag:function(a){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)})}},WaveSurfer.util.extend(WaveSurfer.Region,WaveSurfer.Observer),WaveSurfer.initRegions=function(){this.regions||(this.regions=Object.create(WaveSurfer.Regions),this.regions.init(this))},WaveSurfer.addRegion=function(a){return this.initRegions(),this.regions.add(a)},WaveSurfer.clearRegions=function(){this.regions&&this.regions.clear()},WaveSurfer.enableDragSelection=function(a){this.initRegions(),this.regions.enableDragSelection(a)}; -------------------------------------------------------------------------------- /scripts/plugins/wavesurfer.spectrogram.min.js: -------------------------------------------------------------------------------- 1 | /*! wavesurfer.js 1.0.52 2 | * https://github.com/katspaugh/wavesurfer.js 3 | * @license CC-BY-3.0 */"use strict";WaveSurfer.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.createWrapper(),this.createCanvas(),this.render(),b.drawer.wrapper.onscroll=this.updateScroll.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;ff;f++){for(var g=new Array(a[0].length),h=0;h=j||i>=l?0:Math.min(Math.max(j,k),Math.max(l,i))-Math.max(Math.min(j,k),Math.min(l,i));if(m>0)for(var n=0;n0){var g=0,h=0,i=parseInt(c,10)+1,j=function(a){if("function"==typeof d.formatTimeCallback)return d.formatTimeCallback(a);if(a/60>1){var b=parseInt(a/60),a=parseInt(a%60);return a=10>a?"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;i/k>q;q++)q%l==0?(this.timeCc.fillStyle=this.primaryColor,this.timeCc.fillRect(g,0,1,n),this.timeCc.font=p+"px "+this.fontFamily,this.timeCc.fillStyle=this.primaryFontColor,this.timeCc.fillText(j(h),g+5,n)):q%m==0?(this.timeCc.fillStyle=this.secondaryColor,this.timeCc.fillRect(g,0,1,n),this.timeCc.font=p+"px "+this.fontFamily,this.timeCc.fillStyle=this.secondaryFontColor,this.timeCc.fillText(j(h),g+5,n)):(this.timeCc.fillStyle=this.secondaryColor,this.timeCc.fillRect(g,0,1,o)),h+=k,g+=f*k}},updateScroll:function(){this.wrapper.scrollLeft=this.drawer.wrapper.scrollLeft}},a.util.extend(a.Timeline,a.Observer),a.Timeline}); -------------------------------------------------------------------------------- /scripts/plugins/wavesurfer.umd.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | // AMD. Register as an anonymous module unless amdModuleId is set 4 | define('wavesurfer.js', [], function () { 5 | return (root['WaveSurfer'] = factory()); 6 | }); 7 | } else if (typeof exports === 'object') { 8 | // Node. Does not work with strict CommonJS, but 9 | // only CommonJS-like environments that support module.exports, 10 | // like Node. 11 | module.exports = factory(); 12 | } else { 13 | root['WaveSurfer'] = factory(); 14 | } 15 | }(this, function () { 16 | 17 | /*! wavesurfer.js 1.0.49 18 | * https://github.com/katspaugh/wavesurfer.js 19 | * @license CC-BY-3.0 */ 20 | "use strict";var WaveSurfer={defaultParams:{height:128,waveColor:"#999",progressColor:"#555",cursorColor:"#333",cursorWidth:1,skipLength:2,minPxPerSec:20,pixelRatio:window.devicePixelRatio,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(a){if(this.params=WaveSurfer.util.extend({},this.defaultParams,a),this.container="string"==typeof a.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.createDrawer(),this.createBackend()},createDrawer:function(){var a=this;this.drawer=Object.create(WaveSurfer.Drawer[this.params.renderer]),this.drawer.init(this.container,this.params),this.drawer.on("redraw",function(){a.drawBuffer(),a.drawer.progress(a.backend.getPlayedPercents())}),this.drawer.on("click",function(b,c){setTimeout(function(){a.seekTo(c)},0)}),this.drawer.on("scroll",function(b){a.fireEvent("scroll",b)})},createBackend:function(){var a=this;this.backend&&this.backend.destroy(),"AudioElement"==this.params.backend&&(this.params.backend="MediaElement"),"WebAudio"!=this.params.backend||WaveSurfer.WebAudio.supportsWebAudio()||(this.params.backend="MediaElement"),this.backend=Object.create(WaveSurfer[this.params.backend]),this.backend.init(this.params),this.backend.on("finish",function(){a.fireEvent("finish")}),this.backend.on("play",function(){a.fireEvent("play")}),this.backend.on("pause",function(){a.fireEvent("pause")}),this.backend.on("audioprocess",function(b){a.drawer.progress(a.backend.getPlayedPercents()),a.fireEvent("audioprocess",b)})},getDuration:function(){return this.backend.getDuration()},getCurrentTime:function(){return this.backend.getCurrentTime()},play:function(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){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.isMuted?(this.backend.setVolume(this.savedVolume),this.isMuted=!1):(this.savedVolume=this.backend.getVolume(),this.backend.setVolume(0),this.isMuted=!0)},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||b>a)&&(c=b);var d=this.backend.getPeaks(c);this.drawer.drawPeaks(d,c),this.fireEvent("redraw",d,c)},zoom:function(a){this.params.minPxPerSec=a,this.params.scrollParent=!0,this.drawBuffer(),this.seekAndCenter(this.getCurrentTime()/this.getDuration()),this.fireEvent("zoom",a)},loadArrayBuffer:function(a){this.decodeArrayBuffer(a,function(a){this.loadDecodedBuffer(a)}.bind(this))},loadDecodedBuffer:function(a){this.backend.load(a),this.drawBuffer(),this.fireEvent("ready")},loadBlob:function(a){var b=this,c=new FileReader;c.addEventListener("progress",function(a){b.onProgress(a)}),c.addEventListener("load",function(a){b.loadArrayBuffer(a.target.result)}),c.addEventListener("error",function(){b.fireEvent("error","Error reading file")}),c.readAsArrayBuffer(a),this.empty()},load:function(a,b){switch(this.params.backend){case"WebAudio":return this.loadBuffer(a);case"MediaElement":return this.loadMediaElement(a,b)}},loadBuffer:function(a){return this.empty(),this.getArrayBuffer(a,this.loadArrayBuffer.bind(this))},loadMediaElement:function(a,b){this.empty(),this.backend.load(a,this.mediaContainer,b),this.tmpEvents.push(this.backend.once("canplay",function(){this.drawBuffer(),this.fireEvent("ready")}.bind(this)),this.backend.once("error",function(a){this.fireEvent("error",a)}.bind(this))),!b&&this.backend.supportsWebAudio()&&this.getArrayBuffer(a,function(a){this.decodeArrayBuffer(a,function(a){this.backend.buffer=a,this.drawBuffer()}.bind(this))}.bind(this))},decodeArrayBuffer:function(a,b){this.backend.decodeArrayBuffer(a,this.fireEvent.bind(this,"decoded"),this.fireEvent.bind(this,"error","Error decoding audiobuffer")),this.tmpEvents.push(this.once("decoded",b))},getArrayBuffer:function(a,b){var c=this,d=WaveSurfer.util.ajax({url:a,responseType:"arraybuffer"});return this.tmpEvents.push(d.on("progress",function(a){c.onProgress(a)}),d.on("success",b),d.on("error",function(a){c.fireEvent("error","XHR error: "+a.target.statusText)})),d},onProgress:function(a){if(a.lengthComputable)var b=a.loaded/a.total;else b=a.loaded/(a.loaded+1e6);this.fireEvent("loading",Math.round(100*b),a.target)},exportPCM:function(a,b,c){a=a||1024,b=b||1e4,c=c||!1;var d=this.backend.getPeaks(a,b),e=[].map.call(d,function(a){return Math.round(a*b)/b}),f=JSON.stringify(e);return c||window.open("data:application/json;charset=utf-8,"+encodeURIComponent(f)),f},clearTmpEvents:function(){this.tmpEvents.forEach(function(a){a.un()})},empty:function(){this.backend.isPaused()||(this.stop(),this.backend.disconnectSource()),this.clearTmpEvents(),this.drawer.progress(0),this.drawer.setWidth(0),this.drawer.drawPeaks({length:this.drawer.getWidth()},0)},destroy:function(){this.fireEvent("destroy"),this.clearTmpEvents(),this.unAll(),this.backend.destroy(),this.drawer.destroy()}};WaveSurfer.create=function(a){var b=Object.create(WaveSurfer);return b.init(a),b},WaveSurfer.util={extend:function(a){var b=Array.prototype.slice.call(arguments,1);return b.forEach(function(b){Object.keys(b).forEach(function(c){a[c]=b[c]})}),a},getId:function(){return"wavesurfer_"+Math.random().toString(32).substring(2)},ajax:function(a){var b=Object.create(WaveSurfer.Observer),c=new XMLHttpRequest,d=!1;return c.open(a.method||"GET",a.url,!0),c.responseType=a.responseType||"json",c.addEventListener("progress",function(a){b.fireEvent("progress",a),a.lengthComputable&&a.loaded==a.total&&(d=!0)}),c.addEventListener("load",function(a){d||b.fireEvent("progress",a),b.fireEvent("load",a),200==c.status||206==c.status?b.fireEvent("success",c.response,a):b.fireEvent("error",a)}),c.addEventListener("error",function(a){b.fireEvent("error",a)}),c.send(),b.xhr=c,b}},WaveSurfer.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)})}}},WaveSurfer.util.extend(WaveSurfer,WaveSurfer.Observer),WaveSurfer.WebAudio={scriptBufferSize:256,PLAYING_STATE:0,PAUSED_STATE:1,FINISHED_STATE:2,supportsWebAudio:function(){return!(!window.AudioContext&&!window.webkitAudioContext)},getAudioContext:function(){return WaveSurfer.WebAudio.audioContext||(WaveSurfer.WebAudio.audioContext=new(window.AudioContext||window.webkitAudioContext)),WaveSurfer.WebAudio.audioContext},getOfflineAudioContext:function(a){return WaveSurfer.WebAudio.offlineAudioContext||(WaveSurfer.WebAudio.offlineAudioContext=new(window.OfflineAudioContext||window.webkitOfflineAudioContext)(1,2,a)),WaveSurfer.WebAudio.offlineAudioContext},init:function(a){this.params=a,this.ac=a.audioContext||this.getAudioContext(),this.lastPlay=this.ac.currentTime,this.startPosition=0,this.scheduledPause=null,this.states=[Object.create(WaveSurfer.WebAudio.state.playing),Object.create(WaveSurfer.WebAudio.state.paused),Object.create(WaveSurfer.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.setState(a.PAUSED_STATE),a.fireEvent("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)},getPeaks:function(a){for(var b=this.buffer.length/a,c=~~(b/10)||1,d=this.buffer.numberOfChannels,e=[],f=[],g=0;d>g;g++)for(var h=e[g]=[],i=this.buffer.getChannelData(g),j=0;a>j;j++){for(var k=~~(j*b),l=~~(k+b),m=i[0],n=i[0],o=k;l>o;o+=c){var p=i[o];p>n&&(n=p),m>p&&(m=p)}h[2*j]=n,h[2*j+1]=m,(0==g||n>f[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){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.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())}},WaveSurfer.WebAudio.state={},WaveSurfer.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()}},WaveSurfer.WebAudio.state.paused={init:function(){this.removeOnAudioProcess()},getPlayedPercents:function(){var a=this.getDuration();return this.getCurrentTime()/a||0},getCurrentTime:function(){return this.startPosition}},WaveSurfer.WebAudio.state.finished={init:function(){this.removeOnAudioProcess(),this.fireEvent("finish")},getPlayedPercents:function(){return 1},getCurrentTime:function(){return this.getDuration()}},WaveSurfer.util.extend(WaveSurfer.WebAudio,WaveSurfer.Observer),WaveSurfer.MediaElement=Object.create(WaveSurfer.WebAudio),WaveSurfer.util.extend(WaveSurfer.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},load:function(a,b,c){var d=this,e=document.createElement(this.mediaType);e.controls=this.params.mediaControls,e.autoplay=this.params.autoplay||!1,e.preload="auto",e.src=a,e.style.width="100%",e.addEventListener("error",function(){d.fireEvent("error","Error loading media element")}),e.addEventListener("canplay",function(){d.fireEvent("canplay")}),e.addEventListener("ended",function(){d.fireEvent("finish")}),e.addEventListener("timeupdate",function(){d.fireEvent("audioprocess",d.getCurrentTime())});var f=b.querySelector(this.mediaType);f&&b.removeChild(f),b.appendChild(e),this.media=e,this.peaks=c,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()),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(a){return this.buffer?WaveSurfer.WebAudio.getPeaks.call(this,a):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}}),WaveSurfer.AudioElement=WaveSurfer.MediaElement,WaveSurfer.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.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){a.preventDefault();var b=this.wrapper.getBoundingClientRect();return(a.clientX-b.left+this.wrapper.scrollLeft)/this.wrapper.scrollWidth||0},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&&f>=-d&&d>f){var h=5;f=Math.max(-h,Math.min(h,f)),e=c+f}e=Math.max(0,Math.min(g,e)),e!=c&&(this.wrapper.scrollLeft=e)}},getWidth:function(){return Math.round(this.container.clientWidth*this.params.pixelRatio)},setWidth:function(a){a!=this.width&&(this.width=a,this.params.fillParent||this.params.scrollParent?this.style(this.wrapper,{width:""}):this.style(this.wrapper,{width:~~(this.width/this.params.pixelRatio)+"px"}),this.updateSize())},setHeight:function(a){a!=this.height&&(this.height=a,this.style(this.wrapper,{height:~~(this.height/this.params.pixelRatio)+"px"}),this.updateSize())},progress:function(a){var b=1/this.params.pixelRatio,c=Math.round(a*this.width)*b;if(c=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)},createElements:function(){},updateSize:function(){},drawWave:function(a,b){},clearWave:function(){},updateProgress:function(a){}},WaveSurfer.util.extend(WaveSurfer.Drawer,WaveSurfer.Observer),WaveSurfer.Drawer.Canvas=Object.create(WaveSurfer.Drawer),WaveSurfer.util.extend(WaveSurfer.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(a,b){if(a[0]instanceof Array){var c=a;if(this.params.splitChannels)return this.setHeight(c.length*this.params.height*this.params.pixelRatio),void c.forEach(this.drawBars,this);a=c[0]}var d=[].some.call(a,function(a){return 0>a});d&&(a=[].filter.call(a,function(a,b){return b%2==0}));var e=.5/this.params.pixelRatio,f=this.width,g=this.params.height*this.params.pixelRatio,h=g*b||0,i=g/2,j=a.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=Math.max.apply(Math,a));var o=j/f;this.waveCc.fillStyle=this.params.waveColor,this.progressCc&&(this.progressCc.fillStyle=this.params.progressColor),[this.waveCc,this.progressCc].forEach(function(b){if(b)for(var c=0;f>c;c+=m){var d=Math.round(a[Math.floor(c*o)]/n*i);b.fillRect(c+e,i-d+h,k+e,2*d)}},this)},drawWave:function(a,b){if(a[0]instanceof Array){var c=a;if(this.params.splitChannels)return this.setHeight(c.length*this.params.height*this.params.pixelRatio),void c.forEach(this.drawWave,this);a=c[0]}var d=[].some.call(a,function(a){return 0>a});if(!d){for(var e=[],f=0,g=a.length;g>f;f++)e[2*f]=a[f],e[2*f+1]=-a[f];a=e}var h=.5/this.params.pixelRatio,i=this.params.height*this.params.pixelRatio,j=i*b||0,k=i/2,l=~~(a.length/2),m=1;this.params.fillParent&&this.width!=l&&(m=this.width/l);var n=1;if(this.params.normalize){var o=Math.max.apply(Math,a),p=Math.min.apply(Math,a);n=-p>o?-p:o}this.waveCc.fillStyle=this.params.waveColor,this.progressCc&&(this.progressCc.fillStyle=this.params.progressColor),[this.waveCc,this.progressCc].forEach(function(b){if(b){b.beginPath(),b.moveTo(h,k+j);for(var c=0;l>c;c++){var d=Math.round(a[2*c]/n*k);b.lineTo(c*m+h,k-d+j)}for(var c=l-1;c>=0;c--){var d=Math.round(a[2*c+1]/n*k);b.lineTo(c*m+h,k-d+j)}b.closePath(),b.fill(),b.fillRect(0,k+j-h,this.width,h)}},this)},updateProgress:function(a){var b=Math.round(this.width*a)/this.params.pixelRatio;this.style(this.progressWave,{width:b+"px"})}}),function(){var a=function(){var a=document.querySelectorAll("wavesurfer");Array.prototype.forEach.call(a,function(a){var b=WaveSurfer.util.extend({container:a,backend:"MediaElement",mediaControls:!0},a.dataset);a.style.display="block";var c=WaveSurfer.create(b);if(a.dataset.peaks)var d=JSON.parse(a.dataset.peaks);c.load(a.dataset.url,d)})};"complete"===document.readyState?a():window.addEventListener("load",a)}(); 21 | //# sourceMappingURL=wavesurfer.min.js.map 22 | return WaveSurfer; 23 | 24 | })); 25 | -------------------------------------------------------------------------------- /soundtouch.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SoundTouch JS audio processing library 3 | * Copyright (c) Olli Parviainen 4 | * Copyright (c) Ryan Berdeen 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | (function(window) { 22 | 23 | /** 24 | * Giving this value for the sequence length sets automatic parameter value 25 | * according to tempo setting (recommended) 26 | */ 27 | var USE_AUTO_SEQUENCE_LEN = 0; 28 | 29 | /** 30 | * Default length of a single processing sequence, in milliseconds. This determines to how 31 | * long sequences the original sound is chopped in the time-stretch algorithm. 32 | * 33 | * The larger this value is, the lesser sequences are used in processing. In principle 34 | * a bigger value sounds better when slowing down tempo, but worse when increasing tempo 35 | * and vice versa. 36 | * 37 | * Increasing this value reduces computational burden and vice versa. 38 | */ 39 | //var DEFAULT_SEQUENCE_MS = 130 40 | var DEFAULT_SEQUENCE_MS = USE_AUTO_SEQUENCE_LEN; 41 | 42 | /** 43 | * Giving this value for the seek window length sets automatic parameter value 44 | * according to tempo setting (recommended) 45 | */ 46 | var USE_AUTO_SEEKWINDOW_LEN = 0; 47 | 48 | /** 49 | * Seeking window default length in milliseconds for algorithm that finds the best possible 50 | * overlapping location. This determines from how wide window the algorithm may look for an 51 | * optimal joining location when mixing the sound sequences back together. 52 | * 53 | * The bigger this window setting is, the higher the possibility to find a better mixing 54 | * position will become, but at the same time large values may cause a "drifting" artifact 55 | * because consequent sequences will be taken at more uneven intervals. 56 | * 57 | * If there's a disturbing artifact that sounds as if a constant frequency was drifting 58 | * around, try reducing this setting. 59 | * 60 | * Increasing this value increases computational burden and vice versa. 61 | */ 62 | //var DEFAULT_SEEKWINDOW_MS = 25; 63 | var DEFAULT_SEEKWINDOW_MS = USE_AUTO_SEEKWINDOW_LEN; 64 | 65 | /** 66 | * Overlap length in milliseconds. When the chopped sound sequences are mixed back together, 67 | * to form a continuous sound stream, this parameter defines over how long period the two 68 | * consecutive sequences are let to overlap each other. 69 | * 70 | * This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting 71 | * by a large amount, you might wish to try a smaller value on this. 72 | * 73 | * Increasing this value increases computational burden and vice versa. 74 | */ 75 | var DEFAULT_OVERLAP_MS = 8; 76 | 77 | // Table for the hierarchical mixing position seeking algorithm 78 | var _SCAN_OFFSETS = [ 79 | [ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806, 80 | 868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0], 81 | [-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0, 82 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 83 | [ -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0, 84 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 85 | [ -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0, 86 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]; 87 | 88 | // Adjust tempo param according to tempo, so that variating processing sequence length is used 89 | // at varius tempo settings, between the given low...top limits 90 | var AUTOSEQ_TEMPO_LOW = 0.5; // auto setting low tempo range (-50%) 91 | var AUTOSEQ_TEMPO_TOP = 2.0; // auto setting top tempo range (+100%) 92 | 93 | // sequence-ms setting values at above low & top tempo 94 | var AUTOSEQ_AT_MIN = 125.0; 95 | var AUTOSEQ_AT_MAX = 50.0; 96 | var AUTOSEQ_K = ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW)); 97 | var AUTOSEQ_C = (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW)); 98 | 99 | // seek-window-ms setting values at above low & top tempo 100 | var AUTOSEEK_AT_MIN = 25.0; 101 | var AUTOSEEK_AT_MAX = 15.0; 102 | var AUTOSEEK_K = ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW)); 103 | var AUTOSEEK_C = (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW)); 104 | 105 | function extend(a,b) { 106 | for (var i in b) { 107 | var g = b.__lookupGetter__(i), 108 | s = b.__lookupSetter__(i); 109 | if (g || s) { 110 | if (g) { 111 | a.__defineGetter__(i, g); 112 | } 113 | if (s) { 114 | a.__defineSetter__(i, s); 115 | } 116 | } 117 | else { 118 | a[i] = b[i]; 119 | } 120 | } 121 | return a; 122 | } 123 | 124 | function testFloatEqual(a, b) { 125 | return (a > b ? a - b : b - a) > 1e-10; 126 | } 127 | 128 | function AbstractFifoSamplePipe(createBuffers) { 129 | if (createBuffers) { 130 | this.inputBuffer = new FifoSampleBuffer(); 131 | this.outputBuffer = new FifoSampleBuffer(); 132 | } 133 | else { 134 | this.inputBuffer = this.outputBuffer = null; 135 | } 136 | } 137 | AbstractFifoSamplePipe.prototype = { 138 | get inputBuffer() { 139 | return this._inputBuffer; 140 | }, 141 | set inputBuffer(inputBuffer) { 142 | this._inputBuffer = inputBuffer; 143 | }, 144 | get outputBuffer() { 145 | return this._outputBuffer; 146 | }, 147 | set outputBuffer(outputBuffer) { 148 | this._outputBuffer = outputBuffer; 149 | }, 150 | clear: function() { 151 | this._inputBuffer.clear(); 152 | this._outputBuffer.clear(); 153 | } 154 | }; 155 | 156 | function RateTransposer(createBuffers) { 157 | AbstractFifoSamplePipe.call(this, createBuffers); 158 | this._reset(); 159 | this.rate = 1; 160 | } 161 | extend(RateTransposer.prototype, AbstractFifoSamplePipe.prototype); 162 | extend(RateTransposer.prototype, { 163 | set rate(rate) { 164 | this._rate = rate; 165 | // TODO aa filter 166 | }, 167 | _reset: function() { 168 | this.slopeCount = 0; 169 | this.prevSampleL = 0; 170 | this.prevSampleR = 0; 171 | }, 172 | process: function() { 173 | // TODO aa filter 174 | var numFrames = this._inputBuffer.frameCount; 175 | this._outputBuffer.ensureAdditionalCapacity(numFrames / this._rate + 1); 176 | var numFramesOutput = this._transpose(numFrames); 177 | this._inputBuffer.receive(); 178 | this._outputBuffer.put(numFramesOutput); 179 | }, 180 | _transpose: function(numFrames) { 181 | if (numFrames === 0) { 182 | return 0; // No work. 183 | } 184 | 185 | var src = this._inputBuffer.vector; 186 | var srcOffset = this._inputBuffer.startIndex; 187 | 188 | var dest = this._outputBuffer.vector; 189 | var destOffset = this._outputBuffer.endIndex; 190 | 191 | var used = 0; 192 | var i = 0; 193 | 194 | while (this.slopeCount < 1.0) { 195 | dest[destOffset + 2 * i] = (1.0 - this.slopeCount) * this.prevSampleL + this.slopeCount * src[srcOffset]; 196 | dest[destOffset + 2 * i + 1] = (1.0 - this.slopeCount) * this.prevSampleR + this.slopeCount * src[srcOffset + 1]; 197 | i++; 198 | this.slopeCount += this._rate; 199 | } 200 | 201 | this.slopeCount -= 1.0; 202 | 203 | if (numFrames != 1) { 204 | out: while (true) { 205 | while (this.slopeCount > 1.0) { 206 | this.slopeCount -= 1.0; 207 | used++; 208 | if (used >= numFrames - 1) { 209 | break out; 210 | } 211 | } 212 | 213 | var srcIndex = srcOffset + 2 * used; 214 | dest[destOffset + 2 * i] = (1.0 - this.slopeCount) * src[srcIndex] + this.slopeCount * src[srcIndex + 2]; 215 | dest[destOffset + 2 * i + 1] = (1.0 - this.slopeCount) * src[srcIndex + 1] + this.slopeCount * src[srcIndex + 3]; 216 | 217 | i++; 218 | this.slopeCount += this._rate; 219 | } 220 | } 221 | 222 | this.prevSampleL = src[srcOffset + 2 * numFrames - 2]; 223 | this.prevSampleR = src[srcOffset + 2 * numFrames - 1]; 224 | 225 | return i; 226 | } 227 | }); 228 | 229 | function FifoSampleBuffer() { 230 | this._vector = new Float32Array(); 231 | this._position = 0; 232 | this._frameCount = 0; 233 | } 234 | FifoSampleBuffer.prototype = { 235 | get vector() { 236 | return this._vector; 237 | }, 238 | get position() { 239 | return this._position; 240 | }, 241 | get startIndex() { 242 | return this._position * 2; 243 | }, 244 | get frameCount() { 245 | return this._frameCount; 246 | }, 247 | get endIndex() { 248 | return (this._position + this._frameCount) * 2; 249 | }, 250 | clear: function(frameCount) { 251 | this.receive(frameCount); 252 | this.rewind(); 253 | }, 254 | put: function(numFrames) { 255 | this._frameCount += numFrames; 256 | }, 257 | putSamples: function(samples, position, numFrames) { 258 | position = position || 0; 259 | var sourceOffset = position * 2; 260 | if (!(numFrames >= 0)) { 261 | numFrames = (samples.length - sourceOffset) / 2; 262 | } 263 | var numSamples = numFrames * 2; 264 | 265 | this.ensureCapacity(numFrames + this._frameCount); 266 | 267 | var destOffset = this.endIndex; 268 | this._vector.set(samples.subarray(sourceOffset, sourceOffset + numSamples), destOffset); 269 | 270 | this._frameCount += numFrames; 271 | }, 272 | putBuffer: function(buffer, position, numFrames) { 273 | position = position || 0; 274 | if (!(numFrames >= 0)) { 275 | numFrames = buffer.frameCount - position; 276 | } 277 | this.putSamples(buffer.vector, buffer.position + position, numFrames); 278 | }, 279 | receive: function(numFrames) { 280 | if (!(numFrames >= 0) || numFrames > this._frameCount) { 281 | numFrames = this._frameCount; 282 | } 283 | this._frameCount -= numFrames; 284 | this._position += numFrames; 285 | }, 286 | receiveSamples: function(output, numFrames) { 287 | var numSamples = numFrames * 2; 288 | var sourceOffset = this.startIndex; 289 | output.set(this._vector.subarray(sourceOffset, sourceOffset + numSamples)); 290 | this.receive(numFrames); 291 | }, 292 | extract: function(output, position, numFrames) { 293 | var sourceOffset = this.startIndex + position * 2; 294 | var numSamples = numFrames * 2; 295 | output.set(this._vector.subarray(sourceOffset, sourceOffset + numSamples)); 296 | }, 297 | ensureCapacity: function(numFrames) { 298 | var minLength = numFrames * 2; 299 | if (this._vector.length < minLength) { 300 | var newVector = new Float32Array(minLength); 301 | newVector.set(this._vector.subarray(this.startIndex, this.endIndex)); 302 | this._vector = newVector; 303 | this._position = 0; 304 | } 305 | else { 306 | this.rewind(); 307 | } 308 | }, 309 | ensureAdditionalCapacity: function(numFrames) { 310 | this.ensureCapacity(this.frameCount + numFrames); 311 | }, 312 | rewind: function() { 313 | if (this._position > 0) { 314 | this._vector.set(this._vector.subarray(this.startIndex, this.endIndex)); 315 | this._position = 0; 316 | } 317 | } 318 | }; 319 | 320 | function SimpleFilter(sourceSound, pipe) { 321 | this._pipe = pipe; 322 | this.sourceSound = sourceSound; 323 | this.historyBufferSize = 22050; 324 | this._sourcePosition = 0; 325 | this.outputBufferPosition = 0; 326 | this._position = 0; 327 | } 328 | SimpleFilter.prototype = { 329 | get pipe() { 330 | return this._pipe; 331 | }, 332 | get position() { 333 | return this._position; 334 | }, 335 | set position(position) { 336 | if (position > this._position) { 337 | throw new RangeError('New position may not be greater than current position'); 338 | } 339 | var newOutputBufferPosition = this.outputBufferPosition - (this._position - position); 340 | if (newOutputBufferPosition < 0) { 341 | throw new RangeError('New position falls outside of history buffer'); 342 | } 343 | this.outputBufferPosition = newOutputBufferPosition; 344 | this._position = position; 345 | }, 346 | get sourcePosition() { 347 | return this._sourcePosition; 348 | }, 349 | set sourcePosition(sourcePosition) { 350 | this.clear(); 351 | this._sourcePosition = sourcePosition; 352 | }, 353 | get inputBuffer() { 354 | return this._pipe.inputBuffer; 355 | }, 356 | get outputBuffer() { 357 | return this._pipe.outputBuffer; 358 | }, 359 | fillInputBuffer: function(numFrames) { 360 | var samples = new Float32Array(numFrames * 2); 361 | var numFramesExtracted = this.sourceSound.extract(samples, numFrames, this._sourcePosition); 362 | this._sourcePosition += numFramesExtracted; 363 | this.inputBuffer.putSamples(samples, 0, numFramesExtracted); 364 | }, 365 | fillOutputBuffer: function(numFrames) { 366 | while (this.outputBuffer.frameCount < numFrames) { 367 | // TODO hardcoded buffer size 368 | var numInputFrames = (8192 * 2) - this.inputBuffer.frameCount; 369 | 370 | this.fillInputBuffer(numInputFrames); 371 | 372 | if (this.inputBuffer.frameCount < (8192 * 2)) { 373 | break; 374 | // TODO flush pipe 375 | } 376 | this._pipe.process(); 377 | } 378 | }, 379 | extract: function(target, numFrames) { 380 | this.fillOutputBuffer(this.outputBufferPosition + numFrames); 381 | 382 | var numFramesExtracted = Math.min(numFrames, this.outputBuffer.frameCount - this.outputBufferPosition); 383 | this.outputBuffer.extract(target, this.outputBufferPosition, numFramesExtracted); 384 | 385 | var currentFrames = this.outputBufferPosition + numFramesExtracted; 386 | this.outputBufferPosition = Math.min(this.historyBufferSize, currentFrames); 387 | this.outputBuffer.receive(Math.max(currentFrames - this.historyBufferSize, 0)); 388 | 389 | this._position += numFramesExtracted; 390 | return numFramesExtracted; 391 | }, 392 | handleSampleData: function(e) { 393 | this.extract(e.data, 4096); 394 | }, 395 | clear: function() { 396 | // TODO yuck 397 | this._pipe.clear(); 398 | this.outputBufferPosition = 0; 399 | } 400 | }; 401 | 402 | function Stretch(createBuffers, sampleRate) { 403 | AbstractFifoSamplePipe.call(this, createBuffers); 404 | this.bQuickSeek = true; 405 | this.bMidBufferDirty = false; 406 | 407 | this.pMidBuffer = null; 408 | this.overlapLength = 0; 409 | 410 | this.bAutoSeqSetting = true; 411 | this.bAutoSeekSetting = true; 412 | 413 | this._tempo = 1; 414 | this.setParameters(sampleRate, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS); 415 | } 416 | extend(Stretch.prototype, AbstractFifoSamplePipe.prototype); 417 | extend(Stretch.prototype, { 418 | clear: function() { 419 | AbstractFifoSamplePipe.prototype.clear.call(this); 420 | this._clearMidBuffer(); 421 | }, 422 | _clearMidBuffer: function() { 423 | if (this.bMidBufferDirty) { 424 | this.bMidBufferDirty = false; 425 | this.pMidBuffer = null; 426 | } 427 | }, 428 | 429 | /** 430 | * Sets routine control parameters. These control are certain time constants 431 | * defining how the sound is stretched to the desired duration. 432 | * 433 | * 'sampleRate' = sample rate of the sound 434 | * 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms) 435 | * 'seekwindowMS' = seeking window length for scanning the best overlapping 436 | * position (default = 28 ms) 437 | * 'overlapMS' = overlapping length (default = 12 ms) 438 | */ 439 | setParameters: function(aSampleRate, aSequenceMS, aSeekWindowMS, aOverlapMS) { 440 | // accept only positive parameter values - if zero or negative, use old values instead 441 | if (aSampleRate > 0) { 442 | this.sampleRate = aSampleRate; 443 | } 444 | if (aOverlapMS > 0) { 445 | this.overlapMs = aOverlapMS; 446 | } 447 | 448 | if (aSequenceMS > 0) { 449 | this.sequenceMs = aSequenceMS; 450 | this.bAutoSeqSetting = false; 451 | } 452 | else { 453 | // zero or below, use automatic setting 454 | this.bAutoSeqSetting = true; 455 | } 456 | 457 | if (aSeekWindowMS > 0) { 458 | this.seekWindowMs = aSeekWindowMS; 459 | this.bAutoSeekSetting = false; 460 | } 461 | else { 462 | // zero or below, use automatic setting 463 | this.bAutoSeekSetting = true; 464 | } 465 | 466 | this.calcSeqParameters(); 467 | 468 | this.calculateOverlapLength(this.overlapMs); 469 | 470 | // set tempo to recalculate 'sampleReq' 471 | this.tempo = this._tempo; 472 | }, 473 | 474 | /** 475 | * Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower 476 | * tempo, larger faster tempo. 477 | */ 478 | set tempo(newTempo) { 479 | var intskip; 480 | 481 | this._tempo = newTempo; 482 | 483 | // Calculate new sequence duration 484 | this.calcSeqParameters(); 485 | 486 | // Calculate ideal skip length (according to tempo value) 487 | this.nominalSkip = this._tempo * (this.seekWindowLength - this.overlapLength); 488 | this.skipFract = 0; 489 | intskip = Math.floor(this.nominalSkip + 0.5); 490 | 491 | // Calculate how many samples are needed in the 'inputBuffer' to 492 | // process another batch of samples 493 | this.sampleReq = Math.max(intskip + this.overlapLength, this.seekWindowLength) + this.seekLength; 494 | }, 495 | get inputChunkSize() { 496 | return this.sampleReq; 497 | }, 498 | get outputChunkSize() { 499 | return this.overlapLength + Math.max(0, this.seekWindowLength - 2 * this.overlapLength); 500 | }, 501 | 502 | /** 503 | * Calculates overlapInMsec period length in samples. 504 | */ 505 | calculateOverlapLength: function(overlapInMsec) { 506 | var newOvl; 507 | 508 | // TODO assert(overlapInMsec >= 0); 509 | newOvl = (this.sampleRate * overlapInMsec) / 1000; 510 | if (newOvl < 16) newOvl = 16; 511 | 512 | // must be divisible by 8 513 | newOvl -= newOvl % 8; 514 | 515 | this.overlapLength = newOvl; 516 | 517 | this.pRefMidBuffer = new Float32Array(this.overlapLength * 2); 518 | this.pMidBuffer = new Float32Array(this.overlapLength * 2); 519 | }, 520 | checkLimits: function(x, mi, ma) { 521 | return (x < mi) ? mi : ((x > ma) ? ma : x); 522 | }, 523 | 524 | /** 525 | * Calculates processing sequence length according to tempo setting 526 | */ 527 | calcSeqParameters: function() { 528 | var seq; 529 | var seek; 530 | 531 | if (this.bAutoSeqSetting) { 532 | seq = AUTOSEQ_C + AUTOSEQ_K * this._tempo; 533 | seq = this.checkLimits(seq, AUTOSEQ_AT_MAX, AUTOSEQ_AT_MIN); 534 | this.sequenceMs = Math.floor(seq + 0.5); 535 | } 536 | 537 | if (this.bAutoSeekSetting) { 538 | seek = AUTOSEEK_C + AUTOSEEK_K * this._tempo; 539 | seek = this.checkLimits(seek, AUTOSEEK_AT_MAX, AUTOSEEK_AT_MIN); 540 | this.seekWindowMs = Math.floor(seek + 0.5); 541 | } 542 | 543 | // Update seek window lengths 544 | this.seekWindowLength = Math.floor((this.sampleRate * this.sequenceMs) / 1000); 545 | this.seekLength = Math.floor((this.sampleRate * this.seekWindowMs) / 1000); 546 | }, 547 | 548 | /** 549 | * Enables/disables the quick position seeking algorithm. 550 | */ 551 | set quickSeek(enable) { 552 | this.bQuickSeek = enable; 553 | }, 554 | 555 | /** 556 | * Seeks for the optimal overlap-mixing position. 557 | */ 558 | seekBestOverlapPosition: function() { 559 | if (this.bQuickSeek) { 560 | return this.seekBestOverlapPositionStereoQuick(); 561 | } 562 | else { 563 | return this.seekBestOverlapPositionStereo(); 564 | } 565 | }, 566 | 567 | /** 568 | * Seeks for the optimal overlap-mixing position. The 'stereo' version of the 569 | * routine 570 | * 571 | * The best position is determined as the position where the two overlapped 572 | * sample sequences are 'most alike', in terms of the highest cross-correlation 573 | * value over the overlapping period 574 | */ 575 | seekBestOverlapPositionStereo: function() { 576 | var bestOffs, bestCorr, corr, i; 577 | 578 | // Slopes the amplitudes of the 'midBuffer' samples. 579 | this.precalcCorrReferenceStereo(); 580 | 581 | bestCorr = Number.MIN_VALUE; 582 | bestOffs = 0; 583 | 584 | // Scans for the best correlation value by testing each possible position 585 | // over the permitted range. 586 | for (i = 0; i < this.seekLength; i++) { 587 | // Calculates correlation value for the mixing position corresponding 588 | // to 'i' 589 | corr = this.calcCrossCorrStereo(2 * i, this.pRefMidBuffer); 590 | 591 | // Checks for the highest correlation value. 592 | if (corr > bestCorr) { 593 | bestCorr = corr; 594 | bestOffs = i; 595 | } 596 | } 597 | return bestOffs; 598 | }, 599 | 600 | /** 601 | * Seeks for the optimal overlap-mixing position. The 'stereo' version of the 602 | * routine 603 | * 604 | * The best position is determined as the position where the two overlapped 605 | * sample sequences are 'most alike', in terms of the highest cross-correlation 606 | * value over the overlapping period 607 | */ 608 | seekBestOverlapPositionStereoQuick: function() { 609 | var j, bestOffs, bestCorr, corr, scanCount, corrOffset, tempOffset; 610 | 611 | // Slopes the amplitude of the 'midBuffer' samples 612 | this.precalcCorrReferenceStereo(); 613 | 614 | bestCorr = Number.MIN_VALUE; 615 | bestOffs = 0; 616 | corrOffset = 0; 617 | tempOffset = 0; 618 | 619 | // Scans for the best correlation value using four-pass hierarchical search. 620 | // 621 | // The look-up table 'scans' has hierarchical position adjusting steps. 622 | // In first pass the routine searhes for the highest correlation with 623 | // relatively coarse steps, then rescans the neighbourhood of the highest 624 | // correlation with better resolution and so on. 625 | for (scanCount = 0; scanCount < 4; scanCount++) { 626 | j = 0; 627 | while (_SCAN_OFFSETS[scanCount][j]) { 628 | tempOffset = corrOffset + _SCAN_OFFSETS[scanCount][j]; 629 | if (tempOffset >= this.seekLength) { 630 | break; 631 | } 632 | 633 | // Calculates correlation value for the mixing position corresponding 634 | // to 'tempOffset' 635 | corr = this.calcCrossCorrStereo(2 * tempOffset, this.pRefMidBuffer); 636 | 637 | // Checks for the highest correlation value 638 | if (corr > bestCorr) { 639 | bestCorr = corr; 640 | bestOffs = tempOffset; 641 | } 642 | j++; 643 | } 644 | corrOffset = bestOffs; 645 | } 646 | return bestOffs; 647 | }, 648 | 649 | /** 650 | * Slopes the amplitude of the 'midBuffer' samples so that cross correlation 651 | * is faster to calculate 652 | */ 653 | precalcCorrReferenceStereo: function() { 654 | var i, cnt2, temp; 655 | 656 | for (i = 0; i < this.overlapLength; i++) { 657 | temp = i * (this.overlapLength - i); 658 | cnt2 = i * 2; 659 | this.pRefMidBuffer[cnt2] = this.pMidBuffer[cnt2] * temp; 660 | this.pRefMidBuffer[cnt2 + 1] = this.pMidBuffer[cnt2 + 1] * temp; 661 | } 662 | }, 663 | 664 | calcCrossCorrStereo: function(mixingPos, compare) { 665 | var mixing = this._inputBuffer.vector; 666 | mixingPos += this._inputBuffer.startIndex; 667 | 668 | var corr, i, mixingOffset; 669 | corr = 0; 670 | for (i = 2; i < 2 * this.overlapLength; i += 2) { 671 | mixingOffset = i + mixingPos; 672 | corr += mixing[mixingOffset] * compare[i] + 673 | mixing[mixingOffset + 1] * compare[i + 1]; 674 | } 675 | return corr; 676 | }, 677 | 678 | // TODO inline 679 | /** 680 | * Overlaps samples in 'midBuffer' with the samples in 'pInputBuffer' at position 681 | * of 'ovlPos'. 682 | */ 683 | overlap: function(ovlPos) { 684 | this.overlapStereo(2 * ovlPos); 685 | }, 686 | 687 | /** 688 | * Overlaps samples in 'midBuffer' with the samples in 'pInput' 689 | */ 690 | overlapStereo: function(pInputPos) { 691 | var pInput = this._inputBuffer.vector; 692 | pInputPos += this._inputBuffer.startIndex; 693 | 694 | var pOutput = this._outputBuffer.vector, 695 | pOutputPos = this._outputBuffer.endIndex, 696 | i, cnt2, fTemp, fScale, fi, pInputOffset, pOutputOffset; 697 | 698 | fScale = 1 / this.overlapLength; 699 | for (i = 0; i < this.overlapLength; i++) { 700 | fTemp = (this.overlapLength - i) * fScale; 701 | fi = i * fScale; 702 | cnt2 = 2 * i; 703 | pInputOffset = cnt2 + pInputPos; 704 | pOutputOffset = cnt2 + pOutputPos; 705 | pOutput[pOutputOffset + 0] = pInput[pInputOffset + 0] * fi + this.pMidBuffer[cnt2 + 0] * fTemp; 706 | pOutput[pOutputOffset + 1] = pInput[pInputOffset + 1] * fi + this.pMidBuffer[cnt2 + 1] * fTemp; 707 | } 708 | }, 709 | process: function() { 710 | var ovlSkip, offset, temp, i; 711 | if (this.pMidBuffer === null) { 712 | // if midBuffer is empty, move the first samples of the input stream 713 | // into it 714 | if (this._inputBuffer.frameCount < this.overlapLength) { 715 | // wait until we've got overlapLength samples 716 | return; 717 | } 718 | this.pMidBuffer = new Float32Array(this.overlapLength * 2); 719 | this._inputBuffer.receiveSamples(this.pMidBuffer, this.overlapLength); 720 | } 721 | 722 | var output; 723 | // Process samples as long as there are enough samples in 'inputBuffer' 724 | // to form a processing frame. 725 | while (this._inputBuffer.frameCount >= this.sampleReq) { 726 | // If tempo differs from the normal ('SCALE'), scan for the best overlapping 727 | // position 728 | offset = this.seekBestOverlapPosition(); 729 | 730 | // Mix the samples in the 'inputBuffer' at position of 'offset' with the 731 | // samples in 'midBuffer' using sliding overlapping 732 | // ... first partially overlap with the end of the previous sequence 733 | // (that's in 'midBuffer') 734 | this._outputBuffer.ensureAdditionalCapacity(this.overlapLength); 735 | // FIXME unit? 736 | //overlap(uint(offset)); 737 | this.overlap(Math.floor(offset)); 738 | this._outputBuffer.put(this.overlapLength); 739 | 740 | // ... then copy sequence samples from 'inputBuffer' to output 741 | temp = (this.seekWindowLength - 2 * this.overlapLength); // & 0xfffffffe; 742 | if (temp > 0) { 743 | this._outputBuffer.putBuffer(this._inputBuffer, offset + this.overlapLength, temp); 744 | } 745 | 746 | // Copies the end of the current sequence from 'inputBuffer' to 747 | // 'midBuffer' for being mixed with the beginning of the next 748 | // processing sequence and so on 749 | //assert(offset + seekWindowLength <= (int)inputBuffer.numSamples()); 750 | var start = this.inputBuffer.startIndex + 2 * (offset + this.seekWindowLength - this.overlapLength); 751 | this.pMidBuffer.set(this._inputBuffer.vector.subarray(start, start + 2 * this.overlapLength)); 752 | 753 | // Remove the processed samples from the input buffer. Update 754 | // the difference between integer & nominal skip step to 'skipFract' 755 | // in order to prevent the error from accumulating over time. 756 | this.skipFract += this.nominalSkip; // real skip size 757 | ovlSkip = Math.floor(this.skipFract); // rounded to integer skip 758 | this.skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip 759 | this._inputBuffer.receive(ovlSkip); 760 | } 761 | } 762 | }); 763 | 764 | // https://bugs.webkit.org/show_bug.cgi?id=57295 765 | extend(Stretch.prototype, { 766 | get tempo() { 767 | return this._tempo; 768 | } 769 | }); 770 | 771 | function SoundTouch(sampleRate) { 772 | this.rateTransposer = new RateTransposer(false); 773 | this.tdStretch = new Stretch(false, sampleRate); 774 | 775 | this._inputBuffer = new FifoSampleBuffer(); 776 | this._intermediateBuffer = new FifoSampleBuffer(); 777 | this._outputBuffer = new FifoSampleBuffer(); 778 | 779 | this._rate = 0; 780 | this._tempo = 0; 781 | 782 | this.virtualPitch = 1.0; 783 | this.virtualRate = 1.0; 784 | this.virtualTempo = 1.0; 785 | 786 | this._calculateEffectiveRateAndTempo(); 787 | } 788 | SoundTouch.prototype = { 789 | clear: function() { 790 | rateTransposer.clear(); 791 | tdStretch.clear(); 792 | }, 793 | get rate() { 794 | return this._rate; 795 | }, 796 | set rate(rate) { 797 | this.virtualRate = rate; 798 | this._calculateEffectiveRateAndTempo(); 799 | }, 800 | set rateChange(rateChange) { 801 | this.rate = 1.0 + 0.01 * rateChange; 802 | }, 803 | get tempo() { 804 | return this._tempo; 805 | }, 806 | set tempo(tempo) { 807 | this.virtualTempo = tempo; 808 | this._calculateEffectiveRateAndTempo(); 809 | }, 810 | set tempoChange(tempoChange) { 811 | this.tempo = 1.0 + 0.01 * tempoChange; 812 | }, 813 | set pitch(pitch) { 814 | this.virtualPitch = pitch; 815 | this._calculateEffectiveRateAndTempo(); 816 | }, 817 | set pitchOctaves(pitchOctaves) { 818 | this.pitch = Math.exp(0.69314718056 * pitchOctaves); 819 | this._calculateEffectiveRateAndTempo(); 820 | }, 821 | set pitchSemitones(pitchSemitones) { 822 | this.pitchOctaves = pitchSemitones / 12.0; 823 | }, 824 | get inputBuffer() { 825 | return this._inputBuffer; 826 | }, 827 | get outputBuffer() { 828 | return this._outputBuffer; 829 | }, 830 | _calculateEffectiveRateAndTempo: function() { 831 | var previousTempo = this._tempo; 832 | var previousRate = this._rate; 833 | 834 | this._tempo = this.virtualTempo / this.virtualPitch; 835 | this._rate = this.virtualRate * this.virtualPitch; 836 | 837 | if (testFloatEqual(this._tempo, previousTempo)) { 838 | this.tdStretch.tempo = this._tempo; 839 | } 840 | if (testFloatEqual(this._rate, previousRate)) { 841 | this.rateTransposer.rate = this._rate; 842 | } 843 | 844 | if (this._rate > 1.0) { 845 | if (this._outputBuffer != this.rateTransposer.outputBuffer) { 846 | this.tdStretch.inputBuffer = this._inputBuffer; 847 | this.tdStretch.outputBuffer = this._intermediateBuffer; 848 | 849 | this.rateTransposer.inputBuffer = this._intermediateBuffer; 850 | this.rateTransposer.outputBuffer = this._outputBuffer; 851 | } 852 | } 853 | else { 854 | if (this._outputBuffer != this.tdStretch.outputBuffer) { 855 | this.rateTransposer.inputBuffer = this._inputBuffer; 856 | this.rateTransposer.outputBuffer = this._intermediateBuffer; 857 | 858 | this.tdStretch.inputBuffer = this._intermediateBuffer; 859 | this.tdStretch.outputBuffer = this._outputBuffer; 860 | } 861 | } 862 | }, 863 | process: function() { 864 | if (this._rate > 1.0) { 865 | this.tdStretch.process(); 866 | this.rateTransposer.process(); 867 | } 868 | else { 869 | this.rateTransposer.process(); 870 | this.tdStretch.process(); 871 | } 872 | } 873 | }; 874 | 875 | function WebAudioBufferSource(buffer) { 876 | this.buffer = buffer; 877 | } 878 | WebAudioBufferSource.prototype = { 879 | extract: function(target, numFrames, position) { 880 | var l = this.buffer.getChannelData(0), 881 | r = this.buffer.getChannelData(1); 882 | for (var i = 0; i < numFrames; i++) { 883 | target[i * 2] = l[i + position]; 884 | target[i * 2 + 1] = r[i + position]; 885 | } 886 | return Math.min(numFrames, l.length - position); 887 | } 888 | }; 889 | 890 | function getWebAudioNode(context, filter) { 891 | var BUFFER_SIZE = 4096; 892 | console.log(context); 893 | var node = context.createScriptProcessor(BUFFER_SIZE, 2, 2), 894 | samples = new Float32Array(BUFFER_SIZE * 2); 895 | node.onaudioprocess = function(e) { 896 | var l = e.outputBuffer.getChannelData(0), 897 | r = e.outputBuffer.getChannelData(1); 898 | var framesExtracted = filter.extract(samples, BUFFER_SIZE); 899 | if (framesExtracted === 0) { 900 | node.disconnect(); // Pause. 901 | } 902 | for (var i = 0; i < framesExtracted; i++) { 903 | l[i] = samples[i * 2]; 904 | r[i] = samples[i * 2 + 1]; 905 | } 906 | }; 907 | return node; 908 | } 909 | 910 | window.soundtouch = { 911 | 'RateTransposer': RateTransposer, 912 | 'Stretch': Stretch, 913 | 'SimpleFilter': SimpleFilter, 914 | 'SoundTouch': SoundTouch, 915 | 'WebAudioBufferSource': WebAudioBufferSource, 916 | 'getWebAudioNode': getWebAudioNode 917 | }; 918 | 919 | })(this); -------------------------------------------------------------------------------- /stretcher.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZVK/stretcher/b75efb589ca275dabe22f642a8d3f6a06ae01eca/stretcher.jpg -------------------------------------------------------------------------------- /stretcher.js: -------------------------------------------------------------------------------- 1 | /* 2 | * STRETCHER-JS GLOBAL VARIABLES 3 | */ 4 | var GLOBAL_ACTIONS = { 5 | 'play': function() { 6 | if (current_state !== state.playing && current_state !== state.init) { 7 | current_state = state.playing; 8 | node.connect(wavesurfer.backend.ac.destination); 9 | wavesurfer.play(current_time); 10 | console.log("playing"); 11 | } 12 | }, 13 | 'pause': function() { 14 | if (current_state === state.playing) { 15 | current_state = state.paused; 16 | //wavesurfer.backend.source.stop(0); 17 | current_time = wavesurfer.getCurrentTime(); 18 | node.disconnect(); 19 | wavesurfer.pause(); 20 | console.log("pausing"); 21 | } 22 | }, 23 | 'back': function() { 24 | if(current_state >= state.ready){ 25 | wavesurfer.skipBackward(); 26 | } 27 | }, 28 | 29 | 'forth': function() { 30 | if(current_state >= state.ready){ 31 | wavesurfer.skipForward(); 32 | } 33 | } 34 | }, 35 | state = { 36 | init: 0, 37 | ready: 1, 38 | playing: 2, 39 | paused: 3, 40 | stopped: 4 41 | }, 42 | current_state = state.init, 43 | current_time = 0, 44 | source_position = 0, 45 | sample_rate = 44100, 46 | context = new AudioContext(), 47 | warp = { 48 | down: { 49 | eighth: 0.875, 50 | quarter: 0.75, 51 | half: 0.5 52 | }, 53 | up: { 54 | eighth: 1.125, 55 | quarter: 1.25, 56 | half: 1.50 57 | } 58 | }, 59 | st, filter, node, 60 | /* 61 | * WAVESURFER-JS 62 | */ 63 | wavesurfer = Object.create(WaveSurfer); 64 | wavesurfer.init({ 65 | audioContext: context, 66 | container: '#wave', 67 | waveColor: '#BDCCD4', 68 | progressColor: '#3FA9F5', 69 | audioRate: 1, 70 | normalize: true, 71 | pixelRation: 1, 72 | interact: false 73 | }); 74 | /* 75 | * EVENTS 76 | */ 77 | wavesurfer.on('audioprocess', function(t){ 78 | }); 79 | //waveform error reporting 80 | wavesurfer.on('error', function(err) { 81 | console.error(err); 82 | }); 83 | //waveform initial loading function 84 | wavesurfer.on('loading', function(percent, request) { 85 | console.log(percent); 86 | console.log(request); 87 | if (percent >= 100) { 88 | document.getElementById("loading").innerHTML = "initializing..."; 89 | } else { 90 | document.getElementById("loading").innerHTML = percent + "% loaded"; 91 | } 92 | }); 93 | wavesurfer.on('ready', function(){ 94 | var timeline = Object.create(WaveSurfer.Timeline); 95 | timeline.init({ 96 | wavesurfer: wavesurfer, 97 | container: "#wave-timeline" 98 | }); 99 | wavesurfer.backend.source.loop = true; 100 | wavesurfer.backend.source.loopStart = 0; 101 | wavesurfer.backend.source.loopEnd = 1//wavesurfer.backend.buffer.duration 102 | st = new soundtouch.SoundTouch(sample_rate); 103 | st.tempo = 1.0; 104 | console.log(wavesurfer.backend.source.buffer) 105 | //var buffer = soundtouch.WebAudioBufferSource(wavesurfer.backend.source.buffer) 106 | wavesurfer.backend.source.buffer.extract = function(target, numFrames, position) { 107 | var l = wavesurfer.backend.source.buffer.getChannelData(0), 108 | r = wavesurfer.backend.source.buffer.getChannelData(1); 109 | for (var i = 0; i < numFrames; i++) { 110 | target[i * 2] = l[i + position]; 111 | target[i * 2 + 1] = r[i + position]; 112 | } 113 | return Math.min(numFrames, l.length - position); 114 | }; 115 | filter = new soundtouch.SimpleFilter(wavesurfer.backend.source.buffer, st); 116 | current_state = state.ready; 117 | console.log(wavesurfer.backend) 118 | node = soundtouch.getWebAudioNode(wavesurfer.backend.ac, filter); 119 | wavesurfer.backend.setFilter(node); 120 | document.getElementById("loading").innerHTML = ""; 121 | wavesurfer.backend.filters[0].disconnect(); 122 | }); 123 | //waveform end of track funciton 124 | wavesurfer.on('finish', function() { 125 | console.log('Finished playing'); 126 | }); 127 | //waveform time seek function 128 | wavesurfer.on('seek', function(progress) { 129 | current_time = wavesurfer.getCurrentTime(); 130 | console.log('seeking to ' + Math.round(progress * 100) + "%"); 131 | console.log( progress); 132 | }); 133 | /* 134 | * SOUNDTOUCH-JS DATA SETTERS 135 | */ 136 | function set_tempo(speed) { 137 | console.log("setting speed to " + speed); 138 | st.tempo = speed; 139 | wavesurfer.setPlaybackRate(speed); 140 | document.getElementById("tempo_value").innerHTML = (speed*100)+"%"; 141 | } 142 | 143 | function set_pitch(semitones) { 144 | //calculate factor to multiply frequency by 145 | var frequency_factor = Math.pow(2, (semitones / 12)); 146 | console.log("setting pitch to " + frequency_factor + "% (" + semitones + " semitones)"); 147 | st.pitch = frequency_factor; 148 | document.getElementById("pitch_value").innerHTML = semitones+"st"; 149 | } 150 | 151 | /* 152 | * AUDIO TRANSPORT BUTTONS 153 | */ 154 | function pressButton(action){ 155 | if (action) GLOBAL_ACTIONS[action](); 156 | } 157 | 158 | /* 159 | * BOOTSTRAP-SLIDERS 160 | */ 161 | function createSliders() { 162 | //function to set a batch of attributes for an HTML element and object pair 163 | var set_attributes = function(element, attributes) { 164 | for (attribute in attributes) { 165 | element.setAttribute(attribute, attributes[attribute]); 166 | } 167 | return element; 168 | }; 169 | // Instantiate a tempo slider 170 | var tempo = { 171 | element: document.createElement("INPUT"), 172 | //store preset attributes 173 | styles: { 174 | // initial options object 175 | id: "tempo_slider", 176 | type: "range", 177 | min: 0.35, 178 | max: 2.0, 179 | step: 0.05, 180 | value: 1.0, 181 | onchange: "set_tempo(this.value);" 182 | } 183 | }, 184 | pitch = { 185 | element: document.createElement("INPUT"), 186 | //store preset attributes 187 | styles: { 188 | // initial options object 189 | id: "pitch_slider", 190 | type: "range", 191 | min: -24, 192 | max: 24, 193 | step: 1, 194 | value: 1, 195 | onchange: "set_pitch(this.value);" 196 | } 197 | }; 198 | //build sliders 199 | set_attributes(tempo.element, tempo.styles); 200 | set_attributes(pitch.element, pitch.styles) 201 | //add it to in at the bottom of our html body 202 | document.getElementById("tempo_wrapper").appendChild(tempo.element); 203 | document.getElementById("pitch_wrapper").appendChild(pitch.element); 204 | } 205 | // Drag'n'drop 206 | document.addEventListener('DOMContentLoaded', function () { 207 | var toggleActive = function (e, toggle) { 208 | e.stopPropagation(); 209 | e.preventDefault(); 210 | toggle ? e.target.classList.add('wavesurfer-dragover') : 211 | e.target.classList.remove('wavesurfer-dragover'); 212 | }; 213 | 214 | var handlers = { 215 | // Drop event 216 | drop: function (e) { 217 | toggleActive(e, false); 218 | 219 | // Load the file into wavesurfer 220 | if (e.dataTransfer.files.length) { 221 | wavesurfer.loadBlob(e.dataTransfer.files[0]); 222 | } else { 223 | wavesurfer.fireEvent('error', 'Not a file'); 224 | } 225 | }, 226 | 227 | // Drag-over event 228 | dragover: function (e) { 229 | toggleActive(e, true); 230 | }, 231 | 232 | // Drag-leave event 233 | dragleave: function (e) { 234 | toggleActive(e, false); 235 | } 236 | }; 237 | 238 | var dropTarget = document.querySelector('#drop'); 239 | Object.keys(handlers).forEach(function (event) { 240 | dropTarget.addEventListener(event, handlers[event]); 241 | }); 242 | }); 243 | 244 | function init() { 245 | wavesurfer.load('./Rock_With_You.mp3'); 246 | createSliders(); 247 | } -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var state = { 2 | init: 0, 3 | ready: 1, 4 | playing: 2, 5 | paused: 3, 6 | stopped: 4 7 | }, 8 | current_state = state.init, 9 | sample_rate = 44100, 10 | context = new AudioContext(), 11 | warp = { 12 | down: { 13 | eighth: 0.875, 14 | quarter: 0.75, 15 | half: 0.5 16 | }, 17 | up: {eighth: 1.125, 18 | quarter: 1.25, 19 | half: 1.50 20 | } 21 | }, 22 | source, node, st; 23 | 24 | var wavesurfer = WaveSurfer.create({ 25 | audioContext: context, 26 | container: '#wave', 27 | waveColor: 'blue', 28 | progressColor: 'orange', 29 | audioRate: 1, 30 | normalize: true 31 | }); 32 | wavesurfer.on('play', function(e){ 33 | console.log("playing") 34 | }); 35 | wavesurfer.on('ready', function () { 36 | var timeline = Object.create(WaveSurfer.Timeline); 37 | 38 | timeline.init({ 39 | wavesurfer: wavesurfer, 40 | container: "#wave-timeline" 41 | }); 42 | }); 43 | 44 | wavesurfer.on('loading', function(percent, request) { 45 | console.log(percent); 46 | console.log(request); 47 | if(percent >= 100){ 48 | document.getElementById("loading").innerHTML = "initializing..."; 49 | context.decodeAudioData(request.response, function(decoded_data){ 50 | source = context.createBufferSource(); 51 | source.buffer = decoded_data; 52 | st = new soundtouch.SoundTouch(sample_rate); 53 | st.tempo = 1.0; 54 | var filter = new soundtouch.SimpleFilter(new soundtouch.WebAudioBufferSource(source.buffer), st); 55 | console.log(filter); 56 | node = soundtouch.getWebAudioNode(context, filter); 57 | current_state = state.ready; 58 | wavesurfer.backend.setFilter(node); 59 | document.getElementById("loading").innerHTML = ""; 60 | node.disconnect(); 61 | }, function( e ){ console.log( e ); } //decoding errors 62 | ); 63 | } else { 64 | document.getElementById("loading").innerHTML = percent+"% loaded"; 65 | } 66 | }); 67 | 68 | wavesurfer.on('finish', function () { 69 | console.log('Finished playing'); 70 | }); 71 | 72 | function set_tempo(speed){ 73 | console.log("setting speed to "+speed); 74 | st.tempo = speed; 75 | wavesurfer.setPlaybackRate(speed); 76 | } 77 | 78 | function set_pitch(semitones){ 79 | //calculate factor to multiply frequency by 80 | var frequency_factor = Math.pow(2, (semitones/12)); 81 | console.log("setting pitch to "+frequency_factor+"% ("+semitones+" semitones)"); 82 | st.pitch = frequency_factor; 83 | } 84 | 85 | //function to set a batch of attributes for an HTML element and object pair 86 | function set_attributes(element, attributes) { 87 | for (attribute in attributes){ 88 | element.setAttribute(attribute, attributes[attribute]); 89 | } 90 | } 91 | 92 | function init(){ 93 | wavesurfer.load('./Lightning Bolt - 13 Monsters.mp3'); 94 | //make the tempo slider we prepared above 95 | // Instantiate a tempo slider 96 | var tempo = { 97 | element: document.createElement("INPUT"), 98 | //store preset attributes 99 | styles: { 100 | // initial options object 101 | id: "tempo_slider", 102 | type: "range", 103 | min: 0.35, 104 | max: 2.0, 105 | step: 0.05, 106 | value: 1.0, 107 | onchange: "set_tempo(this.value);" 108 | } 109 | }, 110 | pitch = { 111 | element: document.createElement("INPUT"), 112 | //store preset attributes 113 | styles: { 114 | // initial options object 115 | id: "pitch_slider", 116 | type: "range", 117 | min: -24, 118 | max: 24, 119 | step: 1, 120 | value: 1, 121 | onchange: "set_pitch(this.value);" 122 | } 123 | }; 124 | //build sliders 125 | set_attributes(tempo.element, tempo.styles); 126 | set_attributes(pitch.element, pitch.styles) 127 | //add it to in at the bottom of our html body 128 | document.getElementById("tempo").appendChild(tempo.element); 129 | document.getElementById("pitch").appendChild(pitch.element); 130 | 131 | } 132 | 133 | function play() { 134 | if(current_state !== state.playing && current_state !== state.init){ 135 | console.log("playing"); 136 | current_state = state.playing; 137 | node.connect(context.destination); 138 | wavesurfer.play(); 139 | } 140 | } 141 | function pause() { 142 | if(current_state === state.playing){ 143 | console.log("pausing"); 144 | current_state = state.paused; 145 | node.disconnect(); 146 | wavesurfer.pause(); 147 | } 148 | } 149 | 150 | 151 | --------------------------------------------------------------------------------