Last active
December 19, 2015 03:49
-
-
Save enjalot/5893275 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* T("timbre.js") - A JavaScript library for objective sound programming | |
*/ | |
window.TWebkitAudioContext = new webkitAudioContext(); | |
TReset = function() { | |
if(window.timbre) | |
window.timbre.reset() | |
if(window.T) | |
window.T.reset() | |
delete window.T | |
delete window.timbre | |
} | |
TFactory = function() { | |
(function(undefined) { | |
"use strict"; | |
if (typeof Float32Array === "undefined") { | |
/*jshint latedef:true */ | |
setupTypedArray(); | |
/*jshint latedef:false */ | |
} | |
var timbre = function() { | |
return T.apply(null, arguments); | |
}; | |
var slice = Array.prototype.slice; | |
var FINISHED_STATE = 0; | |
var PLAYING_STATE = 1; | |
var UNSCHEDULED_STATE = 2; // (not use) | |
var SCHEDULED_STATE = 3; // (not use) | |
var ACCEPT_SAMPLERATES = [8000,11025,12000,16000,22050,24000,32000,44100,48000]; | |
var ACCEPT_CELLSIZES = [32,64,128,256]; | |
var _ver = "13.05.03"; | |
var _sys = null; | |
var _constructors = {}; | |
var _factories = {}; | |
var _envtype = (typeof module !== "undefined" && module.exports) ? "node" : | |
(typeof window !== "undefined") ? "browser" : "unknown"; | |
var _envmobile = _envtype === "browser" && /(iPhone|iPad|iPod|Android)/i.test(navigator.userAgent); | |
var _f64mode = false; | |
var _bpm = 120; | |
var T = function() { | |
var args = slice.call(arguments), key = args[0], t, m; | |
switch (typeof key) { | |
case "string": | |
if (_constructors[key]) { | |
t = new _constructors[key](args.slice(1)); | |
} else if (_factories[key]) { | |
t = _factories[key](args.slice(1)); | |
} else { | |
m = /^(.+?)(?:\.(ar|kr))?$/.exec(key); | |
if (m) { | |
key = m[1]; | |
if (_constructors[key]) { | |
t = new _constructors[key](args.slice(1)); | |
} else if (_factories[key]) { | |
t = _factories[key](args.slice(1)); | |
} | |
if (t && m[2]) { | |
t[m[2]](); | |
} | |
} | |
} | |
break; | |
case "number": | |
t = new NumberWrapper(args); | |
break; | |
case "boolean": | |
t = new BooleanWrapper(args); | |
break; | |
case "function": | |
t = new FunctionWrapper(args); | |
break; | |
case "object": | |
if (key !== null) { | |
if (key instanceof TimbreObject) { | |
return key; | |
} else if (key.context instanceof TimbreObject) { | |
return key.context; | |
} else if (isDictionary(key)) { | |
t = new ObjectWrapper(args); | |
} else if (isArray(key)) { | |
t = new ArrayWrapper(args); | |
} | |
} | |
break; | |
} | |
if (t === undefined) { | |
t = new AddNode(args.slice(1)); | |
console.warn("T(\"" + key + "\") is not defined."); | |
} | |
var _ = t._; | |
_.originkey = key; | |
_.meta = __buildMetaData(t); | |
_.emit("init"); | |
return t; | |
}; | |
var __buildMetaData = function(instance) { | |
var meta = instance._.meta; | |
var names, desc; | |
var p = instance; | |
while (p !== null && p.constructor !== Object) { | |
names = Object.getOwnPropertyNames(p); | |
for (var i = 0, imax = names.length; i < imax; ++i) { | |
if (meta[names[i]]) { | |
continue; | |
} | |
if (/^(constructor$|process$|_)/.test(names[i])) { | |
meta[names[i]] = "ignore"; | |
} else { | |
desc = Object.getOwnPropertyDescriptor(p, names[i]); | |
if (typeof desc.value === "function") { | |
meta[names[i]] = "function"; | |
} else if (desc.get || desc.set) { | |
meta[names[i]] = "property"; | |
} | |
} | |
} | |
p = Object.getPrototypeOf(p); | |
} | |
return meta; | |
}; | |
// properties | |
Object.defineProperties(timbre, { | |
version : { value: _ver }, | |
envtype : { value: _envtype }, | |
envmobile: { value: _envmobile }, | |
env: { | |
get: function() { | |
return _sys.impl.env; | |
} | |
}, | |
samplerate: { | |
get: function() { | |
return _sys.samplerate; | |
} | |
}, | |
channels: { | |
get: function() { | |
return _sys.channels; | |
} | |
}, | |
cellsize: { | |
get: function() { | |
return _sys.cellsize; | |
} | |
}, | |
currentTime: { | |
get: function() { | |
return _sys.currentTime; | |
} | |
}, | |
isPlaying: { | |
get: function() { | |
return _sys.status === PLAYING_STATE; | |
} | |
}, | |
isRecording: { | |
get: function() { | |
return _sys.status === SCHEDULED_STATE; | |
} | |
}, | |
amp: { | |
set: function(value) { | |
if (typeof value === "number") { | |
_sys.amp = value; | |
} | |
}, | |
get: function() { | |
return _sys.amp; | |
} | |
}, | |
bpm: { | |
set: function(value) { | |
if (typeof value === "number") { | |
if (5 <= value && value <= 300) { | |
_bpm = value; | |
} | |
} | |
}, | |
get: function() { | |
return _bpm; | |
} | |
} | |
}); | |
timbre.bind = function(Klass, opts) { | |
_sys.bind(Klass, opts); | |
return timbre; | |
}; | |
timbre.setup = function(opts) { | |
_sys.setup(opts); | |
return timbre; | |
}; | |
timbre.play = function() { | |
_sys.play(); | |
return timbre; | |
}; | |
timbre.pause = function() { | |
_sys.pause(); | |
return timbre; | |
}; | |
timbre.reset = function() { | |
_sys.reset(); | |
_sys.events.emit("reset"); | |
return timbre; | |
}; | |
timbre.on = timbre.addListener = function(type, listener) { | |
_sys.on(type, listener); | |
return timbre; | |
}; | |
timbre.once = function(type, listener) { | |
_sys.once(type, listener); | |
return timbre; | |
}; | |
timbre.off = timbre.removeListener = function(type, listener) { | |
_sys.off(type, listener); | |
return timbre; | |
}; | |
timbre.removeAllListeners = function(type) { | |
_sys.removeAllListeners(type); | |
return timbre; | |
}; | |
timbre.listeners = function(type) { | |
return _sys.listeners(type); | |
}; | |
timbre.rec = function() { | |
return _sys.rec.apply(_sys, arguments); | |
}; | |
timbre.timevalue = (function() { | |
var getbpm = function(str) { | |
var m, bpm = _bpm; | |
if ((m = /^bpm(\d+(?:\.\d+)?)/i.exec(str))) { | |
bpm = Math.max(5, Math.min(300, +(m[1]||0))); | |
} | |
return bpm; | |
}; | |
return function(str) { | |
var m, ms, x; | |
if ((m = /^(\d+(?:\.\d+)?)Hz$/i.exec(str))) { | |
return +m[1] === 0 ? 0 : 1000 / +m[1]; | |
} | |
if ((m = /L(\d+)?(\.*)$/i.exec(str))) { | |
ms = 60 / getbpm(str) * (4 / (m[1]||4)) * 1000; | |
ms *= [1, 1.5, 1.75, 1.875][(m[2]||"").length] || 1; | |
return ms; | |
} | |
if ((m = /^(\d+(?:\.\d+)?|\.(?:\d+))(min|sec|m)s?$/i.exec(str))) { | |
switch (m[2]) { | |
case "min": return +(m[1]||0) * 60 * 1000; | |
case "sec": return +(m[1]||0) * 1000; | |
case "m" : return +(m[1]||0); | |
} | |
} | |
if ((m = /^(?:([0-5]?[0-9]):)?(?:([0-5]?[0-9]):)(?:([0-5]?[0-9]))(?:\.([0-9]{1,3}))?$/.exec(str))) { | |
x = (m[1]||0) * 3600 + (m[2]||0) * 60 + (m[3]||0); | |
x = x * 1000 + ((((m[4]||"")+"00").substr(0, 3))|0); | |
return x; | |
} | |
if ((m = /(\d+)\.(\d+)\.(\d+)$/i.exec(str))) { | |
x = (m[1] * 4 + (+m[2])) * 480 + (+m[3]); | |
return 60 / getbpm(str) * (x / 480) * 1000; | |
} | |
if ((m = /(\d+)ticks$/i.exec(str))) { | |
return 60 / getbpm(str) * (m[1] / 480) * 1000; | |
} | |
if ((m = /^(\d+)samples(?:\/(\d+)Hz)?$/i.exec(str))) { | |
return m[1] * 1000 / (m[2] || timbre.samplerate); | |
} | |
return 0; | |
}; | |
})(); | |
var fn = timbre.fn = { | |
SignalArray: Float32Array, | |
currentTimeIncr: 0, | |
emptycell: null, | |
FINISHED_STATE: FINISHED_STATE, | |
PLAYING_STATE: PLAYING_STATE, | |
UNSCHEDULED_STATE: UNSCHEDULED_STATE, | |
SCHEDULED_STATE: SCHEDULED_STATE | |
}; | |
var isArray = fn.isArray = Array.isArray; | |
var isDictionary = fn.isDictionary = function(object) { | |
return typeof object === "object" && object.constructor === Object; | |
}; | |
fn.nop = function() { | |
return this; | |
}; | |
fn.isSignalArray = function(obj) { | |
if (obj instanceof fn.SignalArray) { | |
return true; | |
} | |
if (Array.isArray(obj) && obj.__klass && obj.__klass.type === 2) { | |
return true; | |
} | |
return false; | |
}; | |
// borrowed from coffee-script | |
fn.extend = function(child, parent) { | |
parent = parent || TimbreObject; | |
for (var key in parent) { | |
if (parent.hasOwnProperty(key)) { | |
child[key] = parent[key]; | |
} | |
} | |
/*jshint validthis:true */ | |
function ctor() { | |
this.constructor = child; | |
} | |
/*jshint validthis:false */ | |
ctor.prototype = parent.prototype; | |
child.prototype = new ctor(); | |
child.__super__ = parent.prototype; | |
return child; | |
}; | |
fn.constructorof = function(ctor, Klass) { | |
var f = ctor && ctor.prototype; | |
while (f) { | |
if (f === Klass.prototype) { | |
return true; | |
} | |
f = Object.getPrototypeOf(f); | |
} | |
return false; | |
}; | |
fn.register = function(key, ctor) { | |
if (fn.constructorof(ctor, TimbreObject)) { | |
_constructors[key] = ctor; | |
} else { | |
_factories[key] = ctor; | |
} | |
}; | |
fn.alias = function(key, alias) { | |
if (_constructors[alias]) { | |
_constructors[key] = _constructors[alias]; | |
} else if (_factories[alias]) { | |
_factories[key] = _factories[alias]; | |
} | |
}; | |
fn.getClass = function(key) { | |
return _constructors[key]; | |
}; | |
fn.pointer = function(src, offset, length) { | |
offset = src.byteOffset + offset * src.constructor.BYTES_PER_ELEMENT; | |
if (typeof length === "number") { | |
return new src.constructor(src.buffer, offset, length); | |
} else { | |
return new src.constructor(src.buffer, offset); | |
} | |
}; | |
fn.nextTick = function(func) { | |
_sys.nextTick(func); | |
return timbre; | |
}; | |
fn.fixAR = function(self) { | |
self._.ar = true; | |
self._.aronly = true; | |
}; | |
fn.fixKR = function(self) { | |
self._.ar = false; | |
self._.kronly = true; | |
}; | |
fn.changeWithValue = function() { | |
var _ = this._; | |
var x = _.value * _.mul + _.add; | |
if (isNaN(x)) { | |
x = 0; | |
} | |
var cell = this.cells[0]; | |
for (var i = 0, imax = cell.length; i < imax; ++i) { | |
cell[i] = x; | |
} | |
}; | |
fn.changeWithValue.unremovable = true; | |
fn.clone = function(src) { | |
var new_instance = new src.constructor([]); | |
new_instance._.ar = src._.ar; | |
new_instance._.mul = src._.mul; | |
new_instance._.add = src._.add; | |
new_instance._.bypassed = src._.bypassed; | |
return new_instance; | |
}; | |
fn.timer = (function() { | |
var make_onstart = function(self) { | |
return function() { | |
if (_sys.timers.indexOf(self) === -1) { | |
_sys.timers.push(self); | |
_sys.events.emit("addObject"); | |
self._.emit("start"); | |
fn.buddies_start(self); | |
} | |
}; | |
}; | |
var make_onstop = function(self) { | |
return function() { | |
var i = _sys.timers.indexOf(self); | |
if (i !== -1) { | |
_sys.timers.splice(i, 1); | |
self._.emit("stop"); | |
_sys.events.emit("removeObject"); | |
fn.buddies_stop(self); | |
} | |
}; | |
}; | |
return function(self) { | |
var onstart = make_onstart(self); | |
var onstop = make_onstop(self); | |
self.nodeType = TimbreObject.TIMER; | |
self.start = function() { | |
_sys.nextTick(onstart); | |
return self; | |
}; | |
self.stop = function() { | |
_sys.nextTick(onstop); | |
return self; | |
}; | |
return self; | |
}; | |
})(); | |
fn.listener = (function() { | |
var make_onlisten = function(self) { | |
return function() { | |
if (_sys.listeners.indexOf(self) === -1) { | |
_sys.listeners.push(self); | |
_sys.events.emit("addObject"); | |
self._.emit("listen"); | |
fn.buddies_start(self); | |
} | |
}; | |
}; | |
var make_onunlisten = function(self) { | |
return function() { | |
var i = _sys.listeners.indexOf(self); | |
if (i !== -1) { | |
_sys.listeners.splice(i, 1); | |
self._.emit("unlisten"); | |
_sys.events.emit("removeObject"); | |
fn.buddies_stop(self); | |
} | |
}; | |
}; | |
return function(self) { | |
var onlisten = make_onlisten(self); | |
var onunlisten = make_onunlisten(self); | |
self.nodeType = TimbreObject.LISTENER; | |
self.listen = function(buddies) { | |
if (arguments.length) { | |
self.append.apply(self, arguments); | |
} | |
if (self.nodes.length) { | |
_sys.nextTick(onlisten); | |
} | |
return self; | |
}; | |
self.unlisten = function() { | |
if (arguments.length) { | |
self.remove.apply(self, arguments); | |
} | |
if (!self.nodes.length) { | |
_sys.nextTick(onunlisten); | |
} | |
return self; | |
}; | |
return self; | |
}; | |
})(); | |
fn.make_onended = function(self, lastValue) { | |
return function() { | |
self.playbackState = FINISHED_STATE; | |
if (typeof lastValue === "number") { | |
var cell = self.cells[0]; | |
var cellL = self.cells[1]; | |
var cellR = self.cells[2]; | |
for (var i = 0, imax = cellL.length; i < imax; ++i) { | |
cell[0] = cellL[i] = cellR[i] = lastValue; | |
} | |
} | |
self._.emit("ended"); | |
}; | |
}; | |
fn.inputSignalAR = function(self) { | |
var cell = self.cells[0]; | |
var cellL = self.cells[1]; | |
var cellR = self.cells[2]; | |
var nodes = self.nodes; | |
var i, imax = nodes.length; | |
var j, jmax = cell.length; | |
var tickID = self.tickID; | |
var not_clear, tmp, tmpL, tmpR; | |
if (self.numChannels === 2) { | |
not_clear = true; | |
if (imax !== 0) { | |
for (i = 0; i < imax; ++i) { | |
if (nodes[i].playbackState === PLAYING_STATE) { | |
nodes[i].process(tickID); | |
cellL.set(nodes[i].cells[1]); | |
cellR.set(nodes[i].cells[2]); | |
not_clear = false; | |
++i; | |
break; | |
} | |
} | |
for (; i < imax; ++i) { | |
if (nodes[i].playbackState === PLAYING_STATE) { | |
nodes[i].process(tickID); | |
tmpL = nodes[i].cells[1]; | |
tmpR = nodes[i].cells[2]; | |
for (j = jmax; j; ) { | |
j -= 8; | |
cellL[j ] += tmpL[j ]; cellR[j ] += tmpR[j ]; | |
cellL[j+1] += tmpL[j+1]; cellR[j+1] += tmpR[j+1]; | |
cellL[j+2] += tmpL[j+2]; cellR[j+2] += tmpR[j+2]; | |
cellL[j+3] += tmpL[j+3]; cellR[j+3] += tmpR[j+3]; | |
cellL[j+4] += tmpL[j+4]; cellR[j+4] += tmpR[j+4]; | |
cellL[j+5] += tmpL[j+5]; cellR[j+5] += tmpR[j+5]; | |
cellL[j+6] += tmpL[j+6]; cellR[j+6] += tmpR[j+6]; | |
cellL[j+7] += tmpL[j+7]; cellR[j+7] += tmpR[j+7]; | |
} | |
} | |
} | |
} | |
if (not_clear) { | |
cellL.set(fn.emptycell); | |
cellR.set(fn.emptycell); | |
} | |
} else { | |
not_clear = true; | |
if (imax !== 0) { | |
for (i = 0; i < imax; ++i) { | |
if (nodes[i].playbackState === PLAYING_STATE) { | |
nodes[i].process(tickID); | |
cell.set(nodes[i].cells[0]); | |
not_clear = false; | |
++i; | |
break; | |
} | |
} | |
for (; i < imax; ++i) { | |
if (nodes[i].playbackState === PLAYING_STATE) { | |
tmp = nodes[i].process(tickID).cells[0]; | |
for (j = jmax; j; ) { | |
j -= 8; | |
cell[j ] += tmp[j ]; | |
cell[j+1] += tmp[j+1]; | |
cell[j+2] += tmp[j+2]; | |
cell[j+3] += tmp[j+3]; | |
cell[j+4] += tmp[j+4]; | |
cell[j+5] += tmp[j+5]; | |
cell[j+6] += tmp[j+6]; | |
cell[j+7] += tmp[j+7]; | |
} | |
} | |
} | |
} | |
if (not_clear) { | |
cell.set(fn.emptycell); | |
} | |
} | |
}; | |
fn.inputSignalKR = function(self) { | |
var nodes = self.nodes; | |
var i, imax = nodes.length; | |
var tickID = self.tickID; | |
var tmp = 0; | |
for (i = 0; i < imax; ++i) { | |
if (nodes[i].playbackState === PLAYING_STATE) { | |
tmp += nodes[i].process(tickID).cells[0][0]; | |
} | |
} | |
return tmp; | |
}; | |
fn.outputSignalAR = function(self) { | |
var cell = self.cells[0]; | |
var cellL = self.cells[1]; | |
var cellR = self.cells[2]; | |
var mul = self._.mul, add = self._.add; | |
var i; | |
if (self.numChannels === 2) { | |
for (i = cell.length; i; ) { | |
i -= 8; | |
cellL[i ] = cellL[i ] * mul + add; cellR[i ] = cellR[i ] * mul + add; | |
cellL[i+1] = cellL[i+1] * mul + add; cellR[i+1] = cellR[i+1] * mul + add; | |
cellL[i+2] = cellL[i+2] * mul + add; cellR[i+2] = cellR[i+2] * mul + add; | |
cellL[i+3] = cellL[i+3] * mul + add; cellR[i+3] = cellR[i+3] * mul + add; | |
cellL[i+4] = cellL[i+4] * mul + add; cellR[i+4] = cellR[i+4] * mul + add; | |
cellL[i+5] = cellL[i+5] * mul + add; cellR[i+5] = cellR[i+5] * mul + add; | |
cellL[i+6] = cellL[i+6] * mul + add; cellR[i+6] = cellR[i+6] * mul + add; | |
cellL[i+7] = cellL[i+7] * mul + add; cellR[i+7] = cellR[i+7] * mul + add; | |
cell[i ] = (cellL[i ] + cellR[i ]) * 0.5; | |
cell[i+1] = (cellL[i+1] + cellR[i+1]) * 0.5; | |
cell[i+2] = (cellL[i+2] + cellR[i+2]) * 0.5; | |
cell[i+3] = (cellL[i+3] + cellR[i+3]) * 0.5; | |
cell[i+4] = (cellL[i+4] + cellR[i+4]) * 0.5; | |
cell[i+5] = (cellL[i+5] + cellR[i+5]) * 0.5; | |
cell[i+6] = (cellL[i+6] + cellR[i+6]) * 0.5; | |
cell[i+7] = (cellL[i+7] + cellR[i+7]) * 0.5; | |
} | |
} else { | |
if (mul !== 1 || add !== 0) { | |
for (i = cell.length; i; ) { | |
i -= 8; | |
cell[i ] = cell[i ] * mul + add; | |
cell[i+1] = cell[i+1] * mul + add; | |
cell[i+2] = cell[i+2] * mul + add; | |
cell[i+3] = cell[i+3] * mul + add; | |
cell[i+4] = cell[i+4] * mul + add; | |
cell[i+5] = cell[i+5] * mul + add; | |
cell[i+6] = cell[i+6] * mul + add; | |
cell[i+7] = cell[i+7] * mul + add; | |
} | |
} | |
} | |
}; | |
fn.outputSignalKR = function(self) { | |
var cell = self.cells[0]; | |
var cellL = self.cells[1]; | |
var cellR = self.cells[2]; | |
var mul = self._.mul, add = self._.add; | |
var value = cell[0] * mul + add; | |
var i; | |
if (self.numChannels === 2) { | |
for (i = cell.length; i; ) { | |
i -= 8; | |
cell[i] = cell[i+1] = cell[i+2] = cell[i+3] = cell[i+4] = cell[i+5] = cell[i+6] = cell[i+7] = cellL[i] = cellL[i+1] = cellL[i+2] = cellL[i+3] = cellL[i+4] = cellL[i+5] = cellL[i+6] = cellL[i+7] = cellR[i] = cellR[i+1] = cellR[i+2] = cellR[i+3] = cellR[i+4] = cellR[i+5] = cellR[i+6] = cellR[i+7] = value; | |
} | |
} else { | |
for (i = cell.length; i; ) { | |
i -= 8; | |
cell[i] = cell[i+1] = cell[i+2] = cell[i+3] = cell[i+4] = cell[i+5] = cell[i+6] = cell[i+7] = value; | |
} | |
} | |
}; | |
fn.buddies_start = function(self) { | |
var buddies = self._.buddies; | |
var node, i, imax; | |
for (i = 0, imax = buddies.length; i < imax; ++i) { | |
node = buddies[i]; | |
switch (node.nodeType) { | |
case TimbreObject.DSP: | |
node.play(); | |
break; | |
case TimbreObject.TIMER: | |
node.start(); | |
break; | |
case TimbreObject.LISTENER: | |
node.listen(); | |
break; | |
} | |
} | |
}; | |
fn.buddies_stop = function(self) { | |
var buddies = self._.buddies; | |
var node, i, imax; | |
for (i = 0, imax = buddies.length; i < imax; ++i) { | |
node = buddies[i]; | |
switch (node.nodeType) { | |
case TimbreObject.DSP: | |
node.pause(); | |
break; | |
case TimbreObject.TIMER: | |
node.stop(); | |
break; | |
case TimbreObject.LISTENER: | |
node.unlisten(); | |
break; | |
} | |
} | |
}; | |
fn.fix_iOS6_1_problem = function(flag) { | |
_sys.fix_iOS6_1_problem(flag); | |
}; | |
var modules = timbre.modules = {}; | |
// EventEmitter | |
var EventEmitter = modules.EventEmitter = (function() { | |
function EventEmitter(context) { | |
this.context = context; | |
this.events = {}; | |
} | |
var $ = EventEmitter.prototype; | |
$.emit = function(type) { | |
var handler = this.events[type]; | |
if (!handler) { | |
return false; | |
} | |
var args; | |
if (typeof handler === "function") { | |
switch (arguments.length) { | |
case 1: | |
handler.call(this.context); | |
break; | |
case 2: | |
handler.call(this.context, arguments[1]); | |
break; | |
case 3: | |
handler.call(this.context, arguments[1], arguments[2]); | |
break; | |
default: | |
args = slice.call(arguments, 1); | |
handler.apply(this.context, args); | |
} | |
return true; | |
} else if (isArray(handler)) { | |
args = slice.call(arguments, 1); | |
var listeners = handler.slice(); | |
for (var i = 0, imax = listeners.length; i < imax; ++i) { | |
if (listeners[i] instanceof TimbreObject) { | |
listeners[i].bang.apply(listeners[i], args); | |
} else { | |
listeners[i].apply(this.context, args); | |
} | |
} | |
return true; | |
} else if (handler instanceof TimbreObject) { | |
args = slice.call(arguments, 1); | |
handler.bang.apply(handler, args); | |
} else { | |
return false; | |
} | |
}; | |
$.on = function(type, listener) { | |
if (typeof listener !== "function" && !(listener instanceof TimbreObject)) { | |
throw new Error("addListener takes instances of Function or timbre.Object"); | |
} | |
var e = this.events; | |
if (!e[type]) { | |
e[type] = listener; | |
} else if (isArray(e[type])) { | |
e[type].push(listener); | |
} else { | |
e[type] = [e[type], listener]; | |
} | |
return this; | |
}; | |
$.once = function(type, listener) { | |
var self = this; | |
var g; | |
if (typeof listener === "function") { | |
g = function () { | |
self.off(type, g); | |
listener.apply(self.context, arguments); | |
}; | |
} else if (listener instanceof TimbreObject) { | |
g = function () { | |
self.off(type, g); | |
listener.bang.apply(listener, arguments); | |
}; | |
} else { | |
throw new Error("once takes instances of Function or timbre.Object"); | |
} | |
g.listener = listener; | |
self.on(type, g); | |
return this; | |
}; | |
$.off = function(type, listener) { | |
if (typeof listener !== "function" && !(listener instanceof TimbreObject)) { | |
throw new Error("removeListener takes instances of Function or timbre.Object"); | |
} | |
var e = this.events; | |
if (!e[type]) { | |
return this; | |
} | |
var list = e[type]; | |
if (isArray(list)) { | |
var position = -1; | |
for (var i = 0, imax = list.length; i < imax; ++i) { | |
if (list[i] === listener || | |
// once listener | |
(list[i].listener && list[i].listener === listener)) { | |
position = i; | |
break; | |
} | |
} | |
if (position < 0) { | |
return this; | |
} | |
list.splice(position, 1); | |
if (list.length === 0) { | |
e[type] = null; | |
} | |
} else if (list === listener || | |
// once listener | |
(list.listener && list.listener === listener)) { | |
e[type] = null; | |
} | |
return this; | |
}; | |
$.removeAllListeners = function(type) { | |
var e = this.events; | |
var remain = false; | |
var listeners = e[type]; | |
if (isArray(listeners)) { | |
for (var i = listeners.length; i--; ) { | |
var listener = listeners[i]; | |
if (listener.unremovable) { | |
remain = true; | |
continue; | |
} | |
this.off(type, listener); | |
} | |
} else if (listeners) { | |
if (!listeners.unremovable) { | |
this.off(type, listeners); | |
} else { | |
remain = true; | |
} | |
} | |
if (!remain) { | |
e[type] = null; | |
} | |
return this; | |
}; | |
$.listeners = function(type) { | |
var a, e = this.events; | |
if (!e[type]) { | |
return []; | |
} | |
e = e[type]; | |
if (!isArray(e)) { | |
return e.unremovable ? [] : [e]; | |
} | |
e = e.slice(); | |
a = []; | |
for (var i = 0, imax = e.length; i < imax; ++i) { | |
if (!e[i].unremovable) { | |
a.push(e[i]); | |
} | |
} | |
return a; | |
}; | |
return EventEmitter; | |
})(); | |
var Deferred = modules.Deferred = (function() { | |
function Deferred(context) { | |
this.context = context || this; | |
this._state = "pending"; | |
this._doneList = []; | |
this._failList = []; | |
this._promise = new Promise(this); | |
} | |
var $ = Deferred.prototype; | |
var exec = function(statue, list, context, args) { | |
if (this._state === "pending") { | |
this._state = statue; | |
for (var i = 0, imax = list.length; i < imax; ++i) { | |
list[i].apply(context, args); | |
} | |
this._doneList = this._failList = null; | |
} | |
}; | |
var isDeferred = function(x) { | |
return x && typeof x.promise === "function"; | |
}; | |
$.resolve = function() { | |
var args = slice.call(arguments, 0); | |
exec.call(this, "resolved", this._doneList, this.context || this, args); | |
return this; | |
}; | |
$.resolveWith = function(context) { | |
var args = slice.call(arguments, 1); | |
exec.call(this, "resolved", this._doneList, context, args); | |
return this; | |
}; | |
$.reject = function() { | |
var args = slice.call(arguments, 0); | |
exec.call(this, "rejected", this._failList, this.context || this, args); | |
return this; | |
}; | |
$.rejectWith = function(context) { | |
var args = slice.call(arguments, 1); | |
exec.call(this, "rejected", this._failList, context, args); | |
return this; | |
}; | |
$.promise = function() { | |
return this._promise; | |
}; | |
$.done = function() { | |
var args = slice.call(arguments); | |
var isResolved = (this._state === "resolved"); | |
var isPending = (this._state === "pending"); | |
var list = this._doneList; | |
for (var i = 0, imax = args.length; i < imax; ++i) { | |
if (typeof args[i] === "function") { | |
if (isResolved) { | |
args[i](); | |
} else if (isPending) { | |
list.push(args[i]); | |
} | |
} | |
} | |
return this; | |
}; | |
$.fail = function() { | |
var args = slice.call(arguments); | |
var isRejected = (this._state === "rejected"); | |
var isPending = (this._state === "pending"); | |
var list = this._failList; | |
for (var i = 0, imax = args.length; i < imax; ++i) { | |
if (typeof args[i] === "function") { | |
if (isRejected) { | |
args[i](); | |
} else if (isPending) { | |
list.push(args[i]); | |
} | |
} | |
} | |
return this; | |
}; | |
$.always = function() { | |
this.done.apply(this, arguments); | |
this.fail.apply(this, arguments); | |
return this; | |
}; | |
$.then = function then(done, fail) { | |
return this.done(done).fail(fail); | |
}; | |
$.pipe = function(done, fail) { | |
var self = this; | |
var dfd = new Deferred(this.context); | |
this.done(function() { | |
var res = done.apply(self.context, arguments); | |
if (isDeferred(res)) { | |
res.then(function() { | |
var args = slice.call(arguments); | |
dfd.resolveWith.apply(dfd, [res].concat(args)); | |
}); | |
} else { | |
dfd.resolveWith(self, res); | |
} | |
}); | |
this.fail(function() { | |
if (typeof fail === "function") { | |
var res = fail.apply(self.context, arguments); | |
if (isDeferred(res)) { | |
res.fail(function() { | |
var args = slice.call(arguments); | |
dfd.rejectWith.apply(dfd, [res].concat(args)); | |
}); | |
} | |
} else { | |
dfd.reject.apply(dfd, arguments); | |
} | |
}); | |
return dfd.promise(); | |
}; | |
// $.then = $.pipe; | |
$.isResolved = function() { | |
return this._state === "resolved"; | |
}; | |
$.isRejected = function() { | |
return this._state === "rejected"; | |
}; | |
$.state = function() { | |
return this._state; | |
}; | |
// TODO: test | |
Deferred.when = function(subordinate) { | |
var i = 0; | |
var resolveValues = slice.call(arguments); | |
var length = resolveValues.length; | |
var remaining = length; | |
if (length === 1 && !isDeferred(subordinate)) { | |
remaining = 0; | |
} | |
var deferred = (remaining === 1) ? subordinate : new Deferred(); | |
var updateFunc = function(i, results) { | |
return function(value) { | |
results[i] = arguments.length > 1 ? slice.call(arguments) : value; | |
if (!(--remaining)) { | |
deferred.resolve.apply(deferred, results); | |
} | |
}; | |
}; | |
if (length > 1) { | |
var resolveResults = new Array(length); | |
var onfailed = function() { | |
deferred.reject(); | |
}; | |
for (; i < length; ++i) { | |
if (resolveValues[i] && isDeferred(resolveValues[i])) { | |
resolveValues[i].promise().done( | |
updateFunc(i, resolveResults) | |
).fail(onfailed); | |
} else { | |
resolveResults[i] = resolveValues[i]; | |
--remaining; | |
} | |
} | |
} | |
if (!remaining) { | |
deferred.resolve.apply(deferred, resolveValues); | |
} | |
return deferred.promise(); | |
}; | |
function Promise(object) { | |
this.context = object.context; | |
this.then = object.then; | |
this.done = function() { | |
object.done.apply(object, arguments); | |
return this; | |
}; | |
this.fail = function() { | |
object.fail.apply(object, arguments); | |
return this; | |
}; | |
this.pipe = function() { | |
return object.pipe.apply(object, arguments); | |
}; | |
this.always = function() { | |
object.always.apply(object, arguments); | |
return this; | |
}; | |
this.promise = function() { | |
return this; | |
}; | |
this.isResolved = function() { | |
return object.isResolved(); | |
}; | |
this.isRejected = function() { | |
return object.isRejected(); | |
}; | |
} | |
return Deferred; | |
})(); | |
// root object | |
var TimbreObject = timbre.Object = (function() { | |
function TimbreObject(numChannels, _args) { | |
this._ = {}; // private members | |
var e = this._.events = new EventEmitter(this); | |
this._.emit = function() { | |
return e.emit.apply(e, arguments); | |
}; | |
if (isDictionary(_args[0])) { | |
var params = _args.shift(); | |
var _in = params["in"]; | |
this.once("init", function() { | |
this.set(params); | |
if (_in) { | |
if (isArray(_in)) { | |
this.append.apply(this, _in); | |
} else if (_in instanceof TimbreObject) { | |
this.append(_in); | |
} | |
} | |
}); | |
} | |
this.tickID = -1; | |
this.nodes = _args.map(timbre); | |
this.cells = []; | |
this.numChannels = numChannels; | |
switch (numChannels) { | |
case 0: | |
this.L = this.R = new ChannelObject(null); | |
this.cells[0] = this.cells[1] = this.cells[2] = this.L.cell; | |
break; | |
case 1: | |
this.L = this.R = new ChannelObject(this); | |
this.cells[0] = this.cells[1] = this.cells[2] = this.L.cell; | |
break; | |
case 2: | |
this.L = new ChannelObject(this); | |
this.R = new ChannelObject(this); | |
this.cells[0] = new fn.SignalArray(_sys.cellsize); | |
this.cells[1] = this.L.cell; | |
this.cells[2] = this.R.cell; | |
break; | |
} | |
this.playbackState = PLAYING_STATE; | |
this.nodeType = TimbreObject.DSP; | |
this._.ar = true; | |
this._.mul = 1; | |
this._.add = 0; | |
this._.dac = null; | |
this._.bypassed = false; | |
this._.meta = {}; | |
this._.samplerate = _sys.samplerate; | |
this._.cellsize = _sys.cellsize; | |
this._.buddies = []; | |
} | |
TimbreObject.DSP = 1; | |
TimbreObject.TIMER = 2; | |
TimbreObject.LISTENER = 3; | |
var $ = TimbreObject.prototype; | |
Object.defineProperties($, { | |
isAr: { | |
get: function() { | |
return this._.ar; | |
} | |
}, | |
isKr: { | |
get: function() { | |
return !this._.ar; | |
} | |
}, | |
isBypassed: { | |
get: function() { | |
return this._.bypassed; | |
} | |
}, | |
isEnded: { | |
get: function() { | |
return !(this.playbackState & 1); | |
} | |
}, | |
mul: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.mul = value; | |
this._.emit("setMul", value); | |
} | |
}, | |
get: function() { | |
return this._.mul; | |
} | |
}, | |
add: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.add = value; | |
this._.emit("setAdd", value); | |
} | |
}, | |
get: function() { | |
return this._.add; | |
} | |
}, | |
buddies: { | |
set: function(value) { | |
if (!isArray(value)) { | |
value = [value]; | |
} | |
this._.buddies = value.filter(function(node) { | |
return node instanceof TimbreObject; | |
}); | |
}, | |
get: function() { | |
return this._.buddies; | |
} | |
} | |
}); | |
$.toString = function() { | |
return this.constructor.name; | |
}; | |
$.valueOf = function() { | |
if (_sys.tickID !== this.tickID) { | |
this.process(_sys.tickID); | |
} | |
return this.cells[0][0]; | |
}; | |
$.append = function() { | |
if (arguments.length > 0) { | |
var list = slice.call(arguments).map(timbre); | |
this.nodes = this.nodes.concat(list); | |
this._.emit("append", list); | |
} | |
return this; | |
}; | |
$.appendTo = function(object) { | |
object.append(this); | |
return this; | |
}; | |
$.remove = function() { | |
if (arguments.length > 0) { | |
var j, nodes = this.nodes, list = []; | |
for (var i = 0, imax = arguments.length; i < imax; ++i) { | |
if ((j = nodes.indexOf(arguments[i])) !== -1) { | |
list.push(nodes[j]); | |
nodes.splice(j, 1); | |
} | |
} | |
if (list.length > 0) { | |
this._.emit("remove", list); | |
} | |
} | |
return this; | |
}; | |
$.removeFrom = function(object) { | |
object.remove(this); | |
return this; | |
}; | |
$.removeAll = function() { | |
var list = this.nodes.slice(); | |
this.nodes = []; | |
if (list.length > 0) { | |
this._.emit("remove", list); | |
} | |
return this; | |
}; | |
$.removeAtIndex = function(index) { | |
var item = this.nodes[index]; | |
if (item) { | |
this.nodes.splice(index, 1); | |
this._.emit("remove", [item]); | |
} | |
return this; | |
}; | |
$.postMessage = function(message) { | |
this._.emit("message", message); | |
return this; | |
}; | |
$.to = function(object) { | |
if (object instanceof TimbreObject) { | |
object.append(this); | |
} else { | |
var args = slice.call(arguments); | |
if (isDictionary(args[1])) { | |
args.splice(2, 0, this); | |
} else { | |
args.splice(1, 0, this); | |
} | |
object = T.apply(null, args); | |
} | |
return object; | |
}; | |
$.splice = function(ins, obj, rem) { | |
var i; | |
if (!obj) { | |
if (this._.dac) { | |
if (ins instanceof TimbreObject) { | |
if (rem instanceof TimbreObject) { | |
if (rem._.dac) { | |
rem._.dac._.node = ins; | |
ins._.dac = rem._.dac; | |
rem._.dac = null; | |
ins.nodes.push(this); | |
} | |
} else { | |
if (this._.dac) { | |
this._.dac._.node = ins; | |
ins._.dac = this._.dac; | |
this._.dac = null; | |
ins.nodes.push(this); | |
} | |
} | |
} else if (rem instanceof TimbreObject) { | |
if (rem._.dac) { | |
rem._.dac._.node = this; | |
this._.dac = rem._.dac; | |
rem._.dac = null; | |
} | |
} | |
} | |
} else { | |
if (obj instanceof TimbreObject) { | |
i = obj.nodes.indexOf(rem); | |
if (i !== -1) { | |
obj.nodes.splice(i, 1); | |
} | |
if (ins instanceof TimbreObject) { | |
ins.nodes.push(this); | |
obj.nodes.push(ins); | |
} else { | |
obj.nodes.push(this); | |
} | |
} | |
} | |
return this; | |
}; | |
// EventEmitter | |
$.on = $.addListener = function(type, listener) { | |
this._.events.on(type, listener); | |
return this; | |
}; | |
$.once = function(type, listener) { | |
this._.events.once(type, listener); | |
return this; | |
}; | |
$.off = $.removeListener = function(type, listener) { | |
this._.events.off(type, listener); | |
return this; | |
}; | |
$.removeAllListeners = function(type) { | |
this._.events.removeAllListeners(type); | |
return this; | |
}; | |
$.listeners = function(type) { | |
return this._.events.listeners(type); | |
}; | |
$.set = function(key, value) { | |
var x, desc, meta = this._.meta; | |
switch (typeof key) { | |
case "string": | |
switch (meta[key]) { | |
case "property": | |
this[key] = value; | |
break; | |
case "function": | |
this[key](value); | |
break; | |
default: | |
x = this; | |
while (x !== null) { | |
desc = Object.getOwnPropertyDescriptor(x, key); | |
if (desc) { | |
if (typeof desc.value === "function") { | |
meta[key] = "function"; | |
this[key](value); | |
} else if (desc.get || desc.set) { | |
meta[key] = "property"; | |
this[key] = value; | |
} | |
} | |
x = Object.getPrototypeOf(x); | |
} | |
} | |
break; | |
case "object": | |
for (x in key) { | |
this.set(x, key[x]); | |
} | |
break; | |
} | |
return this; | |
}; | |
$.get = function(key) { | |
if (this._.meta[key] === "property") { | |
return this[key]; | |
} | |
}; | |
$.bang = function() { | |
this._.emit.apply(this, ["bang"].concat(slice.call(arguments))); | |
return this; | |
}; | |
$.process = fn.nop; | |
$.bypass = function() { | |
this._.bypassed = (arguments.length === 0) ? true : !!arguments[0]; | |
return this; | |
}; | |
$.play = function() { | |
var dac = this._.dac; | |
if (dac === null) { | |
dac = this._.dac = new SystemInlet(this); | |
} | |
if (dac.play()) { | |
this._.emit.apply(this, ["play"].concat(slice.call(arguments))); | |
} | |
fn.buddies_start(this); | |
return this; | |
}; | |
$.pause = function() { | |
var dac = this._.dac; | |
if (dac && dac.playbackState === PLAYING_STATE) { | |
dac.pause(); | |
this._.dac = null; | |
this._.emit("pause"); | |
} | |
fn.buddies_stop(this); | |
return this; | |
}; | |
$.start = $.stop = $.listen = $.unlisten = function() { | |
return this; | |
}; | |
$.ar = function() { | |
if ((arguments.length === 0) ? true : !!arguments[0]) { | |
if (!this._.kronly) { | |
this._.ar = true; | |
this._.emit("ar", true); | |
} | |
} else { | |
this.kr(true); | |
} | |
return this; | |
}; | |
$.kr = function() { | |
if ((arguments.length === 0) ? true : !!arguments[0]) { | |
if (!this._.aronly) { | |
this._.ar = false; | |
this._.emit("ar", false); | |
} | |
} else { | |
this.ar(true); | |
} | |
return this; | |
}; | |
if (_envtype === "browser") { | |
$.plot = function(opts) { | |
var _ = this._; | |
var canvas = opts.target; | |
if (!canvas) { | |
return this; | |
} | |
var width = opts.width || canvas.width || 320; | |
var height = opts.height || canvas.height || 240; | |
var offset_x = (opts.x || 0) + 0.5; | |
var offset_y = (opts.y || 0); | |
var context = canvas.getContext("2d"); | |
var foreground; | |
if (opts.foreground !== undefined) { | |
foreground = opts.foreground; | |
} else{ | |
foreground = _.plotForeground || "rgb( 0, 128, 255)"; | |
} | |
var background; | |
if (opts.background !== undefined) { | |
background = opts.background; | |
} else { | |
background = _.plotBackground || "rgb(255, 255, 255)"; | |
} | |
var lineWidth = opts.lineWidth || _.plotLineWidth || 1; | |
var cyclic = !!_.plotCyclic; | |
var data = _.plotData || this.cells[0]; | |
var range = opts.range || _.plotRange || [-1.2, +1.2]; | |
var rangeMin = range[0]; | |
var rangeDelta = height / (range[1] - rangeMin); | |
var x, dx = (width / data.length); | |
var y, dy, y0; | |
var i, imax = data.length; | |
context.save(); | |
context.rect(offset_x, offset_y, width, height); | |
// context.clip(); | |
if (background !== null) { | |
context.fillStyle = background; | |
context.fillRect(offset_x, offset_y, width, height); | |
} | |
if (_.plotBefore) { | |
_.plotBefore.call( | |
this, context, offset_x, offset_y, width, height | |
); | |
} | |
if (_.plotBarStyle) { | |
context.fillStyle = foreground; | |
x = 0; | |
for (i = 0; i < imax; ++i) { | |
dy = (data[i] - rangeMin) * rangeDelta; | |
y = height - dy; | |
context.fillRect(x + offset_x, y + offset_y, dx, dy); | |
x += dx; | |
} | |
} else { | |
context.strokeStyle = foreground; | |
context.lineWidth = lineWidth; | |
context.beginPath(); | |
x = 0; | |
y0 = height - (data[0] - rangeMin) * rangeDelta; | |
context.moveTo(x + offset_x, y0 + offset_y); | |
for (i = 1; i < imax; ++i) { | |
x += dx; | |
y = height - (data[i] - rangeMin) * rangeDelta; | |
context.lineTo(x + offset_x, y + offset_y); | |
} | |
if (cyclic) { | |
context.lineTo(x + dx + offset_x, y0 + offset_y); | |
} else { | |
context.lineTo(x + dx + offset_x, y + offset_y); | |
} | |
context.stroke(); | |
} | |
if (_.plotAfter) { | |
_.plotAfter.call( | |
this, context, offset_x, offset_y, width, height | |
); | |
} | |
var border = opts.border || _.plotBorder; | |
if (border) { | |
context.strokeStyle = | |
(typeof border === "string") ? border : "#000"; | |
context.lineWidth = 1; | |
context.strokeRect(offset_x, offset_y, width, height); | |
} | |
context.restore(); | |
return this; | |
}; | |
} else { | |
$.plot = fn.nop; | |
} | |
return TimbreObject; | |
})(); | |
var ChannelObject = timbre.ChannelObject = (function() { | |
function ChannelObject(parent) { | |
timbre.Object.call(this, -1, []); | |
fn.fixAR(this); | |
this._.parent = parent; | |
this.cell = new fn.SignalArray(_sys.cellsize); | |
this.L = this.R = this; | |
this.cells[0] = this.cells[1] = this.cells[2] = this.cell; | |
this.numChannels = 1; | |
} | |
fn.extend(ChannelObject); | |
ChannelObject.prototype.process = function(tickID) { | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
if (this._.parent) { | |
this._.parent.process(tickID); | |
} | |
} | |
return this; | |
}; | |
return ChannelObject; | |
})(); | |
var AddNode = (function() { | |
function AddNode(_args) { | |
TimbreObject.call(this, 2, _args); | |
} | |
fn.extend(AddNode); | |
AddNode.prototype.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
if (_.ar) { | |
fn.inputSignalAR(this); | |
fn.outputSignalAR(this); | |
} else { | |
this.cells[0][0] = fn.inputSignalKR(this); | |
fn.outputSignalKR(this); | |
} | |
} | |
return this; | |
}; | |
fn.register("+", AddNode); | |
return AddNode; | |
})(); | |
var NumberWrapper = (function() { | |
function NumberWrapper(_args) { | |
TimbreObject.call(this, 1, []); | |
fn.fixKR(this); | |
this.value = _args[0]; | |
if (isDictionary(_args[1])) { | |
var params = _args[1]; | |
this.once("init", function() { | |
this.set(params); | |
}); | |
} | |
this.on("setAdd", fn.changeWithValue); | |
this.on("setMul", fn.changeWithValue); | |
} | |
fn.extend(NumberWrapper); | |
var $ = NumberWrapper.prototype; | |
Object.defineProperties($, { | |
value: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.value = isNaN(value) ? 0 : value; | |
fn.changeWithValue.call(this); | |
} | |
}, | |
get: function() { | |
return this._.value; | |
} | |
} | |
}); | |
return NumberWrapper; | |
})(); | |
var BooleanWrapper = (function() { | |
function BooleanWrapper(_args) { | |
TimbreObject.call(this, 1, []); | |
fn.fixKR(this); | |
this.value = _args[0]; | |
if (isDictionary(_args[1])) { | |
var params = _args[1]; | |
this.once("init", function() { | |
this.set(params); | |
}); | |
} | |
this.on("setAdd", fn.changeWithValue); | |
this.on("setMul", fn.changeWithValue); | |
} | |
fn.extend(BooleanWrapper); | |
var $ = BooleanWrapper.prototype; | |
Object.defineProperties($, { | |
value: { | |
set: function(value) { | |
this._.value = value ? 1 : 0; | |
fn.changeWithValue.call(this); | |
}, | |
get: function() { | |
return !!this._.value; | |
} | |
} | |
}); | |
return BooleanWrapper; | |
})(); | |
var FunctionWrapper = (function() { | |
function FunctionWrapper(_args) { | |
TimbreObject.call(this, 1, []); | |
fn.fixKR(this); | |
this.func = _args[0]; | |
this._.value = 0; | |
if (isDictionary(_args[1])) { | |
var params = _args[1]; | |
this.once("init", function() { | |
this.set(params); | |
}); | |
} | |
this.on("setAdd", fn.changeWithValue); | |
this.on("setMul", fn.changeWithValue); | |
} | |
fn.extend(FunctionWrapper); | |
var $ = FunctionWrapper.prototype; | |
Object.defineProperties($, { | |
func: { | |
set: function(value) { | |
if (typeof value === "function") { | |
this._.func = value; | |
} | |
}, | |
get: function() { | |
return this._.func; | |
} | |
}, | |
args: { | |
set: function(value) { | |
if (isArray(value)) { | |
this._.args = value; | |
} else { | |
this._.args = [value]; | |
} | |
}, | |
get: function() { | |
return this._.args; | |
} | |
} | |
}); | |
$.bang = function() { | |
var _ = this._; | |
var args = slice.call(arguments).concat(_.args); | |
var x = _.func.apply(this, args); | |
if (typeof x === "number") { | |
_.value = x; | |
fn.changeWithValue.call(this); | |
} | |
this._.emit("bang"); | |
return this; | |
}; | |
return FunctionWrapper; | |
})(); | |
var ArrayWrapper = (function() { | |
function ArrayWrapper(_args) { | |
TimbreObject.call(this, 1, []); | |
var i, imax; | |
for (i = 0, imax = _args[0].length; i < imax; ++i) { | |
this.append(_args[0][i]); | |
} | |
if (isDictionary(_args[1])) { | |
var params = _args[1]; | |
this.once("init", function() { | |
this.set(params); | |
}); | |
} | |
} | |
fn.extend(ArrayWrapper); | |
var $ = ArrayWrapper.prototype; | |
Object.defineProperties($, { | |
}); | |
$.bang = function() { | |
var args = ["bang"].concat(slice.call(arguments)); | |
var nodes = this.nodes; | |
var i, imax; | |
for (i = 0, imax = nodes.length; i < imax; ++i) { | |
nodes[i].bang.apply(nodes[i], args); | |
} | |
return this; | |
}; | |
$.postMessage = function(message) { | |
var nodes = this.nodes; | |
var i, imax; | |
for (i = 0, imax = nodes.length; i < imax; ++i) { | |
nodes[i].postMessage(message); | |
} | |
return this; | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
if (_.ar) { | |
fn.inputSignalAR(this); | |
fn.outputSignalAR(this); | |
} else { | |
this.cells[0][0] = fn.inputSignalKR(this); | |
fn.outputSignalKR(this); | |
} | |
} | |
return this; | |
}; | |
return ArrayWrapper; | |
})(); | |
var ObjectWrapper = (function() { | |
function ObjectWrapper(_args) { | |
TimbreObject.call(this, 1, []); | |
fn.fixKR(this); | |
if (isDictionary(_args[1])) { | |
var params = _args[1]; | |
this.once("init", function() { | |
this.set(params); | |
}); | |
} | |
} | |
fn.extend(ObjectWrapper); | |
var $ = ObjectWrapper.prototype; | |
Object.defineProperties($, { | |
}); | |
return ObjectWrapper; | |
})(); | |
var SystemInlet = (function() { | |
function SystemInlet(object) { | |
TimbreObject.call(this, 2, []); | |
this.playbackState = FINISHED_STATE; | |
var _ = this._; | |
_.node = object; | |
_.onplay = make_onplay(this); | |
_.onpause = make_onpause(this); | |
} | |
fn.extend(SystemInlet); | |
var make_onplay = function(self) { | |
return function() { | |
if (_sys.inlets.indexOf(self) === -1) { | |
_sys.inlets.push(self); | |
_sys.events.emit("addObject"); | |
self.playbackState = PLAYING_STATE; | |
self._.emit("play"); | |
} | |
}; | |
}; | |
var make_onpause = function(self) { | |
return function() { | |
var i = _sys.inlets.indexOf(self); | |
if (i !== -1) { | |
_sys.inlets.splice(i, 1); | |
self.playbackState = FINISHED_STATE; | |
self._.emit("pause"); | |
_sys.events.emit("removeObject"); | |
} | |
}; | |
}; | |
var $ = SystemInlet.prototype; | |
$.play = function() { | |
_sys.nextTick(this._.onplay); | |
return (_sys.inlets.indexOf(this) === -1); | |
}; | |
$.pause = function() { | |
_sys.nextTick(this._.onpause); | |
}; | |
$.process = function(tickID) { | |
var node = this._.node; | |
if (node.playbackState & 1) { | |
node.process(tickID); | |
this.cells[1].set(node.cells[1]); | |
this.cells[2].set(node.cells[2]); | |
} else { | |
this.cells[1].set(fn.emptycell); | |
this.cells[2].set(fn.emptycell); | |
} | |
}; | |
return SystemInlet; | |
})(); | |
var SoundSystem = (function() { | |
function SoundSystem() { | |
this.context = this; | |
this.tickID = 0; | |
this.impl = null; | |
this.amp = 0.8; | |
this.status = FINISHED_STATE; | |
this.samplerate = 44100; | |
this.channels = 2; | |
this.cellsize = 64; | |
this.streammsec = 20; | |
this.streamsize = 0; | |
this.currentTime = 0; | |
this.nextTicks = []; | |
this.inlets = []; | |
this.timers = []; | |
this.listeners = []; | |
this.deferred = null; | |
this.recStart = 0; | |
this.recBuffers = null; | |
this.delayProcess = make_delayProcess(this); | |
this.events = null; | |
fn.currentTimeIncr = this.cellsize * 1000 / this.samplerate; | |
fn.emptycell = new fn.SignalArray(this.cellsize); | |
this.reset(true); | |
} | |
var make_delayProcess = function(self) { | |
return function() { | |
self.recStart = Date.now(); | |
self.process(); | |
}; | |
}; | |
var $ = SoundSystem.prototype; | |
$.bind = function(Klass, opts) { | |
if (typeof Klass === "function") { | |
var player = new Klass(this, opts); | |
this.impl = player; | |
if (this.impl.defaultSamplerate) { | |
this.samplerate = this.impl.defaultSamplerate; | |
} | |
} | |
return this; | |
}; | |
$.setup = function(params) { | |
if (typeof params === "object") { | |
if (ACCEPT_SAMPLERATES.indexOf(params.samplerate) !== -1) { | |
if (params.samplerate <= this.impl.maxSamplerate) { | |
this.samplerate = params.samplerate; | |
} else { | |
this.samplerate = this.impl.maxSamplerate; | |
} | |
} | |
if (ACCEPT_CELLSIZES.indexOf(params.cellsize) !== -1) { | |
this.cellsize = params.cellsize; | |
} | |
if (typeof Float64Array !== "undefined" && typeof params.f64 !== "undefined") { | |
_f64mode = !!params.f64; | |
if (_f64mode) { | |
fn.SignalArray = Float64Array; | |
} else { | |
fn.SignalArray = Float32Array; | |
} | |
} | |
} | |
fn.currentTimeIncr = this.cellsize * 1000 / this.samplerate; | |
fn.emptycell = new fn.SignalArray(this.cellsize); | |
return this; | |
}; | |
$.getAdjustSamples = function(samplerate) { | |
var samples, bits; | |
samplerate = samplerate || this.samplerate; | |
samples = this.streammsec / 1000 * samplerate; | |
bits = Math.ceil(Math.log(samples) * Math.LOG2E); | |
bits = (bits < 8) ? 8 : (bits > 14) ? 14 : bits; | |
return 1 << bits; | |
}; | |
$.play = function() { | |
if (this.status === FINISHED_STATE) { | |
this.status = PLAYING_STATE; | |
this.streamsize = this.getAdjustSamples(); | |
this.strmL = new fn.SignalArray(this.streamsize); | |
this.strmR = new fn.SignalArray(this.streamsize); | |
this.impl.play(); | |
this.events.emit("play"); | |
} | |
return this; | |
}; | |
$.pause = function() { | |
if (this.status === PLAYING_STATE) { | |
this.status = FINISHED_STATE; | |
this.impl.pause(); | |
this.events.emit("pause"); | |
} | |
return this; | |
}; | |
$.reset = function(deep) { | |
if (deep) { | |
this.events = new EventEmitter(this).on("addObject", function() { | |
if (this.status === FINISHED_STATE) { | |
this.play(); | |
} | |
}).on("removeObject", function() { | |
if (this.status === PLAYING_STATE) { | |
if (this.inlets.length + this.timers.length + this.listeners.length === 0) { | |
this.pause(); | |
} | |
} | |
}); | |
} | |
this.currentTime = 0; | |
this.nextTicks = []; | |
this.inlets = []; | |
this.timers = []; | |
this.listeners = []; | |
return this; | |
}; | |
$.process = function() { | |
var tickID = this.tickID; | |
var strmL = this.strmL, strmR = this.strmR; | |
var amp = this.amp; | |
var x, tmpL, tmpR; | |
var i, imax = this.streamsize, saved_i = 0; | |
var j, jmax; | |
var k, kmax = this.cellsize; | |
var n = this.streamsize / this.cellsize; | |
var nextTicks; | |
var timers = this.timers; | |
var inlets = this.inlets; | |
var listeners = this.listeners; | |
var currentTimeIncr = fn.currentTimeIncr; | |
for (i = 0; i < imax; ++i) { | |
strmL[i] = strmR[i] = 0; | |
} | |
while (n--) { | |
++tickID; | |
for (j = 0, jmax = timers.length; j < jmax; ++j) { | |
if (timers[j].playbackState & 1) { | |
timers[j].process(tickID); | |
} | |
} | |
for (j = 0, jmax = inlets.length; j < jmax; ++j) { | |
x = inlets[j]; | |
x.process(tickID); | |
if (x.playbackState & 1) { | |
tmpL = x.cells[1]; | |
tmpR = x.cells[2]; | |
for (k = 0, i = saved_i; k < kmax; ++k, ++i) { | |
strmL[i] += tmpL[k]; | |
strmR[i] += tmpR[k]; | |
} | |
} | |
} | |
saved_i += kmax; | |
for (j = 0, jmax = listeners.length; j < jmax; ++j) { | |
if (listeners[j].playbackState & 1) { | |
listeners[j].process(tickID); | |
} | |
} | |
this.currentTime += currentTimeIncr; | |
nextTicks = this.nextTicks.splice(0); | |
for (j = 0, jmax = nextTicks.length; j < jmax; ++j) { | |
nextTicks[j](); | |
} | |
} | |
for (i = 0; i < imax; ++i) { | |
x = strmL[i] * amp; | |
if (x < -1) { | |
x = -1; | |
} else if (x > 1) { | |
x = 1; | |
} | |
strmL[i] = x; | |
x = strmR[i] * amp; | |
if (x < -1) { | |
x = -1; | |
} else if (x > 1) { | |
x = 1; | |
} | |
strmR[i] = x; | |
} | |
this.tickID = tickID; | |
var currentTime = this.currentTime; | |
if (this.status === SCHEDULED_STATE) { | |
if (this.recCh === 2) { | |
this.recBuffers.push(new fn.SignalArray(strmL)); | |
this.recBuffers.push(new fn.SignalArray(strmR)); | |
} else { | |
var strm = new fn.SignalArray(strmL.length); | |
for (i = 0, imax = strm.length; i < imax; ++i) { | |
strm[i] = (strmL[i] + strmR[i]) * 0.5; | |
} | |
this.recBuffers.push(strm); | |
} | |
if (currentTime >= this.maxDuration) { | |
this.deferred.sub.reject(); | |
} else if (currentTime >= this.recDuration) { | |
this.deferred.sub.resolve(); | |
} else { | |
var now = Date.now(); | |
if ((now - this.recStart) > 20) { | |
setTimeout(this.delayProcess, 10); | |
} else { | |
this.process(); | |
} | |
} | |
} | |
}; | |
$.nextTick = function(func) { | |
if (this.status === FINISHED_STATE) { | |
func(); | |
} else { | |
this.nextTicks.push(func); | |
} | |
}; | |
$.rec = function() { | |
fn.fix_iOS6_1_problem(true); | |
var dfd = new Deferred(this); | |
if (this.deferred) { | |
console.warn("rec deferred is exists??"); | |
return dfd.reject().promise(); | |
} | |
if (this.status !== FINISHED_STATE) { | |
console.log("status is not none", this.status); | |
return dfd.reject().promise(); | |
} | |
var i = 0, args = arguments; | |
var opts = isDictionary(args[i]) ? args[i++] : {}; | |
var func = args[i]; | |
if (typeof func !== "function") { | |
// throw error?? | |
console.warn("no function"); | |
return dfd.reject().promise(); | |
} | |
this.deferred = dfd; | |
this.status = SCHEDULED_STATE; | |
this.reset(); | |
var rec_inlet = new T("+"); | |
var inlet_dfd = new Deferred(this); | |
var outlet = { | |
done: function() { | |
inlet_dfd.resolve.apply(inlet_dfd, slice.call(arguments)); | |
}, | |
send: function() { | |
rec_inlet.append.apply(rec_inlet, arguments); | |
} | |
}; | |
var self = this; | |
inlet_dfd.then(recdone, function() { | |
fn.fix_iOS6_1_problem(false); | |
recdone.call(self, true); | |
}); | |
this.deferred.sub = inlet_dfd; | |
this.savedSamplerate = this.samplerate; | |
this.samplerate = opts.samplerate || this.samplerate; | |
this.recDuration = opts.recDuration || Infinity; | |
this.maxDuration = opts.maxDuration || 10 * 60 * 1000; | |
this.recCh = opts.ch || 1; | |
if (this.recCh !== 2) { | |
this.recCh = 1; | |
} | |
this.recBuffers = []; | |
this.streamsize = this.getAdjustSamples(); | |
this.strmL = new fn.SignalArray(this.streamsize); | |
this.strmR = new fn.SignalArray(this.streamsize); | |
this.inlets.push(rec_inlet); | |
func(outlet); | |
setTimeout(this.delayProcess, 10); | |
return dfd.promise(); | |
}; | |
var recdone = function() { | |
this.status = FINISHED_STATE; | |
this.reset(); | |
var recBuffers = this.recBuffers; | |
var samplerate = this.samplerate; | |
var streamsize = this.streamsize; | |
var bufferLength; | |
this.samplerate = this.savedSamplerate; | |
if (this.recDuration !== Infinity) { | |
bufferLength = (this.recDuration * samplerate * 0.001)|0; | |
} else { | |
bufferLength = (recBuffers.length >> (this.recCh-1)) * streamsize; | |
} | |
var result; | |
var i, imax = (bufferLength / streamsize)|0; | |
var j = 0, k = 0; | |
var remaining = bufferLength; | |
if (this.recCh === 2) { | |
var L = new fn.SignalArray(bufferLength); | |
var R = new fn.SignalArray(bufferLength); | |
var mixed = new fn.SignalArray(bufferLength); | |
for (i = 0; i < imax; ++i) { | |
L.set(recBuffers[j++], k); | |
R.set(recBuffers[j++], k); | |
k += streamsize; | |
remaining -= streamsize; | |
if (remaining > 0 && remaining < streamsize) { | |
L.set(recBuffers[j++].subarray(0, remaining), k); | |
R.set(recBuffers[j++].subarray(0, remaining), k); | |
break; | |
} | |
} | |
for (i = 0, imax = bufferLength; i < imax; ++i) { | |
mixed[i] = (L[i] + R[i]) * 0.5; | |
} | |
result = { | |
samplerate: samplerate, | |
channels : 2, | |
buffer: [mixed, L, R] | |
}; | |
} else { | |
var buffer = new fn.SignalArray(bufferLength); | |
for (i = 0; i < imax; ++i) { | |
buffer.set(recBuffers[j++], k); | |
k += streamsize; | |
remaining -= streamsize; | |
if (remaining > 0 && remaining < streamsize) { | |
buffer.set(recBuffers[j++].subarray(0, remaining), k); | |
break; | |
} | |
} | |
result = { | |
samplerate: samplerate, | |
channels : 1, | |
buffer: [buffer] | |
}; | |
} | |
var args = [].concat.apply([result], arguments); | |
this.deferred.resolve.apply(this.deferred, args); | |
this.deferred = null; | |
}; | |
// EventEmitter | |
$.on = function(type, listeners) { | |
this.events.on(type, listeners); | |
}; | |
$.once = function(type, listeners) { | |
this.events.once(type, listeners); | |
}; | |
$.off = function(type, listener) { | |
this.events.off(type, listener); | |
}; | |
$.removeAllListeners = function(type) { | |
this.events.removeListeners(type); | |
}; | |
$.listeners = function(type) { | |
return this.events.listeners(type); | |
}; | |
$.fix_iOS6_1_problem = function(flag) { | |
if (this.impl.fix_iOS6_1_problem) { | |
this.impl.fix_iOS6_1_problem(flag); | |
} | |
}; | |
return SoundSystem; | |
})(); | |
// player | |
var ImplClass = null; | |
/*global webkitAudioContext:true */ | |
if (typeof webkitAudioContext !== "undefined") { | |
ImplClass = function(sys) { | |
if(window.TWebkitAudioContext) { | |
var context = window.TWebkitAudioContext | |
} else { | |
var context = new webkitAudioContext(); | |
} | |
var bufSrc, jsNode; | |
fn._audioContext = context; | |
this.maxSamplerate = context.sampleRate; | |
this.defaultSamplerate = context.sampleRate; | |
this.env = "webkit"; | |
var ua = navigator.userAgent; | |
if (ua.match(/linux/i)) { | |
sys.streammsec *= 8; | |
} else if (ua.match(/win(dows)?\s*(nt 5\.1|xp)/i)) { | |
sys.streammsec *= 4; | |
} | |
this.play = function() { | |
var onaudioprocess; | |
var jsn_streamsize = sys.getAdjustSamples(context.sampleRate); | |
var sys_streamsize = sys.streamsize; | |
var x, dx; | |
if (sys.samplerate === context.sampleRate) { | |
onaudioprocess = function(e) { | |
var outs = e.outputBuffer; | |
sys.process(); | |
outs.getChannelData(0).set(sys.strmL); | |
outs.getChannelData(1).set(sys.strmR); | |
}; | |
} else if (sys.samplerate * 2 === context.sampleRate) { | |
onaudioprocess = function(e) { | |
var inL = sys.strmL; | |
var inR = sys.strmR; | |
var outs = e.outputBuffer; | |
var outL = outs.getChannelData(0); | |
var outR = outs.getChannelData(1); | |
var i, imax = outs.length; | |
var j; | |
sys.process(); | |
for (i = j = 0; i < imax; i += 2, ++j) { | |
outL[i] = outL[i+1] = inL[j]; | |
outR[i] = outR[i+1] = inR[j]; | |
} | |
}; | |
} else { | |
x = sys_streamsize; | |
dx = sys.samplerate / context.sampleRate; | |
onaudioprocess = function(e) { | |
var inL = sys.strmL; | |
var inR = sys.strmR; | |
var outs = e.outputBuffer; | |
var outL = outs.getChannelData(0); | |
var outR = outs.getChannelData(1); | |
var i, imax = outs.length; | |
for (i = 0; i < imax; ++i) { | |
if (x >= sys_streamsize) { | |
sys.process(); | |
x -= sys_streamsize; | |
} | |
outL[i] = inL[x|0]; | |
outR[i] = inR[x|0]; | |
x += dx; | |
} | |
}; | |
} | |
bufSrc = context.createBufferSource(); | |
jsNode = context.createJavaScriptNode(jsn_streamsize, 2, sys.channels); | |
jsNode.onaudioprocess = onaudioprocess; | |
bufSrc.noteOn(0); | |
bufSrc.connect(jsNode); | |
jsNode.connect(context.destination); | |
}; | |
this.pause = function() { | |
bufSrc.disconnect(); | |
jsNode.disconnect(); | |
}; | |
if (_envmobile) { | |
var n = 0; | |
var buf = context.createBufferSource(); | |
this.fix_iOS6_1_problem = function(flag) { | |
n += flag ? 1 : -1; | |
if (n === 1) { | |
buf.noteOn(0); | |
buf.connect(context.destination); | |
} else if (n === 0) { | |
buf.disconnect(); | |
} | |
}; | |
} | |
}; | |
} else if (typeof Audio === "function" && | |
typeof (new Audio()).mozSetup === "function") { | |
ImplClass = function(sys) { | |
/*global URL:true */ | |
var timer = (function() { | |
var source = "var t=0;onmessage=function(e){if(t)t=clearInterval(t),0;if(typeof e.data=='number'&&e.data>0)t=setInterval(function(){postMessage(0);},e.data);};"; | |
var blob = new Blob([source], {type:"text/javascript"}); | |
var path = URL.createObjectURL(blob); | |
return new Worker(path); | |
})(); | |
/*global URL:false */ | |
this.maxSamplerate = 48000; | |
this.defaultSamplerate = 44100; | |
this.env = "moz"; | |
this.play = function() { | |
var audio = new Audio(); | |
var interleaved = new Float32Array(sys.streamsize * sys.channels); | |
var streammsec = sys.streammsec; | |
var written = 0; | |
var writtenIncr = sys.streamsize / sys.samplerate * 1000; | |
var start = Date.now(); | |
var onaudioprocess = function() { | |
if (written > Date.now() - start) { | |
return; | |
} | |
var inL = sys.strmL; | |
var inR = sys.strmR; | |
var i = interleaved.length; | |
var j = inL.length; | |
sys.process(); | |
while (j--) { | |
interleaved[--i] = inR[j]; | |
interleaved[--i] = inL[j]; | |
} | |
audio.mozWriteAudio(interleaved); | |
written += writtenIncr; | |
}; | |
audio.mozSetup(sys.channels, sys.samplerate); | |
timer.onmessage = onaudioprocess; | |
timer.postMessage(streammsec); | |
}; | |
this.pause = function() { | |
timer.postMessage(0); | |
}; | |
}; | |
} else { | |
ImplClass = function(sys) { | |
this.maxSamplerate = 48000; | |
this.defaultSamplerate = 44100; | |
this.env = "nop"; | |
this.play = function() {}; | |
this.pause = function() {}; | |
}; | |
} | |
/*global webkitAudioContext:false */ | |
_sys = new SoundSystem().bind(ImplClass); | |
timbre._sys = _sys | |
var exports = timbre; | |
if (_envtype === "node") { | |
module.exports = global.timbre = exports; | |
} else if (_envtype === "browser") { | |
exports.noConflict = (function() { | |
var _t = window.timbre, _T = window.T; | |
return function(deep) { | |
if (window.T === exports) { | |
window.T = _T; | |
} | |
if (deep && window.timbre === exports) { | |
window.timbre = _t; | |
} | |
return exports; | |
}; | |
})(); | |
window.timbre = window.T = exports; | |
} | |
// Flash fallback | |
(function() { | |
if (_sys.impl.env !== "nop" || _envtype !== "browser" || _envmobile) { | |
return; | |
} | |
var nav = navigator; | |
/*jshint latedef:true */ | |
if (getFlashPlayerVersion(0) < 10) { | |
return; | |
} | |
/*jshint latedef:false */ | |
var swf, PlayerDivID = "TimbreFlashPlayerDiv"; | |
var src = (function() { | |
var scripts = document.getElementsByTagName("script"); | |
if (scripts && scripts.length) { | |
for (var m, i = 0, imax = scripts.length; i < imax; ++i) { | |
if ((m = /^(.*\/)timbre(?:\.dev)?\.js$/i.exec(scripts[i].src))) { | |
return m[1] + "timbre.swf"; | |
} | |
} | |
} | |
})(); | |
window.timbrejs_flashfallback_init = function() { | |
function TimbreFlashPlayer(sys) { | |
var timerId = 0; | |
this.maxSamplerate = 44100; | |
this.defaultSamplerate = 44100; | |
this.env = "flash"; | |
this.play = function() { | |
var onaudioprocess; | |
var interleaved = new Array(sys.streamsize * sys.channels); | |
var streammsec = sys.streammsec; | |
var written = 0; | |
var writtenIncr = sys.streamsize / sys.samplerate * 1000; | |
var start = Date.now(); | |
onaudioprocess = function() { | |
if (written > Date.now() - start) { | |
return; | |
} | |
var inL = sys.strmL; | |
var inR = sys.strmR; | |
var i = interleaved.length; | |
var j = inL.length; | |
sys.process(); | |
while (j--) { | |
interleaved[--i] = (inR[j] * 32768)|0; | |
interleaved[--i] = (inL[j] * 32768)|0; | |
} | |
swf.writeAudio(interleaved.join(" ")); | |
written += writtenIncr; | |
}; | |
if (swf.setup) { | |
swf.setup(sys.channels, sys.samplerate); | |
timerId = setInterval(onaudioprocess, streammsec); | |
} else { | |
console.warn("Cannot find " + src); | |
} | |
}; | |
this.pause = function() { | |
if (timerId !== 0) { | |
swf.cancel(); | |
clearInterval(timerId); | |
timerId = 0; | |
} | |
}; | |
} | |
_sys.bind(TimbreFlashPlayer); | |
delete window.timbrejs_flashfallback_init; | |
}; | |
var o, p; | |
var swfSrc = src; | |
var swfName = swfSrc + "?" + (+new Date()); | |
var swfId = "TimbreFlashPlayer"; | |
var div = document.createElement("div"); | |
div.id = PlayerDivID; | |
div.style.display = "inline"; | |
div.width = div.height = 1; | |
if (nav.plugins && nav.mimeTypes && nav.mimeTypes.length) { | |
// ns | |
o = document.createElement("object"); | |
o.id = swfId; | |
o.classid = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"; | |
o.width = o.height = 1; | |
o.setAttribute("data", swfName); | |
o.setAttribute("type", "application/x-shockwave-flash"); | |
p = document.createElement("param"); | |
p.setAttribute("name", "allowScriptAccess"); | |
p.setAttribute("value", "always"); | |
o.appendChild(p); | |
div.appendChild(o); | |
} else { | |
// ie | |
/*jshint quotmark:single */ | |
div.innerHTML = '<object id="' + swfId + '" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="1" height="1"><param name="movie" value="' + swfName + '" /><param name="bgcolor" value="#FFFFFF" /><param name="quality" value="high" /><param name="allowScriptAccess" value="always" /></object>'; | |
/*jshint quotmark:double */ | |
} | |
window.addEventListener("load", function() { | |
document.body.appendChild(div); | |
swf = document[swfId]; | |
}); | |
function getFlashPlayerVersion(subs) { | |
/*global ActiveXObject:true */ | |
try { | |
if (nav.plugins && nav.mimeTypes && nav.mimeTypes.length) { | |
return nav.plugins["Shockwave Flash"].description.match(/([0-9]+)/)[subs]; | |
} | |
return (new ActiveXObject("ShockwaveFlash.ShockwaveFlash")).GetVariable("$version").match(/([0-9]+)/)[subs]; | |
} catch (e) { | |
return -1; | |
} | |
/*global ActiveXObject:false */ | |
} | |
})(); | |
function setupTypedArray() { | |
var unsigned = 0, signed = 1, floating = 2; | |
function ArrayBuffer(_) { | |
var a = new Array(_.byteLength); | |
var bytes = _.BYTES_PER_ELEMENT, shift; | |
for (var i = 0, imax = a.length; i < imax; ++i) { | |
shift = (i % bytes) * 8; | |
a[i] = (_[(i / bytes)|0] & (0x0FF << shift)) >>> shift; | |
} | |
a.__view = _; | |
return a; | |
} | |
function TypedArray(klass, arg, offset, length) { | |
var a, b, bytes, i, imax; | |
if (Array.isArray(arg)) { | |
if (arg.__view) { | |
if (typeof offset === "undefined") { | |
offset = 0; | |
} | |
if (typeof length === "undefined") { | |
length = arg.length - offset; | |
} | |
bytes = klass.bytes; | |
if (klass.type === floating) { | |
a = arg.__view.slice((offset/bytes)|0, ((offset+length)/bytes)|0); | |
} else { | |
b = arg.slice(offset, offset + length); | |
a = new Array((b.length / bytes)|0); | |
for (i = 0, imax = a.length; i < imax; ++i) { | |
a[i] = 0; | |
} | |
for (i = 0, imax = b.length; i < imax; ++i) { | |
a[(i/bytes)|0] += (b[i] & 0xFF) << ((i % bytes) * 8); | |
} | |
} | |
} else { | |
a = arg.slice(); | |
} | |
} else if (typeof arg === "number" && arg > 0) { | |
a = new Array(arg|0); | |
} else { | |
a = []; | |
} | |
if (klass.type !== floating) { | |
for (i = 0, imax = a.length; i < imax; ++i) { | |
a[i] = (+a[i] || 0) & ((1 << (2 * 8)) - 1); | |
} | |
} else { | |
for (i = 0, imax = a.length; i < imax; ++i) { | |
a[i] = a[i] || 0; | |
} | |
} | |
if (klass.type === signed) { | |
for (i = 0, imax = a.length; i < imax; ++i) { | |
if (a[i] & (1 << ((bytes * 8) - 1))) { | |
a[i] -= 1 << (bytes * 8); | |
} | |
} | |
} | |
a.__klass = klass; | |
a.constructor = window[klass.name]; | |
a.set = set; | |
a.subarray = subarray; | |
a.BYTES_PER_ELEMENT = klass.bytes; | |
a.byteLength = klass.bytes * a.length; | |
a.byteOffset = offset || 0; | |
Object.defineProperty(a, "buffer", { | |
get: function() { | |
return new ArrayBuffer(this); | |
} | |
}); | |
return a; | |
} | |
var set = function(array, offset) { | |
if (typeof offset === "undefined") { | |
offset = 0; | |
} | |
var i, imax = Math.min(this.length - offset, array.length); | |
for (i = 0; i < imax; ++i) { | |
this[offset + i] = array[i]; | |
} | |
}; | |
var subarray = function(begin, end) { | |
if (typeof end === "undefined") { | |
end = this.length; | |
} | |
return new this.constructor(this.slice(begin, end)); | |
}; | |
[["Int8Array" , 1, signed], ["Uint8Array" , 1, unsigned], | |
["Int16Array", 2, signed], ["Uint16Array", 2, unsigned], | |
["Int32Array", 4, signed], ["Uint32Array", 4, unsigned], | |
["Float32Array", 4, floating], ["Float64Array", 8, floating] | |
].forEach(function(_params) { | |
var name = _params[0]; | |
var params = { bytes:_params[1], type:_params[2], name:name }; | |
window[name] = function(arg, offset, length) { | |
return TypedArray.call(this, params, arg, offset, length); | |
}; | |
}); | |
} | |
})(); | |
(function(T) { | |
"use strict"; | |
function Biquad(samplerate) { | |
this.samplerate = samplerate; | |
this.frequency = 340; | |
this.Q = 1; | |
this.gain = 0; | |
this.x1L = this.x2L = this.y1L = this.y2L = 0; | |
this.x1R = this.x2R = this.y1R = this.y2R = 0; | |
this.b0 = this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
this.setType("lpf"); | |
} | |
var $ = Biquad.prototype; | |
$.process = function(cellL, cellR) { | |
var xL, xR, yL, yR; | |
var x1L = this.x1L, x2L = this.x2L, y1L = this.y1L, y2L = this.y2L; | |
var x1R = this.x1R, x2R = this.x2R, y1R = this.y1R, y2R = this.y2R; | |
var b0 = this.b0, b1 = this.b1, b2 = this.b2, a1 = this.a1, a2 = this.a2; | |
var i, imax; | |
for (i = 0, imax = cellL.length; i < imax; ++i) { | |
xL = cellL[i]; | |
yL = b0 * xL + b1 * x1L + b2 * x2L - a1 * y1L - a2 * y2L; | |
x2L = x1L; x1L = xL; y2L = y1L; y1L = yL; | |
xR = cellR[i]; | |
yR = b0 * xR + b1 * x1R + b2 * x2R - a1 * y1R - a2 * y2R; | |
x2R = x1R; x1R = xR; y2R = y1R; y1R = yR; | |
cellL[i] = yL; | |
cellR[i] = yR; | |
} | |
this.x1L = x1L; this.x2L = x2L; this.y1L = y1L; this.y2L = y2L; | |
this.x1R = x1R; this.x2R = x2R; this.y1R = y1R; this.y2R = y2R; | |
}; | |
$.setType = function(type) { | |
var f; | |
if ((f = setParams[type])) { | |
this.type = type; | |
f.call(this, this.frequency, this.Q, this.gain); | |
} | |
}; | |
$.setParams = function(frequency, Q, dbGain) { | |
this.frequency = frequency; | |
this.Q = Q; | |
this.gain = dbGain; | |
var f = setParams[this.type]; | |
if (f) { | |
f.call(this, frequency, Q, dbGain); | |
} | |
return this; | |
}; | |
var setParams = { | |
lowpass: function(cutoff, resonance) { | |
cutoff /= (this.samplerate * 0.5); | |
if (cutoff >= 1) { | |
this.b0 = 1; | |
this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} else if (cutoff <= 0) { | |
this.b0 = this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} else { | |
resonance = (resonance < 0) ? 0 : resonance; | |
var g = Math.pow(10.0, 0.05 * resonance); | |
var d = Math.sqrt((4 - Math.sqrt(16 - 16 / (g * g))) * 0.5); | |
var theta = Math.PI * cutoff; | |
var sn = 0.5 * d * Math.sin(theta); | |
var beta = 0.5 * (1 - sn) / (1 + sn); | |
var gamma = (0.5 + beta) * Math.cos(theta); | |
var alpha = 0.25 * (0.5 + beta - gamma); | |
this.b0 = 2 * alpha; | |
this.b1 = 4 * alpha; | |
this.b2 = this.b0; // 2 * alpha; | |
this.a1 = 2 * -gamma; | |
this.a2 = 2 * beta; | |
} | |
}, | |
highpass: function(cutoff, resonance) { | |
cutoff /= (this.samplerate * 0.5); | |
if (cutoff >= 1) { | |
this.b0 = this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} else if (cutoff <= 0) { | |
this.b0 = 1; | |
this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} else { | |
resonance = (resonance < 0) ? 0 : resonance; | |
var g = Math.pow(10.0, 0.05 * resonance); | |
var d = Math.sqrt((4 - Math.sqrt(16 - 16 / (g * g))) / 2); | |
var theta = Math.PI * cutoff; | |
var sn = 0.5 * d * Math.sin(theta); | |
var beta = 0.5 * (1 - sn) / (1 + sn); | |
var gamma = (0.5 + beta) * Math.cos(theta); | |
var alpha = 0.25 * (0.5 + beta + gamma); | |
this.b0 = 2 * alpha; | |
this.b1 = -4 * alpha; | |
this.b2 = this.b0; // 2 * alpha; | |
this.a1 = 2 * -gamma; | |
this.a2 = 2 * beta; | |
} | |
}, | |
bandpass: function(frequency, Q) { | |
frequency /= (this.samplerate * 0.5); | |
if (frequency > 0 && frequency < 1) { | |
if (Q > 0) { | |
var w0 = Math.PI * frequency; | |
var alpha = Math.sin(w0) / (2 * Q); | |
var k = Math.cos(w0); | |
var ia0 = 1 / (1 + alpha); | |
this.b0 = alpha * ia0; | |
this.b1 = 0; | |
this.b2 = -alpha * ia0; | |
this.a1 = -2 * k * ia0; | |
this.a2 = (1 - alpha) * ia0; | |
} else { | |
this.b0 = this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} | |
} else { | |
this.b0 = this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} | |
}, | |
lowshelf: function(frequency, _dummy_, dbGain) { | |
frequency /= (this.samplerate * 0.5); | |
var A = Math.pow(10.0, dbGain / 40); | |
if (frequency >= 1) { | |
this.b0 = A* A; | |
this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} else if (frequency <= 0) { | |
this.b0 = 1; | |
this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} else { | |
var w0 = Math.PI * frequency; | |
var S = 1; // filter slope (1 is max value) | |
var alpha = 0.5 * Math.sin(w0) * Math.sqrt((A + 1 / A) * (1 / S - 1) + 2); | |
var k = Math.cos(w0); | |
var k2 = 2 * Math.sqrt(A) * alpha; | |
var aPlusOne = A + 1; | |
var aMinusOne = A - 1; | |
var ia0 = 1 / (aPlusOne + aMinusOne * k + k2); | |
this.b0 = (A * (aPlusOne - aMinusOne * k + k2)) * ia0; | |
this.b1 = (2 * A * (aMinusOne - aPlusOne * k)) * ia0; | |
this.b2 = (A * (aPlusOne - aMinusOne * k - k2)) * ia0; | |
this.a1 = (-2 * (aMinusOne + aPlusOne * k)) * ia0; | |
this.a2 = (aPlusOne + aMinusOne * k - k2) * ia0; | |
} | |
}, | |
highshelf: function(frequency, _dummy_, dbGain) { | |
frequency /= (this.samplerate * 0.5); | |
var A = Math.pow(10.0, dbGain / 40); | |
if (frequency >= 1) { | |
this.b0 = 1; | |
this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} else if (frequency <= 0) { | |
this.b0 = A * A; | |
this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} else { | |
var w0 = Math.PI * frequency; | |
var S = 1; // filter slope (1 is max value) | |
var alpha = 0.5 * Math.sin(w0) * Math.sqrt((A + 1 / A) * (1 / S - 1) + 2); | |
var k = Math.cos(w0); | |
var k2 = 2 * Math.sqrt(A) * alpha; | |
var aPlusOne = A + 1; | |
var aMinusOne = A - 1; | |
var ia0 = 1 / (aPlusOne - aMinusOne * k + k2); | |
this.b0 = (A * (aPlusOne + aMinusOne * k + k2)) * ia0; | |
this.b1 = (-2 * A * (aMinusOne + aPlusOne * k)) * ia0; | |
this.b2 = (A * (aPlusOne + aMinusOne * k - k2)) * ia0; | |
this.a1 = (2 * (aMinusOne - aPlusOne * k)) * ia0; | |
this.a2 = (aPlusOne - aMinusOne * k - k2) * ia0; | |
} | |
}, | |
peaking: function(frequency, Q, dbGain) { | |
frequency /= (this.samplerate * 0.5); | |
if (frequency > 0 && frequency < 1) { | |
var A = Math.pow(10.0, dbGain / 40); | |
if (Q > 0) { | |
var w0 = Math.PI * frequency; | |
var alpha = Math.sin(w0) / (2 * Q); | |
var k = Math.cos(w0); | |
var ia0 = 1 / (1 + alpha / A); | |
this.b0 = (1 + alpha * A) * ia0; | |
this.b1 = (-2 * k) * ia0; | |
this.b2 = (1 - alpha * A) * ia0; | |
this.a1 = this.b1; // (-2 * k) * ia0; | |
this.a2 = (1 - alpha / A) * ia0; | |
} else { | |
this.b0 = A * A; | |
this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} | |
} else { | |
this.b0 = 1; | |
this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} | |
}, | |
notch: function(frequency, Q) { | |
frequency /= (this.samplerate * 0.5); | |
if (frequency > 0 && frequency < 1) { | |
if (Q > 0) { | |
var w0 = Math.PI * frequency; | |
var alpha = Math.sin(w0) / (2 * Q); | |
var k = Math.cos(w0); | |
var ia0 = 1 / (1 + alpha); | |
this.b0 = ia0; | |
this.b1 = (-2 * k) * ia0; | |
this.b2 = ia0; | |
this.a1 = this.b1; // (-2 * k) * ia0; | |
this.a2 = (1 - alpha) * ia0; | |
} else { | |
this.b0 = this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} | |
} else { | |
this.b0 = 1; | |
this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} | |
}, | |
allpass: function(frequency, Q) { | |
frequency /= (this.samplerate * 0.5); | |
if (frequency > 0 && frequency < 1) { | |
if (Q > 0) { | |
var w0 = Math.PI * frequency; | |
var alpha = Math.sin(w0) / (2 * Q); | |
var k = Math.cos(w0); | |
var ia0 = 1 / (1 + alpha); | |
this.b0 = (1 - alpha) * ia0; | |
this.b1 = (-2 * k) * ia0; | |
this.b2 = (1 + alpha) * ia0; | |
this.a1 = this.b1; // (-2 * k) * ia0; | |
this.a2 = this.b0; // (1 - alpha) * ia0; | |
} else { | |
this.b0 = -1; | |
this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} | |
} else { | |
this.b0 = 1; | |
this.b1 = this.b2 = this.a1 = this.a2 = 0; | |
} | |
} | |
}; | |
setParams.lpf = setParams.lowpass; | |
setParams.hpf = setParams.highpass; | |
setParams.bpf = setParams.bandpass; | |
setParams.bef = setParams.notch; | |
setParams.brf = setParams.notch; | |
setParams.apf = setParams.allpass; | |
T.modules.Biquad = Biquad; | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
function Chorus(samplerate) { | |
this.samplerate = samplerate; | |
var bits = Math.round(Math.log(samplerate * 0.1) * Math.LOG2E); | |
this.buffersize = 1 << bits; | |
this.bufferL = new T.fn.SignalArray(this.buffersize + 1); | |
this.bufferR = new T.fn.SignalArray(this.buffersize + 1); | |
this.wave = null; | |
this._wave = null; | |
this.writeIndex = this.buffersize >> 1; | |
this.readIndex = 0; | |
this.delayTime = 20; | |
this.rate = 4; | |
this.depth = 20; | |
this.feedback = 0.2; | |
this.wet = 0.5; | |
this.phase = 0; | |
this.phaseIncr = 0; | |
this.phaseStep = 4; | |
this.setWaveType("sin"); | |
this.setDelayTime(this.delayTime); | |
this.setRate(this.rate); | |
} | |
var $ = Chorus.prototype; | |
var waves = []; | |
waves[0] = (function() { | |
var wave = new Float32Array(512); | |
for (var i = 0; i < 512; ++i) { | |
wave[i] = Math.sin(2 * Math.PI * (i/512)); | |
} | |
return wave; | |
})(); | |
waves[1] = (function() { | |
var wave = new Float32Array(512); | |
for (var x, i = 0; i < 512; ++i) { | |
x = (i / 512) - 0.25; | |
wave[i] = 1.0 - 4.0 * Math.abs(Math.round(x) - x); | |
} | |
return wave; | |
})(); | |
$.setWaveType = function(waveType) { | |
if (waveType === "sin") { | |
this.wave = waveType; | |
this._wave = waves[0]; | |
} else if (waveType === "tri") { | |
this.wave = waveType; | |
this._wave = waves[1]; | |
} | |
}; | |
$.setDelayTime = function(delayTime) { | |
this.delayTime = delayTime; | |
var readIndex = this.writeIndex - ((delayTime * this.samplerate * 0.001)|0); | |
while (readIndex < 0) { | |
readIndex += this.buffersize; | |
} | |
this.readIndex = readIndex; | |
}; | |
$.setRate = function(rate) { | |
this.rate = rate; | |
this.phaseIncr = (512 * this.rate / this.samplerate) * this.phaseStep; | |
}; | |
$.process = function(cellL, cellR) { | |
var bufferL = this.bufferL; | |
var bufferR = this.bufferR; | |
var size = this.buffersize; | |
var mask = size - 1; | |
var wave = this._wave; | |
var phase = this.phase; | |
var phaseIncr = this.phaseIncr; | |
var writeIndex = this.writeIndex; | |
var readIndex = this.readIndex; | |
var depth = this.depth; | |
var feedback = this.feedback; | |
var x, index, mod; | |
var wet = this.wet, dry = 1 - wet; | |
var i, imax = cellL.length; | |
var j, jmax = this.phaseStep; | |
for (i = 0; i < imax; ) { | |
mod = wave[phase|0] * depth; | |
phase += phaseIncr; | |
while (phase > 512) { | |
phase -= 512; | |
} | |
for (j = 0; j < jmax; ++j, ++i) { | |
index = (readIndex + size + mod) & mask; | |
x = (bufferL[index] + bufferL[index + 1]) * 0.5; | |
bufferL[writeIndex] = cellL[i] - x * feedback; | |
cellL[i] = (cellL[i] * dry) + (x * wet); | |
x = (bufferR[index] + bufferR[index + 1]) * 0.5; | |
bufferR[writeIndex] = cellR[i] - x * feedback; | |
cellR[i] = (cellR[i] * dry) + (x * wet); | |
writeIndex = (writeIndex + 1) & mask; | |
readIndex = (readIndex + 1) & mask; | |
} | |
} | |
this.phase = phase; | |
this.writeIndex = writeIndex; | |
this.readIndex = readIndex; | |
}; | |
T.modules.Chorus = Chorus; | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var MaxPreDelayFrames = 1024; | |
var MaxPreDelayFramesMask = MaxPreDelayFrames - 1; | |
var DefaultPreDelayFrames = 256; | |
var kSpacingDb = 5; | |
function Compressor(samplerate, channels) { | |
this.samplerate = samplerate; | |
this.channels = channels; | |
this.lastPreDelayFrames = 0; | |
this.preDelayReadIndex = 0; | |
this.preDelayWriteIndex = DefaultPreDelayFrames; | |
this.ratio = -1; | |
this.slope = -1; | |
this.linearThreshold = -1; | |
this.dbThreshold = -1; | |
this.dbKnee = -1; | |
this.kneeThreshold = -1; | |
this.kneeThresholdDb = -1; | |
this.ykneeThresholdDb = -1; | |
this.K = -1; | |
this.attackTime = 0.003; | |
this.releaseTime = 0.25; | |
this.preDelayTime = 0.006; | |
this.dbPostGain = 0; | |
this.effectBlend = 1; | |
this.releaseZone1 = 0.09; | |
this.releaseZone2 = 0.16; | |
this.releaseZone3 = 0.42; | |
this.releaseZone4 = 0.98; | |
this.detectorAverage = 0; | |
this.compressorGain = 1; | |
this.meteringGain = 1; | |
this.delayBufferL = new T.fn.SignalArray(MaxPreDelayFrames); | |
if (channels === 2) { | |
this.delayBufferR = new T.fn.SignalArray(MaxPreDelayFrames); | |
} else { | |
this.delayBufferR = this.delayBufferL; | |
} | |
this.preDelayTime = 6; | |
this.preDelayReadIndex = 0; | |
this.preDelayWriteIndex = DefaultPreDelayFrames; | |
this.maxAttackCompressionDiffDb = -1; | |
this.meteringReleaseK = 1 - Math.exp(-1 / (this.samplerate * 0.325)); | |
this.setAttackTime(this.attackTime); | |
this.setReleaseTime(this.releaseTime); | |
this.setPreDelayTime(this.preDelayTime); | |
this.setParams(-24, 30, 12); | |
} | |
var $ = Compressor.prototype; | |
$.clone = function() { | |
var new_instance = new Compressor(this.samplerate, this.channels); | |
new_instance.setAttackTime(this.attackTime); | |
new_instance.setReleaseTime(this.releaseTime); | |
new_instance.setPreDelayTime(this.preDelayTime); | |
new_instance.setParams(this.dbThreshold, this.dbKnee, this.ratio); | |
return new_instance; | |
}; | |
$.setAttackTime = function(value) { | |
this.attackTime = Math.max(0.001, value); | |
this._attackFrames = this.attackTime * this.samplerate; | |
}; | |
$.setReleaseTime = function(value) { | |
this.releaseTime = Math.max(0.001, value); | |
var releaseFrames = this.releaseTime * this.samplerate; | |
var satReleaseTime = 0.0025; | |
this._satReleaseFrames = satReleaseTime * this.samplerate; | |
var y1 = releaseFrames * this.releaseZone1; | |
var y2 = releaseFrames * this.releaseZone2; | |
var y3 = releaseFrames * this.releaseZone3; | |
var y4 = releaseFrames * this.releaseZone4; | |
this._kA = 0.9999999999999998*y1 + 1.8432219684323923e-16*y2 - 1.9373394351676423e-16*y3 + 8.824516011816245e-18*y4; | |
this._kB = -1.5788320352845888*y1 + 2.3305837032074286*y2 - 0.9141194204840429*y3 + 0.1623677525612032*y4; | |
this._kC = 0.5334142869106424*y1 - 1.272736789213631*y2 + 0.9258856042207512*y3 - 0.18656310191776226*y4; | |
this._kD = 0.08783463138207234*y1 - 0.1694162967925622*y2 + 0.08588057951595272*y3 - 0.00429891410546283*y4; | |
this._kE = -0.042416883008123074*y1 + 0.1115693827987602*y2 - 0.09764676325265872*y3 + 0.028494263462021576*y4; | |
}; | |
$.setPreDelayTime = function(preDelayTime) { | |
this.preDelayTime = preDelayTime; | |
var preDelayFrames = preDelayTime * this.samplerate; | |
if (preDelayFrames > MaxPreDelayFrames - 1) { | |
preDelayFrames = MaxPreDelayFrames - 1; | |
} | |
if (this.lastPreDelayFrames !== preDelayFrames) { | |
this.lastPreDelayFrames = preDelayFrames; | |
for (var i = 0, imax = this.delayBufferL.length; i < imax; ++i) { | |
this.delayBufferL[i] = this.delayBufferR[i] = 0; | |
} | |
this.preDelayReadIndex = 0; | |
this.preDelayWriteIndex = preDelayFrames; | |
} | |
}; | |
$.setParams = function(dbThreshold, dbKnee, ratio) { | |
this._k = this.updateStaticCurveParameters(dbThreshold, dbKnee, ratio); | |
var fullRangeGain = this.saturate(1, this._k); | |
var fullRangeMakeupGain = 1 / fullRangeGain; | |
fullRangeMakeupGain = Math.pow(fullRangeMakeupGain, 0.6); | |
this._masterLinearGain = Math.pow(10, 0.05 * this.dbPostGain) * fullRangeMakeupGain; | |
}; | |
$.kneeCurve = function(x, k) { | |
if (x < this.linearThreshold) { | |
return x; | |
} | |
return this.linearThreshold + (1 - Math.exp(-k * (x - this.linearThreshold))) / k; | |
}; | |
$.saturate = function(x, k) { | |
var y; | |
if (x < this.kneeThreshold) { | |
y = this.kneeCurve(x, k); | |
} else { | |
var xDb = (x) ? 20 * Math.log(x) * Math.LOG10E : -1000; | |
var yDb = this.ykneeThresholdDb + this.slope * (xDb - this.kneeThresholdDb); | |
y = Math.pow(10, 0.05 * yDb); | |
} | |
return y; | |
}; | |
$.slopeAt = function(x, k) { | |
if (x < this.linearThreshold) { | |
return 1; | |
} | |
var x2 = x * 1.001; | |
var xDb = (x ) ? 20 * Math.log(x ) * Math.LOG10E : -1000; | |
var x2Db = (x2) ? 20 * Math.log(x2) * Math.LOG10E : -1000; | |
var y = this.kneeCurve(x , k); | |
var y2 = this.kneeCurve(x2, k); | |
var yDb = (y ) ? 20 * Math.log(y ) * Math.LOG10E : -1000; | |
var y2Db = (y2) ? 20 * Math.log(y2) * Math.LOG10E : -1000; | |
return (y2Db - yDb) / (x2Db - xDb); | |
}; | |
$.kAtSlope = function(desiredSlope) { | |
var xDb = this.dbThreshold + this.dbKnee; | |
var x = Math.pow(10, 0.05 * xDb); | |
var minK = 0.1; | |
var maxK = 10000; | |
var k = 5; | |
for (var i = 0; i < 15; ++i) { | |
var slope = this.slopeAt(x, k); | |
if (slope < desiredSlope) { | |
maxK = k; | |
} else { | |
minK = k; | |
} | |
k = Math.sqrt(minK * maxK); | |
} | |
return k; | |
}; | |
$.updateStaticCurveParameters = function(dbThreshold, dbKnee, ratio) { | |
this.dbThreshold = dbThreshold; | |
this.linearThreshold = Math.pow(10, 0.05 * dbThreshold); | |
this.dbKnee = dbKnee; | |
this.ratio = ratio; | |
this.slope = 1 / this.ratio; | |
this.kneeThresholdDb = dbThreshold + dbKnee; | |
this.kneeThreshold = Math.pow(10, 0.05 * this.kneeThresholdDb); | |
var k = this.kAtSlope(1 / this.ratio); | |
var y = this.kneeCurve(this.kneeThreshold, k); | |
this.ykneeThresholdDb = (y) ? 20 * Math.log(y) * Math.LOG10E : -1000; | |
this._k = k; | |
return this._k; | |
}; | |
$.process = function(cellL, cellR) { | |
var dryMix = 1 - this.effectBlend; | |
var wetMix = this.effectBlend; | |
var k = this._k; | |
var masterLinearGain = this._masterLinearGain; | |
var satReleaseFrames = this._satReleaseFrames; | |
var kA = this._kA; | |
var kB = this._kB; | |
var kC = this._kC; | |
var kD = this._kD; | |
var kE = this._kE; | |
var nDivisionFrames = 64; | |
var nDivisions = cellL.length / nDivisionFrames; | |
var frameIndex = 0; | |
var desiredGain = this.detectorAverage; | |
var compressorGain = this.compressorGain; | |
var maxAttackCompressionDiffDb = this.maxAttackCompressionDiffDb; | |
var i_attackFrames = 1 / this._attackFrames; | |
var preDelayReadIndex = this.preDelayReadIndex; | |
var preDelayWriteIndex = this.preDelayWriteIndex; | |
var detectorAverage = this.detectorAverage; | |
var delayBufferL = this.delayBufferL; | |
var delayBufferR = this.delayBufferR; | |
var meteringGain = this.meteringGain; | |
var meteringReleaseK = this.meteringReleaseK; | |
for (var i = 0; i < nDivisions; ++i) { | |
var scaledDesiredGain = Math.asin(desiredGain) / (0.5 * Math.PI); | |
var envelopeRate; | |
var isReleasing = scaledDesiredGain > compressorGain; | |
var x = compressorGain / scaledDesiredGain; | |
var compressionDiffDb = (x) ? 20 * Math.log(x) * Math.LOG10E : -1000; | |
if (compressionDiffDb === Infinity || isNaN(compressionDiffDb)) { | |
compressionDiffDb = -1; | |
} | |
if (isReleasing) { | |
maxAttackCompressionDiffDb = -1; | |
x = compressionDiffDb; | |
if (x < -12) { | |
x = 0; | |
} else if (x > 0) { | |
x = 3; | |
} else { | |
x = 0.25 * (x + 12); | |
} | |
var x2 = x * x; | |
var x3 = x2 * x; | |
var x4 = x2 * x2; | |
var _releaseFrames = kA + kB * x + kC * x2 + kD * x3 + kE * x4; | |
var _dbPerFrame = kSpacingDb / _releaseFrames; | |
envelopeRate = Math.pow(10, 0.05 * _dbPerFrame); | |
} else { | |
if (maxAttackCompressionDiffDb === -1 || maxAttackCompressionDiffDb < compressionDiffDb) { | |
maxAttackCompressionDiffDb = compressionDiffDb; | |
} | |
var effAttenDiffDb = Math.max(0.5, maxAttackCompressionDiffDb); | |
x = 0.25 / effAttenDiffDb; | |
envelopeRate = 1 - Math.pow(x, i_attackFrames); | |
} | |
var loopFrames = nDivisionFrames; | |
while (loopFrames--) { | |
var compressorInput = 0; | |
var absUndelayedSource = (cellL[frameIndex] + cellR[frameIndex]) * 0.5; | |
delayBufferL[preDelayWriteIndex] = cellL[frameIndex]; | |
delayBufferR[preDelayWriteIndex] = cellR[frameIndex]; | |
if (absUndelayedSource < 0) { | |
absUndelayedSource *= -1; | |
} | |
if (compressorInput < absUndelayedSource) { | |
compressorInput = absUndelayedSource; | |
} | |
var absInput = compressorInput; | |
if (absInput < 0) { | |
absInput *= -1; | |
} | |
var shapedInput = this.saturate(absInput, k); | |
var attenuation = absInput <= 0.0001 ? 1 : shapedInput / absInput; | |
var attenuationDb = (attenuation) ? -20 * Math.log(attenuation) * Math.LOG10E : 1000; | |
if (attenuationDb < 2) { | |
attenuationDb = 2; | |
} | |
var dbPerFrame = attenuationDb / satReleaseFrames; | |
var satReleaseRate = Math.pow(10, 0.05 * dbPerFrame) - 1; | |
var isRelease = (attenuation > detectorAverage); | |
var rate = isRelease ? satReleaseRate : 1; | |
detectorAverage += (attenuation - detectorAverage) * rate; | |
if (detectorAverage > 1) { | |
detectorAverage = 1; | |
} | |
if (envelopeRate < 1) { | |
compressorGain += (scaledDesiredGain - compressorGain) * envelopeRate; | |
} else { | |
compressorGain *= envelopeRate; | |
if (compressorGain > 1) { | |
compressorGain = 1; | |
} | |
} | |
var postWarpCompressorGain = Math.sin(0.5 * Math.PI * compressorGain); | |
var totalGain = dryMix + wetMix * masterLinearGain * postWarpCompressorGain; | |
var dbRealGain = 20 * Math.log(postWarpCompressorGain) * Math.LOG10E; | |
if (dbRealGain < meteringGain) { | |
meteringGain = dbRealGain; | |
} else { | |
meteringGain += (dbRealGain - meteringGain) * meteringReleaseK; | |
} | |
cellL[frameIndex] = delayBufferL[preDelayReadIndex] * totalGain; | |
cellR[frameIndex] = delayBufferR[preDelayReadIndex] * totalGain; | |
frameIndex++; | |
preDelayReadIndex = (preDelayReadIndex + 1) & MaxPreDelayFramesMask; | |
preDelayWriteIndex = (preDelayWriteIndex + 1) & MaxPreDelayFramesMask; | |
} | |
if (detectorAverage < 1e-6) { | |
detectorAverage = 1e-6; | |
} | |
if (compressorGain < 1e-6) { | |
compressorGain = 1e-6; | |
} | |
} | |
this.preDelayReadIndex = preDelayReadIndex; | |
this.preDelayWriteIndex = preDelayWriteIndex; | |
this.detectorAverage = detectorAverage; | |
this.compressorGain = compressorGain; | |
this.maxAttackCompressionDiffDb = maxAttackCompressionDiffDb; | |
this.meteringGain = meteringGain; | |
}; | |
$.reset = function() { | |
this.detectorAverage = 0; | |
this.compressorGain = 1; | |
this.meteringGain = 1; | |
for (var i = 0, imax = this.delayBufferL.length; i < imax; ++i) { | |
this.delayBufferL[i] = this.delayBufferR[i] = 0; | |
} | |
this.preDelayReadIndex = 0; | |
this.preDelayWriteIndex = DefaultPreDelayFrames; | |
this.maxAttackCompressionDiffDb = -1; | |
}; | |
T.modules.Compressor = Compressor; | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
function Decoder() {} | |
Decoder.prototype.decode = function(src, onloadedmetadata, onloadeddata) { | |
if (typeof src === "string") { | |
if (/\.wav$/.test(src)) { | |
return Decoder.wav_decode(src, onloadedmetadata, onloadeddata); | |
} else if (Decoder.ogg_decode && /\.ogg$/.test(src)) { | |
return Decoder.ogg_decode(src, onloadedmetadata, onloadeddata); | |
} else if (Decoder.mp3_decode && /\.mp3$/.test(src)) { | |
return Decoder.mp3_decode(src, onloadedmetadata, onloadeddata); | |
} | |
} else if (typeof src === "object") { | |
if (src.type === "wav") { | |
return Decoder.wav_decode(src.data, onloadedmetadata, onloadeddata); | |
} else if (Decoder.ogg_decode && src.type === "ogg") { | |
return Decoder.ogg_decode(src.data, onloadedmetadata, onloadeddata); | |
} else if (Decoder.mp3_decode && src.type === "mp3") { | |
return Decoder.mp3_decode(src.data, onloadedmetadata, onloadeddata); | |
} | |
} | |
if (Decoder.webkit_decode) { | |
if (typeof src === "object") { | |
return Decoder.webkit_decode(src.data||src, onloadedmetadata, onloadeddata); | |
} else { | |
return Decoder.webkit_decode(src, onloadedmetadata, onloadeddata); | |
} | |
} else if (Decoder.moz_decode) { | |
return Decoder.moz_decode(src, onloadedmetadata, onloadeddata); | |
} | |
onloadedmetadata(false); | |
}; | |
T.modules.Decoder = Decoder; | |
if (T.envtype === "browser") { | |
Decoder.getBinaryWithPath = function(path, callback) { | |
T.fn.fix_iOS6_1_problem(true); | |
var xhr = new XMLHttpRequest(); | |
xhr.open("GET", path); | |
xhr.responseType = "arraybuffer"; | |
xhr.onreadystatechange = function() { | |
if (xhr.readyState === 4) { | |
if (xhr.response) { | |
callback(new Uint8Array(xhr.response)); | |
} else if (xhr.responseBody !== undefined) { | |
/*global VBArray:true */ | |
callback(new Uint8Array(VBArray(xhr.responseBody).toArray())); | |
/*global VBArray:false */ | |
} | |
T.fn.fix_iOS6_1_problem(false); | |
} | |
}; | |
xhr.send(); | |
}; | |
} else { | |
Decoder.getBinaryWithPath = function(path, callback) { | |
callback("no support"); | |
}; | |
} | |
var _24bit_to_32bit = function(uint8) { | |
var b0, b1, b2, bb, x; | |
var int32 = new Int32Array(uint8.length / 3); | |
for (var i = 0, imax = uint8.length, j = 0; i < imax; ) { | |
b0 = uint8[i++] ,b1 = uint8[i++], b2 = uint8[i++]; | |
bb = b0 + (b1 << 8) + (b2 << 16); | |
x = (bb & 0x800000) ? bb - 16777216 : bb; | |
int32[j++] = x; | |
} | |
return int32; | |
}; | |
Decoder.wav_decode = (function() { | |
var _decode = function(data, onloadedmetadata, onloadeddata) { | |
if (String.fromCharCode(data[0], data[1], data[2], data[3]) !== "RIFF") { | |
return onloadedmetadata(false); | |
} | |
var l1 = data[4] + (data[5]<<8) + (data[6]<<16) + (data[7]<<24); | |
if (l1 + 8 !== data.length) { | |
return onloadedmetadata(false); | |
} | |
if (String.fromCharCode(data[8], data[9], data[10], data[11]) !== "WAVE") { | |
return onloadedmetadata(false); | |
} | |
if (String.fromCharCode(data[12], data[13], data[14], data[15]) !== "fmt ") { | |
return onloadedmetadata(false); | |
} | |
var channels = data[22] + (data[23]<<8); | |
var samplerate = data[24] + (data[25]<<8) + (data[26]<<16) + (data[27]<<24); | |
var bitSize = data[34] + (data[35]<<8); | |
var i = 36; | |
while (i < data.length) { | |
if (String.fromCharCode(data[i], data[i+1], data[i+2], data[i+3]) === "data") { | |
break; | |
} | |
i += 1; | |
} | |
if (i >= data.length) { | |
return onloadedmetadata(false); | |
} | |
i += 4; | |
var l2 = data[i] + (data[i+1]<<8) + (data[i+2]<<16) + (data[i+3]<<24); | |
var duration = ((l2 / channels) >> 1) / samplerate; | |
i += 4; | |
if (l2 > data.length - i) { | |
return onloadedmetadata(false); | |
} | |
var mixdown, bufferL, bufferR; | |
mixdown = new Float32Array((duration * samplerate)|0); | |
if (channels === 2) { | |
bufferL = new Float32Array(mixdown.length); | |
bufferR = new Float32Array(mixdown.length); | |
} | |
onloadedmetadata({ | |
samplerate: samplerate, | |
channels : channels, | |
buffer : [mixdown, bufferL, bufferR], | |
duration : duration | |
}); | |
if (bitSize === 8) { | |
data = new Int8Array(data.buffer, i); | |
} else if (bitSize === 16) { | |
data = new Int16Array(data.buffer, i); | |
} else if (bitSize === 32) { | |
data = new Int32Array(data.buffer, i); | |
} else if (bitSize === 24) { | |
data = _24bit_to_32bit(new Uint8Array(data.buffer, i)); | |
} | |
var imax, j, k = 1 / ((1 << (bitSize-1)) - 1), x; | |
if (channels === 2) { | |
for (i = j = 0, imax = mixdown.length; i < imax; ++i) { | |
x = bufferL[i] = data[j++] * k; | |
x += bufferR[i] = data[j++] * k; | |
mixdown[i] = x * 0.5; | |
} | |
} else { | |
for (i = 0, imax = mixdown.length; i < imax; ++i) { | |
mixdown[i] = data[i] * k; | |
} | |
} | |
onloadeddata(); | |
}; | |
return function(src, onloadedmetadata, onloadeddata) { | |
if (typeof src === "string") { | |
Decoder.getBinaryWithPath(src, function(data) { | |
_decode(data, onloadedmetadata, onloadeddata); | |
}); | |
} else { | |
_decode(src, onloadedmetadata, onloadeddata); | |
} | |
}; | |
})(); | |
Decoder.webkit_decode = (function() { | |
if (typeof webkitAudioContext !== "undefined") { | |
var ctx = T.fn._audioContext; | |
var _decode = function(data, onloadedmetadata, onloadeddata) { | |
var samplerate, channels, bufferL, bufferR, duration; | |
if (typeof data === "string") { | |
return onloadeddata(false); | |
} | |
var buffer; | |
try { | |
buffer = ctx.createBuffer(data.buffer, false); | |
} catch (e) { | |
return onloadedmetadata(false); | |
} | |
samplerate = ctx.sampleRate; | |
channels = buffer.numberOfChannels; | |
if (channels === 2) { | |
bufferL = buffer.getChannelData(0); | |
bufferR = buffer.getChannelData(1); | |
} else { | |
bufferL = bufferR = buffer.getChannelData(0); | |
} | |
duration = bufferL.length / samplerate; | |
var mixdown = new Float32Array(bufferL); | |
for (var i = 0, imax = mixdown.length; i < imax; ++i) { | |
mixdown[i] = (mixdown[i] + bufferR[i]) * 0.5; | |
} | |
onloadedmetadata({ | |
samplerate: samplerate, | |
channels : channels, | |
buffer : [mixdown, bufferL, bufferR], | |
duration : duration | |
}); | |
onloadeddata(); | |
}; | |
return function(src, onloadedmetadata, onloadeddata) { | |
/*global File:true */ | |
if (src instanceof File) { | |
var reader = new FileReader(); | |
reader.onload = function(e) { | |
_decode(new Uint8Array(e.target.result), | |
onloadedmetadata, onloadeddata); | |
}; | |
reader.readAsArrayBuffer(src); | |
} else if (typeof src === "string") { | |
Decoder.getBinaryWithPath(src, function(data) { | |
_decode(data, onloadedmetadata, onloadeddata); | |
}); | |
} else { | |
_decode(src, onloadedmetadata, onloadeddata); | |
} | |
/*global File:false */ | |
}; | |
} | |
})(); | |
Decoder.moz_decode = (function() { | |
if (typeof Audio === "function" && typeof new Audio().mozSetup === "function") { | |
return function(src, onloadedmetadata, onloadeddata) { | |
var samplerate, channels, mixdown, bufferL, bufferR, duration; | |
var writeIndex = 0; | |
var audio = new Audio(src); | |
audio.volume = 0.0; | |
audio.addEventListener("loadedmetadata", function() { | |
samplerate = audio.mozSampleRate; | |
channels = audio.mozChannels; | |
duration = audio.duration; | |
mixdown = new Float32Array((audio.duration * samplerate)|0); | |
if (channels === 2) { | |
bufferL = new Float32Array((audio.duration * samplerate)|0); | |
bufferR = new Float32Array((audio.duration * samplerate)|0); | |
} | |
if (channels === 2) { | |
audio.addEventListener("MozAudioAvailable", function(e) { | |
var x, samples = e.frameBuffer; | |
for (var i = 0, imax = samples.length; i < imax; i += 2) { | |
x = bufferL[writeIndex] = samples[i ]; | |
x += bufferR[writeIndex] = samples[i+1]; | |
mixdown[writeIndex] = x * 0.5; | |
writeIndex += 1; | |
} | |
}, false); | |
} else { | |
audio.addEventListener("MozAudioAvailable", function(e) { | |
var samples = e.frameBuffer; | |
for (var i = 0, imax = samples.length; i < imax; ++i) { | |
mixdown[i] = samples[i]; | |
writeIndex += 1; | |
} | |
}, false); | |
} | |
audio.play(); | |
setTimeout(function() { | |
onloadedmetadata({ | |
samplerate: samplerate, | |
channels : channels, | |
buffer : [mixdown, bufferL, bufferR], | |
duration : duration | |
}); | |
}, 1000); | |
}, false); | |
audio.addEventListener("ended", function() { | |
onloadeddata(); | |
}, false); | |
audio.load(); | |
}; | |
} | |
})(); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
function Envelope(samplerate) { | |
this.samplerate = samplerate || 44100; | |
this.value = ZERO; | |
this.status = StatusWait; | |
this.curve = "linear"; | |
this.step = 1; | |
this.releaseNode = null; | |
this.loopNode = null; | |
this.emit = null; | |
this._envValue = new EnvelopeValue(samplerate); | |
this._table = []; | |
this._initValue = ZERO; | |
this._curveValue = 0; | |
this._defaultCurveType = CurveTypeLin; | |
this._index = 0; | |
this._counter = 0; | |
} | |
var ZERO = Envelope.ZERO = 1e-6; | |
var CurveTypeSet = Envelope.CurveTypeSet = 0; | |
var CurveTypeLin = Envelope.CurveTypeLin = 1; | |
var CurveTypeExp = Envelope.CurveTypeExp = 2; | |
var CurveTypeSin = Envelope.CurveTypeSin = 3; | |
var CurveTypeWel = Envelope.CurveTypeWel = 4; | |
var CurveTypeCurve = Envelope.CurveTypeCurve = 5; | |
var CurveTypeSqr = Envelope.CurveTypeSqr = 6; | |
var CurveTypeCub = Envelope.CurveTypeCub = 7; | |
var StatusWait = Envelope.StatusWait = 0; | |
var StatusGate = Envelope.StatusGate = 1; | |
var StatusSustain = Envelope.StatusSustain = 2; | |
var StatusRelease = Envelope.StatusRelease = 3; | |
var StatusEnd = Envelope.StatusEnd = 4; | |
var CurveTypeDict = { | |
set:CurveTypeSet, | |
lin:CurveTypeLin, linear :CurveTypeLin, | |
exp:CurveTypeExp, exponential:CurveTypeExp, | |
sin:CurveTypeSin, sine :CurveTypeSin, | |
wel:CurveTypeWel, welch :CurveTypeWel, | |
sqr:CurveTypeSqr, squared :CurveTypeSqr, | |
cub:CurveTypeCub, cubed :CurveTypeCub | |
}; | |
Envelope.CurveTypeDict = CurveTypeDict; | |
var $ = Envelope.prototype; | |
$.clone = function() { | |
var new_instance = new Envelope(this.samplerate); | |
new_instance._table = this._table; | |
new_instance._initValue = this._initValue; | |
new_instance.setCurve(this.curve); | |
if (this.releaseNode !== null) { | |
new_instance.setReleaseNode(this.releaseNode + 1); | |
} | |
if (this.loopNode !== null) { | |
new_instance.setLoopNode(this.loopNode + 1); | |
} | |
new_instance.setStep(this.step); | |
new_instance.reset(); | |
return new_instance; | |
}; | |
$.setTable = function(value) { | |
this._initValue = value[0]; | |
this._table = value.slice(1); | |
this.value = this._envValue.value = this._initValue; | |
this._index = 0; | |
this._counter = 0; | |
this.status = StatusWait; | |
}; | |
$.setCurve = function(value) { | |
if (typeof value === "number") { | |
this._defaultCurveType = CurveTypeCurve; | |
this._curveValue = value; | |
this.curve = value; | |
} else { | |
this._defaultCurveType = CurveTypeDict[value] || null; | |
this.curve = value; | |
} | |
}; | |
$.setReleaseNode = function(value) { | |
if (typeof value === "number" && value > 0) { | |
this.releaseNode = value - 1; | |
} | |
}; | |
$.setLoopNode = function(value) { | |
if (typeof value === "number" && value > 0) { | |
this.loopNode = value - 1; | |
} | |
}; | |
$.setStep = function(step) { | |
this.step = this._envValue.step = step; | |
}; | |
$.reset = function() { | |
this.value = this._envValue.value = this._initValue; | |
this._index = 0; | |
this._counter = 0; | |
this.status = StatusWait; | |
}; | |
$.release = function() { | |
if (this.releaseNode !== null) { | |
this._counter = 0; | |
this.status = StatusRelease; | |
} | |
}; | |
$.getInfo = function(sustainTime) { | |
var table = this._table; | |
var i, imax; | |
var totalDuration = 0; | |
var loopBeginTime = Infinity; | |
var releaseBeginTime = Infinity; | |
var isEndlessLoop = false; | |
for (i = 0, imax = table.length; i < imax; ++i) { | |
if (this.loopNode === i) { | |
loopBeginTime = totalDuration; | |
} | |
if (this.releaseNode === i) { | |
if (totalDuration < sustainTime) { | |
totalDuration += sustainTime; | |
} else { | |
totalDuration = sustainTime; | |
} | |
releaseBeginTime = totalDuration; | |
} | |
var items = table[i]; | |
if (Array.isArray(items)) { | |
totalDuration += items[1]; | |
} | |
} | |
if (loopBeginTime !== Infinity && releaseBeginTime === Infinity) { | |
totalDuration += sustainTime; | |
isEndlessLoop = true; | |
} | |
return { | |
totalDuration : totalDuration, | |
loopBeginTime : loopBeginTime, | |
releaseBeginTime: releaseBeginTime, | |
isEndlessLoop : isEndlessLoop | |
}; | |
}; | |
$.calcStatus = function() { | |
var status = this.status; | |
var table = this._table; | |
var index = this._index; | |
var counter = this._counter; | |
var curveValue = this._curveValue; | |
var defaultCurveType = this._defaultCurveType; | |
var loopNode = this.loopNode; | |
var releaseNode = this.releaseNode; | |
var envValue = this._envValue; | |
var items, endValue, time, curveType, emit = null; | |
switch (status) { | |
case StatusWait: | |
case StatusEnd: | |
break; | |
case StatusGate: | |
case StatusRelease: | |
while (counter <= 0) { | |
if (index >= table.length) { | |
if (status === StatusGate && loopNode !== null) { | |
index = loopNode; | |
continue; | |
} | |
status = StatusEnd; | |
counter = Infinity; | |
curveType = CurveTypeSet; | |
emit = "ended"; | |
continue; | |
} else if (status === StatusGate && index === releaseNode) { | |
if (loopNode !== null && loopNode < releaseNode) { | |
index = loopNode; | |
continue; | |
} | |
status = StatusSustain; | |
counter = Infinity; | |
curveType = CurveTypeSet; | |
emit = "sustained"; | |
continue; | |
} | |
items = table[index++]; | |
endValue = items[0]; | |
if (items[2] === null) { | |
curveType = defaultCurveType; | |
} else { | |
curveType = items[2]; | |
} | |
if (curveType === CurveTypeCurve) { | |
curveValue = items[3]; | |
if (Math.abs(curveValue) < 0.001) { | |
curveType = CurveTypeLin; | |
} | |
} | |
time = items[1]; | |
counter = envValue.setNext(endValue, time, curveType, curveValue); | |
} | |
break; | |
} | |
this.status = status; | |
this.emit = emit; | |
this._index = index; | |
this._counter = counter; | |
return status; | |
}; | |
$.next = function() { | |
if (this.calcStatus() & 1) { | |
this.value = this._envValue.next() || ZERO; | |
} | |
this._counter -= 1; | |
return this.value; | |
}; | |
$.process = function(cell) { | |
var envValue = this._envValue; | |
var i, imax = cell.length; | |
if (this.calcStatus() & 1) { | |
for (i = 0; i < imax; ++i) { | |
cell[i] = envValue.next() || ZERO; | |
} | |
} else { | |
var value = this.value || ZERO; | |
for (i = 0; i < imax; ++i) { | |
cell[i] = value; | |
} | |
} | |
this.value = cell[imax-1]; | |
this._counter -= cell.length; | |
}; | |
function EnvelopeValue(samplerate) { | |
this.samplerate = samplerate; | |
this.value = ZERO; | |
this.step = 1; | |
this._curveType = CurveTypeLin; | |
this._curveValue = 0; | |
this._grow = 0; | |
this._a2 = 0; | |
this._b1 = 0; | |
this._y1 = 0; | |
this._y2 = 0; | |
} | |
EnvelopeValue.prototype.setNext = function(endValue, time, curveType, curveValue) { | |
var n = this.step; | |
var value = this.value; | |
var grow, w, a1, a2, b1, y1, y2; | |
var counter = ((time * 0.001 * this.samplerate) / n)|0; | |
if (counter < 1) { | |
counter = 1; | |
curveType = CurveTypeSet; | |
} | |
switch (curveType) { | |
case CurveTypeSet: | |
this.value = endValue; | |
break; | |
case CurveTypeLin: | |
grow = (endValue - value) / counter; | |
break; | |
case CurveTypeExp: | |
if (value !== 0) { | |
grow = Math.pow( | |
endValue / value, 1 / counter | |
); | |
} else { | |
grow = 0; | |
} | |
break; | |
case CurveTypeSin: | |
w = Math.PI / counter; | |
a2 = (endValue + value) * 0.5; | |
b1 = 2 * Math.cos(w); | |
y1 = (endValue - value) * 0.5; | |
y2 = y1 * Math.sin(Math.PI * 0.5 - w); | |
value = a2 - y1; | |
break; | |
case CurveTypeWel: | |
w = (Math.PI * 0.5) / counter; | |
b1 = 2 * Math.cos(w); | |
if (endValue >= value) { | |
a2 = value; | |
y1 = 0; | |
y2 = -Math.sin(w) * (endValue - value); | |
} else { | |
a2 = endValue; | |
y1 = value - endValue; | |
y2 = Math.cos(w) * (value - endValue); | |
} | |
value = a2 + y1; | |
break; | |
case CurveTypeCurve: | |
a1 = (endValue - value) / (1.0 - Math.exp(curveValue)); | |
a2 = value + a1; | |
b1 = a1; | |
grow = Math.exp(curveValue / counter); | |
break; | |
case CurveTypeSqr: | |
y1 = Math.sqrt(value); | |
y2 = Math.sqrt(endValue); | |
grow = (y2 - y1) / counter; | |
break; | |
case CurveTypeCub: | |
y1 = Math.pow(value , 0.33333333); | |
y2 = Math.pow(endValue, 0.33333333); | |
grow = (y2 - y1) / counter; | |
break; | |
} | |
this.next = NextFunctions[curveType]; | |
this._grow = grow; | |
this._a2 = a2; | |
this._b1 = b1; | |
this._y1 = y1; | |
this._y2 = y2; | |
return counter; | |
}; | |
var NextFunctions = []; | |
NextFunctions[CurveTypeSet] = function() { | |
return this.value; | |
}; | |
NextFunctions[CurveTypeLin] = function() { | |
this.value += this._grow; | |
return this.value; | |
}; | |
NextFunctions[CurveTypeExp] = function() { | |
this.value *= this._grow; | |
return this.value; | |
}; | |
NextFunctions[CurveTypeSin] = function() { | |
var y0 = this._b1 * this._y1 - this._y2; | |
this.value = this._a2 - y0; | |
this._y2 = this._y1; | |
this._y1 = y0; | |
return this.value; | |
}; | |
NextFunctions[CurveTypeWel] = function() { | |
var y0 = this._b1 * this._y1 - this._y2; | |
this.value = this._a2 + y0; | |
this._y2 = this._y1; | |
this._y1 = y0; | |
return this.value; | |
}; | |
NextFunctions[CurveTypeCurve] = function() { | |
this._b1 *= this._grow; | |
this.value = this._a2 - this._b1; | |
return this.value; | |
}; | |
NextFunctions[CurveTypeSqr] = function() { | |
this._y1 += this._grow; | |
this.value = this._y1 * this._y1; | |
return this.value; | |
}; | |
NextFunctions[CurveTypeCub] = function() { | |
this._y1 += this._grow; | |
this.value = this._y1 * this._y1 * this._y1; | |
return this.value; | |
}; | |
EnvelopeValue.prototype.next = NextFunctions[CurveTypeSet]; | |
T.modules.Envelope = Envelope; | |
T.modules.EnvelopeValue = EnvelopeValue; | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
function FFT(n) { | |
n = (typeof n === "number") ? n : 512; | |
n = 1 << Math.ceil(Math.log(n) * Math.LOG2E); | |
this.length = n; | |
this.buffer = new T.fn.SignalArray(n); | |
this.real = new T.fn.SignalArray(n); | |
this.imag = new T.fn.SignalArray(n); | |
this._real = new T.fn.SignalArray(n); | |
this._imag = new T.fn.SignalArray(n); | |
this.mag = new T.fn.SignalArray(n>>1); | |
this.minDecibels = -30; | |
this.maxDecibels = -100; | |
var params = FFTParams.get(n); | |
this._bitrev = params.bitrev; | |
this._sintable = params.sintable; | |
this._costable = params.costable; | |
} | |
var $ = FFT.prototype; | |
$.setWindow = function(key) { | |
if (typeof key === "string") { | |
var m = /([A-Za-z]+)(?:\(([01]\.?\d*)\))?/.exec(key); | |
if (m !== null) { | |
var name = m[1].toLowerCase(), a = m[2] !== undefined ? +m[2] : 0.25; | |
var f = WindowFunctions[name]; | |
if (f) { | |
if (!this._window) { | |
this._window = new T.fn.SignalArray(this.length); | |
} | |
var w = this._window, n = 0, N = this.length; | |
a = (a < 0) ? 0 : (a > 1) ? 1 : a; | |
for (; n < N; ++n) { | |
w[n] = f(n, N, a); | |
} | |
this.windowName = key; | |
} | |
} | |
} | |
}; | |
$.forward = function(_buffer) { | |
var buffer = this.buffer; | |
var real = this.real; | |
var imag = this.imag; | |
var window = this._window; | |
var bitrev = this._bitrev; | |
var sintable = this._sintable; | |
var costable = this._costable; | |
var n = buffer.length; | |
var i, j, k, k2, h, d, c, s, ik, dx, dy; | |
if (window) { | |
for (i = 0; i < n; ++i) { | |
buffer[i] = _buffer[i] * window[i]; | |
} | |
} else { | |
buffer.set(_buffer); | |
} | |
for (i = 0; i < n; ++i) { | |
real[i] = buffer[bitrev[i]]; | |
imag[i] = 0.0; | |
} | |
for (k = 1; k < n; k = k2) { | |
h = 0; k2 = k + k; d = n / k2; | |
for (j = 0; j < k; j++) { | |
c = costable[h]; | |
s = sintable[h]; | |
for (i = j; i < n; i += k2) { | |
ik = i + k; | |
dx = s * imag[ik] + c * real[ik]; | |
dy = c * imag[ik] - s * real[ik]; | |
real[ik] = real[i] - dx; real[i] += dx; | |
imag[ik] = imag[i] - dy; imag[i] += dy; | |
} | |
h += d; | |
} | |
} | |
var mag = this.mag; | |
var rval, ival; | |
for (i = 0; i < n; ++i) { | |
rval = real[i]; | |
ival = imag[i]; | |
mag[i] = Math.sqrt(rval * rval + ival * ival); | |
} | |
return {real:real, imag:imag}; | |
}; | |
$.inverse = function(_real, _imag) { | |
var buffer = this.buffer; | |
var real = this._real; | |
var imag = this._imag; | |
var bitrev = this._bitrev; | |
var sintable = this._sintable; | |
var costable = this._costable; | |
var n = buffer.length; | |
var i, j, k, k2, h, d, c, s, ik, dx, dy; | |
for (i = 0; i < n; ++i) { | |
j = bitrev[i]; | |
real[i] = +_real[j]; | |
imag[i] = -_imag[j]; | |
} | |
for (k = 1; k < n; k = k2) { | |
h = 0; k2 = k + k; d = n / k2; | |
for (j = 0; j < k; j++) { | |
c = costable[h]; | |
s = sintable[h]; | |
for (i = j; i < n; i += k2) { | |
ik = i + k; | |
dx = s * imag[ik] + c * real[ik]; | |
dy = c * imag[ik] - s * real[ik]; | |
real[ik] = real[i] - dx; real[i] += dx; | |
imag[ik] = imag[i] - dy; imag[i] += dy; | |
} | |
h += d; | |
} | |
} | |
for (i = 0; i < n; ++i) { | |
buffer[i] = real[i] / n; | |
} | |
return buffer; | |
}; | |
$.getFrequencyData = function(array) { | |
var minDecibels = this.minDecibels; | |
var i, imax = Math.min(this.mag.length, array.length); | |
if (imax) { | |
var x, mag = this.mag; | |
var peak = 0; | |
for (i = 0; i < imax; ++i) { | |
x = mag[i]; | |
array[i] = !x ? minDecibels : 20 * Math.log(x) * Math.LOG10E; | |
if (peak < array[i]) { | |
peak = array[i]; | |
} | |
} | |
} | |
return array; | |
}; | |
var FFTParams = { | |
get: function(n) { | |
return FFTParams[n] || (function() { | |
var bitrev = (function() { | |
var x, i, j, k, n2; | |
x = new Int16Array(n); | |
n2 = n >> 1; | |
i = j = 0; | |
for (;;) { | |
x[i] = j; | |
if (++i >= n) { | |
break; | |
} | |
k = n2; | |
while (k <= j) { | |
j -= k; | |
k >>= 1; | |
} | |
j += k; | |
} | |
return x; | |
}()); | |
var i, imax, k = Math.floor(Math.log(n) / Math.LN2); | |
var sintable = new T.fn.SignalArray((1<<k)-1); | |
var costable = new T.fn.SignalArray((1<<k)-1); | |
var PI2 = Math.PI * 2; | |
for (i = 0, imax = sintable.length; i < imax; ++i) { | |
sintable[i] = Math.sin(PI2 * (i / n)); | |
costable[i] = Math.cos(PI2 * (i / n)); | |
} | |
FFTParams[n] = { | |
bitrev: bitrev, sintable:sintable, costable:costable | |
}; | |
return FFTParams[n]; | |
}()); | |
} | |
}; | |
var WindowFunctions = (function() { | |
var PI = Math.PI; | |
var PI2 = Math.PI * 2; | |
var abs = Math.abs; | |
var pow = Math.pow; | |
var cos = Math.cos; | |
var sin = Math.sin; | |
var sinc = function(x) { return sin(PI*x) / (PI*x); }; | |
var E = Math.E; | |
return { | |
rectangular: function() { | |
return 1; | |
}, | |
hann: function(n, N) { | |
return 0.5 * (1 - cos((PI2*n) / (N-1))); | |
}, | |
hamming: function(n, N) { | |
return 0.54 - 0.46 * cos((PI2*n) / (N-1)); | |
}, | |
tukery: function(n, N, a) { | |
if ( n < (a * (N-1))/2 ) { | |
return 0.5 * ( 1 + cos(PI * (((2*n)/(a*(N-1))) - 1)) ); | |
} else if ( (N-1)*(1-(a/2)) < n ) { | |
return 0.5 * ( 1 + cos(PI * (((2*n)/(a*(N-1))) - (2/a) + 1)) ); | |
} else { | |
return 1; | |
} | |
}, | |
cosine: function(n, N) { | |
return sin((PI*n) / (N-1)); | |
}, | |
lanczos: function(n, N) { | |
return sinc(((2*n) / (N-1)) - 1); | |
}, | |
triangular: function(n, N) { | |
return (2/(N+1)) * (((N+1)/2) - abs(n - ((N-1)/2))); | |
}, | |
bartlett: function(n, N) { | |
return (2/(N-1)) * (((N-1)/2) - abs(n - ((N-1)/2))); | |
}, | |
gaussian: function(n, N, a) { | |
return pow(E, -0.5 * pow((n - (N-1) / 2) / (a * (N-1) / 2), 2)); | |
}, | |
bartlettHann: function(n, N) { | |
return 0.62 - 0.48 * abs((n / (N-1)) - 0.5) - 0.38 * cos((PI2*n) / (N-1)); | |
}, | |
blackman: function(n, N, a) { | |
var a0 = (1 - a) / 2, a1 = 0.5, a2 = a / 2; | |
return a0 - a1 * cos((PI2*n) / (N-1)) + a2 * cos((4*PI*n) / (N-1)); | |
} | |
}; | |
}()); | |
T.modules.FFT = FFT; | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
function Oscillator(samplerate) { | |
this.samplerate = samplerate || 44100; | |
this.wave = null; | |
this.step = 1; | |
this.frequency = 0; | |
this.value = 0; | |
this.phase = 0; | |
this.feedback = false; | |
this._x = 0; | |
this._lastouts = 0; | |
this._coeff = TABLE_SIZE / this.samplerate; | |
this._radtoinc = TABLE_SIZE / (Math.PI * 2); | |
} | |
var TABLE_SIZE = 1024; | |
var TABLE_MASK = TABLE_SIZE - 1; | |
var $ = Oscillator.prototype; | |
$.setWave = function(value) { | |
var i, dx, wave = this.wave; | |
if (!this.wave) { | |
this.wave = new Float32Array(TABLE_SIZE + 1); | |
} | |
if (typeof value === "function") { | |
for (i = 0; i < TABLE_SIZE; ++i) { | |
wave[i] = value(i / TABLE_SIZE); | |
} | |
} else if (T.fn.isSignalArray(value)) { | |
if (value.length === wave.length) { | |
wave.set(value); | |
} else { | |
dx = value.length / TABLE_SIZE; | |
for (i = 0; i < TABLE_SIZE; ++i) { | |
wave[i] = value[(i * dx)|0]; | |
} | |
} | |
} else if (typeof value === "string") { | |
if ((dx = getWavetable(value)) !== undefined) { | |
this.wave.set(dx); | |
} | |
} | |
this.wave[TABLE_SIZE] = this.wave[0]; | |
}; | |
$.clone = function() { | |
var new_instance = new Oscillator(this.samplerate); | |
new_instance.wave = this.wave; | |
new_instance.step = this.step; | |
new_instance.frequency = this.frequency; | |
new_instance.value = this.value; | |
new_instance.phase = this.phase; | |
new_instance.feedback = this.feedback; | |
return new_instance; | |
}; | |
$.reset = function() { | |
this._x = 0; | |
}; | |
$.next = function() { | |
var x = this._x; | |
var index = (x + this.phase * this._radtoinc)|0; | |
this.value = this.wave[index & TABLE_MASK]; | |
x += this.frequency * this._coeff * this.step; | |
if (x > TABLE_SIZE) { | |
x -= TABLE_SIZE; | |
} | |
this._x = x; | |
return this.value; | |
}; | |
$.process = function(cell) { | |
var wave = this.wave; | |
var radtoinc = this._radtoinc; | |
var phase, x = this._x; | |
var index, frac, x0, x1, dx = this.frequency * this._coeff; | |
var i, imax = this.step; | |
if (this.feedback) { | |
var lastouts = this._lastouts; | |
radtoinc *= this.phase; | |
for (i = 0; i < imax; ++i) { | |
phase = x + lastouts * radtoinc; | |
index = phase|0; | |
frac = phase - index; | |
index = index & TABLE_MASK; | |
x0 = wave[index ]; | |
x1 = wave[index+1]; | |
cell[i] = lastouts = x0 + frac * (x1 - x0); | |
x += dx; | |
} | |
this._lastouts = lastouts; | |
} else { | |
var phaseoffset = this.phase * radtoinc; | |
for (i = 0; i < imax; ++i) { | |
phase = x + phaseoffset; | |
index = phase|0; | |
frac = phase - index; | |
index = index & TABLE_MASK; | |
x0 = wave[index ]; | |
x1 = wave[index+1]; | |
cell[i] = x0 + frac * (x1 - x0); | |
x += dx; | |
} | |
} | |
if (x > TABLE_SIZE) { | |
x -= TABLE_SIZE; | |
} | |
this._x = x; | |
this.value = cell[cell.length - 1]; | |
}; | |
$.processWithFreqArray = function(cell, freqs) { | |
var wave = this.wave; | |
var radtoinc = this._radtoinc; | |
var phase, x = this._x; | |
var index, frac, x0, x1, dx = this._coeff; | |
var i, imax = this.step; | |
if (this.feedback) { | |
var lastouts = this._lastouts; | |
radtoinc *= this.phase; | |
for (i = 0; i < imax; ++i) { | |
phase = x + lastouts * radtoinc; | |
index = phase|0; | |
frac = phase - index; | |
index = index & TABLE_MASK; | |
x0 = wave[index ]; | |
x1 = wave[index+1]; | |
cell[i] = lastouts = x0 + frac * (x1 - x0); | |
x += freqs[i] * dx; | |
} | |
this._lastouts = lastouts; | |
} else { | |
var phaseoffset = this.phase * this._radtoinc; | |
for (i = 0; i < imax; ++i) { | |
phase = x + phaseoffset; | |
index = phase|0; | |
frac = phase - index; | |
index = index & TABLE_MASK; | |
x0 = wave[index ]; | |
x1 = wave[index+1]; | |
cell[i] = x0 + frac * (x1 - x0); | |
x += freqs[i] * dx; | |
} | |
} | |
if (x > TABLE_SIZE) { | |
x -= TABLE_SIZE; | |
} | |
this._x = x; | |
this.value = cell[cell.length - 1]; | |
}; | |
$.processWithPhaseArray = function(cell, phases) { | |
var wave = this.wave; | |
var radtoinc = this._radtoinc; | |
var phase, x = this._x; | |
var index, frac, x0, x1, dx = this.frequency * this._coeff; | |
var i, imax = this.step; | |
if (this.feedback) { | |
var lastouts = this._lastouts; | |
radtoinc *= this.phase; | |
for (i = 0; i < imax; ++i) { | |
phase = x + lastouts * radtoinc; | |
index = phase|0; | |
frac = phase - index; | |
index = index & TABLE_MASK; | |
x0 = wave[index ]; | |
x1 = wave[index+1]; | |
cell[i] = lastouts = x0 + frac * (x1 - x0); | |
x += dx; | |
} | |
this._lastouts = lastouts; | |
} else { | |
for (i = 0; i < imax; ++i) { | |
phase = x + phases[i] * radtoinc; | |
index = phase|0; | |
frac = phase - index; | |
index = index & TABLE_MASK; | |
x0 = wave[index ]; | |
x1 = wave[index+1]; | |
cell[i] = x0 + frac * (x1 - x0); | |
x += dx; | |
} | |
} | |
if (x > TABLE_SIZE) { | |
x -= TABLE_SIZE; | |
} | |
this._x = x; | |
this.value = cell[cell.length - 1]; | |
}; | |
$.processWithFreqAndPhaseArray = function(cell, freqs, phases) { | |
var wave = this.wave; | |
var radtoinc = this._radtoinc; | |
var phase, x = this._x; | |
var index, frac, x0, x1, dx = this._coeff; | |
var i, imax = this.step; | |
if (this.feedback) { | |
var lastouts = this._lastouts; | |
radtoinc *= this.phase; | |
for (i = 0; i < imax; ++i) { | |
phase = x + lastouts * radtoinc; | |
index = phase|0; | |
frac = phase - index; | |
index = index & TABLE_MASK; | |
x0 = wave[index ]; | |
x1 = wave[index+1]; | |
cell[i] = lastouts = x0 + frac * (x1 - x0); | |
x += freqs[i] * dx; | |
} | |
this._lastouts = lastouts; | |
} else { | |
for (i = 0; i < imax; ++i) { | |
phase = x + phases[i] * TABLE_SIZE; | |
index = phase|0; | |
frac = phase - index; | |
index = index & TABLE_MASK; | |
x0 = wave[index ]; | |
x1 = wave[index+1]; | |
cell[i] = x0 + frac * (x1 - x0); | |
x += freqs[i] * dx; | |
} | |
} | |
if (x > TABLE_SIZE) { | |
x -= TABLE_SIZE; | |
} | |
this._x = x; | |
this.value = cell[cell.length - 1]; | |
}; | |
function waveshape(sign, name, shape, width) { | |
var wave = Wavetables[name]; | |
var _wave; | |
var i, imax, j, jmax; | |
if (wave === undefined) { | |
return; | |
} | |
if (typeof wave === "function") { | |
wave = wave(); | |
} | |
switch (shape) { | |
case "@1": | |
for (i = 512; i < 1024; ++i) { | |
wave[i] = 0; | |
} | |
break; | |
case "@2": | |
for (i = 512; i < 1024; ++i) { | |
wave[i] = Math.abs(wave[i]); | |
} | |
break; | |
case "@3": | |
for (i = 256; i < 512; ++i) { | |
wave[i] = 0; | |
} | |
for (i = 512; i < 768; ++i) { | |
wave[i] = Math.abs(wave[i]); | |
} | |
for (i = 768; i < 1024; ++i) { | |
wave[i] = 0; | |
} | |
break; | |
case "@4": | |
_wave = new Float32Array(1024); | |
for (i = 0; i < 512; ++i) { | |
_wave[i] = wave[i<<1]; | |
} | |
wave = _wave; | |
break; | |
case "@5": | |
_wave = new Float32Array(1024); | |
for (i = 0; i < 512; ++i) { | |
_wave[i] = Math.abs(wave[i<<1]); | |
} | |
wave = _wave; | |
break; | |
} | |
// duty-cycle | |
if (width !== undefined && width !== 50) { | |
width *= 0.01; | |
width = (width < 0) ? 0 : (width > 1) ? 1 : width; | |
_wave = new Float32Array(1024); | |
imax = (1024 * width)|0; | |
for (i = 0; i < imax; ++i) { | |
_wave[i] = wave[(i / imax * 512)|0]; | |
} | |
jmax = (1024 - imax); | |
for (j = 0; i < 1024; ++i, ++j) { | |
_wave[i] = wave[(j / jmax * 512 + 512)|0]; | |
} | |
wave = _wave; | |
} | |
if (sign === "+") { | |
for (i = 0; i < 1024; ++i) { | |
wave[i] = wave[i] * 0.5 + 0.5; | |
} | |
} else if (sign === "-") { | |
for (i = 0; i < 1024; ++i) { | |
wave[i] *= -1; | |
} | |
} | |
return wave; | |
} | |
function wavb(src) { | |
var wave = new Float32Array(1024); | |
var n = src.length >> 1; | |
if ([2,4,8,16,32,64,128,256,512,1024].indexOf(n) !== -1) { | |
for (var i = 0, k = 0; i < n; ++i) { | |
var x = parseInt(src.substr(i * 2, 2), 16); | |
x = (x & 0x80) ? (x-256) / 128.0 : x / 127.0; | |
for (var j = 0, jmax = 1024 / n; j < jmax; ++j) { | |
wave[k++] = x; | |
} | |
} | |
} | |
return wave; | |
} | |
function wavc(src) { | |
var wave = new Float32Array(1024); | |
if (src.length === 8) { | |
var color = parseInt(src, 16); | |
var bar = new Float32Array(8); | |
var i, j; | |
bar[0] = 1; | |
for (i = 0; i < 7; ++i) { | |
bar[i+1] = (color & 0x0f) * 0.0625; // 0.0625 = 1/16 | |
color >>= 4; | |
} | |
for (i = 0; i < 8; ++i) { | |
var x = 0, dx = (i + 1) / 1024; | |
for (j = 0; j < 1024; ++j) { | |
wave[j] += Math.sin(2 * Math.PI * x) * bar[i]; | |
x += dx; | |
} | |
} | |
var maxx = 0, absx; | |
for (i = 0; i < 1024; ++i) { | |
if (maxx < (absx = Math.abs(wave[i]))) { | |
maxx = absx; | |
} | |
} | |
if (maxx > 0) { | |
for (i = 0; i < 1024; ++i) { | |
wave[i] /= maxx; | |
} | |
} | |
} | |
return wave; | |
} | |
var getWavetable = function(key) { | |
var wave = Wavetables[key]; | |
if (wave !== undefined) { | |
if (typeof wave === "function") { | |
wave = wave(); | |
} | |
return wave; | |
} | |
var m; | |
// wave shaping | |
m = /^([\-+]?)(\w+)(?:\((@[0-7])?:?(\d+)?\))?$/.exec(key); | |
if (m !== null) { | |
var sign = m[1], name = m[2], shape = m[3], width = m[4]; | |
wave = waveshape(sign, name, shape, width); | |
if (wave !== undefined) { | |
Wavetables[key] = wave; | |
return wave; | |
} | |
} | |
// wave bytes | |
m = /^wavb\(((?:[0-9a-fA-F][0-9a-fA-F])+)\)$/.exec(key); | |
if (m !== null) { | |
return wavb(m[1]); | |
} | |
// wave color | |
m = /^wavc\(([0-9a-fA-F]{8})\)$/.exec(key); | |
if (m !== null) { | |
return wavc(m[1]); | |
} | |
// warn message | |
}; | |
Oscillator.getWavetable = getWavetable; | |
var setWavetable = function(name, value) { | |
var dx, wave = new Float32Array(1024); | |
var i; | |
if (typeof value === "function") { | |
for (i = 0; i < 1024; ++i) { | |
wave[i] = value(i / 1024); | |
} | |
} else if (T.fn.isSignalArray(value)) { | |
if (value.length === wave.length) { | |
wave.set(value); | |
} else { | |
dx = value.length / 1024; | |
for (i = 0; i < 1024; ++i) { | |
wave[i] = value[(i * dx)|0]; | |
} | |
} | |
} | |
Wavetables[name] = wave; | |
}; | |
Oscillator.setWavetable = setWavetable; | |
var Wavetables = { | |
sin: function() { | |
var wave = new Float32Array(1024); | |
for (var i = 0; i < 1024; ++i) { | |
wave[i] = Math.sin(2 * Math.PI * (i/1024)); | |
} | |
return wave; | |
}, | |
cos: function() { | |
var wave = new Float32Array(1024); | |
for (var i = 0; i < 1024; ++i) { | |
wave[i] = Math.cos(2 * Math.PI * (i/1024)); | |
} | |
return wave; | |
}, | |
pulse: function() { | |
var wave = new Float32Array(1024); | |
for (var i = 0; i < 1024; ++i) { | |
wave[i] = (i < 512) ? +1 : -1; | |
} | |
return wave; | |
}, | |
tri: function() { | |
var wave = new Float32Array(1024); | |
for (var x, i = 0; i < 1024; ++i) { | |
x = (i / 1024) - 0.25; | |
wave[i] = 1.0 - 4.0 * Math.abs(Math.round(x) - x); | |
} | |
return wave; | |
}, | |
saw: function() { | |
var wave = new Float32Array(1024); | |
for (var x, i = 0; i < 1024; ++i) { | |
x = (i / 1024); | |
wave[i] = +2.0 * (x - Math.round(x)); | |
} | |
return wave; | |
}, | |
fami: function() { | |
var d = [ +0.000, +0.125, +0.250, +0.375, +0.500, +0.625, +0.750, +0.875, | |
+0.875, +0.750, +0.625, +0.500, +0.375, +0.250, +0.125, +0.000, | |
-0.125, -0.250, -0.375, -0.500, -0.625, -0.750, -0.875, -1.000, | |
-1.000, -0.875, -0.750, -0.625, -0.500, -0.375, -0.250, -0.125 ]; | |
var wave = new Float32Array(1024); | |
for (var i = 0; i < 1024; ++i) { | |
wave[i] = d[(i / 1024 * d.length)|0]; | |
} | |
return wave; | |
}, | |
konami: function() { | |
var d = [-0.625, -0.875, -0.125, +0.750, + 0.500, +0.125, +0.500, +0.750, | |
+0.250, -0.125, +0.500, +0.875, + 0.625, +0.000, +0.250, +0.375, | |
-0.125, -0.750, +0.000, +0.625, + 0.125, -0.500, -0.375, -0.125, | |
-0.750, -1.000, -0.625, +0.000, - 0.375, -0.875, -0.625, -0.250 ]; | |
var wave = new Float32Array(1024); | |
for (var i = 0; i < 1024; ++i) { | |
wave[i] = d[(i / 1024 * d.length)|0]; | |
} | |
return wave; | |
} | |
}; | |
T.modules.Oscillator = Oscillator; | |
})(timbre); | |
/** | |
* Port of the Freeverb Schrodoer/Moorer reverb model. | |
* https://ccrma.stanford.edu/~jos/pasp/Freeverb.html | |
*/ | |
(function(T) { | |
"use strict"; | |
var CombParams = [1116,1188,1277,1356,1422,1491,1557,1617]; | |
var AllpassParams = [225,556,441,341]; | |
function Reverb(samplerate, buffersize) { | |
this.samplerate = samplerate; | |
var i, imax; | |
var k = samplerate / 44100; | |
imax = CombParams.length * 2; | |
this.comb = new Array(imax); | |
this.combout = new Array(imax); | |
for (i = 0; i < imax; ++i) { | |
this.comb[i] = new CombFilter(CombParams[i % CombParams.length] * k); | |
this.combout[i] = new T.fn.SignalArray(buffersize); | |
} | |
imax = AllpassParams.length * 2; | |
this.allpass = new Array(imax); | |
for (i = 0; i < imax; ++i) { | |
this.allpass[i] = new AllpassFilter(AllpassParams[i % AllpassParams.length] * k); | |
} | |
this.outputs = [ new T.fn.SignalArray(buffersize), | |
new T.fn.SignalArray(buffersize) ]; | |
this.damp = 0; | |
this.wet = 0.33; | |
this.setRoomSize(0.5); | |
this.setDamp(0.5); | |
} | |
var $ = Reverb.prototype; | |
$.setRoomSize = function(roomsize) { | |
var comb = this.comb; | |
var value = (roomsize * 0.28) + 0.7; | |
this.roomsize = roomsize; | |
comb[0].feedback = comb[1].feedback = comb[2].feedback = comb[3].feedback = comb[4].feedback = comb[5].feedback = comb[6].feedback = comb[7].feedback = comb[8].feedback = comb[9].feedback = comb[10].feedback = comb[11].feedback = comb[12].feedback = comb[13].feedback = comb[14].feedback = comb[15].feedback = value; | |
}; | |
$.setDamp = function(damp) { | |
var comb = this.comb; | |
var value = damp * 0.4; | |
this.damp = damp; | |
comb[0].damp = comb[1].damp = comb[2].damp = comb[3].damp = comb[4].damp = comb[5].damp = comb[6].damp = comb[7].damp = comb[8].damp = comb[9].damp = comb[10].damp = comb[11].damp = comb[12].damp = comb[13].damp = comb[14].damp = comb[15].damp = value; | |
}; | |
$.process = function(cellL, cellR) { | |
var comb = this.comb; | |
var combout = this.combout; | |
var allpass = this.allpass; | |
var output0 = this.outputs[0]; | |
var output1 = this.outputs[1]; | |
var wet = this.wet, dry = 1 - wet; | |
var i, imax = cellL.length; | |
comb[0].process(cellL, combout[0]); | |
comb[1].process(cellL, combout[1]); | |
comb[2].process(cellL, combout[2]); | |
comb[3].process(cellL, combout[3]); | |
comb[4].process(cellL, combout[4]); | |
comb[5].process(cellL, combout[5]); | |
comb[6].process(cellL, combout[6]); | |
comb[7].process(cellL, combout[7]); | |
comb[ 8].process(cellR, combout[ 8]); | |
comb[ 9].process(cellR, combout[ 9]); | |
comb[10].process(cellR, combout[10]); | |
comb[11].process(cellR, combout[11]); | |
comb[12].process(cellR, combout[12]); | |
comb[13].process(cellR, combout[13]); | |
comb[14].process(cellR, combout[14]); | |
comb[15].process(cellR, combout[15]); | |
for (i = 0; i < imax; ++i) { | |
output0[i] = combout[0][i] + combout[1][i] + combout[2][i] + combout[3][i] + combout[4][i] + combout[5][i] + combout[6][i] + combout[7][i]; | |
output1[i] = combout[8][i] + combout[9][i] + combout[10][i] + combout[11][i] + combout[12][i] + combout[13][i] + combout[14][i] + combout[15][i]; | |
} | |
allpass[0].process(output0, output0); | |
allpass[1].process(output0, output0); | |
allpass[2].process(output0, output0); | |
allpass[3].process(output0, output0); | |
allpass[4].process(output1, output1); | |
allpass[5].process(output1, output1); | |
allpass[6].process(output1, output1); | |
allpass[7].process(output1, output1); | |
for (i = 0; i < imax; ++i) { | |
cellL[i] = output0[i] * wet + cellL[i] * dry; | |
cellR[i] = output1[i] * wet + cellR[i] * dry; | |
} | |
}; | |
function CombFilter(buffersize) { | |
this.buffer = new T.fn.SignalArray(buffersize|0); | |
this.buffersize = this.buffer.length; | |
this.bufidx = 0; | |
this.feedback = 0; | |
this.filterstore = 0; | |
this.damp = 0; | |
} | |
CombFilter.prototype.process = function(input, output) { | |
var ins, outs; | |
var buffer = this.buffer; | |
var buffersize = this.buffersize; | |
var bufidx = this.bufidx; | |
var filterstore = this.filterstore; | |
var feedback = this.feedback; | |
var damp1 = this.damp, damp2 = 1 - damp1; | |
var i, imax = input.length; | |
for (i = 0; i < imax; ++i) { | |
ins = input[i] * 0.015; | |
outs = buffer[bufidx]; | |
filterstore = (outs * damp2) + (filterstore * damp1); | |
buffer[bufidx] = ins + (filterstore * feedback); | |
if (++bufidx >= buffersize) { | |
bufidx = 0; | |
} | |
output[i] = outs; | |
} | |
this.bufidx = bufidx; | |
this.filterstore = filterstore; | |
}; | |
function AllpassFilter(buffersize) { | |
this.buffer = new T.fn.SignalArray(buffersize|0); | |
this.buffersize = this.buffer.length; | |
this.bufidx = 0; | |
} | |
AllpassFilter.prototype.process = function(input, output) { | |
var ins, outs, bufout; | |
var buffer = this.buffer; | |
var buffersize = this.buffersize; | |
var bufidx = this.bufidx; | |
var i, imax = input.length; | |
for (i = 0; i < imax; ++i) { | |
ins = input[i]; | |
bufout = buffer[bufidx]; | |
outs = -ins + bufout; | |
buffer[bufidx] = ins + (bufout * 0.5); | |
if (++bufidx >= buffersize) { | |
bufidx = 0; | |
} | |
output[i] = outs; | |
} | |
this.bufidx = bufidx; | |
}; | |
T.modules.Reverb = Reverb; | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var DummyBuffer = new Float32Array(60); | |
function Scissor(soundbuffer) { | |
return new Tape(soundbuffer); | |
} | |
var silencebuffer = { | |
buffer:DummyBuffer, samplerate:1 | |
}; | |
Scissor.silence = function(duration) { | |
return new Scissor(silencebuffer).slice(0, 1).fill(duration); | |
}; | |
Scissor.join = function(tapes) { | |
var new_instance = new Tape(); | |
for (var i = 0; i < tapes.length; i++) { | |
if (tapes[i] instanceof Tape) { | |
new_instance.add_fragments(tapes[i].fragments); | |
} | |
} | |
return new_instance; | |
}; | |
function Tape(soundbuffer) { | |
this.fragments = []; | |
if (soundbuffer) { | |
var samplerate = soundbuffer.samplerate || 44100; | |
var duration = soundbuffer.buffer[0].length / samplerate; | |
this.fragments.push( | |
new Fragment(soundbuffer, 0, duration) | |
); | |
} | |
} | |
Scissor.Tape = Tape; | |
Tape.prototype.add_fragment = function(fragment) { | |
this.fragments.push(fragment); | |
return this; | |
}; | |
Tape.prototype.add_fragments = function(fragments) { | |
for (var i = 0; i < fragments.length; i++) { | |
this.fragments.push(fragments[i]); | |
} | |
return this; | |
}; | |
Tape.prototype.duration = function() { | |
var result = 0; | |
for (var i = 0; i < this.fragments.length; i++) { | |
result += this.fragments[i].duration(); | |
} | |
return result; | |
}; | |
Tape.prototype.slice = function(start, length) { | |
var duration = this.duration(); | |
if (start + length > duration) { | |
length = duration - start; | |
} | |
var new_instance = new Tape(); | |
var remainingstart = start; | |
var remaininglength = length; | |
for (var i = 0; i < this.fragments.length; i++) { | |
var fragment = this.fragments[i]; | |
var items = fragment.create(remainingstart, remaininglength); | |
var new_fragment = items[0]; | |
remainingstart = items[1]; | |
remaininglength = items[2]; | |
if (new_fragment) { | |
new_instance.add_fragment(new_fragment); | |
} | |
if (remaininglength === 0) { | |
break; | |
} | |
} | |
return new_instance; | |
}; | |
Tape.prototype.cut = Tape.prototype.slice; | |
Tape.prototype.concat = function(other) { | |
var new_instance = new Tape(); | |
new_instance.add_fragments(this.fragments); | |
new_instance.add_fragments(other.fragments); | |
return new_instance; | |
}; | |
Tape.prototype.loop = function(count) { | |
var i; | |
var orig_fragments = []; | |
for (i = 0; i < this.fragments.length; i++) { | |
orig_fragments.push(this.fragments[i].clone()); | |
} | |
var new_instance = new Tape(); | |
for (i = 0; i < count; i++ ) { | |
new_instance.add_fragments(orig_fragments); | |
} | |
return new_instance; | |
}; | |
Tape.prototype.times = Tape.prototype.loop; | |
Tape.prototype.split = function(count) { | |
var splitted_duration = this.duration() / count; | |
var results = []; | |
for (var i = 0; i < count; i++) { | |
results.push(this.slice(i * splitted_duration, splitted_duration)); | |
} | |
return results; | |
}; | |
Tape.prototype.fill = function(filled_duration) { | |
var duration = this.duration(); | |
if (duration === 0) { | |
throw "EmptyFragment"; | |
} | |
var loop_count = (filled_duration / duration)|0; | |
var remain = filled_duration % duration; | |
return this.loop(loop_count).plus(this.slice(0, remain)); | |
}; | |
Tape.prototype.replace = function(start, length, replaced) { | |
var new_instance = new Tape(); | |
var offset = start + length; | |
new_instance = new_instance.plus(this.slice(0, start)); | |
var new_instance_duration = new_instance.duration(); | |
if (new_instance_duration < start) { | |
new_instance = new_instance.plus(Scissor.silence(start-new_instance_duration)); | |
} | |
new_instance = new_instance.plus(replaced); | |
var duration = this.duration(); | |
if (duration > offset) { | |
new_instance = new_instance.plus(this.slice(offset, duration - offset)); | |
} | |
return new_instance; | |
}; | |
Tape.prototype.reverse = function() { | |
var new_instance = new Tape(); | |
for (var i = this.fragments.length; i--; ) { | |
var fragment = this.fragments[i].clone(); | |
fragment.reverse = !fragment.isReversed(); | |
new_instance.add_fragment(fragment); | |
} | |
return new_instance; | |
}; | |
Tape.prototype.pitch = function(pitch, stretch) { | |
var new_instance = new Tape(); | |
stretch = stretch || false; | |
for (var i = 0; i < this.fragments.length; i++) { | |
var fragment = this.fragments[i].clone(); | |
fragment.pitch *= pitch * 0.01; | |
fragment.stretch = stretch; | |
new_instance.add_fragment(fragment); | |
} | |
return new_instance; | |
}; | |
Tape.prototype.stretch = function(factor) { | |
var factor_for_pitch = 1 / (factor * 0.01) * 100; | |
return this.pitch(factor_for_pitch, true); | |
}; | |
Tape.prototype.pan = function(right_percent) { | |
var new_instance = new Tape(); | |
if (right_percent > 100) { | |
right_percent = 100; | |
} else if (right_percent < 0) { | |
right_percent = 0; | |
} | |
for (var i = 0; i < this.fragments.length; i++) { | |
var fragment = this.fragments[i].clone(); | |
fragment.pan = right_percent; | |
new_instance.add_fragment(fragment); | |
} | |
return new_instance; | |
}; | |
Tape.prototype.silence = function() { | |
return Scissor.silence(this.duration()); | |
}; | |
Tape.prototype.join = function(tapes) { | |
var new_instance = new Tape(); | |
for (var i = 0; i < tapes.length; i++) { | |
if (tapes[i] instanceof Tape) { | |
new_instance.add_fragments(tapes[i].fragments); | |
} | |
} | |
return new_instance; | |
}; | |
Tape.prototype.getBuffer = function() { | |
var samplerate = 44100; | |
if (this.fragments.length > 0) { | |
samplerate = this.fragments[0].samplerate; | |
} | |
var stream = new TapeStream(this, samplerate); | |
var total_samples = (this.duration() * samplerate)|0; | |
return { | |
samplerate: samplerate, | |
buffer : stream.fetch(total_samples) | |
}; | |
}; | |
function Fragment(soundbuffer, start, duration, reverse, pitch, stretch, pan) { | |
if (!soundbuffer) { | |
soundbuffer = silencebuffer; | |
} | |
this.buffer = soundbuffer.buffer[0]; | |
this.samplerate = soundbuffer.samplerate || 44100; | |
this.start = start; | |
this._duration = duration; | |
this.reverse = reverse || false; | |
this.pitch = pitch || 100; | |
this.stretch = stretch || false; | |
this.pan = pan || 50; | |
} | |
Fragment.prototype.duration = function() { | |
return this._duration * (100 / this.pitch); | |
}; | |
Fragment.prototype.original_duration = function() { | |
return this._duration; | |
}; | |
Fragment.prototype.isReversed = function() { | |
return this.reverse; | |
}; | |
Fragment.prototype.isStretched = function() { | |
return this.stretched; | |
}; | |
Fragment.prototype.create = function(remaining_start, remaining_length) { | |
var duration = this.duration(); | |
if (remaining_start >= duration) { | |
return [null, remaining_start - duration, remaining_length]; | |
} | |
var have_remain_to_retuen = (remaining_start + remaining_length) >= duration; | |
var new_length; | |
if (have_remain_to_retuen) { | |
new_length = duration - remaining_start; | |
remaining_length -= new_length; | |
} else { | |
new_length = remaining_length; | |
remaining_length = 0; | |
} | |
var new_fragment = this.clone(); | |
new_fragment.start = this.start + remaining_start * this.pitch * 0.01; | |
new_fragment._duration = new_length * this.pitch * 0.01; | |
new_fragment.reverse = false; | |
return [new_fragment, 0, remaining_length]; | |
}; | |
Fragment.prototype.clone = function() { | |
var new_instance = new Fragment(); | |
new_instance.buffer = this.buffer; | |
new_instance.samplerate = this.samplerate; | |
new_instance.start = this.start; | |
new_instance._duration = this._duration; | |
new_instance.reverse = this.reverse; | |
new_instance.pitch = this.pitch; | |
new_instance.stretch = this.stretch; | |
new_instance.pan = this.pan; | |
return new_instance; | |
}; | |
Scissor.Fragment = Fragment; | |
function TapeStream(tape, samplerate) { | |
this.tape = tape; | |
this.fragments = tape.fragments; | |
this.samplerate = samplerate || 44100; | |
this.isEnded = false; | |
this.buffer = null; | |
this.bufferIndex = 0; | |
this.bufferIndexIncr = 0; | |
this.bufferBeginIndex = 0; | |
this.bufferEndIndex = 0; | |
this.fragment = null; | |
this.fragmentIndex = 0; | |
this.panL = 0.5; | |
this.panR = 0.5; | |
} | |
Scissor.TapeStream = TapeStream; | |
TapeStream.prototype.reset = function() { | |
this.isEnded = false; | |
this.buffer = null; | |
this.bufferIndex = 0; | |
this.bufferIndexIncr = 0; | |
this.bufferBeginIndex = 0; | |
this.bufferEndIndex = 0; | |
this.fragment = null; | |
this.fragmentIndex = 0; | |
this.panL = 0.5; | |
this.panR = 0.5; | |
this.isLooped = false; | |
return this; | |
}; | |
TapeStream.prototype.fetch = function(n) { | |
var cellL = new T.fn.SignalArray(n); | |
var cellR = new T.fn.SignalArray(n); | |
var fragments = this.fragments; | |
if (fragments.length === 0) { | |
return [cellL, cellR]; | |
} | |
var samplerate = this.samplerate * 100; | |
var buffer = this.buffer; | |
var bufferIndex = this.bufferIndex; | |
var bufferIndexIncr = this.bufferIndexIncr; | |
var bufferBeginIndex = this.bufferBeginIndex; | |
var bufferEndIndex = this.bufferEndIndex; | |
var fragment = this.fragment; | |
var fragmentIndex = this.fragmentIndex; | |
var pan; | |
var panL = this.panL; | |
var panR = this.panR; | |
for (var i = 0; i < n; i++) { | |
while (!buffer || | |
bufferIndex < bufferBeginIndex || bufferIndex >= bufferEndIndex) { | |
if (!fragment || fragmentIndex < fragments.length) { | |
fragment = fragments[fragmentIndex++]; | |
buffer = fragment.buffer; | |
bufferIndexIncr = fragment.samplerate / samplerate * fragment.pitch; | |
bufferBeginIndex = fragment.start * fragment.samplerate; | |
bufferEndIndex = bufferBeginIndex + fragment.original_duration() * fragment.samplerate; | |
pan = (fragment.pan * 0.01); | |
panL = 1 - pan; | |
panR = pan; | |
if (fragment.reverse) { | |
bufferIndexIncr *= -1; | |
bufferIndex = bufferEndIndex + bufferIndexIncr; | |
} else { | |
bufferIndex = bufferBeginIndex; | |
} | |
} else { | |
if (this.isLooped) { | |
buffer = null; | |
bufferIndex = 0; | |
bufferIndexIncr = 0; | |
bufferBeginIndex = 0; | |
bufferEndIndex = 0; | |
fragment = null; | |
fragmentIndex = 0; | |
} else { | |
this.isEnded = true; | |
buffer = DummyBuffer; | |
bufferIndexIncr = 0; | |
bufferIndex = 0; | |
break; | |
} | |
} | |
} | |
cellL[i] = buffer[bufferIndex|0] * panL; | |
cellR[i] = buffer[bufferIndex|0] * panR; | |
bufferIndex += bufferIndexIncr; | |
} | |
this.buffer = buffer; | |
this.bufferIndex = bufferIndex; | |
this.bufferIndexIncr = bufferIndexIncr; | |
this.bufferBeginIndex = bufferBeginIndex; | |
this.bufferEndIndex = bufferEndIndex; | |
this.fragment = fragment; | |
this.fragmentIndex = fragmentIndex; | |
this.panL = panL; | |
this.panR = panR; | |
return [cellL, cellR]; | |
}; | |
T.modules.Scissor = Scissor; | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
function StereoDelay(samplerate) { | |
this.samplerate = samplerate; | |
var bits = Math.ceil(Math.log(samplerate * 1.5) * Math.LOG2E); | |
this.buffersize = 1 << bits; | |
this.buffermask = this.buffersize - 1; | |
this.writeBufferL = new T.fn.SignalArray(this.buffersize); | |
this.writeBufferR = new T.fn.SignalArray(this.buffersize); | |
this.readBufferL = this.writeBufferL; | |
this.readBufferR = this.writeBufferR; | |
this.delaytime = null; | |
this.feedback = null; | |
this.cross = null; | |
this.mix = null; | |
this.prevL = 0; | |
this.prevR = 0; | |
this.readIndex = 0; | |
this.writeIndex = 0; | |
this.setParams(125, 0.25, false, 0.45); | |
} | |
var $ = StereoDelay.prototype; | |
$.setParams = function(delaytime, feedback, cross ,mix) { | |
if (this.delaytime !== delaytime) { | |
this.delaytime = delaytime; | |
var offset = (delaytime * 0.001 * this.samplerate)|0; | |
if (offset > this.buffermask) { | |
offset = this.buffermask; | |
} | |
this.writeIndex = (this.readIndex + offset) & this.buffermask; | |
} | |
if (this.feedback !== feedback) { | |
this.feedback = feedback; | |
} | |
if (this.cross !== cross) { | |
this.cross = cross; | |
if (cross) { | |
this.readBufferL = this.writeBufferR; | |
this.readBufferR = this.writeBufferL; | |
} else { | |
this.readBufferL = this.writeBufferL; | |
this.readBufferR = this.writeBufferR; | |
} | |
} | |
if (this.mix !== mix) { | |
this.mix = mix; | |
} | |
}; | |
$.process = function(cellL, cellR) { | |
var readBufferL = this.readBufferL; | |
var readBufferR = this.readBufferR; | |
var writeBufferL = this.writeBufferL; | |
var writeBufferR = this.writeBufferR; | |
var readIndex = this.readIndex; | |
var writeIndex = this.writeIndex; | |
var mask = this.buffermask; | |
var fb = this.feedback; | |
var wet = this.mix, dry = 1 - wet; | |
var prevL = this.prevL; | |
var prevR = this.prevR; | |
var x; | |
var i, imax = cellL.length; | |
for (i = 0; i < imax; ++i) { | |
x = readBufferL[readIndex]; | |
writeBufferL[writeIndex] = cellL[i] - x * fb; | |
cellL[i] = prevL = ((cellL[i] * dry) + (x * wet) + prevL) * 0.5; | |
x = readBufferR[readIndex]; | |
writeBufferR[writeIndex] = cellR[i] - x * fb; | |
cellR[i] = prevR = ((cellR[i] * dry) + (x * wet) + prevR) * 0.5; | |
readIndex += 1; | |
writeIndex = (writeIndex + 1) & mask; | |
} | |
this.readIndex = readIndex & this.buffermask; | |
this.writeIndex = writeIndex; | |
this.prevL = prevL; | |
this.prevR = prevR; | |
}; | |
T.modules.StereoDelay = StereoDelay; | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var modules = T.modules; | |
fn.register("audio", function(_args) { | |
var BufferNode = fn.getClass("buffer"); | |
var instance = new BufferNode(_args); | |
instance.playbackState = fn.FINISHED_STATE; | |
instance._.isLoaded = false; | |
Object.defineProperties(instance, { | |
isLoaded: { | |
get: function() { | |
return this._.isLoaded; | |
} | |
} | |
}); | |
instance.load = load; | |
instance.loadthis = loadthis; | |
return instance; | |
}); | |
var load = function(src) { | |
var self = this, _ = this._; | |
var dfd = new modules.Deferred(this); | |
var args = arguments, i = 1; | |
dfd.done(function() { | |
self._.emit("done"); | |
}); | |
if (typeof args[i] === "function") { | |
dfd.done(args[i++]); | |
if (typeof args[i] === "function") { | |
dfd.fail(args[i++]); | |
} | |
} | |
_.loadedTime = 0; | |
var onloadedmetadata = function(result, msg) { | |
var _ = self._; | |
if (result) { | |
self.playbackState = fn.PLAYING_STATE; | |
_.samplerate = result.samplerate; | |
_.channels = result.channels; | |
_.bufferMix = null; | |
_.buffer = result.buffer; | |
_.phase = 0; | |
_.phaseIncr = result.samplerate / T.samplerate; | |
_.duration = result.duration * 1000; | |
_.currentTime = 0; | |
if (_.isReversed) { | |
_.phaseIncr *= -1; | |
_.phase = result.buffer[0].length + _.phaseIncr; | |
} | |
self._.emit("loadedmetadata"); | |
} else { | |
dfd.reject(msg); | |
} | |
}; | |
var onloadeddata = function() { | |
self._.isLoaded = true; | |
self._.plotFlush = true; | |
self._.emit("loadeddata"); | |
dfd.resolveWith(self); | |
}; | |
new modules.Decoder().decode(src, onloadedmetadata, onloadeddata); | |
return dfd.promise(); | |
}; | |
var loadthis = function() { | |
load.apply(this, arguments); | |
return this; | |
}; | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var FFT = T.modules.FFT; | |
var Biquad = T.modules.Biquad; | |
var PLOT_LOW_FREQ = 20; | |
function BiquadNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.biquad = new Biquad(_.samplerate); | |
_.freq = T(340); | |
_.band = T(1); | |
_.gain = T(0); | |
_.plotBefore = plotBefore; | |
_.plotRange = [-18, 18]; | |
_.plotFlush = true; | |
} | |
fn.extend(BiquadNode); | |
var plotBefore = function(context, x, y, width, height) { | |
context.lineWidth = 1; | |
context.strokeStyle = "rgb(192, 192, 192)"; | |
var nyquist = this._.samplerate * 0.5; | |
for (var i = 1; i <= 10; ++i) { | |
for (var j = 1; j <= 4; j++) { | |
var f = i * Math.pow(10, j); | |
if (f <= PLOT_LOW_FREQ || nyquist <= f) { | |
continue; | |
} | |
context.beginPath(); | |
var _x = (Math.log(f/PLOT_LOW_FREQ)) / (Math.log(nyquist/PLOT_LOW_FREQ)); | |
_x = ((_x * width + x)|0) + 0.5; | |
context.moveTo(_x, y); | |
context.lineTo(_x, y + height); | |
context.stroke(); | |
} | |
} | |
var h = height / 6; | |
for (i = 1; i < 6; i++) { | |
context.beginPath(); | |
var _y = ((y + (i * h))|0) + 0.5; | |
context.moveTo(x, _y); | |
context.lineTo(x + width, _y); | |
context.stroke(); | |
} | |
}; | |
var $ = BiquadNode.prototype; | |
Object.defineProperties($, { | |
type: { | |
set: function(value) { | |
var _ = this._; | |
if (value !== _.biquad.type) { | |
_.biquad.setType(value); | |
_.plotFlush = true; | |
} | |
}, | |
get: function() { | |
return this._.biquad.type; | |
} | |
}, | |
freq: { | |
set: function(value) { | |
this._.freq = T(value); | |
}, | |
get: function() { | |
return this._.freq; | |
} | |
}, | |
cutoff: { | |
set: function(value) { | |
this._.freq = T(value); | |
}, | |
get: function() { | |
return this._.freq; | |
} | |
}, | |
res: { | |
set: function(value) { | |
this._.band = T(value); | |
}, | |
get: function() { | |
return this._.band; | |
} | |
}, | |
Q: { | |
set: function(value) { | |
this._.band = T(value); | |
}, | |
get: function() { | |
return this._.band; | |
} | |
}, | |
band: { | |
set: function(value) { | |
this._.band = T(value); | |
}, | |
get: function() { | |
return this._.band; | |
} | |
}, | |
gain: { | |
set: function(value) { | |
this._.gain = T(value); | |
}, | |
get: function() { | |
return this._.gain; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
var freq = _.freq.process(tickID).cells[0][0]; | |
var band = _.band.process(tickID).cells[0][0]; | |
var gain = _.gain.process(tickID).cells[0][0]; | |
if (_.prevFreq !== freq || _.prevband !== band || _.prevGain !== gain) { | |
_.prevFreq = freq; | |
_.prevband = band; | |
_.prevGain = gain; | |
_.biquad.setParams(freq, band, gain); | |
_.plotFlush = true; | |
} | |
if (!_.bypassed) { | |
_.biquad.process(this.cells[1], this.cells[2]); | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
var fft = new FFT(2048); | |
var super_plot = T.Object.prototype.plot; | |
$.plot = function(opts) { | |
if (this._.plotFlush) { | |
var biquad = new Biquad(this._.samplerate); | |
biquad.setType(this.type); | |
biquad.setParams(this.freq.valueOf(), this.band.valueOf(), this.gain.valueOf()); | |
var impluse = new Float32Array(fft.length); | |
impluse[0] = 1; | |
biquad.process(impluse, impluse); | |
fft.forward(impluse); | |
var size = 512; | |
var data = new Float32Array(size); | |
var nyquist = this._.samplerate * 0.5; | |
var spectrum = new Float32Array(size); | |
var i, j, f, index, delta, x0, x1, xx; | |
fft.getFrequencyData(spectrum); | |
for (i = 0; i < size; ++i) { | |
f = Math.pow(nyquist / PLOT_LOW_FREQ, i / size) * PLOT_LOW_FREQ; | |
j = f / (nyquist / spectrum.length); | |
index = j|0; | |
delta = j - index; | |
if (index === 0) { | |
x1 = x0 = xx = spectrum[index]; | |
} else { | |
x0 = spectrum[index - 1]; | |
x1 = spectrum[index]; | |
xx = ((1.0 - delta) * x0 + delta * x1); | |
} | |
data[i] = xx; | |
} | |
this._.plotData = data; | |
this._.plotFlush = null; | |
} | |
return super_plot.call(this, opts); | |
}; | |
fn.register("biquad", BiquadNode); | |
fn.register("lowpass", function(_args) { | |
return new BiquadNode(_args).set("type", "lowpass"); | |
}); | |
fn.register("highpass", function(_args) { | |
return new BiquadNode(_args).set("type", "highpass"); | |
}); | |
fn.register("bandpass", function(_args) { | |
return new BiquadNode(_args).set("type", "bandpass"); | |
}); | |
fn.register("lowshelf", function(_args) { | |
return new BiquadNode(_args).set("type", "lowshelf"); | |
}); | |
fn.register("highshelf", function(_args) { | |
return new BiquadNode(_args).set("type", "highshelf"); | |
}); | |
fn.register("peaking", function(_args) { | |
return new BiquadNode(_args).set("type", "peaking"); | |
}); | |
fn.register("notch", function(_args) { | |
return new BiquadNode(_args).set("type", "notch"); | |
}); | |
fn.register("allpass", function(_args) { | |
return new BiquadNode(_args).set("type", "allpass"); | |
}); | |
fn.alias("lpf", "lowpass"); | |
fn.alias("hpf", "highpass"); | |
fn.alias("bpf", "bandpass"); | |
fn.alias("bef", "notch"); | |
fn.alias("brf", "notch"); | |
fn.alias("apf", "allpass"); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var Tape = T.modules.Scissor.Tape; | |
var isSignalArray = function(obj) { | |
return fn.isSignalArray(obj) || obj instanceof Float32Array; | |
}; | |
function BufferNode(_args) { | |
T.Object.call(this, 1, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.pitch = T(1); | |
_.samplerate = 44100; | |
_.channels = 0; | |
_.bufferMix = null; | |
_.buffer = []; | |
_.isLooped = false; | |
_.isReversed = false; | |
_.duration = 0; | |
_.currentTime = 0; | |
_.currentTimeObj = null; | |
_.phase = 0; | |
_.phaseIncr = 0; | |
_.onended = fn.make_onended(this, 0); | |
_.onlooped = make_onlooped(this); | |
} | |
fn.extend(BufferNode); | |
var make_onlooped = function(self) { | |
return function() { | |
var _ = self._; | |
if (_.phase >= _.buffer[0].length) { | |
_.phase = 0; | |
} else if (_.phase < 0) { | |
_.phase = _.buffer[0].length + _.phaseIncr; | |
} | |
self._.emit("looped"); | |
}; | |
}; | |
var $ = BufferNode.prototype; | |
var setBuffer = function(value) { | |
var _ = this._; | |
if (typeof value === "object") { | |
var buffer = [], samplerate, channels; | |
if (isSignalArray(value)) { | |
buffer[0] = value; | |
channels = 1; | |
} else if (typeof value === "object") { | |
if (value instanceof T.Object) { | |
value = value.buffer; | |
} else if (value instanceof Tape) { | |
value = value.getBuffer(); | |
} | |
if (Array.isArray(value.buffer)) { | |
if (isSignalArray(value.buffer[0])) { | |
if (isSignalArray(value.buffer[1]) && | |
isSignalArray(value.buffer[2])) { | |
channels = 2; | |
buffer = value.buffer; | |
} else { | |
channels = 1; | |
buffer = [value.buffer[0]]; | |
} | |
} | |
} else if (isSignalArray(value.buffer)) { | |
channels = 1; | |
buffer = [value.buffer]; | |
} | |
if (typeof value.samplerate === "number") { | |
samplerate = value.samplerate; | |
} | |
} | |
if (buffer.length) { | |
if (samplerate > 0) { | |
_.samplerate = value.samplerate; | |
} | |
_.bufferMix = null; | |
_.buffer = buffer; | |
_.phase = 0; | |
_.phaseIncr = _.samplerate / T.samplerate; | |
_.duration = _.buffer[0].length * 1000 / _.samplerate; | |
_.currentTime = 0; | |
_.plotFlush = true; | |
this.reverse(_.isReversed); | |
} | |
} | |
}; | |
Object.defineProperties($, { | |
buffer: { | |
set: setBuffer, | |
get: function() { | |
var _ = this._; | |
return { | |
samplerate: _.samplerate, | |
channels : _.channels, | |
buffer : _.buffer | |
}; | |
} | |
}, | |
pitch: { | |
set: function(value) { | |
this._.pitch = T(value); | |
}, | |
get: function() { | |
return this._.pitch; | |
} | |
}, | |
isLooped: { | |
get: function() { | |
return this._.isLooped; | |
} | |
}, | |
isReversed: { | |
get: function() { | |
return this._.isReversed; | |
} | |
}, | |
samplerate: { | |
get: function() { | |
return this._.samplerate; | |
} | |
}, | |
duration: { | |
get: function() { | |
return this._.duration; | |
} | |
}, | |
currentTime: { | |
set: function(value) { | |
if (typeof value === "number") { | |
var _ = this._; | |
if (0 <= value && value <= _.duration) { | |
_.phase = (value / 1000) * _.samplerate; | |
_.currentTime = value; | |
} | |
} else if (value instanceof T.Object) { | |
this._.currentTimeObj = value; | |
} else if (value === null) { | |
this._.currentTimeObj = null; | |
} | |
}, | |
get: function() { | |
if (this._.currentTimeObj) { | |
return this._.currentTimeObj; | |
} else { | |
return this._.currentTime; | |
} | |
} | |
} | |
}); | |
$.clone = function() { | |
var _ = this._; | |
var instance = fn.clone(this); | |
if (_.buffer.length) { | |
setBuffer.call(instance, { | |
buffer : _.buffer, | |
samplerate: _.samplerate, | |
channels : _.channels | |
}); | |
} | |
instance.loop(_.isLooped); | |
instance.reverse(_.isReversed); | |
return instance; | |
}; | |
$.slice = function(begin, end) { | |
var _ = this._; | |
var instance = T(_.originkey); | |
var isReversed = _.isReversed; | |
if (_.buffer.length) { | |
if (typeof begin === "number" ){ | |
begin = (begin * 0.001 * _.samplerate)|0; | |
} else { | |
begin = 0; | |
} | |
if (typeof end === "number") { | |
end = (end * 0.001 * _.samplerate)|0; | |
} else { | |
end = _.buffer[0].length; | |
} | |
if (begin > end) { | |
var tmp = begin; | |
begin = end; | |
end = tmp; | |
isReversed = !isReversed; | |
} | |
if (_.channels === 2) { | |
setBuffer.call(instance, { | |
buffer : [ fn.pointer(_.buffer[0], begin, end-begin), | |
fn.pointer(_.buffer[1], begin, end-begin), | |
fn.pointer(_.buffer[2], begin, end-begin) ], | |
samplerate: _.samplerate | |
}); | |
} else { | |
setBuffer.call(instance, { | |
buffer: fn.pointer(_.buffer[0], begin, end-begin), | |
samplerate: _.samplerate | |
}); | |
} | |
instance.playbackState = fn.PLAYING_STATE; | |
} | |
instance.loop(_.isLooped); | |
instance.reverse(_.isReversed); | |
return instance; | |
}; | |
$.reverse = function(value) { | |
var _ = this._; | |
_.isReversed = !!value; | |
if (_.isReversed) { | |
if (_.phaseIncr > 0) { | |
_.phaseIncr *= -1; | |
} | |
if (_.phase === 0 && _.buffer.length) { | |
_.phase = _.buffer[0].length + _.phaseIncr; | |
} | |
} else { | |
if (_.phaseIncr < 0) { | |
_.phaseIncr *= -1; | |
} | |
} | |
return this; | |
}; | |
$.loop = function(value) { | |
this._.isLooped = !!value; | |
return this; | |
}; | |
$.bang = function(value) { | |
this.playbackState = (value === false ? fn.FINISHED_STATE : fn.PLAYING_STATE); | |
this._.phase = 0; | |
this._.emit("bang"); | |
return this; | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (!_.buffer.length) { | |
return this; | |
} | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var phase = _.phase; | |
var i, imax = _.cellsize; | |
var bufferL, bufferR; | |
if (_.channels === 2) { | |
bufferL = _.buffer[1]; | |
bufferR = _.buffer[2]; | |
} else { | |
bufferL = bufferR = _.buffer[0]; | |
} | |
if (_.currentTimeObj) { | |
var pos = _.currentTimeObj.process(tickID).cells[0]; | |
var t, sr = _.samplerate * 0.001; | |
for (i = 0; i < imax; ++i) { | |
t = pos[i]; | |
phase = t * sr; | |
cellL[i] = (bufferL[phase|0] || 0); | |
cellR[i] = (bufferR[phase|0] || 0); | |
} | |
_.phase = phase; | |
_.currentTime = t; | |
} else { | |
var pitch = _.pitch.process(tickID).cells[0][0]; | |
var phaseIncr = _.phaseIncr * pitch; | |
for (i = 0; i < imax; ++i) { | |
cellL[i] = (bufferL[phase|0] || 0); | |
cellR[i] = (bufferR[phase|0] || 0); | |
phase += phaseIncr; | |
} | |
if (phase >= bufferL.length) { | |
if (_.isLooped) { | |
fn.nextTick(_.onlooped); | |
} else { | |
fn.nextTick(_.onended); | |
} | |
} else if (phase < 0) { | |
if (_.isLooped) { | |
fn.nextTick(_.onlooped); | |
} else { | |
fn.nextTick(_.onended); | |
} | |
} | |
_.phase = phase; | |
_.currentTime += fn.currentTimeIncr; | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
var super_plot = T.Object.prototype.plot; | |
$.plot = function(opts) { | |
var _ = this._; | |
var bufferL, bufferR; | |
if (_.plotFlush) { | |
if (_.channels === 2) { | |
bufferL = _.buffer[1]; | |
bufferR = _.buffer[2]; | |
} else { | |
bufferL = bufferR = _.buffer[0]; | |
} | |
var data = new Float32Array(2048); | |
var x = 0, xIncr = bufferL.length / 2048; | |
for (var i = 0; i < 2048; i++) { | |
data[i] = (bufferL[x|0] + bufferR[x|0]) * 0.5; | |
x += xIncr; | |
} | |
_.plotData = data; | |
_.plotFlush = null; | |
} | |
return super_plot.call(this, opts); | |
}; | |
fn.register("buffer", BufferNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var Chorus = T.modules.Chorus; | |
function ChorusNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var chorus = new Chorus(this._.samplerate); | |
chorus.setDelayTime(20); | |
chorus.setRate(4); | |
chorus.depth = 20; | |
chorus.feedback = 0.2; | |
chorus.mix = 0.33; | |
this._.chorus = chorus; | |
} | |
fn.extend(ChorusNode); | |
var $ = ChorusNode.prototype; | |
Object.defineProperties($, { | |
type: { | |
set: function(value) { | |
this._.chorus.setDelayTime(value); | |
}, | |
get: function() { | |
return this._.chorus.wave; | |
} | |
}, | |
delay: { | |
set: function(value) { | |
if (0.5 <= value && value <= 80) { | |
this._.chorus.setDelayTime(value); | |
} | |
}, | |
get: function() { | |
return this._.chorus.delayTime; | |
} | |
}, | |
rate: { | |
set: function(value) { | |
if (typeof value === "number" && value > 0) { | |
this._.chorus.setRate(value); | |
} | |
}, | |
get: function() { | |
return this._.chorus.rate; | |
} | |
}, | |
depth: { | |
set: function(value) { | |
if (typeof value === "number") { | |
if (0 <= value && value <= 100) { | |
value *= this._.samplerate / 44100; | |
this._.chorus.depth = value; | |
} | |
} | |
}, | |
get: function() { | |
return this._.chorus.depth; | |
} | |
}, | |
fb: { | |
set: function(value) { | |
if (typeof value === "number") { | |
if (-1 <= value && value <= 1) { | |
this._.chorus.feedback = value * 0.99996; | |
} | |
} | |
}, | |
get: function() { | |
return this._.chorus.feedback; | |
} | |
}, | |
mix: { | |
set: function(value) { | |
this._.mix = T(value); | |
}, | |
get: function() { | |
return this._.mix; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
if (!_.bypassed) { | |
_.chorus.process(this.cells[1], this.cells[2]); | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("chorus", ChorusNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function ClipNode(_args) { | |
T.Object.call(this, 2, _args); | |
var _ = this._; | |
_.min = -0.8; | |
_.max = +0.8; | |
} | |
fn.extend(ClipNode); | |
var $ = ClipNode.prototype; | |
Object.defineProperties($, { | |
minmax: { | |
set: function(value) { | |
var _ = this._; | |
if (typeof value === "number") { | |
_.min = -Math.abs(value); | |
_.max = -_.min; | |
} | |
}, | |
get: function() { | |
return this._.max; | |
} | |
}, | |
min: { | |
set: function(value) { | |
var _ = this._; | |
if (typeof value === "number") { | |
if (_.max < value) { | |
_.max = value; | |
} else { | |
_.min = value; | |
} | |
} | |
}, | |
get: function() { | |
return this._.min; | |
} | |
}, | |
max: { | |
set: function(value) { | |
var _ = this._; | |
if (typeof value === "number") { | |
if (value < _.min) { | |
_.min = value; | |
} else { | |
_.max = value; | |
} | |
} | |
}, | |
get: function() { | |
return this._.max; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var i, imax = cellL.length; | |
var min = _.min, max = _.max; | |
var value; | |
if (_.ar) { | |
fn.inputSignalAR(this); | |
for (i = 0; i < imax; ++i) { | |
value = cellL[i]; | |
if (value < min) { | |
value = min; | |
} else if (value > max) { | |
value = max; | |
} | |
cellL[i] = value; | |
value = cellR[i]; | |
if (value < min) { | |
value = min; | |
} else if (value > max) { | |
value = max; | |
} | |
cellR[i] = value; | |
} | |
fn.outputSignalAR(this); | |
} else { | |
value = fn.inputSignalKR(this); | |
if (value < min) { | |
value = min; | |
} else if (value > max) { | |
value = max; | |
} | |
this.cells[0][0] = value; | |
fn.outputSignalKR(this); | |
} | |
} | |
return this; | |
}; | |
fn.register("clip", ClipNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
var Compressor = T.modules.Compressor; | |
function CompressorNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.prevThresh = -24; | |
_.prevKnee = 30; | |
_.prevRatio = 12; | |
_.thresh = T(_.prevThresh); | |
_.knee = T(_.prevKnee); | |
_.ratio = T(_.prevRatio); | |
_.postGain = 6; | |
_.reduction = 0; | |
_.attack = 3; | |
_.release = 25; | |
_.comp = new Compressor(_.samplerate); | |
_.comp.dbPostGain = _.postGain; | |
_.comp.setAttackTime(_.attack * 0.001); | |
_.comp.setReleaseTime(_.release * 0.001); | |
_.comp.setPreDelayTime(6); | |
_.comp.setParams(_.prevThresh, _.prevKnee, _.prevRatio); | |
} | |
fn.extend(CompressorNode); | |
var $ = CompressorNode.prototype; | |
Object.defineProperties($, { | |
thresh: { | |
set: function(value) { | |
this._.thresh = T(value); | |
}, | |
get: function() { | |
return this._.thresh; | |
} | |
}, | |
thre: { | |
set: function(value) { | |
this._.thresh = T(value); | |
}, | |
get: function() { | |
return this._.thre; | |
} | |
}, | |
knee: { | |
set: function(value) { | |
this._.kne = T(value); | |
}, | |
get: function() { | |
return this._.knee; | |
} | |
}, | |
ratio: { | |
set: function(value) { | |
this._.ratio = T(value); | |
}, | |
get: function() { | |
return this._.ratio; | |
} | |
}, | |
gain: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.comp.dbPostGain = value; | |
} | |
}, | |
get: function() { | |
return this._.comp.dbPostGain; | |
} | |
}, | |
attack: { | |
set: function(value) { | |
if (typeof value === "string") { | |
value = timevalue(value); | |
} | |
if (typeof value === "number") { | |
value = (value < 0) ? 0 : (1000 < value) ? 1000 : value; | |
this._.attack = value; | |
this._.comp.setAttackTime(value * 0.001); | |
} | |
}, | |
get: function() { | |
return this._.attack; | |
} | |
}, | |
release: { | |
set: function(value) { | |
if (typeof value === "string") { | |
value = timevalue(value); | |
} | |
if (typeof value === "number") { | |
value = (value < 0) ? 0 : (1000 < value) ? 1000 : value; | |
this._.release = value; | |
this._.comp.setReleaseTime(value * 0.001); | |
} | |
}, | |
get: function() { | |
return this._.release; | |
} | |
}, | |
reduction: { | |
get: function() { | |
return this._.reduction; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
var thresh = _.thresh.process(tickID).cells[0][0]; | |
var knee = _.knee.process(tickID).cells[0][0]; | |
var ratio = _.ratio.process(tickID).cells[0][0]; | |
if (_.prevThresh !== thresh || _.prevKnee !== knee || _.prevRatio !== ratio) { | |
_.prevThresh = thresh; | |
_.prevKnee = knee; | |
_.prevRatio = ratio; | |
_.comp.setParams(thresh, knee, ratio); | |
} | |
if (!_.bypassed) { | |
_.comp.process(this.cells[1], this.cells[2]); | |
_.reduction = _.comp.meteringGain; | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("comp", CompressorNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
var StereoDelay = T.modules.StereoDelay; | |
function DelayNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.time = T(100); | |
_.fb = T(0.2); | |
_.cross = T(false); | |
_.mix = 0.33; | |
_.delay = new StereoDelay(_.samplerate); | |
} | |
fn.extend(DelayNode); | |
var $ = DelayNode.prototype; | |
Object.defineProperties($, { | |
time: { | |
set: function(value) { | |
if (typeof value === "string") { | |
value = timevalue(value); | |
} | |
this._.time = T(value); | |
}, | |
get: function() { | |
return this._.time; | |
} | |
}, | |
fb: { | |
set: function(value) { | |
this._.fb = T(value); | |
}, | |
get: function() { | |
return this._.fb; | |
} | |
}, | |
cross: { | |
set: function(value) { | |
this._.cross = T(value); | |
}, | |
get: function() { | |
return this._.cross; | |
} | |
}, | |
mix: { | |
set: function(value) { | |
if (typeof value === "number") { | |
value = (value > 1) ? 1 : (value < 0) ? 0 : value; | |
this._.mix = value; | |
} | |
}, | |
get: function() { | |
return this._.mix; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var time = _.time.process(tickID).cells[0][0]; | |
var fb = _.fb.process(tickID).cells[0][0]; | |
var cross = _.cross.process(tickID).cells[0][0] !== 0; | |
var mix = _.mix; | |
if (_.prevTime !== time || _.prevFb !== fb || _.prevCross !== cross || _.prevMix !== mix) { | |
_.prevTime = time; | |
_.prevFb = fb; | |
_.prevCross = cross; | |
_.prevMix = mix; | |
_.delay.setParams(time, fb, cross, mix); | |
} | |
fn.inputSignalAR(this); | |
if (!_.bypassed) { | |
_.delay.process(this.cells[1], this.cells[2]); | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("delay", DelayNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function DistNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.pre = T( 60); | |
_.post = T(-18); | |
_.x1L = _.x2L = _.y1L = _.y2L = 0; | |
_.x1R = _.x2R = _.y1R = _.y2R = 0; | |
_.b0 = _.b1 = _.b2 = _.a1 = _.a2 = 0; | |
_.cutoff = 0; | |
_.Q = 1; | |
_.preScale = 0; | |
_.postScale = 0; | |
} | |
fn.extend(DistNode); | |
var $ = DistNode.prototype; | |
Object.defineProperties($, { | |
cutoff: { | |
set: function(value) { | |
if (typeof value === "number" && value > 0) { | |
this._.cutoff = value; | |
} | |
}, | |
get: function() { | |
return this._.cutoff; | |
} | |
}, | |
pre: { | |
set: function(value) { | |
this._.pre = T(value); | |
}, | |
get: function() { | |
return this._.pre; | |
} | |
}, | |
post: { | |
set: function(value) { | |
this._.post = T(value); | |
}, | |
get: function() { | |
return this._.post; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
var preGain = -_.pre.process(tickID).cells[0][0]; | |
var postGain = -_.post.process(tickID).cells[0][0]; | |
if (_.prevPreGain !== preGain || _.prevPostGain !== postGain) { | |
_.prevPreGain = preGain; | |
_.prevPostGain = postGain; | |
_.preScale = Math.pow(10, -preGain * 0.05); | |
_.postScale = Math.pow(10, -postGain * 0.05); | |
} | |
if (!_.bypassed) { | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var preScale = _.preScale; | |
var postScale = _.postScale; | |
var i, imax, value, x0, y0; | |
if (_.cutoff) { | |
if (_.prevCutoff !== _.cutoff) { | |
_.prevCutoff = _.cutoff; | |
lowpass_params(_); | |
} | |
var x1L = _.x1L, x2L = _.x2L, y1L = _.y1L, y2L = _.y2L; | |
var x1R = _.x1R, x2R = _.x2R, y1R = _.y1R, y2R = _.y2R; | |
var b0 = _.b0, b1 = _.b1, b2 = _.b2, a1 = _.a1, a2 = _.a2; | |
for (i = 0, imax = cellL.length; i < imax; ++i) { | |
x0 = cellL[i] * preScale; | |
y0 = b0 * x0 + b1 * x1L + b2 * x2L - a1 * y1L - a2 * y2L; | |
value = y0 * postScale; | |
if (value < -1) { | |
value = -1; | |
} else if (value > 1) { | |
value = 1; | |
} | |
cellL[i] = value; | |
x2L = x1L; x1L = x0; y2L = y1L; y1L = y0; | |
x0 = cellR[i] * preScale; | |
y0 = b0 * x0 + b1 * x1R + b2 * x2R - a1 * y1R - a2 * y2R; | |
value = y0 * postScale; | |
if (value < -1) { | |
value = -1; | |
} else if (value > 1) { | |
value = 1; | |
} | |
cellR[i] = value; | |
x2R = x1R; x1R = x0; y2R = y1R; y1R = y0; | |
} | |
_.x1L = x1L; _.x2L = x2L; _.y1L = y1L; _.y2L = y2L; | |
_.x1R = x1R; _.x2R = x2R; _.y1R = y1R; _.y2R = y2R; | |
} else { | |
for (i = 0, imax = cellL.length; i < imax; ++i) { | |
value = cellL[i] * preScale * postScale; | |
if (value < -1) { | |
value = -1; | |
} else if (value > 1) { | |
value = 1; | |
} | |
cellL[i] = value; | |
value = cellR[i] * preScale * postScale; | |
if (value < -1) { | |
value = -1; | |
} else if (value > 1) { | |
value = 1; | |
} | |
cellR[i] = value; | |
} | |
} | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
var lowpass_params = function(_) { | |
var w0 = 2 * Math.PI * _.cutoff / _.samplerate; | |
var cos = Math.cos(w0); | |
var sin = Math.sin(w0); | |
var alpha = sin / (2 * _.Q); | |
var ia0 = 1 / (1 + alpha); | |
_.b0 = (1 - cos) * 0.5 * ia0; | |
_.b1 = 1 - cos * ia0; | |
_.b2 = (1 - cos) * 0.5 * ia0; | |
_.a1 = -2 * cos * ia0; | |
_.a2 = 1 - alpha * ia0; | |
}; | |
fn.register("dist", DistNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function DivNode(_args) { | |
T.Object.call(this, 2, _args); | |
this._.ar = false; | |
} | |
fn.extend(DivNode); | |
var $ = DivNode.prototype; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var nodes = this.nodes; | |
var cell = this.cells[0]; | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var i, imax = nodes.length; | |
var j, jmax = cell.length; | |
var tmp, tmpL, tmpR, div; | |
if (_.ar) { | |
if (nodes.length > 0) { | |
nodes[0].process(tickID); | |
tmpL = nodes[0].cells[1]; | |
tmpR = nodes[0].cells[2]; | |
cellL.set(tmpL); | |
cellR.set(tmpR); | |
for (i = 1; i < imax; ++i) { | |
nodes[i].process(tickID); | |
tmpL = nodes[i].cells[1]; | |
tmpR = nodes[i].cells[2]; | |
for (j = 0; j < jmax; ++j) { | |
div = tmpL[j]; | |
cellL[j] = (div === 0) ? 0 : cellL[j] / div; | |
div = tmpR[j]; | |
cellR[j] = (div === 0) ? 0 : cellR[j] / div; | |
} | |
} | |
} else { | |
for (j = 0; j < jmax; ++j) { | |
cellL[j] = cellR[i] = 0; | |
} | |
} | |
fn.outputSignalAR(this); | |
} else { | |
if (nodes.length > 0) { | |
tmp = nodes[0].process(tickID).cells[0][0]; | |
for (i = 1; i < imax; ++i) { | |
div = nodes[i].process(tickID).cells[0][0]; | |
tmp = (div === 0) ? 0 : tmp / div; | |
} | |
} else { | |
tmp = 0; | |
} | |
cell[0] = tmp; | |
fn.outputSignalKR(this); | |
} | |
} | |
return this; | |
}; | |
fn.register("/", DivNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
var Envelope = T.modules.Envelope; | |
var isDictionary = fn.isDictionary; | |
function EnvNode(_args) { | |
T.Object.call(this, 2, _args); | |
var _ = this._; | |
_.env = new Envelope(_.samplerate); | |
_.env.setStep(_.cellsize); | |
_.tmp = new fn.SignalArray(_.cellsize); | |
_.ar = false; | |
_.plotFlush = true; | |
_.onended = make_onended(this); | |
this.on("ar", onar); | |
} | |
fn.extend(EnvNode); | |
var onar = function(value) { | |
this._.env.setStep((value) ? 1 : this._.cellsize); | |
}; | |
var make_onended = function(self) { | |
return function() { | |
self._.emit("ended"); | |
}; | |
}; | |
var $ = EnvNode.prototype; | |
Object.defineProperties($, { | |
table: { | |
set: function(value) { | |
if (Array.isArray(value)) { | |
setTable.call(this, value); | |
this._.plotFlush = true; | |
} | |
}, | |
get: function() { | |
return this._.env.table; | |
} | |
}, | |
curve: { | |
set: function(value) { | |
this._.env.setCurve(value); | |
}, | |
get: function() { | |
return this._.env.curve; | |
} | |
}, | |
releaseNode: { | |
set: function(value) { | |
this._.env.setReleaseNode(value); | |
this._.plotFlush = true; | |
}, | |
get: function() { | |
return this._.env.releaseNode + 1; | |
} | |
}, | |
loopNode: { | |
set: function(value) { | |
this._.env.setLoopNode(value); | |
this._.plotFlush = true; | |
}, | |
get: function() { | |
return this._.env.loopNode + 1; | |
} | |
} | |
}); | |
$.clone = function() { | |
var instance = fn.clone(this); | |
instance._.env = this._.env.clone(); | |
return instance; | |
}; | |
$.reset = function() { | |
this._.env.reset(); | |
return this; | |
}; | |
$.release = function() { | |
var _ = this._; | |
_.env.release(); | |
_.emit("released"); | |
return this; | |
}; | |
$.bang = function() { | |
var _ = this._; | |
_.env.reset(); | |
_.env.status = Envelope.StatusGate; | |
_.emit("bang"); | |
return this; | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var i, imax = _.cellsize; | |
if (this.nodes.length) { | |
fn.inputSignalAR(this); | |
} else { | |
for (i = 0; i < imax; ++i) { | |
cellL[i] = cellR[i] = 1; | |
} | |
} | |
var value, emit = null; | |
if (_.ar) { | |
var tmp = _.tmp; | |
_.env.process(tmp); | |
for (i = 0; i < imax; ++i) { | |
cellL[i] *= tmp[i]; | |
cellR[i] *= tmp[i]; | |
} | |
emit = _.env.emit; | |
} else { | |
value = _.env.next(); | |
for (i = 0; i < imax; ++i) { | |
cellL[i] *= value; | |
cellR[i] *= value; | |
} | |
emit = _.env.emit; | |
} | |
fn.outputSignalAR(this); | |
if (emit) { | |
if (emit === "ended") { | |
fn.nextTick(_.onended); | |
} else { | |
this._.emit(emit, _.value); | |
} | |
} | |
} | |
return this; | |
}; | |
var setTable = function(list) { | |
var env = this._.env; | |
var table = [list[0] || ZERO]; | |
var value, time, curveType, curveValue; | |
for (var i = 1, imax = list.length; i < imax; ++i) { | |
value = list[i][0] || ZERO; | |
time = list[i][1]; | |
curveType = list[i][2]; | |
if (typeof time !== "number") { | |
if (typeof time === "string") { | |
time = timevalue(time); | |
} else { | |
time = 10; | |
} | |
} | |
if (time < 10) { | |
time = 10; | |
} | |
if (typeof curveType === "number") { | |
curveValue = curveType; | |
curveType = Envelope.CurveTypeCurve; | |
} else { | |
curveType = Envelope.CurveTypeDict[curveType] || null; | |
curveValue = 0; | |
} | |
table.push([value, time, curveType, curveValue]); | |
} | |
env.setTable(table); | |
}; | |
var super_plot = T.Object.prototype.plot; | |
$.plot = function(opts) { | |
if (this._.plotFlush) { | |
var env = this._.env.clone(); | |
var info = env.getInfo(1000); | |
var totalDuration = info.totalDuration; | |
var loopBeginTime = info.loopBeginTime; | |
var releaseBeginTime = info.releaseBeginTime; | |
var data = new Float32Array(256); | |
var duration = 0; | |
var durationIncr = totalDuration / data.length; | |
var isReleased = false; | |
var samples = (totalDuration * 0.001 * this._.samplerate)|0; | |
var i, imax; | |
samples /= data.length; | |
env.setStep(samples); | |
env.status = Envelope.StatusGate; | |
for (i = 0, imax = data.length; i < imax; ++i) { | |
data[i] = env.next(); | |
duration += durationIncr; | |
if (!isReleased && duration >= releaseBeginTime) { | |
env.release(); | |
isReleased = true; | |
} | |
} | |
this._.plotData = data; | |
this._.plotBefore = function(context, x, y, width, height) { | |
var x1, w; | |
if (loopBeginTime !== Infinity && releaseBeginTime !== Infinity) { | |
x1 = x + (width * (loopBeginTime / totalDuration)); | |
w = x + (width * (releaseBeginTime / totalDuration)); | |
w = w - x1; | |
context.fillStyle = "rgba(224, 224, 224, 0.8)"; | |
context.fillRect(x1, 0, w, height); | |
} | |
if (releaseBeginTime !== Infinity) { | |
x1 = x + (width * (releaseBeginTime / totalDuration)); | |
w = width - x1; | |
context.fillStyle = "rgba(212, 212, 212, 0.8)"; | |
context.fillRect(x1, 0, w, height); | |
} | |
}; | |
// y-range | |
var minValue = Infinity, maxValue = -Infinity; | |
for (i = 0; i < imax; ++i) { | |
if (data[i] < minValue) { | |
minValue = data[i]; | |
} else if (data[i] > maxValue) { | |
maxValue = data[i]; | |
} | |
} | |
if (maxValue < 1) { | |
maxValue = 1; | |
} | |
this._.plotRange = [minValue, maxValue]; | |
this._.plotData = data; | |
this._.plotFlush = null; | |
} | |
return super_plot.call(this, opts); | |
}; | |
fn.register("env", EnvNode); | |
function envValue(opts, min, def, name1, name2, func) { | |
var x = def; | |
if (typeof opts[name1] === "number") { | |
x = opts[name1]; | |
} else if (typeof opts[name2] === "number") { | |
x = opts[name2]; | |
} else if (func) { | |
if (typeof opts[name1] === "string") { | |
x = func(opts[name1]); | |
} else if (typeof opts[name2] === "string") { | |
x = func(opts[name2]); | |
} | |
} | |
if (x < min) { | |
x = min; | |
} | |
return x; | |
} | |
var ZERO = Envelope.ZERO; | |
fn.register("perc", function(_args) { | |
if (!isDictionary(_args[0])) { | |
_args.unshift({}); | |
} | |
var opts = _args[0]; | |
var a = envValue(opts, 10, 10, "a" , "attackTime" , timevalue); | |
var r = envValue(opts, 10, 1000, "r" , "releaseTime", timevalue); | |
var lv = envValue(opts, ZERO, 1, "lv", "level" ); | |
opts.table = [ZERO, [lv, a], [ZERO, r]]; | |
return new EnvNode(_args); | |
}); | |
fn.register("adsr", function(_args) { | |
if (!isDictionary(_args[0])) { | |
_args.unshift({}); | |
} | |
var opts = _args[0]; | |
var a = envValue(opts, 10, 10, "a" , "attackTime" , timevalue); | |
var d = envValue(opts, 10, 300, "d" , "decayTime" , timevalue); | |
var s = envValue(opts, ZERO, 0.5, "s" , "sustainLevel"); | |
var r = envValue(opts, 10, 1000, "r" , "decayTime" , timevalue); | |
var lv = envValue(opts, ZERO, 1, "lv", "level" ); | |
opts.table = [ZERO, [lv, a], [s, d], [ZERO, r]]; | |
opts.releaseNode = 3; | |
return new EnvNode(_args); | |
}); | |
fn.register("adshr", function(_args) { | |
if (!isDictionary(_args[0])) { | |
_args.unshift({}); | |
} | |
var opts = _args[0]; | |
var a = envValue(opts, 10, 10, "a" , "attackTime" , timevalue); | |
var d = envValue(opts, 10, 300, "d" , "decayTime" , timevalue); | |
var s = envValue(opts, ZERO, 0.5, "s" , "sustainLevel"); | |
var h = envValue(opts, 10, 500, "h" , "holdTime" , timevalue); | |
var r = envValue(opts, 10, 1000, "r" , "decayTime" , timevalue); | |
var lv = envValue(opts, ZERO, 1, "lv", "level" ); | |
opts.table = [ZERO, [lv, a], [s, d], [s, h], [ZERO, r]]; | |
return new EnvNode(_args); | |
}); | |
fn.register("asr", function(_args) { | |
if (!isDictionary(_args[0])) { | |
_args.unshift({}); | |
} | |
var opts = _args[0]; | |
var a = envValue(opts, 10, 10, "a" , "attackTime" , timevalue); | |
var s = envValue(opts, ZERO, 0.5, "s" , "sustainLevel"); | |
var r = envValue(opts, 10, 1000, "r" , "releaseTime" , timevalue); | |
opts.table = [ZERO, [s, a], [ZERO, r]]; | |
opts.releaseNode = 2; | |
return new EnvNode(_args); | |
}); | |
fn.register("dadsr", function(_args) { | |
if (!isDictionary(_args[0])) { | |
_args.unshift({}); | |
} | |
var opts = _args[0]; | |
var dl = envValue(opts, 10, 100, "dl", "delayTime" , timevalue); | |
var a = envValue(opts, 10, 10, "a" , "attackTime" , timevalue); | |
var d = envValue(opts, 10, 300, "d" , "decayTime" , timevalue); | |
var s = envValue(opts, ZERO, 0.5, "s" , "sustainLevel"); | |
var r = envValue(opts, 10, 1000, "r" , "relaseTime" , timevalue); | |
var lv = envValue(opts, ZERO, 1, "lv", "level" ); | |
opts.table = [ZERO, [ZERO, dl], [lv, a], [s, d], [ZERO, r]]; | |
opts.releaseNode = 4; | |
return new EnvNode(_args); | |
}); | |
fn.register("ahdsfr", function(_args) { | |
if (!isDictionary(_args[0])) { | |
_args.unshift({}); | |
} | |
var opts = _args[0]; | |
var a = envValue(opts, 10, 10, "a" , "attackTime" , timevalue); | |
var h = envValue(opts, 10, 10, "h" , "holdTime" , timevalue); | |
var d = envValue(opts, 10, 300, "d" , "decayTime" , timevalue); | |
var s = envValue(opts, ZERO, 0.5, "s" , "sustainLevel"); | |
var f = envValue(opts, 10, 5000, "f" , "fadeTime" , timevalue); | |
var r = envValue(opts, 10, 1000, "r" , "relaseTime" , timevalue); | |
var lv = envValue(opts, ZERO, 1, "lv", "level" ); | |
opts.table = [ZERO, [lv, a], [lv, h], [s, d], [ZERO, f], [ZERO, r]]; | |
opts.releaseNode = 5; | |
return new EnvNode(_args); | |
}); | |
fn.register("linen", function(_args) { | |
if (!isDictionary(_args[0])) { | |
_args.unshift({}); | |
} | |
var opts = _args[0]; | |
var a = envValue(opts, 10, 10, "a" , "attackTime" , timevalue); | |
var s = envValue(opts, 10, 1000, "s" , "sustainTime", timevalue); | |
var r = envValue(opts, 10, 1000, "r" , "releaseTime", timevalue); | |
var lv = envValue(opts, ZERO, 1, "lv", "level" ); | |
opts.table = [ZERO, [lv, a], [lv, s], [ZERO, r]]; | |
return new EnvNode(_args); | |
}); | |
fn.register("env.tri", function(_args) { | |
if (!isDictionary(_args[0])) { | |
_args.unshift({}); | |
} | |
var opts = _args[0]; | |
var dur = envValue(opts, 20, 1000, "dur", "duration", timevalue); | |
var lv = envValue(opts, ZERO, 1, "lv" , "level" ); | |
dur *= 0.5; | |
opts.table = [ZERO, [lv, dur], [ZERO, dur]]; | |
return new EnvNode(_args); | |
}); | |
fn.register("env.cutoff", function(_args) { | |
if (!isDictionary(_args[0])) { | |
_args.unshift({}); | |
} | |
var opts = _args[0]; | |
var r = envValue(opts, 10, 100, "r" , "relaseTime", timevalue); | |
var lv = envValue(opts, ZERO, 1, "lv", "level" ); | |
opts.table = [lv, [ZERO, r]]; | |
return new EnvNode(_args); | |
}); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var FFT = T.modules.FFT; | |
var Biquad = T.modules.Biquad; | |
var PLOT_LOW_FREQ = 20; | |
var PARAM_NAMES = { | |
hpf:0, lf:1, lmf:2, mf:3, hmf:4, hf:5, lpf:6 | |
}; | |
function EQNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.biquads = new Array(7); | |
_.plotBefore = plotBefore; | |
_.plotRange = [-18, 18]; | |
_.plotFlush = true; | |
} | |
fn.extend(EQNode); | |
var plotBefore = function(context, x, y, width, height) { | |
context.lineWidth = 1; | |
context.strokeStyle = "rgb(192, 192, 192)"; | |
var nyquist = this._.samplerate * 0.5; | |
for (var i = 1; i <= 10; ++i) { | |
for (var j = 1; j <= 4; j++) { | |
var f = i * Math.pow(10, j); | |
if (f <= PLOT_LOW_FREQ || nyquist <= f) { | |
continue; | |
} | |
context.beginPath(); | |
var _x = (Math.log(f/PLOT_LOW_FREQ)) / (Math.log(nyquist/PLOT_LOW_FREQ)); | |
_x = ((_x * width + x)|0) + 0.5; | |
context.moveTo(_x, y); | |
context.lineTo(_x, y + height); | |
context.stroke(); | |
} | |
} | |
var h = height / 6; | |
for (i = 1; i < 6; i++) { | |
context.beginPath(); | |
var _y = ((y + (i * h))|0) + 0.5; | |
context.moveTo(x, _y); | |
context.lineTo(x + width, _y); | |
context.stroke(); | |
} | |
}; | |
var $ = EQNode.prototype; | |
Object.defineProperties($, { | |
params: { | |
set: function(value) { | |
if (typeof value === "object") { | |
var keys = Object.keys(value); | |
for (var i = 0, imax = keys.length; i < imax; ++i) { | |
var items = value[keys[i]]; | |
if (Array.isArray(items)) { | |
this.setParams(keys[i], items[0], items[1], items[2]); | |
} else { | |
this.setParams(keys[i]); | |
} | |
} | |
} | |
} | |
} | |
}); | |
$.setParams = function(index, freq, Q, gain) { | |
var _ = this._; | |
if (typeof index === "string") { | |
index = PARAM_NAMES[index]; | |
} | |
if (0 <= index && index < _.biquads.length) { | |
index |= 0; | |
if (typeof freq === "number" && typeof Q === "number") { | |
if (typeof gain !== "number") { | |
gain = 0; | |
} | |
var biquad = _.biquads[index]; | |
if (!biquad) { | |
biquad = _.biquads[index] = new Biquad(_.samplerate); | |
switch (index) { | |
case 0: | |
biquad.setType("highpass"); | |
break; | |
case _.biquads.length - 1: | |
biquad.setType("lowpass"); | |
break; | |
default: | |
biquad.setType("peaking"); | |
break; | |
} | |
} | |
biquad.setParams(freq, Q, gain); | |
} else { | |
_.biquads[index] = undefined; | |
} | |
_.plotFlush = true; | |
} | |
return this; | |
}; | |
$.getParams = function(index) { | |
var _ = this._; | |
var biquad = _.biquads[index|0]; | |
if (biquad) { | |
return {freq:biquad.frequency, Q:biquad.Q, gain:biquad.gain}; | |
} | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
if (!_.bypassed) { | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var biquads = _.biquads; | |
for (var i = 0, imax = biquads.length; i < imax; ++i) { | |
if (biquads[i]) { | |
biquads[i].process(cellL, cellR); | |
} | |
} | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
var fft = new FFT(2048); | |
var super_plot = T.Object.prototype.plot; | |
$.plot = function(opts) { | |
if (this._.plotFlush) { | |
var _ = this._; | |
var impluse = new Float32Array(fft.length); | |
impluse[0] = 1; | |
for (var i = 0, imax = _.biquads.length; i < imax; ++i) { | |
var params = this.getParams(i); | |
if (params) { | |
var biquad = new Biquad(_.samplerate); | |
if (i === 0) { | |
biquad.setType("highpass"); | |
} else if (i === imax - 1) { | |
biquad.setType("lowpass"); | |
} else { | |
biquad.setType("peaking"); | |
} | |
biquad.setParams(params.freq, params.Q, params.gain); | |
biquad.process(impluse, impluse); | |
} | |
} | |
fft.forward(impluse); | |
var size = 512; | |
var data = new Float32Array(size); | |
var nyquist = _.samplerate * 0.5; | |
var spectrum = new Float32Array(size); | |
var j, f, index, delta, x0, x1, xx; | |
fft.getFrequencyData(spectrum); | |
for (i = 0; i < size; ++i) { | |
f = Math.pow(nyquist / PLOT_LOW_FREQ, i / size) * PLOT_LOW_FREQ; | |
j = f / (nyquist / spectrum.length); | |
index = j|0; | |
delta = j - index; | |
if (index === 0) { | |
x1 = x0 = xx = spectrum[index]; | |
} else { | |
x0 = spectrum[index - 1]; | |
x1 = spectrum[index]; | |
xx = ((1.0 - delta) * x0 + delta * x1); | |
} | |
data[i] = xx; | |
} | |
this._.plotData = data; | |
this._.plotFlush = null; | |
} | |
return super_plot.call(this, opts); | |
}; | |
fn.register("eq", EQNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var FFT = T.modules.FFT; | |
function FFTNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.listener(this); | |
fn.fixAR(this); | |
this.real = new T.ChannelObject(this); | |
this.imag = new T.ChannelObject(this); | |
this.cells[3] = this.real.cell; | |
this.cells[4] = this.imag.cell; | |
var _ = this._; | |
_.fft = new FFT(_.cellsize * 2); | |
_.fftCell = new fn.SignalArray(_.fft.length); | |
_.prevCell = new fn.SignalArray(_.cellsize); | |
_.freqs = new fn.SignalArray(_.fft.length>>1); | |
_.plotFlush = true; | |
_.plotRange = [0, 32]; | |
_.plotBarStyle = true; | |
} | |
fn.extend(FFTNode); | |
var $ = FFTNode.prototype; | |
Object.defineProperties($, { | |
window: { | |
set: function(value) { | |
this._.fft.setWindow(value); | |
}, | |
get: function() { | |
return this._.fft.windowName; | |
} | |
}, | |
spectrum: { | |
get: function() { | |
return this._.fft.getFrequencyData(this._.freqs); | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
fn.outputSignalAR(this); | |
var cell = this.cells[0]; | |
var cellsize = _.cellsize; | |
_.fftCell.set(_.prevCell); | |
_.fftCell.set(cell, cellsize); | |
_.fft.forward(_.fftCell); | |
_.prevCell.set(cell); | |
_.plotFlush = true; | |
this.cells[3].set(_.fft.real.subarray(0, cellsize)); | |
this.cells[4].set(_.fft.imag.subarray(0, cellsize)); | |
} | |
return this; | |
}; | |
var super_plot = T.Object.prototype.plot; | |
$.plot = function(opts) { | |
if (this._.plotFlush) { | |
this._.plotData = this.spectrum; | |
this._.plotFlush = null; | |
} | |
return super_plot.call(this, opts); | |
}; | |
fn.register("fft", FFTNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function FNoiseNode(_args) { | |
T.Object.call(this, 1, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.freq = T(440); | |
_.reg = 0x8000; | |
_.shortFlag = false; | |
_.phase = 0; | |
_.lastValue = 0; | |
} | |
fn.extend(FNoiseNode); | |
var $ = FNoiseNode.prototype; | |
Object.defineProperties($, { | |
shortFlag: { | |
set: function(value) { | |
this._.shortFlag = !!value; | |
}, | |
get: function() { | |
return this._.shortFlag; | |
} | |
}, | |
freq: { | |
set: function(value) { | |
this._.freq = T(value); | |
}, | |
get: function() { | |
return this._.freq; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
var cell = this.cells[0]; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var lastValue = _.lastValue; | |
var phase = _.phase; | |
var phaseStep = _.freq.process(tickID).cells[0][0] / _.samplerate; | |
var reg = _.reg; | |
var mul = _.mul, add = _.add; | |
var i, imax; | |
if (_.shortFlag) { | |
for (i = 0, imax = cell.length; i < imax; ++i) { | |
if (phase >= 1) { | |
reg >>= 1; | |
reg |= ((reg ^ (reg >> 6)) & 1) << 15; | |
lastValue = ((reg & 1) - 0.5); | |
phase -= 1; | |
} | |
cell[i] = lastValue * mul + add; | |
phase += phaseStep; | |
} | |
} else { | |
for (i = 0, imax = cell.length; i < imax; ++i) { | |
if (phase >= 1) { | |
reg >>= 1; | |
reg |= ((reg ^ (reg >> 1)) & 1) << 15; | |
lastValue = ((reg & 1) - 0.5); | |
phase -= 1; | |
} | |
cell[i] = lastValue * mul + add; | |
phase += phaseStep; | |
} | |
} | |
_.reg = reg; | |
_.phase = phase; | |
_.lastValue = lastValue; | |
} | |
return this; | |
}; | |
fn.register("fnoise", FNoiseNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var GateChannelNode = (function() { | |
function GateChannelNode(parent) { | |
T.Object.call(this, 2, []); | |
fn.fixAR(this); | |
this._.parent = parent; | |
} | |
fn.extend(GateChannelNode); | |
GateChannelNode.prototype.process = function(tickID) { | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
this._.parent.process(tickID); | |
} | |
return this; | |
}; | |
return GateChannelNode; | |
})(); | |
function GateNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
this._.selected = 0; | |
this._.outputs = []; | |
} | |
fn.extend(GateNode); | |
var $ = GateNode.prototype; | |
Object.defineProperties($, { | |
selected: { | |
set: function(value) { | |
var _ = this._; | |
if (typeof value === "number") { | |
_.selected = value; | |
var outputs = _.outputs; | |
for (var i = 0, imax = outputs.length; i < imax; ++i) { | |
if (outputs[i]) { | |
outputs[i].cells[0].set(fn.emptycell); | |
outputs[i].cells[1].set(fn.emptycell); | |
outputs[i].cells[2].set(fn.emptycell); | |
} | |
} | |
} | |
}, | |
get: function() { | |
return this._.selected; | |
} | |
} | |
}); | |
$.at = function(index) { | |
var _ = this._; | |
var output = _.outputs[index]; | |
if (!output) { | |
_.outputs[index] = output = new GateChannelNode(this); | |
} | |
return output; | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
fn.outputSignalAR(this); | |
var output = _.outputs[_.selected]; | |
if (output) { | |
output.cells[0].set(this.cells[0]); | |
output.cells[1].set(this.cells[1]); | |
output.cells[2].set(this.cells[2]); | |
} | |
} | |
return this; | |
}; | |
fn.register("gate", GateNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var FFT = T.modules.FFT; | |
function IFFTNode(_args) { | |
T.Object.call(this, 1, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.fft = new FFT(_.cellsize * 2); | |
_.fftCell = new fn.SignalArray(this._.fft.length); | |
_.realBuffer = new fn.SignalArray(this._.fft.length); | |
_.imagBuffer = new fn.SignalArray(this._.fft.length); | |
} | |
fn.extend(IFFTNode); | |
var $ = IFFTNode.prototype; | |
Object.defineProperties($, { | |
real: { | |
set: function(value) { | |
this._.real = T(value); | |
}, | |
get: function() { | |
return this._.real; | |
} | |
}, | |
imag: { | |
set: function(value) { | |
this._.imag = T(value); | |
}, | |
get: function() { | |
return this._.imag; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
if (_.real && _.imag) { | |
var cell = this.cells[0]; | |
var real = _.realBuffer; | |
var imag = _.imagBuffer; | |
var _real = _.real.process(tickID).cells[0]; | |
var _imag = _.imag.process(tickID).cells[0]; | |
real.set(_real); | |
imag.set(_imag); | |
cell.set(_.fft.inverse(real, imag).subarray(0, _.cellsize)); | |
fn.outputSignalAR(this); | |
} | |
} | |
return this; | |
}; | |
fn.register("ifft", IFFTNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
function IntervalNode(_args) { | |
T.Object.call(this, 1, _args); | |
fn.timer(this); | |
fn.fixKR(this); | |
var _ = this._; | |
_.interval = T(1000); | |
_.count = 0; | |
_.delay = 0; | |
_.timeout = Infinity; | |
_.currentTime = 0; | |
_.delaySamples = 0; | |
_.countSamples = 0; | |
_.onended = fn.make_onended(this); | |
this.on("start", onstart); | |
} | |
fn.extend(IntervalNode); | |
var onstart = function() { | |
var _ = this._; | |
this.playbackState = fn.PLAYING_STATE; | |
_.delaySamples = (_.samplerate * (_.delay * 0.001))|0; | |
_.countSamples = _.count = _.currentTime = 0; | |
}; | |
Object.defineProperty(onstart, "unremovable", { | |
value:true, writable:false | |
}); | |
var $ = IntervalNode.prototype; | |
Object.defineProperties($, { | |
interval: { | |
set: function(value) { | |
if (typeof value === "string") { | |
value = timevalue(value); | |
if (value <= 0) { | |
value = 0; | |
} | |
} | |
this._.interval = T(value); | |
}, | |
get: function() { | |
return this._.interval; | |
} | |
}, | |
delay: { | |
set: function(value) { | |
if (typeof value === "string") { | |
value = timevalue(value); | |
} | |
if (typeof value === "number" && value >= 0) { | |
this._.delay = value; | |
this._.delaySamples = (this._.samplerate * (value * 0.001))|0; | |
} | |
}, | |
get: function() { | |
return this._.delay; | |
} | |
}, | |
count: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.count = value; | |
} | |
}, | |
get: function() { | |
return this._.count; | |
} | |
}, | |
timeout: { | |
set: function(value) { | |
if (typeof value === "string") { | |
value = timevalue(value); | |
} | |
if (typeof value === "number" && value >= 0) { | |
this._.timeout = value; | |
} | |
}, | |
get: function() { | |
return this._.timeout; | |
} | |
}, | |
currentTime: { | |
get: function() { | |
return this._.currentTime; | |
} | |
} | |
}); | |
$.bang = function() { | |
var _ = this._; | |
this.playbackState = fn.PLAYING_STATE; | |
_.delaySamples = (_.samplerate * (_.delay * 0.001))|0; | |
_.countSamples = _.count = _.currentTime = 0; | |
_.emit("bang"); | |
return this; | |
}; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
if (_.delaySamples > 0) { | |
_.delaySamples -= cell.length; | |
} | |
var interval = _.interval.process(tickID).cells[0][0]; | |
if (_.delaySamples <= 0) { | |
_.countSamples -= cell.length; | |
if (_.countSamples <= 0) { | |
_.countSamples += (_.samplerate * interval * 0.001)|0; | |
var nodes = this.nodes; | |
var count = _.count; | |
var x = count * _.mul + _.add; | |
for (var j = 0, jmax = cell.length; j < jmax; ++j) { | |
cell[j] = x; | |
} | |
for (var i = 0, imax = nodes.length; i < imax; ++i) { | |
nodes[i].bang(count); | |
} | |
_.count += 1; | |
} | |
} | |
_.currentTime += fn.currentTimeIncr; | |
if (_.currentTime >= _.timeout) { | |
fn.nextTick(_.onended); | |
} | |
} | |
return this; | |
}; | |
fn.register("interval", IntervalNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
function LagNode(_args) { | |
T.Object.call(this, 1, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
var bits = Math.ceil(Math.log(_.samplerate) * Math.LOG2E); | |
_.buffersize = 1 << bits; | |
_.buffermask = _.buffersize - 1; | |
_.buffer = new fn.SignalArray(_.buffersize); | |
_.time = 0; | |
_.readIndex = 0; | |
_.writeIndex = 0; | |
} | |
fn.extend(LagNode); | |
var $ = LagNode.prototype; | |
Object.defineProperties($, { | |
time: { | |
set: function(value) { | |
if (typeof value === "string") { | |
value = timevalue(value); | |
} | |
if (typeof value === "number" && value > 0) { | |
var _ = this._; | |
_.time = value; | |
var offset = (value * 0.001 * _.samplerate)|0; | |
if (offset > _.buffermask) { | |
offset = _.buffermask; | |
} | |
_.writeIndex = (_.readIndex + offset) & _.buffermask; | |
} | |
}, | |
get: function() { | |
return this._.time; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
var cell = this.cells[0]; | |
var buffer = _.buffer; | |
var mask = _.buffermask; | |
var readIndex = _.readIndex; | |
var writeIndex = _.writeIndex; | |
var i, imax = cell.length; | |
for (i = 0; i < imax; ++i) { | |
buffer[writeIndex] = cell[i]; | |
cell[i] = buffer[readIndex]; | |
readIndex += 1; | |
writeIndex = (writeIndex + 1) & mask; | |
} | |
_.readIndex = readIndex & mask; | |
_.writeIndex = writeIndex; | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("lag", LagNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function MapNode(_args) { | |
T.Object.call(this, 1, _args); | |
var _ = this._; | |
_.input = 0; | |
_.value = 0; | |
_.prev = null; | |
_.ar = false; | |
_.map = defaultFunction; | |
} | |
fn.extend(MapNode); | |
var defaultFunction = function(x) { | |
return x; | |
}; | |
var $ = MapNode.prototype; | |
Object.defineProperties($, { | |
input: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.input = value; | |
} | |
}, | |
get: function() { | |
return this._.input; | |
} | |
}, | |
map: { | |
set: function(value) { | |
if (typeof value === "function") { | |
this._.map = value; | |
} | |
}, | |
get: function() { | |
return this._.map; | |
} | |
} | |
}); | |
$.bang = function() { | |
this._.prev = null; | |
this._.emit("bang"); | |
return this; | |
}; | |
$.at = function(input) { | |
return (this._.map) ? this._.map(input) : 0; | |
}; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var len = this.nodes.length; | |
var i, imax = cell.length; | |
if (_.ar && len) { | |
fn.inputSignalAR(this); | |
var map = _.map; | |
if (map) { | |
for (i = 0; i < imax; ++i) { | |
cell[i] = map(cell[i]); | |
} | |
} | |
_.value = cell[imax-1]; | |
fn.outputSignalAR(this); | |
} else { | |
var input = len ? fn.inputSignalKR(this) : _.input; | |
if (_.map && _.prev !== input) { | |
_.prev = input; | |
_.value = _.map(input); | |
} | |
var value = _.value * _.mul + _.add; | |
for (i = 0; i < imax; ++i) { | |
cell[i] = value; | |
} | |
} | |
} | |
return this; | |
}; | |
fn.register("map", MapNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function MaxNode(_args) { | |
T.Object.call(this, 1, _args); | |
} | |
fn.extend(MaxNode); | |
var $ = MaxNode.prototype; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var nodes = this.nodes; | |
var i, imax = nodes.length; | |
var j, jmax = cell.length; | |
var tmp, val; | |
if (_.ar) { | |
if (nodes.length > 0) { | |
tmp = nodes[0].process(tickID).cells[0]; | |
cell.set(tmp); | |
for (i = 1; i < imax; ++i) { | |
tmp = nodes[i].process(tickID).cells[0]; | |
for (j = 0; j < jmax; ++j) { | |
val = tmp[j]; | |
if (cell[j] < val) { | |
cell[j] = val; | |
} | |
} | |
} | |
} else { | |
for (j = 0; j < jmax; ++j) { | |
cell[j] = 0; | |
} | |
} | |
fn.outputSignalAR(this); | |
} else { | |
if (nodes.length > 0) { | |
tmp = nodes[0].process(tickID).cells[0][0]; | |
for (i = 1; i < imax; ++i) { | |
val = nodes[i].process(tickID).cells[0][0]; | |
if (tmp < val) { | |
tmp = val; | |
} | |
} | |
} else { | |
tmp = 0; | |
} | |
cell[0] = tmp; | |
fn.outputSignalKR(this); | |
} | |
} | |
return this; | |
}; | |
fn.register("max", MaxNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
if (T.envtype !== "browser") { | |
return; | |
} | |
var fn = T.fn; | |
var BUFFER_SIZE = 4096; | |
var BUFFER_MASK = BUFFER_SIZE - 1; | |
function MediaStreamNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.src = _.func = null; | |
_.bufferL = new fn.SignalArray(BUFFER_SIZE); | |
_.bufferR = new fn.SignalArray(BUFFER_SIZE); | |
_.readIndex = 0; | |
_.writeIndex = 0; | |
_.totalRead = 0; | |
_.totalWrite = 0; | |
} | |
fn.extend(MediaStreamNode); | |
var $ = MediaStreamNode.prototype; | |
$.listen = function(audio) { | |
var _impl = impl[T.env]; | |
if (_impl) { | |
_impl.set.call(this, audio); | |
_impl.listen.call(this); | |
} | |
}; | |
$.unlisten = function() { | |
var _impl = impl[T.env]; | |
if (_impl) { | |
_impl.unlisten.call(this); | |
} | |
this.cells[0].set(fn.emptycell); | |
this.cells[1].set(fn.emptycell); | |
this.cells[2].set(fn.emptycell); | |
var _ = this._; | |
var bufferL = _.bufferL, bufferR = _.bufferR; | |
for (var i = 0, imax = bufferL.length; i < imax; ++i) { | |
bufferL[i] = bufferR[i] = 0; | |
} | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (_.src === null) { | |
return this; | |
} | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var cellsize = _.cellsize; | |
if (_.totalWrite > _.totalRead + cellsize) { | |
var begin = _.readIndex; | |
var end = begin + cellsize; | |
this.cells[1].set(_.bufferL.subarray(begin, end)); | |
this.cells[2].set(_.bufferR.subarray(begin, end)); | |
_.readIndex = end & BUFFER_MASK; | |
_.totalRead += cellsize; | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
var impl = {}; | |
impl.webkit = { | |
set: function(src) { | |
var _ = this._; | |
/*global HTMLMediaElement:true */ | |
if (src instanceof HTMLMediaElement) { | |
var context = fn._audioContext; | |
_.src = context.createMediaElementSource(src); | |
} | |
/*global HTMLMediaElement:false */ | |
}, | |
listen: function() { | |
var _ = this._; | |
var context = fn._audioContext; | |
_.gain = context.createGainNode(); | |
_.gain.gain.value = 0; | |
_.node = context.createJavaScriptNode(1024, 2, 2); | |
_.node.onaudioprocess = onaudioprocess(this); | |
_.src.connect(_.node); | |
_.node.connect(_.gain); | |
_.gain.connect(context.destination); | |
}, | |
unlisten: function() { | |
var _ = this._; | |
if (_.src) { | |
_.src.disconnect(); | |
} | |
if (_.gain) { | |
_.gain.disconnect(); | |
} | |
if (_.node) { | |
_.node.disconnect(); | |
} | |
} | |
}; | |
var onaudioprocess = function(self) { | |
return function(e) { | |
var _ = self._; | |
var ins = e.inputBuffer; | |
var length = ins.length; | |
var writeIndex = _.writeIndex; | |
_.bufferL.set(ins.getChannelData(0), writeIndex); | |
_.bufferR.set(ins.getChannelData(1), writeIndex); | |
_.writeIndex = (writeIndex + length) & BUFFER_MASK; | |
_.totalWrite += length; | |
}; | |
}; | |
impl.moz = { | |
set: function(src) { | |
var _ = this._; | |
/*global HTMLAudioElement:true */ | |
if (src instanceof HTMLAudioElement) { | |
_.src = src; | |
_.istep = _.samplerate / src.mozSampleRate; | |
} | |
/*global HTMLAudioElement:false */ | |
}, | |
listen: function() { | |
var _ = this._; | |
var o0 = _.bufferL; | |
var o1 = _.bufferR; | |
var prev0 = 0, prev1 = 0; | |
if (_.src.mozChannels === 2) { | |
_.x = 0; | |
_.func = function(e) { | |
var writeIndex = _.writeIndex; | |
var totalWrite = _.totalWrite; | |
var samples = e.frameBuffer; | |
var x, istep = _.istep; | |
var i, imax = samples.length; | |
x = _.x; | |
for (i = 0; i < imax; i+= 2) { | |
x += istep; | |
while (x > 0) { | |
o0[writeIndex] = (samples[i ] + prev0) * 0.5; | |
o1[writeIndex] = (samples[i+1] + prev1) * 0.5; | |
writeIndex = (writeIndex + 1) & BUFFER_MASK; | |
++totalWrite; | |
x -= 1; | |
} | |
prev0 = samples[i ]; | |
prev1 = samples[i+1]; | |
} | |
_.x = x; | |
_.writeIndex = writeIndex; | |
_.totalWrite = totalWrite; | |
}; | |
} else { | |
_.x = 0; | |
_.func = function(e) { | |
var writeIndex = _.writeIndex; | |
var totalWrite = _.totalWrite; | |
var samples = e.frameBuffer; | |
var x, istep = _.istep; | |
var i, imax = samples.length; | |
x = _.x; | |
for (i = 0; i < imax; ++i) { | |
x += istep; | |
while (x >= 0) { | |
o0[writeIndex] = o1[writeIndex] = (samples[i] + prev0) * 0.5; | |
writeIndex = (writeIndex + 1) & BUFFER_MASK; | |
++totalWrite; | |
x -= 1; | |
} | |
prev0 = samples[i]; | |
} | |
_.x = x; | |
_.writeIndex = writeIndex; | |
_.totalWrite = totalWrite; | |
}; | |
} | |
_.src.addEventListener("MozAudioAvailable", _.func); | |
}, | |
unlisten: function() { | |
var _ = this._; | |
if (_.func) { | |
_.src.removeEventListener("MozAudioAvailable", _.func); | |
_.func = null; | |
} | |
} | |
}; | |
fn.register("mediastream", MediaStreamNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function MidiCpsNode(_args) { | |
T.Object.call(this, 1, _args); | |
var _ = this._; | |
_.midi = 0; | |
_.value = 0; | |
_.prev = null; | |
_.a4 = 440; | |
_.ar = false; | |
} | |
fn.extend(MidiCpsNode); | |
var $ = MidiCpsNode.prototype; | |
Object.defineProperties($, { | |
midi: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.midi = value; | |
} | |
}, | |
get: function() { | |
return this._.midi; | |
} | |
}, | |
a4: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.a4 = value; | |
this._.prev = null; | |
} | |
}, | |
get: function() { | |
return this._.a4; | |
} | |
} | |
}); | |
$.bang = function() { | |
this._.prev = null; | |
this._.emit("bang"); | |
return this; | |
}; | |
$.at = function(midi) { | |
var _ = this._; | |
return _.a4 * Math.pow(2, (midi - 69) / 12); | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var cell = this.cells[0]; | |
var len = this.nodes.length; | |
var i, imax = cell.length; | |
if (_.ar && len) { | |
fn.inputSignalAR(this); | |
var a4 = _.a4; | |
for (i = 0; i < imax; ++i) { | |
cell[i] = a4 * Math.pow(2, (cell[i] - 69) / 12); | |
} | |
_.value = cell[imax-1]; | |
fn.outputSignalAR(this); | |
} else { | |
var input = (len) ? fn.inputSignalKR(this) : _.midi; | |
if (_.prev !== input) { | |
_.prev = input; | |
_.value = _.a4 * Math.pow(2, (input - 69) / 12); | |
} | |
cell[0] = _.value; | |
fn.outputSignalKR(this); | |
} | |
} | |
return this; | |
}; | |
fn.register("midicps", MidiCpsNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function MidiRatioNode(_args) { | |
T.Object.call(this, 1, _args); | |
var _ = this._; | |
_.midi = 0; | |
_.value = 0; | |
_.prev = null; | |
_.range = 12; | |
_.ar = false; | |
} | |
fn.extend(MidiRatioNode); | |
var $ = MidiRatioNode.prototype; | |
Object.defineProperties($, { | |
midi: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.midi = value; | |
} | |
}, | |
get: function() { | |
return this._.midi; | |
} | |
}, | |
range: { | |
set: function(value) { | |
if (typeof value === "number" && value > 0) { | |
this._.range = value; | |
} | |
}, | |
get: function() { | |
return this._.range; | |
} | |
} | |
}); | |
$.bang = function() { | |
this._.prev = null; | |
this._.emit("bang"); | |
return this; | |
}; | |
$.at = function(midi) { | |
var _ = this._; | |
return Math.pow(2, midi / _.range); | |
}; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var len = this.nodes.length; | |
var i, imax = cell.length; | |
if (_.ar && len) { | |
fn.inputSignalAR(this); | |
var range = _.range; | |
for (i = 0; i < imax; ++i) { | |
cell[i] = Math.pow(2, cell[i] / range); | |
} | |
_.value = cell[imax-1]; | |
fn.outputSignalAR(this); | |
} else { | |
var input = (this.nodes.length) ? fn.inputSignalKR(this) : _.midi; | |
if (_.prev !== input) { | |
_.prev = input; | |
_.value = Math.pow(2, input / _.range); | |
} | |
var value = _.value * _.mul + _.add; | |
for (i = 0; i < imax; ++i) { | |
cell[i] = value; | |
} | |
} | |
} | |
return this; | |
}; | |
fn.register("midiratio", MidiRatioNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function MinNode(_args) { | |
T.Object.call(this, 1, _args); | |
} | |
fn.extend(MinNode); | |
var $ = MinNode.prototype; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var nodes = this.nodes; | |
var i, imax = nodes.length; | |
var j, jmax = cell.length; | |
var tmp, val; | |
if (_.ar) { | |
if (nodes.length > 0) { | |
tmp = nodes[0].process(tickID).cells[0]; | |
cell.set(tmp); | |
for (i = 1; i < imax; ++i) { | |
tmp = nodes[i].process(tickID).cells[0]; | |
for (j = 0; j < jmax; ++j) { | |
val = tmp[j]; | |
if (cell[j] > val) { | |
cell[j] = val; | |
} | |
} | |
} | |
} else { | |
for (j = 0; j < jmax; ++j) { | |
cell[j] = 0; | |
} | |
} | |
fn.outputSignalAR(this); | |
} else { | |
if (nodes.length > 0) { | |
tmp = nodes[0].process(tickID).cells[0][0]; | |
for (i = 1; i < imax; ++i) { | |
val = nodes[i].process(tickID).cells[0][0]; | |
if (tmp > val) { | |
tmp = val; | |
} | |
} | |
} else { | |
tmp = 0; | |
} | |
cell[0] = tmp; | |
fn.outputSignalKR(this); | |
} | |
} | |
return this; | |
}; | |
fn.register("min", MinNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function MML(_args) { | |
T.Object.call(this, 0, _args); | |
fn.timer(this); | |
fn.fixKR(this); | |
var _ = this._; | |
_.tracks = []; | |
_.onended = fn.make_onended(this); | |
_.currentTime = 0; | |
this.on("start", onstart); | |
} | |
fn.extend(MML); | |
var onstart = function() { | |
var self = this, _ = this._; | |
var mml = _.mml; | |
if (typeof mml === "string") { | |
mml = [mml]; | |
} | |
_.tracks = mml.map(function(mml, i) { | |
return new MMLTrack(self, i, mml); | |
}); | |
_.currentTime = 0; | |
this.playbackState = fn.PLAYING_STATE; | |
}; | |
Object.defineProperty(onstart, "unremoved", { | |
value:true, writable:false | |
}); | |
var $ = MML.prototype; | |
Object.defineProperties($, { | |
mml: { | |
set: function(value) { | |
var _ = this._; | |
if (typeof value === "string" || Array.isArray(value)) { | |
_.mml = value; | |
} | |
}, | |
get: function() { | |
return this._.mml; | |
} | |
}, | |
currentTime: { | |
get: function() { | |
return this._.currentTime; | |
} | |
} | |
}); | |
$.on = $.addListener = function(type, listener) { | |
if (type === "mml") { | |
type = "data"; | |
console.warn("A 'mml' event listener was deprecated in ~v13.03.01. use 'data' event listener."); | |
} | |
this._.events.on(type, listener); | |
return this; | |
}; | |
$.once = function(type, listener) { | |
if (type === "mml") { | |
type = "data"; | |
console.warn("A 'mml' event listener was deprecated in ~v13.03.01. use 'data' event listener."); | |
} | |
this._.events.once(type, listener); | |
return this; | |
}; | |
$.off = $.removeListener = function(type, listener) { | |
if (type === "mml") { | |
type = "data"; | |
console.warn("A 'mml' event listener was deprecated in ~v13.03.01. use 'data' event listener."); | |
} | |
this._.events.off(type, listener); | |
return this; | |
}; | |
$.removeAllListeners = function(type) { | |
if (type === "mml") { | |
console.warn("A 'mml' event listener was deprecated in ~v13.03.01. use 'data' event listener."); | |
type = "data"; | |
} | |
this._.events.removeAllListeners(type); | |
return this; | |
}; | |
$.listeners = function(type) { | |
if (type === "mml") { | |
console.warn("A 'mml' event listener was deprecated in ~v13.03.01. use 'data' event listener."); | |
type = "data"; | |
} | |
return this._.events.listeners(type); | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var i, imax; | |
var tracks = _.tracks; | |
for (i = 0, imax = tracks.length; i < imax; ++i) { | |
tracks[i].process(); | |
} | |
while (i--) { | |
if (tracks[i].ended) { | |
tracks.splice(i, 1); | |
} | |
} | |
if (tracks.length === 0) { | |
fn.nextTick(_.onended); | |
} | |
_.currentTime += fn.currentTimeIncr; | |
} | |
return this; | |
}; | |
fn.register("mml", MML); | |
var MMLTrack = (function() { | |
function MMLTrack(sequencer, trackNum, mml) { | |
var _ = this._ = {}; | |
_.sequencer = sequencer; | |
_.trackNum = trackNum; | |
_.commands = compile(mml); | |
_.status = {t:120, l:4, o:4, v:12, q:6, dot:0, tie:false}; | |
_.index = 0; | |
_.queue = []; | |
_.currentTime = 0; | |
_.queueTime = 0; | |
_.segnoIndex = -1; | |
_.loopStack = []; | |
_.prevNote = 0; | |
_.remain = Infinity; | |
this.ended = false; | |
sched(this); | |
} | |
var EOF = 0; | |
var NOTEON = 1; | |
var NOTEOFF = 2; | |
var COMMAND = 3; | |
MMLTrack.prototype.process = function() { | |
var _ = this._; | |
var sequencer = _.sequencer; | |
var trackNum = _.trackNum; | |
var queue = _.queue; | |
var eof = false; | |
if (queue.length) { | |
while (queue[0][0] <= _.currentTime) { | |
var nextItem = _.queue.shift(); | |
switch (nextItem[1]) { | |
case NOTEON: | |
noteOn(sequencer, trackNum, nextItem[2], nextItem[3]); | |
_.remain = nextItem[4]; | |
sched(this); | |
break; | |
case NOTEOFF: | |
noteOff(sequencer, trackNum, nextItem[2], nextItem[3]); | |
break; | |
case COMMAND: | |
command(sequencer, nextItem[2]); | |
break; | |
case EOF: | |
eof = true; | |
break; | |
} | |
if (queue.length === 0) { | |
break; | |
} | |
} | |
} | |
_.remain -= fn.currentTimeIncr; | |
if (eof) { | |
this.ended = true; | |
} | |
_.currentTime += fn.currentTimeIncr; | |
}; | |
var noteOn = function(sequencer, trackNum, noteNum, velocity) { | |
var gen, i, imax; | |
var nodes = sequencer.nodes; | |
for (i = 0, imax = nodes.length; i < imax; ++i) { | |
gen = nodes[i]; | |
if (gen.noteOn) { | |
gen.noteOn(noteNum, velocity); | |
} else { | |
gen.bang(); | |
} | |
} | |
sequencer._.emit("data", "noteOn", { | |
trackNum:trackNum, noteNum:noteNum, velocity:velocity | |
}); | |
}; | |
var noteOff = function(sequencer, trackNum, noteNum, velocity) { | |
var gen, i, imax; | |
var nodes = sequencer.nodes; | |
for (i = 0, imax = nodes.length; i < imax; ++i) { | |
gen = nodes[i]; | |
if (gen.noteOff) { | |
gen.noteOff(noteNum, velocity); | |
} else if (gen.release) { | |
gen.release(); | |
} | |
} | |
sequencer._.emit("data", "noteOff", { | |
trackNum:trackNum, noteNum:noteNum, velocity:velocity | |
}); | |
}; | |
var command = function(sequencer, cmd) { | |
sequencer._.emit("data", "command", { | |
command: cmd | |
}); | |
}; | |
var sched = function(self) { | |
var _ = self._; | |
var sequencer = _.sequencer; | |
var cmd, commands = _.commands; | |
var queue = _.queue; | |
var index = _.index; | |
var status = _.status; | |
var queueTime = _.queueTime; | |
var loopStack = _.loopStack; | |
var tempo, val, len, dot, vel; | |
var duration, quantize, pending, _queueTime; | |
var peek; | |
var i, imax; | |
pending = []; | |
outer: | |
while (true) { | |
if (commands.length <= index) { | |
if (_.segnoIndex >= 0) { | |
index = _.segnoIndex; | |
} else { | |
break; | |
} | |
} | |
cmd = commands[index++]; | |
switch (cmd.name) { | |
case "@": | |
queue.push([queueTime, COMMAND, cmd.val]); | |
break; | |
case "n": | |
tempo = status.t || 120; | |
if (cmd.len !== null) { | |
len = cmd.len; | |
dot = cmd.dot || 0; | |
} else { | |
len = status.l; | |
dot = cmd.dot || status.dot; | |
} | |
duration = (60 / tempo) * (4 / len) * 1000; | |
duration *= [1, 1.5, 1.75, 1.875][dot] || 1; | |
vel = status.v << 3; | |
if (status.tie) { | |
for (i = queue.length; i--; ) { | |
if (queue[i][2]) { | |
queue.splice(i, 1); | |
break; | |
} | |
} | |
val = _.prevNote; | |
} else { | |
val = _.prevNote = (cmd.val) + (status.o + 1) * 12; | |
queue.push([queueTime, NOTEON, val, vel, duration]); | |
} | |
if (len > 0) { | |
quantize = status.q / 8; | |
// noteOff | |
if (quantize < 1) { | |
_queueTime = queueTime + (duration * quantize); | |
queue.push([_queueTime, NOTEOFF, val, vel]); | |
for (i = 0, imax = pending.length; i < imax; ++i) { | |
queue.push([_queueTime, NOTEOFF, pending[i], vel]); | |
} | |
} | |
pending = []; | |
queueTime += duration; | |
if (!status.tie) { | |
break outer; | |
} | |
} else { | |
pending.push(val); | |
} | |
status.tie = false; | |
break; | |
case "r": | |
tempo = status.t || 120; | |
if (cmd.len !== null) { | |
len = cmd.len; | |
dot = cmd.dot || 0; | |
} else { | |
len = status.l; | |
dot = cmd.dot || status.dot; | |
} | |
if (len > 0) { | |
duration = (60 / tempo) * (4 / len) * 1000; | |
duration *= [1, 1.5, 1.75, 1.875][dot] || 1; | |
queueTime += duration; | |
} | |
break; | |
case "l": | |
status.l = cmd.val; | |
status.dot = cmd.dot; | |
break; | |
case "o": | |
status.o = cmd.val; | |
break; | |
case "<": | |
if (status.o < 9) { | |
status.o += 1; | |
} | |
break; | |
case ">": | |
if (status.o > 0) { | |
status.o -= 1; | |
} | |
break; | |
case "v": | |
status.v = cmd.val; | |
break; | |
case "(": | |
if (status.v < 15) { | |
status.v += 1; | |
} | |
break; | |
case ")": | |
if (status.v > 0) { | |
status.v -= 1; | |
} | |
break; | |
case "q": | |
status.q = cmd.val; | |
break; | |
case "&": | |
status.tie = true; | |
break; | |
case "$": | |
_.segnoIndex = index; | |
break; | |
case "[": | |
loopStack.push([index, null, null]); | |
break; | |
case "|": | |
peek = loopStack[loopStack.length - 1]; | |
if (peek) { | |
if (peek[1] === 1) { | |
loopStack.pop(); | |
index = peek[2]; | |
} | |
} | |
break; | |
case "]": | |
peek = loopStack[loopStack.length - 1]; | |
if (peek) { | |
if (peek[1] === null) { | |
peek[1] = cmd.count; | |
peek[2] = index; | |
} | |
peek[1] -= 1; | |
if (peek[1] === 0) { | |
loopStack.pop(); | |
} else { | |
index = peek[0]; | |
} | |
} | |
break; | |
case "t": | |
status.t = (cmd.val === null) ? 120 : cmd.val; | |
break; | |
case "EOF": | |
queue.push([queueTime, EOF]); | |
break; | |
} | |
} | |
_.index = index; | |
_.queueTime = queueTime; | |
}; | |
var compile = function(mml) { | |
var def, re, m, cmd; | |
var i, imax, j, jmax; | |
var checked = new Array(mml.length); | |
var commands = []; | |
for (i = 0, imax = MMLCommands.length; i < imax; ++i) { | |
def = MMLCommands[i]; | |
re = def.re; | |
while ((m = re.exec(mml))) { | |
if (!checked[m.index]) { | |
for (j = 0, jmax = m[0].length; j < jmax; ++j) { | |
checked[m.index + j] = true; | |
} | |
if (def.func) { | |
cmd = def.func(m); | |
} else { | |
cmd = {name:m[0]}; | |
} | |
if (cmd) { | |
cmd.index = m.index; | |
cmd.origin = m[0]; | |
commands.push(cmd); | |
} | |
} | |
while (re.lastIndex < mml.length) { | |
if (!checked[re.lastIndex]) { | |
break; | |
} | |
++re.lastIndex; | |
} | |
} | |
} | |
commands.sort(function(a, b) { | |
return a.index - b.index; | |
}); | |
commands.push({name:"EOF"}); | |
return commands; | |
}; | |
var MMLCommands = [ | |
{ re:/@(\d*)/g, func: function(m) { | |
return { | |
name: "@", | |
val: m[1] || null | |
}; | |
}}, | |
{ re:/([cdefgab])([\-+]?)(\d*)(\.*)/g, func: function(m) { | |
return { | |
name: "n", | |
val : {c:0,d:2,e:4,f:5,g:7,a:9,b:11}[m[1]] + ({"-":-1,"+":+1}[m[2]]||0), | |
len : (m[3] === "") ? null : Math.min(m[3]|0, 64), | |
dot : m[4].length | |
}; | |
}}, | |
{ re:/r(\d*)(\.*)/g, func: function(m) { | |
return { | |
name: "r", | |
len : (m[1] === "") ? null : Math.max(1, Math.min(m[1]|0, 64)), | |
dot : m[2].length | |
}; | |
}}, | |
{ re:/&/g }, | |
{ re:/l(\d*)(\.*)/g, func: function(m) { | |
return { | |
name: "l", | |
val : (m[1] === "") ? 4 : Math.min(m[1]|0, 64), | |
dot : m[2].length | |
}; | |
}}, | |
{ re:/o([0-9])/g, func: function(m) { | |
return { | |
name: "o", | |
val : (m[1] === "") ? 4 : m[1]|0 | |
}; | |
}}, | |
{ re:/[<>]/g }, | |
{ re:/v(\d*)/g, func: function(m) { | |
return { | |
name: "v", | |
val : (m[1] === "") ? 12 : Math.min(m[1]|0, 15) | |
}; | |
}}, | |
{ re:/[()]/g }, | |
{ re:/q([0-8])/g, func: function(m) { | |
return { | |
name: "q", | |
val : (m[1] === "") ? 6 : Math.min(m[1]|0, 8) | |
}; | |
}}, | |
{ re:/\[/g }, | |
{ re:/\|/g }, | |
{ re:/\](\d*)/g, func: function(m) { | |
return { | |
name: "]", | |
count: (m[1]|0)||2 | |
}; | |
}}, | |
{ re:/t(\d*)/g, func: function(m) { | |
return { | |
name: "t", | |
val : (m[1] === "") ? null : Math.max(5, Math.min(m[1]|0, 300)) | |
}; | |
}}, | |
{ re:/\$/g } | |
]; | |
return MMLTrack; | |
})(); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function MonoNode(_args) { | |
T.Object.call(this, 1, _args); | |
} | |
fn.extend(MonoNode); | |
MonoNode.prototype.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
if (_.ar) { | |
fn.inputSignalAR(this); | |
fn.outputSignalAR(this); | |
} else { | |
this.cells[0][0] = fn.inputSignalKR(this); | |
fn.outputSignalKR(this); | |
} | |
} | |
return this; | |
}; | |
fn.register("mono", MonoNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function MulNode(_args) { | |
T.Object.call(this, 2, _args); | |
} | |
fn.extend(MulNode); | |
var $ = MulNode.prototype; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var nodes = this.nodes; | |
var cell = this.cells[0]; | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var i, imax = nodes.length; | |
var j, jmax = cell.length; | |
var tmp, tmpL, tmpR; | |
if (_.ar) { | |
if (nodes.length > 0) { | |
nodes[0].process(tickID); | |
tmpL = nodes[0].cells[1]; | |
tmpR = nodes[0].cells[2]; | |
cellL.set(tmpL); | |
cellR.set(tmpR); | |
for (i = 1; i < imax; ++i) { | |
nodes[i].process(tickID); | |
tmpL = nodes[i].cells[1]; | |
tmpR = nodes[i].cells[2]; | |
for (j = 0; j < jmax; ++j) { | |
cellL[j] *= tmpL[j]; | |
cellR[j] *= tmpR[j]; | |
} | |
} | |
} else { | |
for (j = 0; j < jmax; ++j) { | |
cellL[j] = cellR[j] = 0; | |
} | |
} | |
fn.outputSignalAR(this); | |
} else { | |
if (nodes.length > 0) { | |
tmp = nodes[0].process(tickID).cells[0][0]; | |
for (i = 1; i < imax; ++i) { | |
tmp *= nodes[i].process(tickID).cells[0][0]; | |
} | |
} else { | |
tmp = 0; | |
} | |
cell[0] = tmp; | |
fn.outputSignalKR(this); | |
} | |
} | |
return this; | |
}; | |
fn.register("*", MulNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function NDictNode(_args) { | |
T.Object.call(this, 1, _args); | |
var _ = this._; | |
_.defaultValue = 0; | |
_.index = 0; | |
_.dict = {}; | |
_.ar = false; | |
} | |
fn.extend(NDictNode); | |
var $ = NDictNode.prototype; | |
Object.defineProperties($, { | |
dict: { | |
set: function(value) { | |
if (typeof value === "object") { | |
this._.dict = value; | |
} else if (typeof value === "function") { | |
var dict = {}; | |
for (var i = 0; i < 128; ++i) { | |
dict[i] = value(i); | |
} | |
this._.dict = dict; | |
} | |
}, | |
get: function() { | |
return this._.dict; | |
} | |
}, | |
defaultValue: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.defaultValue = value; | |
} | |
}, | |
get: function() { | |
return this._.defaultValue; | |
} | |
}, | |
index: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.index = value; | |
} | |
}, | |
get: function() { | |
return this._.index; | |
} | |
} | |
}); | |
$.at = function(index) { | |
var _ = this._; | |
return (_.dict[index|0] || _.defaultValue) * _.mul + _.add; | |
}; | |
$.clear = function() { | |
this._.dict = {}; | |
return this; | |
}; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var len = this.nodes.length; | |
var index, value; | |
var dict = _.dict, defaultValue = _.defaultValue; | |
var mul = _.mul, add = _.add; | |
var i, imax = cell.length; | |
if (_.ar && len) { | |
fn.inputSignalAR(this); | |
for (i = 0; i < imax; ++i) { | |
index = cell[i]; | |
if (index < 0) { | |
index = (index - 0.5)|0; | |
} else { | |
index = (index + 0.5)|0; | |
} | |
cell[i] = (dict[index] || defaultValue) * mul + add; | |
} | |
fn.outputSignalAR(this); | |
} else { | |
index = (this.nodes.length) ? fn.inputSignalKR(this) : _.index; | |
if (index < 0) { | |
index = (index - 0.5)|0; | |
} else { | |
index = (index + 0.5)|0; | |
} | |
value = (dict[index] || defaultValue) * mul + add; | |
for (i = 0; i < imax; ++i) { | |
cell[i] = value; | |
} | |
} | |
} | |
return this; | |
}; | |
fn.register("ndict", NDictNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function NoiseNode(_args) { | |
T.Object.call(this, 1, _args); | |
} | |
fn.extend(NoiseNode); | |
var $ = NoiseNode.prototype; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var mul = _.mul, add = _.add; | |
var i, imax, x; | |
if (_.ar) { // audio-rate | |
for (i = 0, imax = cell.length; i < imax; ++i) { | |
cell[i] = (Math.random() * 2 - 1) * mul + add; | |
} | |
} else { // control-rate | |
x = (Math.random() * 2 + 1) * mul + add; | |
for (i = 0, imax = cell.length; i < imax; ++i) { | |
cell[i] = x; | |
} | |
} | |
} | |
return this; | |
}; | |
fn.register("noise", NoiseNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
var Oscillator = T.modules.Oscillator; | |
function OscNode(_args) { | |
T.Object.call(this, 2, _args); | |
var _ = this._; | |
_.freq = T(440); | |
_.phase = T(0); | |
_.osc = new Oscillator(_.samplerate); | |
_.tmp = new fn.SignalArray(_.cellsize); | |
_.osc.step = _.cellsize; | |
this.once("init", oninit); | |
} | |
fn.extend(OscNode); | |
var oninit = function() { | |
var _ = this._; | |
if (!this.wave) { | |
this.wave = "sin"; | |
} | |
_.plotData = _.osc.wave; | |
_.plotLineWidth = 2; | |
_.plotCyclic = true; | |
_.plotBefore = plotBefore; | |
}; | |
var $ = OscNode.prototype; | |
Object.defineProperties($, { | |
wave: { | |
set: function(value) { | |
this._.osc.setWave(value); | |
}, | |
get: function() { | |
return this._.osc.wave; | |
} | |
}, | |
freq: { | |
set: function(value) { | |
if (typeof value === "string") { | |
value = timevalue(value); | |
if (value <= 0) { | |
value = 0; | |
} else { | |
value = 1000 / value; | |
} | |
} | |
this._.freq = T(value); | |
}, | |
get: function() { | |
return this._.freq; | |
} | |
}, | |
phase: { | |
set: function(value) { | |
this._.phase = T(value); | |
this._.osc.feedback = false; | |
}, | |
get: function() { | |
return this._.phase; | |
} | |
}, | |
fb: { | |
set: function(value) { | |
this._.phase = T(value); | |
this._.osc.feedback = true; | |
}, | |
get: function() { | |
return this._.phase; | |
} | |
} | |
}); | |
$.clone = function() { | |
var instance = fn.clone(this); | |
instance._.osc = this._.osc.clone(); | |
instance._.freq = this._.freq; | |
instance._.phase = this._.phase; | |
return instance; | |
}; | |
$.bang = function() { | |
this._.osc.reset(); | |
this._.emit("bang"); | |
return this; | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var i, imax = _.cellsize; | |
if (this.nodes.length) { | |
fn.inputSignalAR(this); | |
} else { | |
for (i = 0; i < imax; ++i) { | |
cellL[i] = cellR[i] = 1; | |
} | |
} | |
var osc = _.osc; | |
var freq = _.freq.process(tickID).cells[0]; | |
var phase = _.phase.process(tickID).cells[0]; | |
osc.frequency = freq[0]; | |
osc.phase = phase[0]; | |
if (_.ar) { | |
var tmp = _.tmp; | |
if (_.freq.isAr) { | |
if (_.phase.isAr) { | |
osc.processWithFreqAndPhaseArray(tmp, freq, phase); | |
} else { | |
osc.processWithFreqArray(tmp, freq); | |
} | |
} else { | |
if (_.phase.isAr) { | |
osc.processWithPhaseArray(tmp, phase); | |
} else { | |
osc.process(tmp); | |
} | |
} | |
for (i = 0; i < imax; ++i) { | |
cellL[i] *= tmp[i]; | |
cellR[i] *= tmp[i]; | |
} | |
} else { | |
var value = osc.next(); | |
for (i = 0; i < imax; ++i) { | |
cellL[i] *= value; | |
cellR[i] *= value; | |
} | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
var plotBefore; | |
if (T.envtype === "browser") { | |
plotBefore = function(context, offset_x, offset_y, width, height) { | |
var y = (height >> 1) + 0.5; | |
context.strokeStyle = "#ccc"; | |
context.lineWidth = 1; | |
context.beginPath(); | |
context.moveTo(offset_x, y + offset_y); | |
context.lineTo(offset_x + width, y + offset_y); | |
context.stroke(); | |
}; | |
} | |
fn.register("osc", OscNode); | |
fn.register("sin", function(_args) { | |
return new OscNode(_args).set("wave", "sin"); | |
}); | |
fn.register("cos", function(_args) { | |
return new OscNode(_args).set("wave", "cos"); | |
}); | |
fn.register("pulse", function(_args) { | |
return new OscNode(_args).set("wave", "pulse"); | |
}); | |
fn.register("tri", function(_args) { | |
return new OscNode(_args).set("wave", "tri"); | |
}); | |
fn.register("saw", function(_args) { | |
return new OscNode(_args).set("wave", "saw"); | |
}); | |
fn.register("fami", function(_args) { | |
return new OscNode(_args).set("wave", "fami"); | |
}); | |
fn.register("konami", function(_args) { | |
return new OscNode(_args).set("wave", "konami"); | |
}); | |
fn.register("+sin", function(_args) { | |
return new OscNode(_args).set("wave", "+sin").kr(); | |
}); | |
fn.register("+pulse", function(_args) { | |
return new OscNode(_args).set("wave", "+pulse").kr(); | |
}); | |
fn.register("+tri", function(_args) { | |
return new OscNode(_args).set("wave", "+tri").kr(); | |
}); | |
fn.register("+saw", function(_args) { | |
return new OscNode(_args).set("wave", "+saw").kr(); | |
}); | |
fn.alias("square", "pulse"); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function PanNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.pos = T(0); | |
_.panL = 0.5; | |
_.panR = 0.5; | |
} | |
fn.extend(PanNode); | |
var $ = PanNode.prototype; | |
Object.defineProperties($, { | |
pos: { | |
set: function(value) { | |
this._.pos = T(value); | |
}, | |
get: function() { | |
return this._.pos; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var pos = _.pos.process(tickID).cells[0][0]; | |
if (_.prevPos !== pos) { | |
var index = pos * 0.5 + 0.5; | |
_.panL = 1 - pos; | |
_.panR = _.prevPos = pos; | |
} | |
var nodes = this.nodes; | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var i, imax = nodes.length; | |
var j, jmax = cellL.length; | |
var tmp; | |
if (imax) { | |
tmp = nodes[0].process(tickID).cells[0]; | |
for (j = 0; j < jmax; ++j) { | |
cellL[j] = cellR[j] = tmp[j]; | |
} | |
for (i = 1; i < imax; ++i) { | |
tmp = nodes[i].process(tickID).cells[0]; | |
for (j = 0; j < jmax; ++j) { | |
cellL[j] = (cellR[j] += tmp[j]); | |
} | |
} | |
var panL = _.panL; | |
var panR = _.panR; | |
for (j = 0; j < jmax; ++j) { | |
cellL[j] = cellL[j] * panL; | |
cellR[j] = cellR[j] * panR; | |
} | |
} else { | |
cellL.set(fn.emptycell); | |
cellR.set(fn.emptycell); | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("pan", PanNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
var Envelope = T.modules.Envelope; | |
var EnvelopeValue = T.modules.EnvelopeValue; | |
function ParamNode(_args) { | |
T.Object.call(this, 2, _args); | |
var _ = this._; | |
_.value = 0; | |
_.env = new EnvelopeValue(_.samplerate); | |
_.env.step = _.cellsize; | |
_.curve = "lin"; | |
_.counter = 0; | |
_.ar = false; | |
_.onended = make_onended(this); | |
this.on("ar", onar); | |
} | |
fn.extend(ParamNode); | |
var make_onended = function(self, lastValue) { | |
return function() { | |
if (typeof lastValue === "number") { | |
var cell = self.cells[0]; | |
var cellL = self.cells[1]; | |
var cellR = self.cells[2]; | |
var value = self._.env.value; | |
for (var i = 0, imax = cellL.length; i < imax; ++i) { | |
cell[0] = cellL[i] = cellR[i] = value; | |
} | |
} | |
self._.emit("ended"); | |
}; | |
}; | |
var onar = function(value) { | |
this._.env.step = (value) ? 1 : this._.cellsize; | |
}; | |
var $ = ParamNode.prototype; | |
Object.defineProperties($, { | |
value: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.env.value = value; | |
} | |
}, | |
get: function() { | |
return this._.env.value; | |
} | |
} | |
}); | |
$.to = function(nextValue, time, curve) { | |
var _ = this._; | |
var env = _.env; | |
if (typeof time === "string") { | |
time = timevalue(time); | |
} else if (typeof time === "undefined") { | |
time = 0; | |
} | |
if (typeof curve === "undefined") { | |
_.counter = env.setNext(nextValue, time, Envelope.CurveTypeLin); | |
_.curve = "lin"; | |
} else { | |
var _curve = Envelope.CurveTypeDict[curve]; | |
if (typeof _curve === "undefined") { | |
_.counter = env.setNext(nextValue, time, Envelope.CurveTypeCurve, curve); | |
} else { | |
_.counter = env.setNext(nextValue, time, _curve); | |
} | |
_.curve = curve; | |
} | |
_.plotFlush = true; | |
return this; | |
}; | |
$.setAt = function(nextValue, time) { | |
var _ = this._; | |
this.to(_.env.value, time, "set"); | |
_.atValue = nextValue; | |
return this; | |
}; | |
$.linTo = function(nextValue, time) { | |
return this.to(nextValue, time, "lin"); | |
}; | |
$.expTo = function(nextValue, time) { | |
return this.to(nextValue, time, "exp"); | |
}; | |
$.sinTo = function(nextValue, time) { | |
return this.to(nextValue, time, "sin"); | |
}; | |
$.welTo = function(nextValue, time) { | |
return this.to(nextValue, time, "wel"); | |
}; | |
$.sqrTo = function(nextValue, time) { | |
return this.to(nextValue, time, "sqr"); | |
}; | |
$.cubTo = function(nextValue, time) { | |
return this.to(nextValue, time, "cub"); | |
}; | |
$.cancel = function() { | |
var _ = this._; | |
_.counter = _.env.setNext(_.env.value, 0, Envelope.CurveTypeSet); | |
return this; | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var i, imax = _.cellsize; | |
var env = _.env; | |
var counter = _.counter; | |
var value; | |
if (this.nodes.length) { | |
fn.inputSignalAR(this); | |
} else { | |
for (i = 0; i < imax; ++i) { | |
cellL[i] = cellR[i] = 1; | |
} | |
} | |
if (counter <= 0) { | |
if (_.curve === "set") { | |
env.setNext(_.atValue, 0, Envelope.CurveTypeSet); | |
} else { | |
env.setNext(env.value, 0, Envelope.CurveTypeSet); | |
} | |
fn.nextTick(_.onended); | |
_.counter = Infinity; | |
} | |
if (_.ar) { | |
for (i = 0; i < imax; ++i) { | |
value = env.next(); | |
cellL[i] *= value; | |
cellR[i] *= value; | |
} | |
_.counter -= _.cellsize; | |
} else { | |
value = env.next(); | |
for (i = 0; i < imax; ++i) { | |
cellL[i] *= value; | |
cellR[i] *= value; | |
} | |
_.counter -= 1; | |
} | |
fn.outputSignalAR(this); | |
_.value = value; | |
} | |
return this; | |
}; | |
var super_plot = T.Object.prototype.plot; | |
$.plot = function(opts) { | |
var _ = this._; | |
if (_.plotFlush) { | |
var env = new EnvelopeValue(128); | |
var data = new Float32Array(128); | |
var curve, i, imax; | |
if (_.curve === "set") { | |
for (i = 100, imax = data.length; i < imax; ++i) { | |
data[i] = 1; | |
} | |
} else { | |
curve = Envelope.CurveTypeDict[_.curve]; | |
if (typeof curve === "undefined") { | |
env.setNext(1, 1000, Envelope.CurveTypeCurve, _.curve); | |
} else { | |
env.setNext(1, 1000, curve); | |
} | |
for (i = 0, imax = data.length; i < imax; ++i) { | |
data[i] = env.next(); | |
} | |
} | |
_.plotData = data; | |
_.plotRange = [0, 1]; | |
_.plotFlush = null; | |
} | |
return super_plot.call(this, opts); | |
}; | |
fn.register("param", ParamNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var Biquad = T.modules.Biquad; | |
function PhaserNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.freq = T("sin", {freq:1, add:1000, mul:250}).kr(); | |
_.Q = T(1); | |
_.allpass = []; | |
this.steps = 2; | |
} | |
fn.extend(PhaserNode); | |
var $ = PhaserNode.prototype; | |
Object.defineProperties($, { | |
freq: { | |
set: function(value) { | |
this._.freq = value; | |
}, | |
get: function() { | |
return this._.freq; | |
} | |
}, | |
Q: { | |
set: function(value) { | |
this._.Q = T(value); | |
}, | |
get: function() { | |
return this._.Q; | |
} | |
}, | |
steps: { | |
set: function(value) { | |
if (typeof value === "number") { | |
value |= 0; | |
if (value === 2 || value === 4 || value === 8 || value === 12) { | |
var allpass = this._.allpass; | |
if (allpass.length < value) { | |
for (var i = allpass.length; i < value; ++i) { | |
allpass[i] = new Biquad(this._.samplerate); | |
allpass[i].setType("allpass"); | |
} | |
} | |
} | |
this._.steps = value; | |
} | |
}, | |
get: function() { | |
return this._.steps; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
if (!_.bypassed) { | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var freq = _.freq.process(tickID).cells[0][0]; | |
var Q = _.Q.process(tickID).cells[0][0]; | |
var steps = _.steps; | |
var i; | |
for (i = 0; i < steps; i += 2) { | |
_.allpass[i ].setParams(freq, Q, 0); | |
_.allpass[i ].process(cellL, cellR); | |
_.allpass[i+1].setParams(freq, Q, 0); | |
_.allpass[i+1].process(cellL, cellR); | |
} | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("phaser", PhaserNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
// Voss algorithm | |
// http://www.firstpr.com.au/dsp/pink-noise/ | |
var MAX_KEY = 31; | |
var fn = T.fn; | |
function PinkNoiseNode(_args) { | |
T.Object.call(this, 1, _args); | |
fn.fixAR(this); | |
var whites = new Uint8Array(5); | |
for (var i = 0; i < 5; ++i) { | |
whites[i] = ((Math.random() * (1<<30))|0) % 25; | |
} | |
this._.whites = whites; | |
this._.key = 0; | |
} | |
fn.extend(PinkNoiseNode); | |
var $ = PinkNoiseNode.prototype; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var i, imax, j; | |
var key = _.key, whites = _.whites; | |
var mul = _.mul, add = _.add; | |
var last_key, sum, diff; | |
for (i = 0, imax = cell.length; i < imax; ++i) { | |
last_key = key++; | |
if (key > MAX_KEY) { | |
key = 0; | |
} | |
diff = last_key ^ key; | |
for (j = sum = 0; j < 5; ++j) { | |
if (diff & (1 << j)) { | |
whites[j] = ((Math.random() * (1<<30))|0) % 25; | |
} | |
sum += whites[j]; | |
} | |
cell[i] = ((sum * 0.01666666) - 1) * mul + add; | |
} | |
_.key = key; | |
} | |
return this; | |
}; | |
fn.register("pink", PinkNoiseNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function PluckNode(_args) { | |
T.Object.call(this, 1, _args); | |
this._.freq = 440; | |
this._.buffer = null; | |
this._.index = 0; | |
} | |
fn.extend(PluckNode); | |
var $ = PluckNode.prototype; | |
Object.defineProperties($, { | |
freq: { | |
set: function(value) { | |
if (typeof value === "number") { | |
if (value < 0) { | |
value = 0; | |
} | |
this._.freq = value; | |
} | |
}, | |
get: function() { | |
return this._.freq; | |
} | |
} | |
}); | |
$.bang = function() { | |
var _ = this._; | |
var freq = _.freq; | |
var size = (_.samplerate / freq + 0.5)|0; | |
var buffer = _.buffer = new fn.SignalArray(size); | |
for (var i = 0; i < size; ++i) { | |
buffer[i] = Math.random() * 2 - 1; | |
} | |
_.index = 0; | |
_.emit("bang"); | |
return this; | |
}; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var buffer = _.buffer; | |
if (buffer) { | |
var bufferLength = buffer.length; | |
var index = _.index, write; | |
var mul = _.mul, add = _.add; | |
var x, i, imax = cell.length; | |
for (i = 0; i < imax; ++i) { | |
write = index; | |
x = buffer[index++]; | |
if (index >= bufferLength) { | |
index = 0; | |
} | |
x = (x + buffer[index]) * 0.5; | |
buffer[write] = x; | |
cell[i] = x * mul + add; | |
} | |
_.index = index; | |
} | |
} | |
return this; | |
}; | |
fn.register("pluck", PluckNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
var STATUS_WAIT = 0; | |
var STATUS_REC = 1; | |
function RecNode(_args) { | |
T.Object.call(this, 1, _args); | |
fn.listener(this); | |
fn.fixAR(this); | |
var _ = this._; | |
_.timeout = 5000; | |
_.status = STATUS_WAIT; | |
_.writeIndex = 0; | |
_.writeIndexIncr = 1; | |
_.currentTime = 0; | |
_.currentTimeIncr = 1000 / _.samplerate; | |
_.onended = make_onended(this); | |
} | |
fn.extend(RecNode); | |
var make_onended = function(self) { | |
return function() { | |
var _ = self._; | |
var buffer = new fn.SignalArray(_.buffer.subarray(0, _.writeIndex|0)); | |
_.status = STATUS_WAIT; | |
_.writeIndex = 0; | |
_.currentTime = 0; | |
_.emit("ended", { | |
buffer:buffer, samplerate:_.samplerate | |
}); | |
}; | |
}; | |
var $ = RecNode.prototype; | |
Object.defineProperties($, { | |
timeout: { | |
set: function(value) { | |
if (typeof value === "string") { | |
value = timevalue(value); | |
} | |
if (typeof value === "number" && value > 0) { | |
this._.timeout = value; | |
} | |
}, | |
get: function() { | |
return this._.timeout; | |
} | |
}, | |
samplerate: { | |
set: function(value) { | |
if (typeof value === "number") { | |
if (0 < value && value <= this._.samplerate) { | |
this._.samplerate = value; | |
} | |
} | |
}, | |
get: function() { | |
return this._.samplerate; | |
} | |
}, | |
currentTime: { | |
get: function() { | |
return this._.currentTime; | |
} | |
} | |
}); | |
$.start = function() { | |
var _ = this._, len; | |
if (_.status === STATUS_WAIT) { | |
len = (_.timeout * 0.01 * _.samplerate)|0; | |
if (!_.buffer || _.buffer.length < len) { | |
_.buffer = new fn.SignalArray(len); | |
} | |
_.writeIndex = 0; | |
_.writeIndexIncr = _.samplerate / T.samplerate; | |
_.currentTime = 0; | |
_.status = STATUS_REC; | |
_.emit("start"); | |
this.listen(); | |
} | |
return this; | |
}; | |
$.stop = function() { | |
var _ = this._; | |
if (_.status === STATUS_REC) { | |
_.status = STATUS_WAIT; | |
_.emit("stop"); | |
fn.nextTick(_.onended); | |
this.unlisten(); | |
} | |
return this; | |
}; | |
$.bang = function() { | |
if (this._.status === STATUS_WAIT) { | |
this.srart(); | |
} else if (this._.status === STATUS_REC) { | |
this.stop(); | |
} | |
this._.emit("bang"); | |
return this; | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
var cell = this.cells[0]; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
if (_.status === STATUS_REC) { | |
var i, imax = cell.length; | |
var buffer = _.buffer; | |
var timeout = _.timeout; | |
var writeIndex = _.writeIndex; | |
var writeIndexIncr = _.writeIndexIncr; | |
var currentTime = _.currentTime; | |
var currentTimeIncr = _.currentTimeIncr; | |
for (i = 0; i < imax; ++i) { | |
buffer[writeIndex|0] = cell[i]; | |
writeIndex += writeIndexIncr; | |
currentTime += currentTimeIncr; | |
if (timeout <= currentTime) { | |
fn.nextTick(_.onended); | |
} | |
} | |
_.writeIndex = writeIndex; | |
_.currentTime = currentTime; | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("record", RecNode); | |
fn.alias("rec", "record"); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var Reverb = T.modules.Reverb; | |
function ReverbNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
this._.reverb = new Reverb(this._.samplerate, this._.cellsize); | |
} | |
fn.extend(ReverbNode); | |
var $ = ReverbNode.prototype; | |
Object.defineProperties($, { | |
room: { | |
set: function(value) { | |
if (typeof value === "number") { | |
value = (value > 1) ? 1 : (value < 0) ? 0 : value; | |
this._.reverb.setRoomSize(value); | |
} | |
}, | |
get: function() { | |
return this._.reverb.roomsize; | |
} | |
}, | |
damp: { | |
set: function(value) { | |
if (typeof value === "number") { | |
value = (value > 1) ? 1 : (value < 0) ? 0 : value; | |
this._.reverb.setDamp(value); | |
} | |
}, | |
get: function() { | |
return this._.reverb.damp; | |
} | |
}, | |
mix: { | |
set: function(value) { | |
if (typeof value === "number") { | |
value = (value > 1) ? 1 : (value < 0) ? 0 : value; | |
this._.reverb.wet = value; | |
} | |
}, | |
get: function() { | |
return this._.reverb.wet; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
if (!_.bypassed) { | |
_.reverb.process(this.cells[1], this.cells[2]); | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("reverb", ReverbNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
function ScheduleNode(_args) { | |
T.Object.call(this, 0, _args); | |
fn.timer(this); | |
fn.fixKR(this); | |
var _ = this._; | |
_.queue = []; | |
_.currentTime = 0; | |
_.maxRemain = 1000; | |
} | |
fn.extend(ScheduleNode); | |
var $ = ScheduleNode.prototype; | |
Object.defineProperties($, { | |
queue: { | |
get: function() { | |
return this._.queue; | |
} | |
}, | |
remain: { | |
get: function() { | |
return this._.queue.length; | |
} | |
}, | |
maxRemain: { | |
set: function(value) { | |
if (typeof value === "number" && value > 0) { | |
this._.maxRemain = value; | |
} | |
}, | |
get: function() { | |
return this._.maxRemain; | |
} | |
}, | |
isEmpty: { | |
get: function() { | |
return this._.queue.length === 0; | |
} | |
}, | |
currentTime: { | |
get: function() { | |
return this._.currentTime; | |
} | |
} | |
}); | |
$.sched = function(delta, item, args) { | |
if (typeof delta === "string") { | |
delta = timevalue(delta); | |
} | |
if (typeof delta === "number") { | |
this.schedAbs(this._.currentTime + delta, item, args); | |
} | |
return this; | |
}; | |
$.schedAbs = function(time, item, args) { | |
if (typeof time === "string") { | |
time = timevalue(time); | |
} | |
if (typeof time === "number") { | |
var _ = this._; | |
var queue = _.queue; | |
if (queue.length >= _.maxRemain) { | |
return this; | |
} | |
for (var i = queue.length; i--; ) { | |
if (queue[i][0] < time) { | |
break; | |
} | |
} | |
queue.splice(i + 1, 0, [time, T(item), args]); | |
} | |
return this; | |
}; | |
$.advance = function(delta) { | |
if (typeof delta === "string") { | |
delta = timevalue(delta); | |
} | |
if (typeof delta === "number") { | |
this._.currentTime += delta; | |
} | |
return this; | |
}; | |
$.clear = function() { | |
this._.queue.splice(0); | |
return this; | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var emit = null; | |
var queue = _.queue; | |
if (queue.length) { | |
while (queue[0][0] < _.currentTime) { | |
var nextItem = _.queue.shift(); | |
nextItem[1].bang(nextItem[2]); | |
emit = "sched"; | |
if (queue.length === 0) { | |
emit = "empty"; | |
break; | |
} | |
} | |
} | |
_.currentTime += fn.currentTimeIncr; | |
if (emit) { | |
_.emit(emit); | |
} | |
} | |
return this; | |
}; | |
fn.register("schedule", ScheduleNode); | |
fn.alias("sched", "schedule"); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
function ScopeNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.listener(this); | |
fn.fixAR(this); | |
var _ = this._; | |
_.samples = 0; | |
_.writeIndex = 0; | |
_.plotFlush = true; | |
this.once("init", oninit); | |
} | |
fn.extend(ScopeNode); | |
var oninit = function() { | |
if (!this._.buffer) { | |
this.size = 1024; | |
} | |
if (!this._.interval) { | |
this.interval = 1000; | |
} | |
}; | |
var $ = ScopeNode.prototype; | |
Object.defineProperties($, { | |
size: { | |
set: function(value) { | |
var _ = this._; | |
if (!_.buffer) { | |
if (typeof value === "number") { | |
var n = (value < 64) ? 64 : (value > 2048) ? 2048 : value; | |
_.buffer = new fn.SignalArray(n); | |
if (_.reservedinterval) { | |
this.interval = _.reservedinterval; | |
_.reservedinterval = null; | |
} | |
} | |
} | |
}, | |
get: function() { | |
return this._.buffer.length; | |
} | |
}, | |
interval: { | |
set: function(value) { | |
var _ = this._; | |
if (typeof value === "string") { | |
value = timevalue(value); | |
} | |
if (typeof value === "number" && value > 0) { | |
if (!_.buffer) { | |
_.reservedinterval = value; | |
} else { | |
_.interval = value; | |
_.samplesIncr = value * 0.001 * _.samplerate / _.buffer.length; | |
if (_.samplesIncr < 1) { | |
_.samplesIncr = 1; | |
} | |
} | |
} | |
}, | |
get: function() { | |
return this._.interval; | |
} | |
} | |
}); | |
$.bang = function() { | |
var _ = this._; | |
var buffer = _.buffer; | |
for (var i = 0, imax = buffer.length; i < imax; ++i) { | |
buffer[i] = 0; | |
} | |
_.samples = 0; | |
_.writeIndex = 0; | |
this._.emit("bang"); | |
return this; | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
fn.outputSignalAR(this); | |
var cell = this.cells[0]; | |
var i, imax = _.cellsize; | |
var samples = _.samples; | |
var samplesIncr = _.samplesIncr; | |
var buffer = _.buffer; | |
var writeIndex = _.writeIndex; | |
var emit = false; | |
var bufferlength = buffer.length; | |
for (i = 0; i < imax; ++i) { | |
if (samples <= 0) { | |
buffer[writeIndex++] = cell[i]; | |
if (writeIndex >= bufferlength) { | |
writeIndex = 0; | |
} | |
emit = _.plotFlush = true; | |
samples += samplesIncr; | |
} | |
--samples; | |
} | |
_.samples = samples; | |
_.writeIndex = writeIndex; | |
if (emit) { | |
this._.emit("data"); | |
} | |
} | |
return this; | |
}; | |
var super_plot = T.Object.prototype.plot; | |
$.plot = function(opts) { | |
var _ = this._; | |
if (_.plotFlush) { | |
var buffer = _.buffer; | |
var mask = buffer.length - 1; | |
var data = new Float32Array(buffer.length); | |
var j = _.writeIndex; | |
for (var i = 0, imax = buffer.length; i < imax; i++) { | |
data[i] = buffer[++j & mask]; | |
} | |
_.plotData = data; | |
_.plotFlush = null; | |
} | |
return super_plot.call(this, opts); | |
}; | |
fn.register("scope", ScopeNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function ScriptProcessorNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.numberOfInputs = 0; | |
_.numberOfOutputs = 0; | |
_.bufferSize = 0; | |
_.bufferMask = 0; | |
_.duration = 0; | |
_.inputBufferL = null; | |
_.inputBufferR = null; | |
_.outputBufferL = null; | |
_.outputBufferR = null; | |
_.onaudioprocess = null; | |
_.index = 0; | |
this.once("init", oninit); | |
} | |
fn.extend(ScriptProcessorNode); | |
var oninit = function() { | |
var _ = this._; | |
if (_.numberOfInputs === 0) { | |
this.numberOfInputs = 1; | |
} | |
if (_.numberOfOutputs === 0) { | |
this.numberOfOutputs = 1; | |
} | |
if (_.bufferSize === 0) { | |
this.bufferSize = 1024; | |
} | |
}; | |
var $ = ScriptProcessorNode.prototype; | |
Object.defineProperties($, { | |
numberOfInputs: { | |
set: function(value) { | |
var _ = this._; | |
if (_.numberOfInputs === 0) { | |
_.numberOfInputs = (value === 2) ? 2 : 1; | |
} | |
}, | |
get: function() { | |
return this._.numberOfInputs; | |
} | |
}, | |
numberOfOutputs: { | |
set: function(value) { | |
var _ = this._; | |
if (_.numberOfOutputs === 0) { | |
_.numberOfOutputs = (value === 2) ? 2 : 1; | |
} | |
}, | |
get: function() { | |
return this._.numberOfOutputs; | |
} | |
}, | |
bufferSize: { | |
set: function(value) { | |
var _ = this._; | |
if (_.bufferSize === 0) { | |
if ([256, 512, 1024, 2048, 4096, 8192, 16384].indexOf(value) !== -1) { | |
_.bufferSize = value; | |
_.bufferMask = value - 1; | |
_.duration = value / _.samplerate; | |
_.inputBufferL = new fn.SignalArray(value); | |
_.inputBufferR = new fn.SignalArray(value); | |
_.outputBufferL = new fn.SignalArray(value); | |
_.outputBufferR = new fn.SignalArray(value); | |
} | |
} | |
}, | |
get: function() { | |
return this._.bufferSize; | |
} | |
}, | |
onaudioprocess: { | |
set: function(value) { | |
if (typeof value === "function") { | |
this._.onaudioprocess = value; | |
} | |
}, | |
get: function() { | |
return this._.onaudioprocess; | |
} | |
} | |
}); | |
function AudioBuffer(self, buffers) { | |
this.samplerate = self._.samplerate; | |
this.length = self._.bufferSize; | |
this.duration = self._.duration; | |
this.numberOfChannels = buffers.length; | |
this.getChannelData = function(n) { | |
return buffers[n]; | |
}; | |
} | |
function AudioProcessingEvent(self) { | |
var _ = self._; | |
this.node = self; | |
this.playbackTime = T.currentTime; | |
if (_.numberOfInputs === 2) { | |
this.inputBuffer = new AudioBuffer(self, [_.inputBufferL, _.inputBufferR]); | |
} else { | |
this.inputBuffer = new AudioBuffer(self, [_.inputBufferL]); | |
} | |
if (_.numberOfOutputs === 2) { | |
this.outputBuffer = new AudioBuffer(self, [_.outputBufferL, _.outputBufferR]); | |
} else { | |
this.outputBuffer = new AudioBuffer(self, [_.outputBufferL]); | |
} | |
} | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var cellsize = _.cellsize; | |
var bufferMask = _.bufferMask; | |
var begin = _.index; | |
var end = begin + cellsize; | |
var buffer; | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
fn.inputSignalAR(this); | |
if (_.numberOfInputs === 2) { | |
_.inputBufferL.set(cellL, begin); | |
_.inputBufferR.set(cellR, begin); | |
} else { | |
buffer = _.inputBufferL; | |
for (var i = 0; i < cellsize; i++) { | |
buffer[begin + i] = (cellL[i] + cellR[i]) * 0.5; | |
} | |
} | |
cellL.set(_.outputBufferL.subarray(begin, end)); | |
cellR.set(_.outputBufferR.subarray(begin, end)); | |
_.index = end & bufferMask; | |
if (_.index === 0 && _.onaudioprocess) { | |
_.onaudioprocess(new AudioProcessingEvent(this)); | |
if (_.numberOfOutputs === 1) { | |
_.outputBufferR.set(_.outputBufferL); | |
} | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("script", ScriptProcessorNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function SelectorNode(_args) { | |
T.Object.call(this, 2, _args); | |
this._.selected = 0; | |
this._.background = false; | |
} | |
fn.extend(SelectorNode); | |
var $ = SelectorNode.prototype; | |
Object.defineProperties($, { | |
selected: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.selected = value; | |
this.cells[1].set(fn.emptycell); | |
this.cells[2].set(fn.emptycell); | |
} | |
}, | |
get: function() { | |
return this._.selected; | |
} | |
}, | |
background: { | |
set: function(value) { | |
this._.background = !!value; | |
}, | |
get: function() { | |
return this._.background; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var nodes = this.nodes; | |
var i, imax = nodes.length; | |
if (_.background) { | |
for (i = 0; i < imax; ++i) { | |
nodes[i].process(tickID); | |
} | |
} | |
var tmp = nodes[_.selected]; | |
if (tmp) { | |
if (!_.background) { | |
tmp.process(tickID); | |
} | |
this.cells[1].set(tmp.cells[1]); | |
this.cells[2].set(tmp.cells[2]); | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("selector", SelectorNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
var FFT = T.modules.FFT; | |
var WAIT_STATE = 0; | |
var EXEC_STATE = 1; | |
function SpectrumNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.listener(this); | |
fn.fixAR(this); | |
var _ = this._; | |
_.status = WAIT_STATE; | |
_.samples = 0; | |
_.samplesIncr = 0; | |
_.writeIndex = 0; | |
_.plotFlush = true; | |
_.plotRange = [0, 32]; | |
_.plotBarStyle = true; | |
this.once("init", oninit); | |
} | |
fn.extend(SpectrumNode); | |
var oninit = function() { | |
var _ = this._; | |
if (!_.fft) { | |
this.size = 512; | |
} | |
if (!_.interval) { | |
this.interval = 500; | |
} | |
}; | |
var $ = SpectrumNode.prototype; | |
Object.defineProperties($, { | |
size: { | |
set: function(value) { | |
var _ = this._; | |
if (!_.fft) { | |
if (typeof value === "number") { | |
var n = (value < 256) ? 256 : (value > 2048) ? 2048 : value; | |
_.fft = new FFT(n); | |
_.buffer = new fn.SignalArray(_.fft.length); | |
_.freqs = new fn.SignalArray(_.fft.length>>1); | |
if (_.reservedwindow) { | |
_.fft.setWindow(_.reservedwindow); | |
_.reservedwindow = null; | |
} | |
if (_.reservedinterval) { | |
this.interval = _.reservedinterval; | |
_.reservedinterval = null; | |
} | |
} | |
} | |
}, | |
get: function() { | |
return this._.buffer.length; | |
} | |
}, | |
window: { | |
set: function(value) { | |
this._.fft.setWindow(value); | |
}, | |
get: function() { | |
return this._.fft.windowName; | |
} | |
}, | |
interval: { | |
set: function(value) { | |
var _ = this._; | |
if (typeof value === "string") { | |
value = timevalue(value); | |
} | |
if (typeof value === "number" && value > 0) { | |
if (!_.buffer) { | |
_.reservedinterval = value; | |
} else { | |
_.interval = value; | |
_.samplesIncr = (value * 0.001 * _.samplerate); | |
if (_.samplesIncr < _.buffer.length) { | |
_.samplesIncr = _.buffer.length; | |
_.interval = _.samplesIncr * 1000 / _.samplerate; | |
} | |
} | |
} | |
}, | |
get: function() { | |
return this._.interval; | |
} | |
}, | |
spectrum: { | |
get: function() { | |
return this._.fft.getFrequencyData(this._.freqs); | |
} | |
}, | |
real: { | |
get: function() { | |
return this._.fft.real; | |
} | |
}, | |
imag: { | |
get: function() { | |
return this._.fft.imag; | |
} | |
} | |
}); | |
$.bang = function() { | |
this._.samples = 0; | |
this._.writeIndex = 0; | |
this._.emit("bang"); | |
return this; | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
fn.outputSignalAR(this); | |
var cell = this.cells[0]; | |
var i, imax = cell.length; | |
var status = _.status; | |
var samples = _.samples; | |
var samplesIncr = _.samplesIncr; | |
var writeIndex = _.writeIndex; | |
var buffer = _.buffer; | |
var bufferLength = buffer.length; | |
var emit; | |
for (i = 0; i < imax; ++i) { | |
if (samples <= 0) { | |
if (status === WAIT_STATE) { | |
status = EXEC_STATE; | |
writeIndex = 0; | |
samples += samplesIncr; | |
} | |
} | |
if (status === EXEC_STATE) { | |
buffer[writeIndex++] = cell[i]; | |
if (bufferLength <= writeIndex) { | |
_.fft.forward(buffer); | |
emit = _.plotFlush = true; | |
status = WAIT_STATE; | |
} | |
} | |
--samples; | |
} | |
_.samples = samples; | |
_.status = status; | |
_.writeIndex = writeIndex; | |
if (emit) { | |
this._.emit("data"); | |
} | |
} | |
return this; | |
}; | |
var super_plot = T.Object.prototype.plot; | |
$.plot = function(opts) { | |
if (this._.plotFlush) { | |
this._.plotData = this.spectrum; | |
this._.plotFlush = null; | |
} | |
return super_plot.call(this, opts); | |
}; | |
fn.register("spectrum", SpectrumNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function SubtractNode(_args) { | |
T.Object.call(this, 2, _args); | |
this._.ar = false; | |
} | |
fn.extend(SubtractNode); | |
var $ = SubtractNode.prototype; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var nodes = this.nodes; | |
var cell = this.cells[0]; | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var i, imax = nodes.length; | |
var j, jmax = cell.length; | |
var tmp, tmpL, tmpR; | |
if (_.ar) { | |
if (nodes.length > 0) { | |
nodes[0].process(tickID); | |
tmpL = nodes[0].cells[1]; | |
tmpR = nodes[0].cells[2]; | |
cellL.set(tmpL); | |
cellR.set(tmpR); | |
for (i = 1; i < imax; ++i) { | |
nodes[i].process(tickID); | |
tmpL = nodes[i].cells[1]; | |
tmpR = nodes[i].cells[2]; | |
for (j = 0; j < jmax; ++j) { | |
cellL[j] -= tmpL[j]; | |
cellR[j] -= tmpR[j]; | |
} | |
} | |
} else { | |
for (j = 0; j < jmax; ++j) { | |
cellL[j] = cellR[i] = 0; | |
} | |
} | |
fn.outputSignalAR(this); | |
} else { | |
if (nodes.length > 0) { | |
tmp = nodes[0].process(tickID).cells[0][0]; | |
for (i = 1; i < imax; ++i) { | |
tmp -= nodes[i].process(tickID).cells[0][0]; | |
} | |
} else { | |
tmp = 0; | |
} | |
cell[0] = tmp; | |
fn.outputSignalKR(this); | |
} | |
} | |
return this; | |
}; | |
fn.register("-", SubtractNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function SynthDefNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
this.playbackState = fn.FINISHED_STATE; | |
_.poly = 4; | |
_.genList = []; | |
_.genDict = {}; | |
_.synthdef = null; | |
_.remGen = make_remGen(this); | |
_.onended = fn.make_onended(this); | |
} | |
fn.extend(SynthDefNode); | |
var $ = SynthDefNode.prototype; | |
Object.defineProperties($, { | |
def: { | |
set: function(value) { | |
if (typeof value === "function") { | |
this._.synthdef = value; | |
} | |
}, | |
get: function() { | |
return this._.synthdef; | |
} | |
}, | |
poly: { | |
set: function(value) { | |
if (typeof value === "number") { | |
if (0 < value && value <= 64) { | |
this._.poly = value; | |
} | |
} | |
}, | |
get: function() { | |
return this._.poly; | |
} | |
} | |
}); | |
var make_doneAction = function(self, opts) { | |
return function() { | |
self._.remGen(opts.gen); | |
}; | |
}; | |
var make_remGen = function(self) { | |
return function(gen) { | |
var _ = self._; | |
var i = _.genList.indexOf(gen); | |
if (i !== -1) { | |
_.genList.splice(i, 1); | |
} | |
if (typeof gen.noteNum !== "undefined") { | |
_.genDict[gen.noteNum] = null; | |
} | |
}; | |
}; | |
var noteOn = function(noteNum, freq, velocity, _opts) { | |
velocity |= 0; | |
if (velocity <= 0) { | |
this.noteOff(this, noteNum); | |
} else if (velocity > 127) { | |
velocity = 127; | |
} | |
var _ = this._; | |
var list = _.genList, dict = _.genDict; | |
var gen = dict[noteNum]; | |
if (gen) { | |
_.remGen(gen); | |
} | |
var opts = { | |
freq : freq, | |
noteNum : noteNum, | |
velocity: velocity, | |
mul : velocity * 0.0078125 | |
}; | |
if (_opts) { | |
for (var key in _opts) { | |
opts[key] = _opts[key]; | |
} | |
} | |
opts.doneAction = make_doneAction(this, opts); | |
gen = _.synthdef.call(this, opts); | |
if (gen instanceof T.Object) { | |
gen.noteNum = noteNum; | |
list.push(gen); | |
dict[noteNum] = opts.gen = gen; | |
this.playbackState = fn.PLAYING_STATE; | |
if (list.length > _.poly) { | |
_.remGen(list[0]); | |
} | |
} | |
}; | |
var midicps = (function() { | |
var table = new Float32Array(128); | |
for (var i = 0; i < 128; ++i) { | |
table[i] = 440 * Math.pow(2, (i - 69) * 1 / 12); | |
} | |
return table; | |
})(); | |
var cpsmidi = function(cps) { | |
if (cps > 0) { | |
return Math.log(cps * 1 / 440) * Math.LOG2E * 12 + 69; | |
} else { | |
return 0; | |
} | |
}; | |
$.noteOn = function(noteNum, velocity, _opts) { | |
var freq = midicps[noteNum] || (440 * Math.pow(2, (noteNum - 69) / 12)); | |
noteOn.call(this, (noteNum + 0.5)|0, freq, velocity, _opts); | |
return this; | |
}; | |
$.noteOff = function(noteNum) { | |
var gen = this._.genDict[noteNum]; | |
if (gen && gen.release) { | |
gen.release(); | |
} | |
return this; | |
}; | |
$.noteOnWithFreq = function(freq, velocity, _opts) { | |
var noteNum = cpsmidi(freq); | |
noteOn.call(this, (noteNum + 0.5)|0, freq, velocity, _opts); | |
return this; | |
}; | |
$.noteOffWithFreq = function(freq) { | |
var noteNum = cpsmidi(freq); | |
return this.noteOff((noteNum + 0.5)|0); | |
}; | |
$.allNoteOff = function() { | |
var list = this._.genList; | |
for (var i = 0, imax = list.length; i < imax; ++i) { | |
if (list[i].release) { | |
list[i].release(); | |
} | |
} | |
}; | |
$.allSoundOff = function() { | |
var _ = this._; | |
var list = _.genList; | |
var dict = _.genDict; | |
while (list.length) { | |
delete dict[list.shift().noteNum]; | |
} | |
}; | |
$.synth = function(_opts) { | |
var _ = this._; | |
var list = _.genList; | |
var gen, opts = {}; | |
if (_opts) { | |
for (var key in _opts) { | |
opts[key] = _opts[key]; | |
} | |
} | |
opts.doneAction = make_doneAction(this, opts); | |
gen = _.synthdef.call(this, opts); | |
if (gen instanceof T.Object) { | |
list.push(gen); | |
opts.gen = gen; | |
this.playbackState = fn.PLAYING_STATE; | |
if (list.length > _.poly) { | |
_.remGen(list[0]); | |
} | |
} | |
return this; | |
}; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
if (this.playbackState === fn.PLAYING_STATE) { | |
var list = _.genList; | |
var gen; | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var i, imax; | |
var j, jmax = cell.length; | |
var tmpL, tmpR; | |
if (list.length) { | |
gen = list[0]; | |
gen.process(tickID); | |
cellL.set(gen.cells[1]); | |
cellR.set(gen.cells[2]); | |
for (i = 1, imax = list.length; i < imax; ++i) { | |
gen = list[i]; | |
gen.process(tickID); | |
tmpL = gen.cells[1]; | |
tmpR = gen.cells[2]; | |
for (j = 0; j < jmax; ++j) { | |
cellL[j] += tmpL[j]; | |
cellR[j] += tmpR[j]; | |
} | |
} | |
} else { | |
fn.nextTick(_.onended); | |
} | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("SynthDef", SynthDefNode); | |
var env_desc = { | |
set: function(value) { | |
if (fn.isDictionary(value)) { | |
if (typeof value.type === "string") { | |
this._.env = value; | |
} | |
} else if (value instanceof T.Object) { | |
this._.env = value; | |
} | |
}, | |
get: function() { | |
return this._.env; | |
} | |
}; | |
fn.register("OscGen", (function() { | |
var osc_desc = { | |
set: function(value) { | |
if (value instanceof T.Object) { | |
this._.osc = value; | |
} | |
}, | |
get: function() { | |
return this._.osc; | |
} | |
}; | |
var wave_desc = { | |
set: function(value) { | |
if (typeof value === "string") { | |
this._.wave = value; | |
} | |
}, | |
get: function() { | |
return this._.wave; | |
} | |
}; | |
var synthdef = function(opts) { | |
var _ = this._; | |
var synth, osc, env, envtype; | |
osc = _.osc || null; | |
env = _.env || {}; | |
envtype = env.type || "perc"; | |
if (osc instanceof T.Object) { | |
if (typeof osc.clone === "function") { | |
osc = osc.clone(); | |
} | |
} | |
if (!osc) { | |
osc = T("osc", {wave:_.wave}); | |
} | |
osc.freq = opts.freq; | |
osc.mul = osc.mul * opts.velocity/128; | |
synth = osc; | |
if (env instanceof T.Object) { | |
if (typeof env.clone === "function") { | |
synth = env.clone().append(synth); | |
} | |
} else { | |
synth = T(envtype, env, synth); | |
} | |
synth.on("ended", opts.doneAction).bang(); | |
return synth; | |
}; | |
return function(_args) { | |
var instance = new SynthDefNode(_args); | |
instance._.wave = "sin"; | |
Object.defineProperties(instance, { | |
env: env_desc, osc: osc_desc, wave: wave_desc | |
}); | |
instance.def = synthdef; | |
return instance; | |
}; | |
})()); | |
fn.register("PluckGen", (function() { | |
var synthdef = function(opts) { | |
var _ = this._; | |
var synth, env, envtype; | |
env = _.env || {}; | |
envtype = env.type || "perc"; | |
synth = T("pluck", {freq:opts.freq, mul:opts.velocity/128}).bang(); | |
if (env instanceof T.Object) { | |
if (typeof env.clone === "function") { | |
synth = env.clone().append(synth); | |
} | |
} else { | |
synth = T(envtype, env, synth); | |
} | |
synth.on("ended", opts.doneAction).bang(); | |
return synth; | |
}; | |
return function(_args) { | |
var instance = new SynthDefNode(_args); | |
Object.defineProperties(instance, { | |
env: env_desc | |
}); | |
instance.def = synthdef; | |
return instance; | |
}; | |
})()); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var Scissor = T.modules.Scissor; | |
var Tape = Scissor.Tape; | |
var TapeStream = Scissor.TapeStream; | |
var isSignalArray = fn.isSignalArray; | |
function ScissorNode(_args) { | |
T.Object.call(this, 2, _args); | |
fn.fixAR(this); | |
var _ = this._; | |
_.isLooped = false; | |
_.onended = fn.make_onended(this, 0); | |
} | |
fn.extend(ScissorNode); | |
var $ = ScissorNode.prototype; | |
Object.defineProperties($, { | |
tape: { | |
set: function(tape) { | |
if (tape instanceof Tape) { | |
this.playbackState = fn.PLAYING_STATE; | |
this._.tape = tape; | |
this._.tapeStream = new TapeStream(tape, this._.samplerate); | |
this._.tapeStream.isLooped = this._.isLooped; | |
} else { | |
if (tape instanceof T.Object) { | |
if (tape.buffer) { | |
tape = tape.buffer; | |
} | |
} | |
if (typeof tape === "object") { | |
if (Array.isArray(tape.buffer) && isSignalArray(tape.buffer[0])) { | |
this.playbackState = fn.PLAYING_STATE; | |
this._.tape = new Scissor(tape); | |
this._.tapeStream = new TapeStream(this._.tape, this._.samplerate); | |
this._.tapeStream.isLooped = this._.isLooped; | |
} | |
} | |
} | |
}, | |
get: function() { | |
return this._.tape; | |
} | |
}, | |
isLooped: { | |
get: function() { | |
return this._.isLooped; | |
} | |
}, | |
buffer: { | |
get: function() { | |
if (this._.tape) { | |
return this._.tape.getBuffer(); | |
} | |
} | |
} | |
}); | |
$.loop = function(value) { | |
this._.isLooped = !!value; | |
if (this._.tapeStream) { | |
this._.tapeStream.isLooped = this._.isLooped; | |
} | |
return this; | |
}; | |
$.bang = function() { | |
this.playbackState = fn.PLAYING_STATE; | |
if (this._.tapeStream) { | |
this._.tapeStream.reset(); | |
} | |
this._.emit("bang"); | |
return this; | |
}; | |
$.getBuffer = function() { | |
if (this._.tape) { | |
return this._.tape.getBuffer(); | |
} | |
}; | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var tapeStream = _.tapeStream; | |
if (tapeStream) { | |
var cellL = this.cells[1]; | |
var cellR = this.cells[2]; | |
var tmp = tapeStream.fetch(cellL.length); | |
cellL.set(tmp[0]); | |
cellR.set(tmp[1]); | |
if (this.playbackState === fn.PLAYING_STATE && tapeStream.isEnded) { | |
fn.nextTick(_.onended); | |
} | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("tape", ScissorNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
var FunctionWrapper = T(function(){}).constructor; | |
function TaskNode(_args) { | |
T.Object.call(this, 1, _args); | |
fn.timer(this); | |
var _ = this._; | |
this.playbackState = fn.FINISHED_STATE; | |
_.task = []; | |
_.i = 0; | |
_.j = 0; | |
_.imax = 0; | |
_.jmax = 0; | |
_.wait = 0; | |
_.count = 0; | |
_.args = {}; | |
_.doNum = 1; | |
_.initFunc = fn.nop; | |
_.onended = make_onended(this); | |
this.on("start", onstart); | |
} | |
fn.extend(TaskNode); | |
var onstart = function() { | |
var _ = this._, args; | |
this.playbackState = fn.PLAYING_STATE; | |
_.task = this.nodes.map(function(x) { | |
return x instanceof FunctionWrapper ? x.func : false; | |
}).filter(function(x) { | |
return !!x; | |
}); | |
_.i = _.j = 0; | |
_.imax = _.doNum; | |
_.jmax = _.task.length; | |
args = _.initFunc(); | |
if (!fn.isDictionary(args)) { | |
args = {param:args}; | |
} | |
_.args = args; | |
}; | |
var make_onended = function(self) { | |
return function() { | |
self.playbackState = fn.FINISHED_STATE; | |
var _ = self._; | |
var cell = self.cells[0]; | |
var cellL = self.cells[1]; | |
var cellR = self.cells[2]; | |
var lastValue = _.args; | |
if (typeof lastValue === "number") { | |
for (var i = 0, imax = cellL.length; i < imax; ++i) { | |
cell[0] = cellL[i] = cellR[i] = lastValue; | |
} | |
} | |
_.emit("ended", _.args); | |
}; | |
}; | |
var $ = TaskNode.prototype; | |
Object.defineProperties($, { | |
"do": { | |
set: function(value) { | |
if (typeof value === "number" && value > 0) { | |
this._.doNum = value === Infinity ? Infinity : value|0; | |
} | |
}, | |
get: function() { | |
return this._.doNum; | |
} | |
}, | |
init: { | |
set: function(value) { | |
if (typeof value === "function") { | |
this._.initFunc = value; | |
} | |
}, | |
get: function() { | |
return this._.initFunc; | |
} | |
} | |
}); | |
$.bang = function() { | |
var _ = this._; | |
_.count = 0; | |
_.emit("bang"); | |
return this; | |
}; | |
$.wait = function(time) { | |
if (typeof time === "string") { | |
time = timevalue(time); | |
} | |
if (typeof time === "number" && time > 0) { | |
this._.count += (this._.samplerate * time * 0.001)|0; | |
} | |
return this; | |
}; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
var args, func; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
if (_.i < _.imax) { | |
while (_.count <= 0) { | |
if (_.j >= _.jmax) { | |
++_.i; | |
if (_.i >= _.imax) { | |
fn.nextTick(_.onended); | |
break; | |
} | |
_.j = 0; | |
} | |
func = _.task[_.j++]; | |
if (func) { | |
func.call(this, _.i, _.args); | |
} | |
} | |
_.count -= cell.length; | |
} | |
} | |
return this; | |
}; | |
fn.register("task", TaskNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
var timevalue = T.timevalue; | |
function TimeoutNode(_args) { | |
T.Object.call(this, 0, _args); | |
fn.timer(this); | |
fn.fixKR(this); | |
var _ = this._; | |
this.playbackState = fn.FINISHED_STATE; | |
_.currentTime = 0; | |
_.samplesMax = 0; | |
_.samples = 0; | |
_.onended = fn.make_onended(this); | |
this.once("init", oninit); | |
this.on("start", onstart); | |
} | |
fn.extend(TimeoutNode); | |
var oninit = function() { | |
if (!this._.timeout) { | |
this.timeout = 1000; | |
} | |
}; | |
var onstart = function() { | |
this.playbackState = fn.PLAYING_STATE; | |
}; | |
Object.defineProperty(onstart, "unremovable", { | |
value:true, writable:false | |
}); | |
var $ = TimeoutNode.prototype; | |
Object.defineProperties($, { | |
timeout: { | |
set: function(value) { | |
var _ = this._; | |
if (typeof value === "string") { | |
value = timevalue(value); | |
} | |
if (typeof value === "number" && value >= 0) { | |
this.playbackState = fn.PLAYING_STATE; | |
_.timeout = value; | |
_.samplesMax = (_.samplerate * (value * 0.001))|0; | |
_.samples = _.samplesMax; | |
} | |
}, | |
get: function() { | |
return this._.timeout; | |
} | |
}, | |
currentTime: { | |
get: function() { | |
return this._.currentTime; | |
} | |
} | |
}); | |
$.bang = function() { | |
var _ = this._; | |
this.playbackState = fn.PLAYING_STATE; | |
_.samples = _.samplesMax; | |
_.currentTime = 0; | |
_.emit("bang"); | |
return this; | |
}; | |
$.process = function(tickID) { | |
var cell = this.cells[0]; | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
if (_.samples > 0) { | |
_.samples -= cell.length; | |
} | |
if (_.samples <= 0) { | |
var nodes = this.nodes; | |
for (var i = 0, imax = nodes.length; i < imax; ++i) { | |
nodes[i].bang(); | |
} | |
fn.nextTick(_.onended); | |
} | |
_.currentTime += fn.currentTimeIncr; | |
} | |
return this; | |
}; | |
fn.register("timeout", TimeoutNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function WaveShaperNode(_args) { | |
T.Object.call(this, 1, _args); | |
fn.fixAR(this); | |
this._.curve = null; | |
} | |
fn.extend(WaveShaperNode); | |
var $ = WaveShaperNode.prototype; | |
Object.defineProperties($, { | |
curve: { | |
set: function(value) { | |
if (fn.isSignalArray(value)) { | |
this._.curve = value; | |
} | |
}, | |
get: function() { | |
return this._.curve; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
fn.inputSignalAR(this); | |
if (_.curve) { | |
var cell = this.cells[0]; | |
var curve = _.curve; | |
var len = curve.length; | |
var x, i, imax = _.cellsize; | |
for (i = 0; i < imax; ++i) { | |
x = (((cell[i] + 1) * 0.5) * len + 0.5)|0; | |
if (x < 0) { | |
x = 0; | |
} else if (x >= len - 1) { | |
x = len - 1; | |
} | |
cell[i] = curve[x]; | |
} | |
} | |
fn.outputSignalAR(this); | |
} | |
return this; | |
}; | |
fn.register("waveshaper", WaveShaperNode); | |
})(timbre); | |
(function(T) { | |
"use strict"; | |
var fn = T.fn; | |
function ZMapNode(_args) { | |
T.Object.call(this, 1, _args); | |
var _ = this._; | |
_.inMin = 0; | |
_.inMax = 1; | |
_.outMin = 0; | |
_.outMax = 1; | |
_.ar = false; | |
this.once("init", oninit); | |
} | |
fn.extend(ZMapNode); | |
var oninit = function() { | |
if (!this._.warp) { | |
this.warp = "linlin"; | |
} | |
}; | |
var $ = ZMapNode.prototype; | |
Object.defineProperties($, { | |
inMin: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.inMin = value; | |
} | |
}, | |
get: function() { | |
return this._.inMin; | |
} | |
}, | |
inMax: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.inMax = value; | |
} | |
}, | |
get: function() { | |
return this._.inMax; | |
} | |
}, | |
outMin: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.outMin = value; | |
} | |
}, | |
get: function() { | |
return this._.outMin; | |
} | |
}, | |
outMax: { | |
set: function(value) { | |
if (typeof value === "number") { | |
this._.outMax = value; | |
} | |
}, | |
get: function() { | |
return this._.outMax; | |
} | |
}, | |
warp: { | |
set: function(value) { | |
if (typeof value === "string") { | |
var f = WarpFunctions[value]; | |
if (f) { | |
this._.warp = f; | |
this._.warpName = value; | |
} | |
} | |
}, | |
get: function() { | |
return this._.warpName; | |
} | |
} | |
}); | |
$.process = function(tickID) { | |
var _ = this._; | |
var cell = this.cells[0]; | |
if (this.tickID !== tickID) { | |
this.tickID = tickID; | |
var inMin = _.inMin, inMax = _.inMax; | |
var outMin = _.outMin, outMax = _.outMax; | |
var warp = _.warp; | |
var len = this.nodes.length; | |
var mul = _.mul, add = _.add; | |
var i, imax = cell.length; | |
if (_.ar && len) { | |
fn.inputSignalAR(this); | |
for (i = 0; i < imax; ++i) { | |
cell[i] = warp(cell[i], inMin, inMax, outMin, outMax) * mul + add; | |
} | |
fn.outputSignalAR(this); | |
} else { | |
var input = (this.nodes.length) ? fn.inputSignalKR(this) : 0; | |
var value = warp(input, inMin, inMax, outMin, outMax) * mul + add; | |
for (i = 0; i < imax; ++i) { | |
cell[i] = value; | |
} | |
} | |
} | |
return this; | |
}; | |
var WarpFunctions = { | |
linlin: function(x, inMin, inMax, outMin, outMax) { | |
if (x < inMin) { | |
return outMin; | |
} else if (x > inMax) { | |
return outMax; | |
} | |
if (inMax === inMin) { | |
return outMin; | |
} | |
return (x-inMin) / (inMax-inMin) * (outMax-outMin) + outMin; | |
}, | |
linexp: function(x, inMin, inMax, outMin, outMax) { | |
if (x < inMin) { | |
return outMin; | |
} else if (x > inMax) { | |
return outMax; | |
} | |
if (outMin === 0) { | |
return 0; | |
} | |
if (inMax === inMin) { | |
return outMax; | |
} | |
return Math.pow(outMax/outMin, (x-inMin)/(inMax-inMin)) * outMin; | |
}, | |
explin: function(x, inMin, inMax, outMin, outMax) { | |
if (x < inMin) { | |
return outMin; | |
} else if (x > inMax) { | |
return outMax; | |
} | |
if (inMin === 0) { | |
return outMax; | |
} | |
return Math.log(x/inMin) / Math.log(inMax/inMin) * (outMax-outMin) + outMin; | |
}, | |
expexp: function(x, inMin, inMax, outMin, outMax) { | |
if (x < inMin) { | |
return outMin; | |
} else if (x > inMax) { | |
return outMax; | |
} | |
if (inMin === 0 || outMin === 0) { | |
return 0; | |
} | |
return Math.pow(outMax/outMin, Math.log(x/inMin) / Math.log(inMax/inMin)) * outMin; | |
} | |
}; | |
fn.register("zmap", ZMapNode); | |
})(timbre); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment