examples/js/overlap-demo.js
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/* global AudioContext */
var ac = new AudioContext()
var load = require('audio-loader')
var play = require('./play')(ac)
var ts = require('../..')
var SEQ = 4410
console.log('Loading audio...')
load(ac, 'audio/amen.wav').then(function (buffer) {
draw(document.getElementById('source'), buffer)
draw(document.getElementById('sliced'),
ts.stretch(ac, buffer, 0.9, { overlap: 1 }))
draw(document.getElementById('overlap'),
ts.stretch(ac, buffer, 0.9))
})
function draw (canvas, buffer) {
canvas.onclick = function (e) {
play(0, 4, buffer)
}
var x, y
var data = buffer.getChannelData(0)
var endSeq = Math.round(canvas.width / 5)
var hm = Math.round(canvas.height / 2)
var start = SEQ - endSeq
var ctx = canvas.getContext('2d')
ctx.fillStyle = '#efefef'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.strokeStyle = '#333'
ctx.beginPath()
for (x = 0; x < endSeq ; x++) {
y = hm + Math.round(hm * data[x + start])
ctx.lineTo(x, y)
}
ctx.stroke()
ctx.strokeStyle = '#C00'
ctx.beginPath()
ctx.lineTo(x, y)
for (x = endSeq; x < SEQ; x++) {
y = hm + Math.round(hm * data[x + start])
ctx.lineTo(x, y)
}
ctx.stroke()
}
},{"../..":3,"./play":2,"audio-loader":5}],2:[function(require,module,exports){
module.exports = function (ac) {
return function play (when, duration, buffer) {
var source = ac.createBufferSource()
source.buffer = buffer
source.connect(ac.destination)
source.start(when, 0, duration)
}
}
},{}],3:[function(require,module,exports){
'use strict'
/**
* Copy `len` bytes generated by a function to `array` starting at `pos`
*/
function copy (len, array, pos, fn) {
for (var i = 0; i < len; i++) {
array[pos + i] = fn(i)
}
}
function stretch (ac, input, scale, options) {
// OPTIONS
var opts = options || {}
// Processing sequence size (100 msec with 44100Hz sample rate)
var seqSize = opts.seqSize || 4410
// Overlapping size (20 msec)
var overlap = opts.overlap || 882
// Best overlap offset seeking window (15 msec)
// var seekWindow = opts.seekWindow || 662
// The theoretical start of the next sequence
var nextOffset = Math.round(seqSize / scale)
// Setup the buffers
var numSamples = input.length
var output = ac.createBuffer(1, numSamples * scale, input.sampleRate)
var inL = input.getChannelData(0)
var outL = output.getChannelData(0)
// STATE
// where to read then next sequence
var read = 0
// where to write the next sequence
var write = 0
// where to read the next fadeout
var readOverlap = 0
while (numSamples - read > seqSize) {
// write the first overlap
copy(overlap, outL, write, function (i) {
var fadeIn = i / overlap
var fadeOut = 1 - fadeIn
// Mix the begin of the new sequence with the tail of the sequence last
return (inL[read + i] * fadeIn + inL[readOverlap + i] * fadeOut) / 2
})
copy(seqSize - overlap, outL, write + overlap, function (i) {
// Copy the tail of the sequence
return inL[read + overlap + i]
})
// the next overlap is after this sequence
readOverlap += read + seqSize
// the next sequence is after the nextOffset
read += nextOffset
// we wrote a complete sequence
write += seqSize
}
return output
}
module.exports = { stretch: stretch }
},{}],4:[function(require,module,exports){
'use strict'
// DECODE UTILITIES
function b64ToUint6 (nChr) {
return nChr > 64 && nChr < 91 ? nChr - 65
: nChr > 96 && nChr < 123 ? nChr - 71
: nChr > 47 && nChr < 58 ? nChr + 4
: nChr === 43 ? 62
: nChr === 47 ? 63
: 0
}
// Decode Base64 to Uint8Array
// ---------------------------
function decode (sBase64, nBlocksSize) {
var sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, '')
var nInLen = sB64Enc.length
var nOutLen = nBlocksSize
? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize
: nInLen * 3 + 1 >> 2
var taBytes = new Uint8Array(nOutLen)
for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
nMod4 = nInIdx & 3
nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4
if (nMod4 === 3 || nInLen - nInIdx === 1) {
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255
}
nUint24 = 0
}
}
return taBytes
}
module.exports = { decode: decode }
},{}],5:[function(require,module,exports){
'use strict'
var base64 = require('./base64')
var request = require('./request')
// Given a regex, return a function that test if against a string
function fromRegex (r) {
return function (o) { return typeof o === 'string' && r.test(o) }
}
// Try to apply a prefix to a name
function prefix (pre, name) {
return typeof pre === 'string' ? pre + name
: typeof pre === 'function' ? pre(name)
: name
}
/**
* Load one or more audio files
*
* @param {AudioContext} ac - the audio context
* @param {Object} source - the object to be loaded
* @param {Object} options - (Optional) the load options for that object
* @param {Object} defaultValue - (Optional) the default value to return as
* in a promise if not valid loader found
*/
function load (ac, source, options, defVal) {
var loader =
// Basic audio loading
isArrayBuffer(source) ? loadArrayBuffer
: isAudioFileName(source) ? loadAudioFile
: isPromise(source) ? loadPromise
// Compound objects
: isArray(source) ? loadArrayData
: isObject(source) ? loadObjectData
: isJsonFileName(source) ? loadJsonFile
// Base64 encoded audio
: isBase64Audio(source) ? loadBase64Audio
: isJsFileName(source) ? loadMidiJSFile
: isSoundFont(source) ? loadSoundFont
: null
var opts = options || {}
return loader ? loader(ac, source, opts)
: defVal ? Promise.resolve(defVal)
: Promise.reject('Source not valid (' + source + ')')
}
load.request = request
// BASIC AUDIO LOADING
// ===================
// Load (decode) an array buffer
function isArrayBuffer (o) { return o instanceof ArrayBuffer }
function loadArrayBuffer (ac, array, options) {
return new Promise(function (done, reject) {
ac.decodeAudioData(array,
function (buffer) { done(buffer) },
function () { reject("Can't decode audio data (" + array.slice(0, 30) + '...)') }
)
})
}
// Load an audio filename
var isAudioFileName = fromRegex(/\.(mp3|wav|ogg)(\?.*)?$/i)
function loadAudioFile (ac, name, options) {
var url = prefix(options.from, name)
return load(ac, load.request(url, 'arraybuffer'), options)
}
// Load the result of a promise
function isPromise (o) { return o && typeof o.then === 'function' }
function loadPromise (ac, promise, options) {
return promise.then(function (value) {
return load(ac, value, options)
})
}
// COMPOUND OBJECTS
// ================
// Try to load all the items of an array
var isArray = Array.isArray
function loadArrayData (ac, array, options) {
return Promise.all(array.map(function (data) {
return load(ac, data, options, data)
}))
}
// Try to load all the values of a key/value object
function isObject (o) { return typeof o === 'object' }
function loadObjectData (ac, obj, options) {
var dest = {}
var promises = Object.keys(obj).map(function (key) {
if (options.only && options.only.indexOf(key) === -1) return null
var value = obj[key]
return load(ac, value, options, value).then(function (audio) {
dest[key] = audio
})
})
return Promise.all(promises).then(function () { return dest })
}
// Load the content of a JSON file
var isJsonFileName = fromRegex(/\.json(\?.*)?$/i)
function loadJsonFile (ac, name, options) {
var url = prefix(options.from, name)
return load(ac, load.request(url, 'text').then(JSON.parse), options)
}
// BASE64 ENCODED FORMATS
// ======================
// Load strings with Base64 encoded audio
var isBase64Audio = fromRegex(/^data:audio/)
function loadBase64Audio (ac, source, options) {
var i = source.indexOf(',')
return load(ac, base64.decode(source.slice(i + 1)).buffer, options)
}
// Load .js files with MidiJS soundfont prerendered audio
var isJsFileName = fromRegex(/\.js(\?.*)?$/i)
function loadMidiJSFile (ac, name, options) {
var url = prefix(options.from, name)
return load(ac, load.request(url, 'text').then(midiJsToJson), options)
}
function midiJsToJson (data) {
var begin = data.indexOf('MIDI.Soundfont.')
if (begin < 0) throw Error('Invalid MIDI.js Soundfont format')
begin = data.indexOf('=', begin) + 2
var end = data.lastIndexOf(',')
return JSON.parse(data.slice(begin, end) + '}')
}
// Load Benjamin Gleitzman's pre-rendered soundfonts via rawgit
var isSoundFont = fromRegex(/^@soundfont\//)
function loadSoundFont (ac, sf, options) {
var name = sf.slice(sf.indexOf('/'))
var url = 'https://cdn.rawgit.com/gleitz/midi-js-Soundfonts/master/FluidR3_GM/' + name + '-ogg.js'
return load(ac, url, options)
}
if (typeof module === 'object' && module.exports) module.exports = load
if (typeof window !== 'undefined') window.loadAudio = load
},{"./base64":4,"./request":6}],6:[function(require,module,exports){
/* global XMLHttpRequest */
'use strict'
// Wrap a XMLHttpRequest into a Promise
module.exports = function (url, type) {
return new Promise(function (done, reject) {
var req = new XMLHttpRequest()
if (type) req.responseType = type
req.open('GET', url)
req.onload = function () {
req.status === 200 ? done(req.response) : reject(Error(req.statusText))
}
req.onerror = function () { reject(Error('Network Error')) }
req.send()
})
}
},{}]},{},[1]);