vendor: 4,044 bytes, lines 1-138
1'use strict'; 2 3WaveSurfer.WebAudio = { 4 scriptBufferSize: 256, 5 PLAYING_STATE: 0, 6 PAUSED_STATE: 1, 7 FINISHED_STATE: 2, 8 9 supportsWebAudio: function () { 10 return !!(window.AudioContext || window.webkitAudioContext); 11 }, 12 13 getAudioContext: function () { 14 if (!WaveSurfer.WebAudio.audioContext) { 15 WaveSurfer.WebAudio.audioContext = new ( 16 window.AudioContext || window.webkitAudioContext 17 ); 18 } 19 return WaveSurfer.WebAudio.audioContext; 20 }, 21 22 getOfflineAudioContext: function (sampleRate) { 23 if (!WaveSurfer.WebAudio.offlineAudioContext) { 24 WaveSurfer.WebAudio.offlineAudioContext = new ( 25 window.OfflineAudioContext || window.webkitOfflineAudioContext 26 )(1, 2, sampleRate); 27 } 28 return WaveSurfer.WebAudio.offlineAudioContext; 29 }, 30 31 init: function (params) { 32 this.params = params; 33 this.ac = params.audioContext || this.getAudioContext(); 34 35 this.lastPlay = this.ac.currentTime; 36 this.startPosition = 0; 37 this.scheduledPause = null; 38 39 this.states = [ 40 Object.create(WaveSurfer.WebAudio.state.playing), 41 Object.create(WaveSurfer.WebAudio.state.paused), 42 Object.create(WaveSurfer.WebAudio.state.finished) 43 ]; 44 45 this.createVolumeNode(); 46 this.createScriptNode(); 47 this.createAnalyserNode(); 48 49 this.setState(this.PAUSED_STATE); 50 this.setPlaybackRate(this.params.audioRate); 51 }, 52 53 disconnectFilters: function () { 54 if (this.filters) { 55 this.filters.forEach(function (filter) { 56 filter && filter.disconnect(); 57 }); 58 this.filters = null; 59 // Reconnect direct path 60 this.analyser.connect(this.gainNode); 61 } 62 }, 63 64 setState: function (state) { 65 if (this.state !== this.states[state]) { 66 this.state = this.states[state]; 67 this.state.init.call(this); 68 } 69 }, 70 71 // Unpacked filters 72 setFilter: function () { 73 this.setFilters([].slice.call(arguments)); 74 }, 75 76 /** 77 * @param {Array} filters Packed ilters array 78 */ 79 setFilters: function (filters) { 80 // Remove existing filters 81 this.disconnectFilters(); 82 83 // Insert filters if filter array not empty 84 if (filters && filters.length) { 85 this.filters = filters; 86 87 // Disconnect direct path before inserting filters 88 this.analyser.disconnect(); 89 90 // Connect each filter in turn 91 filters.reduce(function (prev, curr) { 92 prev.connect(curr); 93 return curr; 94 }, this.analyser).connect(this.gainNode); 95 } 96 97 }, 98 99 createScriptNode: function () { 100 if (this.ac.createScriptProcessor) { 101 this.scriptNode = this.ac.createScriptProcessor(this.scriptBufferSize); 102 } else { 103 this.scriptNode = this.ac.createJavaScriptNode(this.scriptBufferSize); 104 } 105 106 this.scriptNode.connect(this.ac.destination); 107 }, 108 109 addOnAudioProcess: function () { 110 var my = this; 111 112 this.scriptNode.onaudioprocess = function () { 113 var time = my.getCurrentTime(); 114 115 if (time >= my.getDuration()) { 116 my.setState(my.FINISHED_STATE); 117 my.fireEvent('pause'); 118 } else if (time >= my.scheduledPause) { 119 my.setState(my.PAUSED_STATE); 120 my.fireEvent('pause'); 121 } else if (my.state === my.states[my.PLAYING_STATE]) { 122 my.fireEvent('audioprocess', time); 123 } 124 }; 125 }, 126 127 removeOnAudioProcess: function () { 128 this.scriptNode.onaudioprocess = null; 129 }, 130 131 createAnalyserNode: function () { 132 this.analyser = this.ac.createAnalyser(); 133 this.analyser.connect(this.gainNode); 134 }, 135 136 /** 137 * Create the gain node needed to control the playback volume. 138 */
139 createVolumeNode: function () { 140 // Create gain node using the AudioContext 141 if (this.ac.createGain) { 142 this.gainNode = this.ac.createGain(); 143 } else { 144 this.gainNode = this.ac.createGainNode(); 145 } 146 // Add the gain node to the graph 147 this.gainNode.connect(this.ac.destination); 148 }, 149 150 /** 151 * Set the gain to a new value. 152 * 153 * @param {Number} newGain The new gain, a floating point value 154 * between 0 and 1. 0 being no gain and 1 being maximum gain. 155 */ 156 setVolume: function (newGain) { 157 this.gainNode.gain.value = newGain; 158 }, 159 160 /** 161 * Get the current gain. 162 * 163 * @returns {Number} The current gain, a floating point value 164 * between 0 and 1. 0 being no gain and 1 being maximum gain. 165 */ 166 getVolume: function () { 167 return this.gainNode.gain.value; 168 }, 169 170 decodeArrayBuffer: function (arraybuffer, callback, errback) { 171 if (!this.offlineAc) { 172 this.offlineAc = this.getOfflineAudioContext(this.ac ? this.ac.sampleRate : 44100); 173 } 174 this.offlineAc.decodeAudioData(arraybuffer, (function (data) { 175 callback(data); 176 }).bind(this), errback); 177 }, 178 179 /** 180 * Compute the max and min value of the waveform when broken into 181 * <length> subranges. 182 * @param {Number} How many subranges to break the waveform into. 183 * @returns {Array} Array of 2*<length> peaks or array of arrays 184 * of peaks consisting of (max, min) values for each subrange. 185 */ 186 getPeaks: function (length) { 187 var sampleSize = this.buffer.length / length; 188 var sampleStep = ~~(sampleSize / 10) || 1; 189 var channels = this.buffer.numberOfChannels; 190 var splitPeaks = []; 191 var mergedPeaks = []; 192 193 for (var c = 0; c < channels; c++) { 194 var peaks = splitPeaks[c] = []; 195 var chan = this.buffer.getChannelData(c); 196 197 for (var i = 0; i < length; i++) { 198 var start = ~~(i * sampleSize); 199 var end = ~~(start + sampleSize); 200 var min = chan[0]; 201 var max = chan[0]; 202 203 for (var j = start; j < end; j += sampleStep) { 204 var value = chan[j]; 205 206 if (value > max) { 207 max = value; 208 } 209 210 if (value < min) { 211 min = value; 212 } 213 } 214 215 peaks[2 * i] = max; 216 peaks[2 * i + 1] = min; 217 218 if (c == 0 || max > mergedPeaks[2 * i]) { 219 mergedPeaks[2 * i] = max; 220 } 221 222 if (c == 0 || min < mergedPeaks[2 * i + 1]) { 223 mergedPeaks[2 * i + 1] = min; 224 } 225 } 226 } 227 228 return this.params.splitChannels ? splitPeaks : mergedPeaks; 229 }, 230 231 getPlayedPercents: function () { 232 return this.state.getPlayedPercents.call(this); 233 }, 234 235 disconnectSource: function () { 236 if (this.source) { 237 this.source.disconnect(); 238 } 239 }, 240 241 destroy: function () { 242 if (!this.isPaused()) { 243 this.pause(); 244 } 245 this.unAll(); 246 this.buffer = null; 247 this.disconnectFilters(); 248 this.disconnectSource(); 249 this.gainNode.disconnect(); 250 this.scriptNode.disconnect(); 251 this.analyser.disconnect(); 252 }, 253 254 load: function (buffer) { 255 this.startPosition = 0; 256 this.lastPlay = this.ac.currentTime; 257 this.buffer = buffer; 258 this.createSource(); 259 }, 260 261 createSource: function () { 262 this.disconnectSource(); 263 this.source = this.ac.createBufferSource(); 264 265 //adjust for old browsers. 266 this.source.start = this.source.start || this.source.noteGrainOn; 267 this.source.stop = this.source.stop || this.source.noteOff; 268 269 this.source.playbackRate.value = this.playbackRate; 270 this.source.buffer = this.buffer; 271 this.source.connect(this.analyser); 272 }, 273 274 isPaused: function () { 275 return this.state !== this.states[this.PLAYING_STATE]; 276 }, 277 278 getDuration: function () { 279 if (!this.buffer) { 280 return 0; 281 } 282 return this.buffer.duration; 283 }, 284 285 seekTo: function (start, end) { 286 this.scheduledPause = null; 287 288 if (start == null) { 289 start = this.getCurrentTime(); 290 if (start >= this.getDuration()) { 291 start = 0; 292 } 293 } 294 if (end == null) { 295 end = this.getDuration(); 296 } 297 298 this.startPosition = start; 299 this.lastPlay = this.ac.currentTime; 300 301 if (this.state === this.states[this.FINISHED_STATE]) { 302 this.setState(this.PAUSED_STATE); 303 } 304 305 return { start: start, end: end }; 306 }, 307 308 getPlayedTime: function () { 309 return (this.ac.currentTime - this.lastPlay) * this.playbackRate; 310 }, 311 312 /** 313 * Plays the loaded audio region. 314 * 315 * @param {Number} start Start offset in seconds, 316 * relative to the beginning of a clip. 317 * @param {Number}
317 end When to stop 318 * relative to the beginning of a clip. 319 */ 320 play: function (start, end) { 321 // need to re-create source on each playback 322 this.createSource(); 323 324 var adjustedTime = this.seekTo(start, end); 325 326 start = adjustedTime.start; 327 end = adjustedTime.end; 328 329 this.scheduledPause = end; 330 331 this.source.start(0, start, end - start); 332 333 this.setState(this.PLAYING_STATE); 334 335 this.fireEvent('play'); 336 }, 337 338 /** 339 * Pauses the loaded audio. 340 */ 341 pause: function () { 342 this.scheduledPause = null; 343 344 this.startPosition += this.getPlayedTime(); 345 this.source && this.source.stop(0); 346 347 this.setState(this.PAUSED_STATE); 348 349 this.fireEvent('pause'); 350 }, 351 352 /** 353 * Returns the current time in seconds relative to the audioclip's duration. 354 */ 355 getCurrentTime: function () { 356 return this.state.getCurrentTime.call(this); 357 }, 358 359 /** 360 * Set the audio source playback rate. 361 */ 362 setPlaybackRate: function (value) { 363 value = value || 1; 364 if (this.isPaused()) { 365 this.playbackRate = value; 366 } else { 367 this.pause(); 368 this.playbackRate = value; 369 this.play(); 370 } 371 } 372}; 373 374WaveSurfer.WebAudio.state = {}; 375 376WaveSurfer.WebAudio.state.playing = { 377 init: function () { 378 this.addOnAudioProcess(); 379 }, 380 getPlayedPercents: function () { 381 var duration = this.getDuration(); 382 return (this.getCurrentTime() / duration) || 0; 383 }, 384 getCurrentTime: function () { 385 return this.startPosition + this.getPlayedTime(); 386 } 387}; 388 389WaveSurfer.WebAudio.state.paused = { 390 init: function () { 391 this.removeOnAudioProcess(); 392 }, 393 getPlayedPercents: function () { 394 var duration = this.getDuration(); 395 return (this.getCurrentTime() / duration) || 0; 396 }, 397 getCurrentTime: function () { 398 return this.startPosition; 399 } 400}; 401 402WaveSurfer.WebAudio.state.finished = { 403 init: function () { 404 this.removeOnAudioProcess(); 405 this.fireEvent('finish'); 406 }, 407 getPlayedPercents: function () { 408 return 1; 409 }, 410 getCurrentTime: function () { 411 return this.getDuration(); 412 } 413}; 414 415WaveSurfer.util.extend(WaveSurfer.WebAudio, WaveSurfer.Observer);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.