{
   "ancestor_list" : [],
   "description" : "GUI for recording and playing quantized loops.\r\nIf you change the tempo, hit on refresh.\r\nYou can change the output bus for the click (first variable declared)\r\nIf you have troubles with the font, change it to a font you might have installed, I'm using: Synchro LET\r\nYou might use MIDI responders to activate the buttons, but you have to write that yourself.\r\n\r\nExecute, record, play, have fun",
   "name" : "Looping Station",
   "author" : "rukano",
   "id" : "1-1HT",
   "is_private" : null,
   "code" : "var click_out = 0;\r\nvar win = Window(\"Super Looper\", Rect(100, 200, 800, 600)).front;\r\nvar numbufs = 64;\r\nvar layout = win.addFlowLayout((0@0), (0@0));\r\nvar container = ();\r\nvar size = (40@40);\r\nvar metronome = ();\r\nvar font = Font(\"Synchro LET\", 18);\r\nvar buffer = nil!numbufs;\r\nvar durations = [2,4,8,16];\r\nvar player = nil!numbufs;\r\nvar player_button = (nil!(numbufs/4))!4;\r\nvar recorder_button = (nil!(numbufs/4))!4;\r\nvar reload_func = {\r\n\tvar tempo = TempoClock.default.tempo.reciprocal;\r\n\tnumbufs.do{ |i|\r\n\t\tvar slot = floor(i/(numbufs/4));\r\n\t\tbuffer[i].free;\r\n\t\tbuffer[i] = Buffer.alloc(s, s.sampleRate * durations[slot] * tempo);\r\n\t};\r\n};\r\n\r\nTempoClock.default.tempo = 90/60;\r\n\r\n// Window\r\nwin.view.background_(Color.white);\r\n\r\n// Controls\r\nStaticText(win, (800@10));\r\n\r\nStaticText(win, (100@30))\r\n.font_(font)\r\n.string_(\" Tempo:\");\r\n\r\nNumberBox(win, (100@30))\r\n.align_(\\right)\r\n.font_(font)\r\n.step_(0.01)\r\n.scroll_step_(0.1)\r\n.clipLo_(40)\r\n.clipHi_(400)\r\n.value_(TempoClock.default.tempo * 60)\r\n.action_{ |v| TempoClock.default.tempo = (v.value / 60).postln};\r\n\r\nStaticText(win, (40@10));\r\nButton(win, (120@30))\r\n.font_(font)\r\n.states_([[\"Reload\", Color.red, Color.white]])\r\n.action_{ reload_func.value };\r\nStaticText(win, (40@10));\r\n\r\n\r\nStaticText(win, (100@30))\r\n.font_(font)\r\n.align_(\\center)\r\n.string_(\" Click:\");\r\n\r\nmetronome.button = Button(win, (100@30)).states_([\r\n\t[\"OFF\", Color.white, Color.black],\r\n\t[\"ON\", Color.white, Color.red(0.75)],\t\r\n])\r\n.font_(font)\r\n.action_{ |v|\r\n\tif ( v.value.booleanValue )\r\n\t\t{ metronome.player_instance = metronome.player.play(quant:4) }\r\n\t\t{ metronome.player_instance.stop }\r\n};\r\n\r\nmetronome.display = 4.collect{\r\n\tStaticText(win, (15@10));\r\n\tUserView(win, (30@30)).background_(Color.red(0.25))\r\n};\r\nStaticText(win, (15@10));\r\nStaticText(win, (800@10));\r\nlayout.nextLine;\r\n\r\n4.do{ |i| StaticText(win, (200@30)).align_(\\center).font_(font).string_(durations[i]) };\r\n\r\n\r\n// Loopers\r\n4.do{ |i|\r\n\tvar font = Font(\"Synchro LET\", 14);\r\n\tvar bsize = 43;\r\n\tcontainer[i] = ();\r\n\tcontainer[i].view = CompositeView(win, (200@400));\r\n\tcontainer[i].view.background_([Color.black, Color.white, Color.black, Color.white][i]);\r\n\tcontainer[i].layout = container[i].view.addFlowLayout((5@5),(5@5));\r\n\t(numbufs/4).do{ |j|\r\n\t\tvar current_buffer = (i * (numbufs/4))+j;\r\n\t\t// REC\r\n\t\trecorder_button[i][j] = Button(container[i].view, (bsize@bsize))\r\n\t\t.font_(font)\r\n\t\t.states_([\r\n\t\t\t[\"REC\", Color.red, Color.white],\r\n\t\t\t[\"-\", Color.white, Color.red]\r\n\t\t])\r\n\t\t.action_{ |v|\r\n\t\t\t(\"will record buffer: \"++current_buffer).postln;\r\n\t\t\tPbind(\r\n\t\t\t\t\\instrument, \\buf_recorder,\r\n\t\t\t\t\\buf, Pseq([buffer[current_buffer]], 1)\r\n\t\t\t).play(quant:4);\r\n\t\t\tAppClock.sched(\r\n\t\t\t\t(TempoClock.default.tempo.reciprocal * durations[i])\r\n\t\t\t\t+ ((TempoClock.default.nextBar - TempoClock.default.beats) * TempoClock.default.beatDur),\r\n\t\t\t\t{ v.value_(0) }\r\n\t\t\t);\r\n\t\t};\r\n\t\t\r\n\t\t// PLAY\r\n\t\tplayer_button[i][j] = Button(container[i].view, (bsize@bsize))\r\n\t\t.font_(font)\r\n\t\t.states_([\r\n\t\t\t[\"PLAY\", Color.green, Color.white],\r\n\t\t\t[\"STOP\", Color.black, Color.green(0.5)]\r\n\t\t])\r\n\t\t.action_{ |v|\r\n\t\t\tif (v.value.booleanValue) {\r\n\t\t\t\t(\"will play buffer: \"++current_buffer).postln;\r\n\t\t\t\tbuffer[current_buffer].postln;\r\n\t\t\t\tplayer[current_buffer] = Pbind(\r\n\t\t\t\t\t\\instrument, \\buf_player,\r\n\t\t\t\t\t\\buf, buffer[current_buffer],\r\n\t\t\t\t\t\\dur, durations[i]\r\n\t\t\t\t).play(quant:4);\r\n\t\t\t} {\r\n\t\t\t\t(\"stopping buffer: \"++current_buffer).postln;\r\n\t\t\t\tplayer[current_buffer].stop;\r\n\t\t\t}\r\n\t\t};\r\n\t};\r\n};\r\n\r\nStaticText(win, (800@100)).font_(font).string_(\"Record and play loops in different lengths as you wish.\r\nIf you change the tempo, be sure to hit 'refresh'\r\nHave fun!!!\");\r\n\r\n// Players\r\nmetronome.player = Pbind(\r\n\t\\instrument, \\click,\r\n\t\\freq, Pseq([1000, Pn(500,3)], inf)\r\n);\r\n\r\nmetronome.display_player = Pbind(\r\n\t\\type, \\setProperties,\r\n\t\\args, #[\\background],\r\n\t\\receiver, Pstutter(2, Pseq(metronome.display, inf)),\r\n\t\\background, Pseq([Color.green, Color.red(0.25)], inf),\r\n\t\\dur, Pseq([9,1]/10, inf)\r\n).play(quant:4);\r\n\r\nwin.onClose_{\r\n\tmetronome.display_player.stop;\r\n\tmetronome.player_instance.stop;\r\n\tnumbufs.do{ |i| buffer[i].free };\r\n\tSystemClock.clear;\r\n\tAppClock.clear;\r\n\tTempoClock.default.clear;\r\n};\r\n\r\ns = Server.default;\r\ns.waitForBoot {\r\n\r\nreload_func.value;\r\n\r\nSynthDef(\\click, { |freq|\r\n\tvar snd = SinOsc.ar(freq, 0.5pi) * EnvGen.ar(Env.perc(0.001,0.05), doneAction:2);\r\n\tOffsetOut.ar(click_out, snd!2);\r\n}).add;\r\n\r\nSynthDef(\\buf_recorder, { |buf|\r\n\tRecordBuf.ar(SoundIn.ar(0), buf, loop:0, doneAction:2)\r\n}).add;\r\n\r\nSynthDef(\\buf_player, { |out, buf, rate=1|\r\n\tvar snd = PlayBuf.ar(1, buf, rate, loop:0, doneAction:2);\r\n\tOffsetOut.ar(out, snd!2)\r\n}).add;\r\n};",
   "labels" : [
      "looper"
   ]
}
