// 20KB Ditty cover of "Camel by camel" by Sandy Marton (1984) // by srtuss // // Again, this song was first created in FL Studio and then converted into JS code. // This time the synths employ a slow-update technique, which means that all the costly code that creates parameter/option automation // of any kind, executes only at a rate of 100Hz instead of at audio rate (44100Hz) which gives a noticeable performance boost. This is // sufficient for most parameter automation, while it adds only minimal additional code! // // On lower-end devices there still will be occasional performance hiccups when the orchestra-hit synth or the vocal synth triggers. // Although, this seems to only happen the first time that the synth gets triggered, and the playback runs smoothly from there on. class Delayline { constructor(n) { this.n = ~~n; this.p = 0; this.lastOut = 0; this.data = new Float32Array(n); } process(input) { this.lastOut = this.data[this.p]; this.data[this.p] = input; if(++this.p >= this.n) this.p = 0; return this.lastOut; } tap(offset) { var x = this.p - offset - 1; x %= this.n; if(x < 0) x += this.n; return this.data[x]; } } const echo = filter.def(class { constructor(opt) { this.lastOut = [0, 0]; var division = opt.division || 3/4; var pan = clamp01((opt.pan || 0)*.5+.5); var sidetime = (opt.sidetime || 0) / ditty.dt; var time = 60 * division / ditty.bpm; this.fb = clamp(opt.feedback || 0, -1, 1); this.kl = 1-pan; this.kr = pan; this.wet = opt.wet || .5; this.stereo = isFinite(opt.stereo) ? opt.stereo : 1; var n = ~~(time / ditty.dt); this.delay = [new Delayline(n), new Delayline(n)]; this.dside = new Delayline(~~sidetime); } process(inv, opt) { this.dside.process(inv[0]); var l = this.dside.lastOut * this.kl; var r = inv[1] * this.kr; var nextl = l + this.delay[1].lastOut * this.fb; var nextr = r + this.delay[0].lastOut * this.fb; this.lastOut[0] = inv[0] + this.delay[0].lastOut * this.wet; this.lastOut[1] = inv[1] + this.delay[1].lastOut * this.wet; this.delay[0].process(nextl); this.delay[1].process(nextr); if(this.stereo != 1) { var m = (this.lastOut[0] + this.lastOut[1])*.5; var s = (this.lastOut[0] - this.lastOut[1])*.5; s *= this.stereo; this.lastOut[0] = m+s; this.lastOut[1] = m-s; } return this.lastOut; } }, {sidetime: .01, division: 1/2, pan: .5, wet: .5, feedback: .6, stereo: 2}); // Simple allpass reverberator, based on this article: // http://www.spinsemi.com/knowledge_base/effects.html const reverb = filter.def(class { constructor(opt) { this.lastReturn = 0; this.density = opt.density; this.delaylines = []; // Create several delay lines with random lengths [263,863,1319,1433,439,359,887,1399,233,1367,4253,2903].forEach((dl) => this.delaylines.push(new Delayline(dl))); this.tap = [111,2250,311,1150,511,50,4411,540]; this.dry = 1-opt.mix; this.wet = opt.mix*.25; this.pred = new Delayline(opt.pred / ditty.dt); } allpass(delayline, x, k) { var delayin = x - delayline.lastOut * k; var y = delayline.lastOut + k * delayin; delayline.process(delayin); return y; } process(input, options) { var inv = input[0] + input[1]; if(this.pred.n > 0) inv = this.pred.process(inv); var v = this.lastReturn; var dls = this.delaylines; // Let the signal pass through the loop of delay lines. Inject input signal at multiple locations. v = this.allpass(dls[0], v + inv, .5); v = this.allpass(dls[1], v, .5); dls[2].process(v); v = dls[2].lastOut * this.density; v = this.allpass(dls[3], v + inv, .5); v = this.allpass(dls[4], v, .5); dls[5].process(v); v = dls[5].lastOut * this.density; v = this.allpass(dls[6], v + inv, .5); v = this.allpass(dls[7], v, .5); dls[8].process(v); v = dls[8].lastOut * this.density; v = this.allpass(dls[9], v + inv, .5); v = this.allpass(dls[10], v, .5); dls[11].process(v); v = dls[11].lastOut * this.density; this.lastReturn = v; // Tap the delay lines at randomized locations and accumulate the output signal. var ret = [0, 0]; ret[0] += dls[2].tap(this.tap[0]); ret[1] += dls[2].tap(this.tap[1]); ret[0] += dls[5].tap(this.tap[2]); ret[1] += dls[5].tap(this.tap[3]); ret[0] += dls[8].tap(this.tap[4]); ret[1] += dls[8].tap(this.tap[5]); ret[0] += dls[11].tap(this.tap[6]); ret[1] += dls[11].tap(this.tap[7]); // Mix wet + dry signal. ret[0] = ret[0] * this.wet + input[0] * this.dry; ret[1] = ret[1] * this.wet + input[1] * this.dry; // Stereo widening: var m = (ret[0]+ret[1]) * .5; var s = (ret[1]-ret[0]) * .5; ret[0] = m + s * 1.5; ret[1] = m - s * 1.5; return ret; } }, {mix: .1, density: .5, pred: .01}); // filtered sawtooth function form athibaul's ditties: function softclip(x) { return x<-1?-1:x>1?1:1.5*(1-x*x/3)*x; } function varsaw(p, formant) { let x = p-~~p; return (x - 0.5) * softclip(formant*x*(1-x)); } const fract = (x) => x - Math.floor(x); const triangle01 = x => Math.abs(fract(x + .5) - .5) * 2; const triangle11 = x => Math.abs(fract(x + .75) - .5) * 4 - 1; class Analog { constructor(opt) { var def = {nunison:1,spread:.9,detune:.1,flt1:.5,flt2:.02,fm12:0,fshimmer:0,pw:.3}; for(const x in def) if(!isFinite(opt[x])) opt[x]=def[x]; this.ops = []; var vol = opt.nunison > 1 ? 1 / opt.nunison : 1; for(var i = 0; i < opt.nunison; ++i) { var t = opt.nunison > 1 ? i / (opt.nunison-1) : .5; var x = t*2-1; var pan = x * opt.spread; this.ops.push({ pha1:Math.random(), pha2:Math.random(), pitch: 2 ** (Math.sin(x * 12) * opt.detune / 12), fl: (pan>0?1-pan:1) * vol, fr: (pan<0?1+pan:1) * vol }); } this.tshimmer = 0; this.fshimmer = opt.fshimmer; this.timbre1 = opt.timbre1; this.timbre2 = opt.timbre2; this.tupd = 0; this.kupd = 100 * ditty.dtick; this.pw = opt.pw; } process(note, env, tick, opt) { if(this.tupd <= 0) { this.tupd += 1; var pitch = opt.pitch||0; this.pinc = midi_to_hz(note + pitch) * ditty.dt; this.o1flt = opt.flt1; this.o2flt = opt.flt2; this.fm12 = opt.fm12; } this.tupd -= this.kupd; if(this.tshimmer >= 1) { for(var i = 0; i < this.ops.length; ++i) { var op = this.ops[i]; op.pitch = 2 ** ((Math.random()*2-1) * opt.detune / 12); } this.tshimmer -= 1; } this.tshimmer += ditty.dt * this.fshimmer; var vl=0, vr=0; for(var i = 0; i < this.ops.length; ++i) { var op = this.ops[i]; var fbase = this.pinc * op.pitch; var osc1 = varsaw(op.pha1, this.o1flt / fbase); if(this.timbre1==1) osc1 = triangle11(op.pha1); else if(this.timbre1==2) osc1 -= varsaw(op.pha1+this.pw, this.o1flt / fbase); var osc2 = varsaw(op.pha2, this.o2flt / fbase); vl += osc1 * op.fl; vr += osc1 * op.fr; op.pha1 += fbase * (1+osc2*this.fm12); op.pha2 += fbase * .99; } return [vl*env.value, vr*env.value]; } }; const lead = synth.def(Analog, {nunison:4, detune:.2, attack: .01, fm12:20, amp: 1.3, pitch: (tick, opt)=>triangle11(Math.max(tick-.25,0)*3)}); const arpbass = synth.def(Analog, {nunison:4, detune:.2, attack: .01, fm12:(tick,opt)=>20+(ditty.tick%16)*4, amp: 1.3, pitch: (tick, opt)=>triangle11(Math.max(tick-.25,0)*3)}); const lead2 = synth.def(Analog, {nunison:4, detune:.2, attack: .01, timbre1:1, amp: 1, pitch: (tick, opt)=>12+triangle11(Math.max(tick-.25,0)*3)}); const bass = synth.def(Analog, {nunison:1, detune:.01, attack: .001, timbre1:2, sustain: 1, amp: 1, fm12:0, flt1:(tick, opt)=>2**Math.max(-tick * 24, -5)}); const pad = synth.def(Analog, {nunison:4, detune:.3, attack: .01, fm12:1, amp: 1, flt1: .15, pitch: 0, spread: 1, fshimmer: 30}); const bell = synth.def((ph, env, tick, opt) => Math.sin(Math.PI*2*(ph+Math.sin(Math.PI*2*ph*3) * Math.exp(tick*-20))) * env.value); var conga = synth.def((ph, env, tick, opt) => Math.sin(ph * Math.PI * 2) * env.value, {note: hz_to_midi(251), amp: .8, duration: .013, release: .1}); var crash = synth.def((ph, env, tick, opt) => (Math.random()-.5) * env.value, {amp: .8, duration: .013, release: 1.5}); const orchhit = synth.def(Analog, {nunison:5, detune:.2, attack: .001, timbre1:0, decay: .1, sustain: .4, amp: 2.5, release: .1, fm12:15, flt1:(tick, opt)=>.01+2**(-tick * 20)}); ditty.bpm = 110; loop( (lc) => { const pat0 = { p:[69,71,72,72,69,71,72,74,72,71,72,69,69,71,72,74,72,71,72,69,71,67,69], x:[.25,.25,.5,.5,.25,.25,.25,.25,.25,.25,.5,.75,.25,.25,.25,.5,.25,.25,.5,.25,.5,.25,.5], d:[.198,.198,.115,.458,.198,.198,.104,.24,.146,.135,.469,.198,.125,.208,.24,.458,.198,.198,.448,.146,.24,.115,.188], s:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] }; const pat1 = { p:[69,71,72,74,60,60,62,65,64,62,60,60,62,65,64,62,67], x:[.25,.25,.25,3.25,.25,.25,.5,.5,.25,6.25,.25,.25,.5,.5,.25,.5,1.75], d:[.219,.219,.219,.219,.208,.208,.208,.396,.208,1.542,.208,.208,.208,.396,.208,.167,1.385], s:[0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1] }; const pat2 = { p:[57,58,61,61,57,58,61,62,61,58,57,60,60,62,65,64,62,57,58,61,61,57,58,61,62,61,58,57,60,60,62,65,64,62,67], x:[.25,.25,.5,.5,.25,.25,.25,.25,.25,.25,1,.25,.25,.5,.5,.25,2.25,.25,.25,.5,.5,.25,.25,.25,.25,.25,.25,1,.25,.25,.5,.5,.25,.5,1.75], d:[.25,.25,.125,.406,.25,.25,.125,.219,.219,.219,.469,.208,.208,.208,.396,.208,1.542,.25,.25,.125,.406,.25,.25,.125,.219,.219,.219,.469,.208,.208,.208,.396,.208,.167,1.385], s:[0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1] }; const pat3 = { p:[57,58,61,61,57,58,61,61,57,58,61,61,57,58,61,57,58,61,62,61,58,57], x:[.25,.25,.5,.5,.25,.25,.5,.5,.25,.25,.5,.5,.25,.25,.5,.25,.25,.25,.25,.25,.25,1], d:[.25,.25,.125,.406,.25,.25,.125,.406,.25,.25,.125,.406,.25,.25,.406,.25,.25,.125,.219,.219,.219,.469], s:[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] }; const insts = [lead, lead2]; var pats = [pat3,pat3,pat0,pat0,pat0,pat0,pat1,pat0,pat0,pat1,pat2,pat0,pat0,pat0,pat0]; var seq = pats[lc%pats.length]; for (let i=0; i < seq.p.length; i++) { insts[seq.s[i]].play(seq.p[i]+2, { duration: seq.d[i], release: .01}); bell.play(seq.p[i]+14, { duration: seq.d[i], release: .01, amp: .25}); sleep(seq.x[i]); } }, { name: 'lead' }).connect(echo.create()); loop( (lc) => { sleep(16); for(var j = 0; j < 64; ++j) { const seq = { p:[0,0,12,0,0,12,0,12], x:[.5,2,.5,.25,1.25,1,.75,.75,.25], d:[.1,.1,.2,.1,.1,.2,.2,.1,.1] }; for (let i=0; i < seq.p.length; i++) { arpbass.play(seq.p[i]+e2, { detune: .02, duration: seq.d[i], release: .1}); sleep(.25); } } }, { name: 'lead_low' }); loop( (lc) => { const pat0 = { p:[38,38,38,38,38,38,38,50,38,38,38,36,38,38,38,50,36,38], x:[1.5,.25,1.75,.25,.75,.5,.25,2.75,1.5,.25,1.75,.25,.75,.5,.25,1.25,.75,.75], d:[.344,.198,.344,.198,.344,.344,.198,.344,.344,.198,.344,.198,.344,.344,.177,.354,.344,.344] }; const pat1 = { p:[45,57,45,57,57,41,53,41,53,53,43,55,43,55,55,38,50,38,41,53,43,45,57,45,57,57,41,53,41,41,53,53,43,55,43,55,55,38,50,38,41,53,43], x:[.5,.5,.25,.5,.25,.5,.5,.25,.5,.25,.5,.5,.25,.5,.25,.5,.25,.25,.25,.5,.25,.5,.5,.25,.5,.25,.5,.25,.25,.25,.5,.25,.5,.5,.25,.5,.25,.5,.25,.25,.25,.5,.25], d:[.427,.427,.177,.427,.177,.427,.427,.177,.427,.177,.427,.427,.177,.427,.177,.427,.177,.177,.177,.427,.177,.427,.427,.177,.427,.177,.427,.177,.177,.177,.427,.177,.427,.427,.177,.427,.177,.427,.177,.177,.177,.427,.188] }; const pats = [,pat0,pat0,pat1,pat0,pat1,pat1,,,]; var seq = pats[lc%pats.length]; if(seq) { for (let i=0; i < seq.p.length; i++) { bass.play(seq.p[i]+2-12, { duration: seq.d[i], release: .01}); sleep(seq.x[i]); } } else sleep(16); }, { name: 'bass' }).connect(echo.create()); let sat = x => Math.max(Math.min(x, 1), -1); var kick = synth.def((ph, env, tick, opt) => sat(Math.sin(Math.sqrt(ph) * 8 + ph * .5) * 2 * env.value), {note: c4, attack: .001, release: .2, duration: .1, amp: .6}); var snare = synth.def((ph, env, tick, opt) => sat(((Math.random() - .5) + Math.sin(Math.sqrt(ph) * 15 + ph * 3) * .5) * env.value), {note: c4, amp: 1.1, attack: 0, release: .3}); var clhat = synth.def((ph, env, tick, opt) => (Math.random()-.5) * env.value, {release: .05, amp: .5}); var ohat = synth.def((ph, env, tick, opt) => (Math.random()-.5) * env.value, {release: .2, amp: .6}); var woodblock = synth.def((ph, env, tick, opt) => Math.sin(ph * Math.PI * 2) * env.value, {note: hz_to_midi(537.8), amp: .4, duration: .013, release: .036}); var clave = synth.def((ph, env, tick, opt) => Math.sin(ph * Math.PI * 2) * env.value, {note: hz_to_midi(2100), amp: .4, duration: .013, release: .036}); loop( (lc) => { var pats = ["x...", "xxxx.x.xx.xx.x..", "x... ..."]; var seq = [0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,,,,,,,,,]; var pat = pats[seq[lc%seq.length]]; for(var i = 0; i < 16; ++i) { if(pat && pat[i%pat.length] == 'x') kick.play(c4); sleep(.25); } }, { name: 'kick' }); loop( (lc) => { var pats = [" ...x...", " ...x... x..x..x", " ...x... x..xxxx"]; var seq = [,,,,0,0,0,1,0,0,0,2,0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,2,,,,,,,,2]; var pat = pats[seq[lc%seq.length]]; for(var i = 0; i < 16; ++i) { if(pat && pat[i%pat.length] == 'x') snare.play(c4); sleep(.25); } }, { name: 'snare' }); loop( () => { clhat.play(c4); sleep(.25); }, { name: 'hat' }); loop( () => { var pat = "..xx....xxx..x....xx....xxx..x.."; for(var i = 0; i < pat.length; ++i) { if(pat[i] == 'x') clave.play(c4); sleep(.25); } }, { name: 'clave' }); loop( () => { var pat = "....x......x........x......x..x."; for(var i = 0; i < pat.length; ++i) { if(pat[i] == 'x') woodblock.play(c4); sleep(.25); } }, { name: 'woodblock' }); loop( () => { var pat = "xxxx..xx.x.xxx.x"; for(var j = 0; j < 4; ++j) { for(var i = 0; i < pat.length; ++i) { if(pat[i] == 'x') conga.play(c4); sleep(.25); } } }, { name: 'conga', sync: 144 }); loop( () => { var pat = "..............................x."; for(var i = 0; i < pat.length; ++i) { if(pat[i] == 'x') crash.play(c4); sleep(.25); } }, { name: 'crash', sync: 144 }).connect(reverb.create({dry:.7,wet:.3})); loop( () => { const seq = { p:[59,59,59,59,59,59,59,59,59], x:[4.5,.25,.25,1.25,.25,.5,.25,.5,.75], d:[.969,.188,.188,.438,.188,.188,.188,.188,.719] }; sleep(7.5); for (let i=0; i < seq.p.length; i++) { orchhit.play(seq.p[i]-24, { duration: seq.d[i]}); orchhit.play(seq.p[i], { duration: seq.d[i]}); orchhit.play(seq.p[i]-12, { duration: seq.d[i]}); sleep(seq.x[i]); } }, { name: 'orchhit', sync: 144 }).connect(reverb.create({mix: 1, density: .1, pred:0 })); loop( (lc) => { sleep(112) const seq = { p:[57,76,69,53,72,65,55,74,67,50,69,62,57,76,69,53,77,72,55,79,74,81,50,74], x:[0,0,2,0,0,2,0,0,2,0,0,2,0,0,2,0,0,2,0,0,2,0,0,2], d:[1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8,1.8] }; for(var j = 0; j < 2; ++j) { for (let i=0; i < seq.p.length; i++) { pad.play(seq.p[i]+14, { duration: seq.d[i], attack: .4, release: 2}); sleep(seq.x[i]); } } }, { name: 'pad', sync: 144, amp: .8 }).connect(echo.create()); // State Variable Filter based on an article by cytomic Sound Music Software // https://cytomic.com/files/dsp/SvfLinearTrapOptimised2.pdf class SVF { constructor(opt) { this.stages = []; this.mode = opt ? opt.mode || 'lp' : 'lp'; this.fc = 0; this.q = 1; this.num = opt ? opt.num || 1 : 1; // g parameter determines cutoff // k parameter = 1/Q for(var i = 0; i < this.num; ++i) { this.stages.push({lp:0, bp:0, hp:0, ap:0, ic1eq:0, ic2eq:0}); } this.q = opt && isFinite(opt.q) ? opt.q : 1; this.fc = opt && isFinite(opt.fc) ? opt.fc : .25; } _run(s, input, a1, a2, a3, k) { var v1, v2, v3; v3 = input - s.ic2eq; v1 = a1 * s.ic1eq + a2 * v3; v2 = s.ic2eq + a2 * s.ic1eq + a3 * v3; s.ic1eq = 2 * v1 - s.ic1eq; s.ic2eq = 2 * v2 - s.ic2eq; s.lp = v2; s.bp = v1; s.hp = input - k * v1 - v2; s.ap = s.lp + s.hp - k * s.bp; } process(input) { if(this.fc != this._fc) { this._fc = this.fc; this._q = this.q; var fc = this.fc * .5; if (fc >= 0 && fc < .5) { this.g = Math.tan(Math.PI * fc); this.k = 1 / this.q; this.a1 = 1 / (1 + this.g * (this.g + this.k)); this.a2 = this.g * this.a1; this.a3 = this.g * this.a2; } } if(this.q != this._q) { this._q = this.q; this.k = 1 / this.q; this.a1 = 1 / (1 + this.g * (this.g + this.k)); this.a2 = this.g * this.a1; this.a3 = this.g * this.a2; } for(var i = 0; i < this.num; ++i) { this._run(this.stages[i], input, this.a1, this.a2, this.a3, this.k); this._run(this.stages[i], input, this.a1, this.a2, this.a3, this.k); input = this.stages[i][this.mode]; } return input; } } const tenor = [ {f:[650,1080,2650,2900,3250], a:[0,-6,-7,-8,-22]}, {f:[400,1700,2600,3200,3580], a:[0,-14,-12,-14,-20]}, {f:[290,1870,2800,3250,3540], a:[0,-15,-18,-20,-30]}, {f:[400,800,2600,2800,3000], a:[0,-10,-12,-12,-26]}, {f:[350,600,2700,2900,3300], a:[0,-20,-17,-14,-26]} ]; const soprano = [ {f:[800,1150,2900,3900,4950], a:[0,-6,-32,-20,-50]}, {f:[350,2000,2800,3600,4950], a:[0,-20,-15,-40,-56]}, {f:[270,2140,2950,3900,4950], a:[0,-12,-26,-26,-44]}, {f:[450,800,2830,3800,4950], a:[0,-11,-22,-22,-50]}, {f:[325,700,2700,3800,4950], a:[0,-16,-35,-40,-60]} ]; const voice = synth.def(class { constructor(opt) { this.f = []; for(var i = 0; i < 5; ++i) this.f.push(new SVF({mode:'bp', num:Math.floor(18-i*4), q:1})); this.p = 0; } process(note, env, tick, opt) { var k = Math.floor(opt.vowel); var inp = (Math.random()-.5) * .8; inp += (this.p-~~this.p)*2-1; this.p += ditty.dt * midi_to_hz(note - clamp01((.1-tick)*50)*.4 + Math.sin(tick*11)*.2); var v = 0; for(var i = 0; i < 5; ++i) { var freq = lerp(opt.vowels[k].f[i],opt.vowels[(k+1)%5].f[i], opt.vowel-k); var ampdb = lerp(opt.vowels[k].a[i],opt.vowels[(k+1)%5].a[i], opt.vowel-k); freq *= 2**opt.scale; this.f[i].fc = freq * ditty.dt; v += this.f[i].process(inp) * 10**(ampdb/20) } return v*env.value; } }); loop(()=>{ sleep(80); for(var j = 0; j < 2; ++j) { const seq = { a:[.01,.01,.05,.01,.01], p:[50,47,52,50,47], x:[.75,.75,.5,.75,1.25], d:[.667,.417,.354,.625,.375], vowel:[1,(tick,opt)=>lerp(3,4,clamp01(tick*3)),(tick,opt)=>clamp01(tick*5+.2),1,(tick,opt)=>lerp(3,4,clamp01(tick*3))], amp:[(tick,opt)=>(1-Math.exp(Math.abs(tick-.1)*-40)),1,(tick,opt)=>(1-Math.exp(Math.abs(tick-.1)*-40)),(tick,opt)=>1-Math.exp(Math.abs(tick-.1)*-40),1] }; for (let i=0; i < seq.p.length; i++) { voice.play(seq.p[i], { duration: seq.d[i], attack:seq.a[i], decay: .1, sustain: .8, release: .05, vowels: tenor, vowel: seq.vowel[i], scale:0.3, amp: seq.amp[i]}); voice.play(seq.p[i]+12, { duration: seq.d[i], attack:seq.a[i], decay: .1, sustain: .8, release: .05, vowels: soprano, vowel: seq.vowel[i], scale:0.2, amp: seq.amp[i]}); sleep(seq.x[i]); } sleep(4); } }, {name: "vocals", sync: 144, amp: 3}).connect(reverb.create({mix:.5}));