{
   "labels" : [
      "gui",
      "synthesis techniques",
      "granular sampling"
   ],
   "code" : "// ************************************\r\n// Granular Synthesis Demo (GUI)\r\n// Patch 2 - Granular Sampling with Multiple Files\r\n// Bruno Ruviaro, 2013-08-21\r\n// ************************************\r\n\r\n/*\r\n\r\nUse the \"add files\" button to load several wave or aif files.\r\nThis granulator needs mono files. If you open a stereo file,\r\nonly the left channel will be actually used.\r\n\r\nTransp: rate of transposition in semitones.\r\nDuration: duration of individual samples (sequential: \"one sample every X seconds\").\r\nPan: distribution of samples in the stereo field (left / right).\r\nAmplitude: amplitude of individual samples.\r\nReverse: probability of a sample to be played backwards (0-100%).\r\nRests: probability of rests to occur (0-100%).\r\nOverlap: smaller numbers are more 'staccato', bigger numbers more 'legato'.\r\n\r\nSamples will be chosen randomly from any of the loaded sound files.\r\n\r\nHow to start: select all (ctrl + A), then evaluate (ctrl + enter).\r\n(on a Mac, use the command key instead of control)\r\n\r\n*/\r\n\r\ns.waitForBoot({\r\n\r\n\tvar win, subwin, openButton, startButton, durSlider, transpSlider, ampSlider, panSlider, restSlider, reverseSlider, overlapSlider, displayFileName, transpToRate, bufferList, staticTextList, rateLo, rateHi, durLo, durHi, panLo, panHi, ampLo, ampHi, restProb, reverseProb, overlap, pattern, player;\r\n\r\n\t// Init values\r\n\trateLo = 1; rateHi = 1;\r\n\tdurLo = 0.1; durHi = 0.2;\r\n\tpanLo = 0; panHi = 0;\r\n\tampLo = 0.3; ampHi = 0.4;\r\n\trestProb = 0.1;\r\n\toverlap = 1;\r\n\treverseProb = 0;\r\n\r\n\tWindow.closeAll;\r\n\r\n\r\n\tstaticTextList = List.new;\r\n\r\n\t// Main window\r\n\twin = Window.new(\"Granular Sampling - Sequential, Multiple Files\", Rect(50, 50, 600, 560), false).front;\r\n\twin.background = Color.grey(0.1, 0.9);\r\n\twin.onClose = { player.stop };\r\n\r\n\r\n\t// Sub view to group all sliders\r\n\tsubwin = CompositeView.new(win, Rect(20, 220, 560, 325))\r\n\t// .background_(Color.red(0.4))\r\n\t;\r\n\tsubwin.decorator = FlowLayout(subwin.bounds, margin: 0@0, gap: 5@10);\r\n\r\n\t// FUNCTIONS\r\n\r\n\t// Convert transpSlider values (in semitones)\r\n\t// to rate values for TGrains (1 = no transp):\r\n\ttranspToRate = {arg transp; transp.linexp(-24, 24, 0.25, 4)};\r\n\r\n\t// Display file names on random places on window\r\n\tdisplayFileName = {arg fileName;\r\n\t\tvar left, top, width, height;\r\n\t\twidth = 150;\r\n\t\theight = 30;\r\n\t\tleft = rrand(0, win.bounds.width-width);\r\n\t\ttop = rrand(0, 220-height);\r\n\r\n\t\tstaticTextList.add(\r\n\t\t\tStaticText.new(win, Rect(left, top, width, height))\r\n\t\t\t.string_(fileName.asString)\r\n\t\t\t.stringColor_(Color.gray);\r\n\t\t);\r\n\r\n\t\topenButton.front;\r\n\t};\r\n\r\n\r\n\topenButton = Button.new(win, Rect(240, 90, 120, 30))\r\n\t.states_([[\"add files\", Color.black, Color.gray]])\r\n\t.action_({\r\n\t\t// Stop player if it's running:\r\n\t\tif(player.isNil, {\"do nothing\"}, { player.stop; player = nil; });\r\n\t\t// Let user select files:\r\n\t\tDialog.openPanel(\r\n\t\t\tokFunc: {arg pathList;\r\n\t\t\t\tpathList.do({arg path;\r\n\t\t\t\t\tbufferList.add(Buffer.readChannel(s, path, channels: [0]));\r\n\t\t\t\t\tdisplayFileName.value(path.split.last);\r\n\t\t\t\t});\r\n\t\t\t},\r\n\t\t\tcancelFunc: { \"cancelled\".postln },\r\n\t\t\tmultipleSelection: true);\r\n\t});\r\n\r\n\r\n\ttranspSlider = EZRanger(\r\n\t\tparent: subwin,\r\n\t\tbounds: 560@30,\r\n\t\tlabel: \"Transp  \",\r\n\t\tcontrolSpec: ControlSpec(\r\n\t\t\tminval:\t-24, // two octaves below\r\n\t\t\tmaxval: 24, // two octaves above\r\n\t\t\twarp: 'lin',\r\n\t\t\tstep: 1, // step by semitones\r\n\t\t\tunits: \" ST\"),\r\n\t\taction: {arg v;\r\n\t\t\trateLo = transpToRate.value(v.lo);\r\n\t\t\trateHi = transpToRate.value(v.hi)\r\n\t\t},\r\n\t\tinitVal: [0, 0],\r\n\t\tlabelWidth: 60,\r\n\t\tunitWidth: 30)\r\n\t.setColors(Color.grey,Color.white, Color.grey(0.7),Color.grey, Color.white, Color.yellow);\r\n\r\n\tdurSlider = EZRanger(\r\n\t\tparent: subwin,\r\n\t\tbounds: 560@30,\r\n\t\tlabel: \"Duration  \",\r\n\t\tcontrolSpec: ControlSpec(\r\n\t\t\tminval:\t0.05,\r\n\t\t\tmaxval: 2,\r\n\t\t\twarp: 'exp',\r\n\t\t\tstep: 0.01,\r\n\t\t\tunits: \"sec\"),\r\n\t\taction: {arg v;\r\n\t\t\tdurLo = v.lo;\r\n\t\t\tdurHi = v.hi;\r\n\t\t},\r\n\t\tinitVal: [durLo, durHi],\r\n\t\tlabelWidth: 70,\r\n\t\tunitWidth: 30)\r\n\t.setColors(Color.grey,Color.white, Color.grey(0.7),Color.grey, Color.white, Color.yellow);\r\n\r\n\tpanSlider = EZRanger(\r\n\t\tparent: subwin,\r\n\t\tbounds: 560@30,\r\n\t\tlabel: \"Pan     \",\r\n\t\tcontrolSpec: ControlSpec(\r\n\t\t\tminval:\t-1,\r\n\t\t\tmaxval: 1,\r\n\t\t\twarp: 'lin',\r\n\t\t\tstep: 0.1,\r\n\t\t\tunits: \"L/R\"),\r\n\t\taction: {arg v;\r\n\t\t\tpanLo = v.lo;\r\n\t\t\tpanHi = v.hi;\r\n\t\t},\r\n\t\tinitVal: [panLo, panHi],\r\n\t\tlabelWidth: 60,\r\n\t\tunitWidth: 30)\r\n\t.setColors(Color.grey,Color.white, Color.grey(0.7),Color.grey, Color.white, Color.yellow);\r\n\r\n\r\n\tampSlider = EZRanger(\r\n\t\tparent: subwin,\r\n\t\tbounds: 560@30,\r\n\t\tlabel: \"Amplitude \",\r\n\t\tcontrolSpec: ControlSpec(\r\n\t\t\tminval:\t0.0,\r\n\t\t\tmaxval: 1,\r\n\t\t\twarp: 'lin',\r\n\t\t\tstep: 0.01,\r\n\t\t\tunits: \"amp\"),\r\n\t\taction: {arg v;\r\n\t\t\tampLo = v.lo;\r\n\t\t\tampHi = v.hi;\r\n\t\t},\r\n\t\tinitVal: [ampLo, ampHi],\r\n\t\tlabelWidth: 78,\r\n\t\tunitWidth: 35)\r\n\t.setColors(Color.grey,Color.white, Color.grey(0.7),Color.grey, Color.white, Color.yellow);\r\n\r\n\r\n\treverseSlider = EZSlider(\r\n\t\tparent: subwin,\r\n\t\tbounds: 560@30,\r\n\t\tlabel: \"Reverse \",\r\n\t\tcontrolSpec: ControlSpec(\r\n\t\t\tminval:\t0,\r\n\t\t\tmaxval: 100,\r\n\t\t\twarp: 'lin',\r\n\t\t\tstep: 1,\r\n\t\t\tunits: \"%\"),\r\n\t\taction: {arg v;\r\n\t\t\treverseProb = v.value/100;\r\n\t\t},\r\n\t\tinitVal: reverseProb,\r\n\t\tlabelWidth: 60,\r\n\t\tunitWidth: 35)\r\n\t.setColors(Color.grey,Color.white, Color.grey(0.7),Color.grey, Color.white, Color.yellow);\r\n\r\n\trestSlider = EZSlider(\r\n\t\tparent: subwin,\r\n\t\tbounds: 560@30,\r\n\t\tlabel: \"Rests  \",\r\n\t\tcontrolSpec: ControlSpec(\r\n\t\t\tminval:\t0,\r\n\t\t\tmaxval: 100,\r\n\t\t\twarp: 'lin',\r\n\t\t\tstep: 1,\r\n\t\t\tunits: \"%\"),\r\n\t\taction: {arg v;\r\n\t\t\trestProb = v.value/100;\r\n\t\t},\r\n\t\tinitVal: 0.0,\r\n\t\tlabelWidth: 50,\r\n\t\tunitWidth: 35)\r\n\t.setColors(Color.grey,Color.white, Color.grey(0.7),Color.grey, Color.white, Color.yellow);\r\n\r\n\toverlapSlider = EZSlider(\r\n\t\tparent: subwin,\r\n\t\tbounds: 560@30,\r\n\t\tlabel: \"Overlap \",\r\n\t\tcontrolSpec: ControlSpec(\r\n\t\t\tminval:\t0.1,\r\n\t\t\tmaxval: 2,\r\n\t\t\twarp: 'lin',\r\n\t\t\tstep: 0.1),\r\n\t\taction: {arg v;\r\n\t\t\toverlap = v.value;\r\n\t\t},\r\n\t\tinitVal: overlap,\r\n\t\tlabelWidth: 63,\r\n\t/*unitWidth: 35*/)\r\n\t.setColors(Color.grey,Color.white, Color.grey(0.7),Color.grey, Color.white, Color.yellow);\r\n\r\n\r\n\t// Start button\r\n\tstartButton = Button.new(subwin, 560@50)\r\n\t.states_([[\"START\", Color.black, Color.gray], [\"STOP\", Color.black, Color.gray]])\r\n\t.action_({arg button;\r\n\t\tif(button.value==1,\r\n\t\t\t{ player = pattern.play;\r\n\t\t\t\t(\"Now sampling from \" ++ bufferList.size ++ \" sound files\").postln;\r\n\t\t\t\t/*bufferList.postln;\r\n\t\t\t\t[rateLo, rateHi].postln;\r\n\t\t\t\t[durLo, durHi].postln;\r\n\t\t\t\t[ampLo, ampHi].postln;\r\n\t\t\t\t\"\".postln;*/\r\n\t\t\t},\r\n\t\t\t{ player.stop; player = nil; });\r\n\t});\r\n\r\n\t/////////////////\r\n\t// SynthDef\r\n\t/////////////////\r\n\r\n\tSynthDef(\"grain-asr\", {arg bufnum, rate = 1, reverse = 1, startPos = 0, gate = 1, att = 0.1, rel = 0.1, amp = 1, pan = 0;\r\n\t\tvar env, snd;\r\n\t\tenv = EnvGen.kr(Env.asr(att, amp, rel), gate, doneAction: 2);\r\n\t\tsnd = PlayBuf.ar(1, bufnum, rate: rate * reverse, startPos: startPos);\r\n\t\tsnd = snd * env;\r\n\t\tOut.ar(0, Pan2.ar(snd, pan));\r\n\t}).add;\r\n\r\n\t// Buffer list\r\n\r\n\tbufferList = List.newClear(1); // Prand won't like if initial list is empty\r\n\r\n\t// Pattern\r\n\r\n\tpattern = Pbind(\r\n\t\t\\instrument, \"grain-asr\",\r\n\t\t\\buffer, Prand(bufferList, inf),\r\n\t\t\\bufnum, Pfunc({arg evt; evt.at(\\buffer).bufnum}),\r\n\t\t\\startPos, Pwhite(0, Pfunc({arg evt; evt.at(\\buffer).numFrames})),\r\n\t\t\\dur, Pwhite(Pfunc({durLo}), Pfunc({durHi})),\r\n\t\t\\makeRest, Pif(Pfunc({ restProb.coin }), Rest, 709),\r\n\t\t\\rate, Pwhite(Pfunc({rateLo}), Pfunc({rateHi})),\r\n\t\t\\reverse, Pif(Pfunc({ reverseProb.coin }), -1, 1),\r\n\t\t\\amp, Pwhite(Pfunc({ampLo}), Pfunc({ampHi})),\r\n\t\t\\pan, Pwhite(Pfunc({panLo}), Pfunc({panHi})),\r\n\t\t\\legato, Pfunc({overlap})\r\n\t);\r\n\r\n\tbufferList.pop; // remove 'nil', we only want actual buffers here\r\n\r\n}); // end of block",
   "id" : "1-4UG",
   "is_private" : null,
   "name" : "Granular Sampling GUI Demo 2",
   "author" : "Bruno Ruviaro",
   "ancestor_list" : [],
   "description" : "Graphical interface to experiment with granular sampling (multiple sound files at a time)."
}
