diff --git a/README.md b/README.md index dcdb3d0..b8b9fb3 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,11 @@ T("sin", {freq:880, mul:0.5}).play(); MIT ## ChangeLog ## -** WORKING ** +**13.05.01** (361.75KB) +* Added: `T("task")` +* Added: `TimbreObject.to()` +* Added: `TimbreObject.splice()` +* Added: `TimbreObject.postMessage()` * Updated: `T("mml")` support multi tracks, command token * Updated: `TimbreObject` support buddies interface * Bugfix: `T("mouse.x")`, `T("mouse.y")` diff --git a/package.json b/package.json index 0b7cdff..fcda91e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "timbre", - "version": "13.04.19", + "version": "13.05.01", "description": "JavaScript library for objective sound programming", "author": "nao yonamine ", "repository": { diff --git a/timbre.dev.js b/timbre.dev.js index 7a4b352..a7caeb5 100644 --- a/timbre.dev.js +++ b/timbre.dev.js @@ -24,7 +24,7 @@ var ACCEPT_SAMPLERATES = [8000,11025,12000,16000,22050,24000,32000,44100,48000]; var ACCEPT_CELLSIZES = [32,64,128,256]; - var _ver = "13.04.19"; + var _ver = "13.05.01"; var _sys = null; var _constructors = {}; var _factories = {}; @@ -35,7 +35,7 @@ var _bpm = 120; var T = function() { - var args = slice.call(arguments), key = args[0], t; + var args = slice.call(arguments), key = args[0], t, m; switch (typeof key) { case "string": @@ -43,6 +43,19 @@ 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": @@ -393,6 +406,7 @@ _sys.timers.push(self); _sys.events.emit("addObject"); self._.emit("start"); + fn.buddies_start(self); } }; }; @@ -403,12 +417,14 @@ _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; @@ -428,6 +444,7 @@ _sys.listeners.push(self); _sys.events.emit("addObject"); self._.emit("listen"); + fn.buddies_start(self); } }; }; @@ -438,13 +455,15 @@ _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.listen = function() { + self.nodeType = TimbreObject.LISTENER; + self.listen = function(buddies) { if (arguments.length) { self.append.apply(self, arguments); } @@ -640,6 +659,44 @@ } }; + 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); }; @@ -1045,8 +1102,16 @@ }; 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); + } + } }); } @@ -1072,6 +1137,7 @@ break; } this.playbackState = PLAYING_STATE; + this.nodeType = TimbreObject.DSP; this._.ar = true; this._.mul = 1; @@ -1081,7 +1147,11 @@ this._.meta = {}; this._.samplerate = _sys.samplerate; this._.cellsize = _sys.cellsize; + this._.buddies = []; } + TimbreObject.DSP = 1; + TimbreObject.TIMER = 2; + TimbreObject.LISTENER = 3; var $ = TimbreObject.prototype; @@ -1127,6 +1197,19 @@ 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; + } } }); @@ -1194,6 +1277,71 @@ 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); @@ -1279,10 +1427,10 @@ if (dac === null) { dac = this._.dac = new SystemInlet(this); } - if (dac.playbackState === FINISHED_STATE) { - dac.play(); + if (dac.play()) { this._.emit.apply(this, ["play"].concat(slice.call(arguments))); } + fn.buddies_start(this); return this; }; @@ -1293,6 +1441,7 @@ this._.dac = null; this._.emit("pause"); } + fn.buddies_stop(this); return this; }; @@ -1623,7 +1772,11 @@ var ArrayWrapper = (function() { function ArrayWrapper(_args) { TimbreObject.call(this, 1, []); - fn.fixKR(this); + + 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]; @@ -1640,6 +1793,40 @@ }); + $.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; })(); @@ -1705,6 +1892,7 @@ $.play = function() { _sys.nextTick(this._.onplay); + return (_sys.inlets.indexOf(this) === -1); }; $.pause = function() { @@ -1712,8 +1900,7 @@ }; $.process = function(tickID) { - var node = this._.node; - + var node = this._.node; if (node.playbackState & 1) { node.process(tickID); this.cells[1].set(node.cells[1]); @@ -5107,6 +5294,19 @@ 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; @@ -5691,6 +5891,7 @@ "use strict"; var fn = T.fn; + var Tape = T.modules.Scissor.Tape; var isSignalArray = function(obj) { return fn.isSignalArray(obj) || obj instanceof Float32Array; }; @@ -5740,6 +5941,11 @@ 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]) && @@ -6996,8 +7202,8 @@ } var opts = _args[0]; - var a = envValue(opts, 10, 10, "a" , "attackTime", timevalue); - var r = envValue(opts, 10, 1000, "r" , "decayTime" , timevalue); + 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]]; @@ -8513,37 +8719,25 @@ fn.fixKR(this); var _ = this._; - _.mml = ""; - _.status = {t:120, l:4, o:4, v:12, q:6, dot:0, tie:false}; - _.commands = []; - _.index = 0; - _.queue = []; - _.currentTime = 0; - _.queueTime = 0; - _.segnoIndex = -1; - _.loopStack = []; - _.prevNote = 0; - _.remain = Infinity; - _.onended = fn.make_onended(this); + _.tracks = []; + _.onended = fn.make_onended(this); + _.currentTime = 0; this.on("start", onstart); } fn.extend(MML); var onstart = function() { - var _ = this._; + 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; - _.commands = compile(_.mml); - _.index = 0; - _.queue = []; - _.currentTime = 0; - _.queueTime = 0; - _.segnoIndex = -1; - _.loopStack = []; - _.prevNote = 0; - _.remain = Infinity; - - sched(this); }; Object.defineProperty(onstart, "unremoved", { value:true, writable:false @@ -8555,7 +8749,7 @@ mml: { set: function(value) { var _ = this._; - if (typeof value === "string") { + if (typeof value === "string" || Array.isArray(value)) { _.mml = value; } }, @@ -8620,35 +8814,77 @@ if (this.tickID !== tickID) { this.tickID = tickID; - var nodes = this.nodes; + 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 gen, i, imax; + var eof = false; if (queue.length) { while (queue[0][0] <= _.currentTime) { var nextItem = _.queue.shift(); - if (nextItem[1]) { - for (i = 0, imax = nodes.length; i < imax; ++i) { - gen = nodes[i]; - if (gen.noteOn) { - gen.noteOn(nextItem[1], nextItem[3]); - } else { - gen.bang(); - } - } + switch (nextItem[1]) { + case NOTEON: + noteOn(sequencer, trackNum, nextItem[2], nextItem[3]); _.remain = nextItem[4]; - _.emit("data", "noteOn", {noteNum:nextItem[1], velocity:nextItem[3]}); sched(this); - } else { - for (i = 0, imax = nodes.length; i < imax; ++i) { - gen = nodes[i]; - if (gen.noteOff) { - gen.noteOff(nextItem[2], nextItem[3]); - } else if (gen.release) { - gen.release(); - } - } - _.emit("data", "noteOff", {noteNum:nextItem[2], velocity:nextItem[3]}); + 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; @@ -8656,280 +8892,330 @@ } } _.remain -= fn.currentTimeIncr; - if (queue.length === 0 && _.remain <= 0) { - fn.nextTick(_.onended); + if (eof) { + this.ended = true; } _.currentTime += fn.currentTimeIncr; - } - - return this; - }; - - var sched = function(self) { - var _ = self._; - - 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; + 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 { - break; + gen.bang(); } } - cmd = commands[index++]; + sequencer._.emit("data", "noteOn", { + trackNum:trackNum, noteNum:noteNum, velocity:velocity + }); + }; - switch (cmd.name) { - 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; + 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(); } - duration = (60 / tempo) * (4 / len) * 1000; - duration *= [1, 1.5, 1.75, 1.875][dot] || 1; + } + sequencer._.emit("data", "noteOff", { + trackNum:trackNum, noteNum:noteNum, velocity:velocity + }); + }; - 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, val, null, vel, duration]); - } - - if (len > 0) { - quantize = status.q / 8; - // noteOff - if (quantize < 1) { - _queueTime = queueTime + (duration * quantize); - queue.push([_queueTime, null, val, vel]); - for (i = 0, imax = pending.length; i < imax; ++i) { - queue.push([_queueTime, null, pending[i], vel]); - } - } - pending = []; - queueTime += duration; - if (!status.tie) { - break outer; + 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; } - } 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) { + 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; - 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]; + + 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); } - } - 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(); + status.tie = false; + break; + case "r": + tempo = status.t || 120; + if (cmd.len !== null) { + len = cmd.len; + dot = cmd.dot || 0; } else { - index = peek[0]; + 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; } - break; - case "t": - status.t = (cmd.val === null) ? 120 : cmd.val; - break; } - } - _.index = index; - _.queueTime = queueTime; - }; + _.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); + 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; + while (re.lastIndex < mml.length) { + if (!checked[re.lastIndex]) { + break; + } + ++re.lastIndex; } - ++re.lastIndex; } } - } - commands.sort(function(a, b) { - return a.index - b.index; - }); - return commands; - }; + commands.sort(function(a, b) { + return a.index - b.index; + }); + commands.push({name:"EOF"}); + return commands; + }; - var MMLCommands = [ - { 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 } - ]; + 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 } + ]; - fn.register("mml", MML); + return MMLTrack; + })(); })(timbre); (function(T) { @@ -10181,17 +10467,17 @@ } }); - $.sched = function(delta, item) { + $.sched = function(delta, item, args) { if (typeof delta === "string") { delta = timevalue(delta); } if (typeof delta === "number") { - this.schedAbs(this._.currentTime + delta, item); + this.schedAbs(this._.currentTime + delta, item, args); } return this; }; - $.schedAbs = function(time, item) { + $.schedAbs = function(time, item, args) { if (typeof time === "string") { time = timevalue(time); } @@ -10206,7 +10492,7 @@ break; } } - queue.splice(i + 1, 0, [time, T(item)]); + queue.splice(i + 1, 0, [time, T(item), args]); } return this; }; @@ -10238,7 +10524,7 @@ if (queue.length) { while (queue[0][0] < _.currentTime) { var nextItem = _.queue.shift(); - nextItem[1].bang(); // TODO: args? + nextItem[1].bang(nextItem[2]); emit = "sched"; if (queue.length === 0) { emit = "empty"; @@ -10255,7 +10541,7 @@ }; fn.register("schedule", ScheduleNode); - fn.alias("sche", "schedule"); + fn.alias("sched", "schedule"); })(timbre); (function(T) { @@ -10956,7 +11242,9 @@ if (i !== -1) { _.genList.splice(i, 1); } - _.genDict[gen.noteNum] = null; + if (typeof gen.noteNum !== "undefined") { + _.genDict[gen.noteNum] = null; + } }; }; @@ -10977,7 +11265,8 @@ var opts = { freq : freq, noteNum : noteNum, - velocity: velocity + velocity: velocity, + mul : velocity * 0.0078125 }; if (_opts) { for (var key in _opts) { @@ -10986,7 +11275,7 @@ } opts.doneAction = make_doneAction(this, opts); - gen = this._.synthdef.call(this, opts); + gen = _.synthdef.call(this, opts); if (gen instanceof T.Object) { gen.noteNum = noteNum; @@ -11060,6 +11349,33 @@ } }; + $.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._; @@ -11255,11 +11571,20 @@ this.playbackState = fn.PLAYING_STATE; this._.tape = tape; this._.tapeStream = new TapeStream(tape, this._.samplerate); - } else 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(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; + } } } }, @@ -11271,6 +11596,13 @@ get: function() { return this._.isLooped; } + }, + buffer: { + get: function() { + if (this._.tape) { + return this._.tape.getBuffer(); + } + } } }); @@ -11291,6 +11623,12 @@ return this; }; + $.getBuffer = function() { + if (this._.tape) { + return this._.tape.getBuffer(); + } + }; + $.process = function(tickID) { var _ = this._; @@ -11318,6 +11656,144 @@ 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";