{
   "labels" : [
      "granular",
      "synthesis techniques",
      "pedagogy"
   ],
   "id" : "1-4Ye",
   "is_private" : null,
   "code" : "(\r\nvar tick = 4,\r\ngrainSamps = 100,\r\ngrainHeight = 50,\r\ngrainIOI = 50,  // samples between grain onsets\r\ngrainStep = 50,  // step size between source windows\r\nlinkI = 0;\r\n\r\nvar p = Platform.resourceDir +/+ \"sounds/a11wlk01.wav\", f, u, w, ratioSl, d, indexSl;\r\nvar viewWidth = 500;\r\nvar buf, synth;\r\n\r\nf = SoundFile.openRead(p);\r\nf.seek(86965);\r\nf.readData(d = Signal.newClear((f.sampleRate * 0.333).roundUp));\r\nf.close;\r\n\r\nw = Window(\"granular windows\", Rect(800, 200, viewWidth + 4, 450));\r\n\r\nu = UserView(w, Rect(2, 2, viewWidth, 400)).front;\r\nu.background = Color.gray(0.1);\r\n\r\nindexSl = EZSlider(w, Rect(2, u.bounds.bottom + 2, viewWidth, 20),\r\n\t\"Grain index\",\r\n\t[0, 7, \\lin, 1, 0],\r\n\t{ |view|\r\n\t\tif(view.value != linkI) {\r\n\t\t\tlinkI = view.value;\r\n\t\t\tu.refresh;\r\n\t\t};\r\n\t},\r\n\tlinkI,\r\n\tlabelWidth: 120\r\n);\r\n\r\nratioSl = EZSlider(w, Rect(2, indexSl.bounds.bottom + 2, viewWidth, 20),\r\n\t\"Step ratio\",\r\n\t[0.25, 2, \\exp],\r\n\t{ |view|\r\n\t\tgrainStep = grainIOI * view.value;\r\n\t\tsynth.set(\\stepRatio, view.value);\r\n\t\tu.refresh;\r\n\t},\r\n\t1,\r\n\tlabelWidth: 120\r\n);\r\n\r\nd = d.resamp1(viewWidth);\r\nd = d / (d.maxValue(_.abs));\r\n\r\nf = { |view|\r\n\tvar grainStart = 0,\r\n\tgrainX = 0,\r\n\tgrainY = 0,\r\n\tgrainCt = 0;\r\n\r\n\tvar bounds = view.bounds.moveTo(0, 0),\r\n\twidth = bounds.width,\r\n\theight = bounds.height,\r\n\twaveTop = height * 0.25,\r\n\twaveMid = 0.5 * (waveTop + height),\r\n\tmapY = { |x, y| Point(x, y.linlin(-1, 1, height-1, waveTop)) };\r\n\r\n\tvar fg = Color.gray(0.8),\r\n\tscoreColor = Color(1, 1, 0.4, 0.5);\r\n\r\n\tvar oneGrain = { |start = 0, samps = 100, plotX = 0, plotY = 0, height = 50, drawLink = false|\r\n\t\tvar data = d[start.asInteger .. (start + samps - 1).asInteger],\r\n\t\tenv = Env.sine(1).discretize(samps),\r\n\t\ttop = plotY+height,\r\n\t\tmapY = { |x, y| Point(plotX + x, y.linlin(-1, 1, top, plotY)) };\r\n\r\n\t\t// envelope\r\n\t\tPen.color_(fg)\r\n\t\t.moveTo(mapY.(0, 0));\r\n\t\t(1 .. samps-1).do { |i|\r\n\t\t\tPen.lineTo(mapY.(i, env[i]));\r\n\t\t};\r\n\t\t// signal\r\n\t\tPen.moveTo(mapY.(0, 0));\r\n\t\t(1 .. samps-1).do { |i|\r\n\t\t\tPen.lineTo(mapY.(i, data[i] * env[i]));\r\n\t\t};\r\n\t\tPen.stroke;\r\n\t\tif(drawLink) {\r\n\t\t\tPen.color_(scoreColor)\r\n\t\t\t.moveTo(mapY.(0, 0))\r\n\t\t\t.lineTo(Point(start, waveMid))\r\n\t\t\t.moveTo(mapY.(samps-1, 0))\r\n\t\t\t.lineTo(Point(start + samps, waveMid))\r\n\t\t\t.stroke;\r\n\t\t};\r\n\t};\r\n\r\n\tPen.color_(fg)\r\n\t.moveTo(Point(tick, waveTop))\r\n\t.lineTo(Point(0, waveTop))\r\n\t.lineTo(Point(0, height-1))\r\n\t.lineTo(Point(tick, height-1))\r\n\t.moveTo(Point(0, waveMid))\r\n\t.lineTo(Point(width-1, waveMid))\r\n\t.moveTo(Point(width-1, waveMid - (0.75 * tick)))\r\n\t.lineTo(Point(width-1, waveMid + (0.75 * tick)))\r\n\t.stroke;\r\n\r\n\tPen.moveTo(mapY.(0, d[0]));\r\n\t(1 .. d.size - 1).do { |i|\r\n\t\tPen.lineTo(mapY.(i, d[i]));\r\n\t};\r\n\tPen.stroke;\r\n\r\n\twhile { grainStart + grainSamps < width and: {\r\n\t\tgrainX + grainSamps < width\r\n\t} } {\r\n\t\toneGrain.(grainStart, grainSamps, grainX, grainY, grainHeight, grainCt == linkI);\r\n\t\tgrainStart = grainStart + grainStep;\r\n\t\tgrainX = grainX + grainIOI;\r\n\t\tgrainY = grainHeight - grainY;\r\n\t\tgrainCt = grainCt + 1;\r\n\t};\r\n};\r\nu.drawFunc = { |view| f.value(view) };\r\nu.refresh;\r\n\r\nw.front;\r\nw.onClose = {\r\n\tsynth.free;\r\n\tbuf.free;\r\n};\r\n\r\ns.waitForBoot {\r\n\tbuf = Buffer.read(s, p, 86965, (44100 * 0.333).roundUp);\r\n\tSynthDef(\\grains, { |out = 0, bufnum, retrigFreq = 0.8, trigFreq = 20, stepRatio = 1|\r\n\t\tvar phase = Sweep.ar(Impulse.ar(retrigFreq), stepRatio),\r\n\t\tgrainHalfDur = trigFreq.reciprocal,\r\n\t\tgrainDur = grainHalfDur * 2,\r\n\t\ttrig = Impulse.ar(trigFreq) * (phase + grainDur < BufDur.kr(bufnum)),\r\n\t\tsig = TGrains.ar(2, trig, bufnum, 1, phase + grainHalfDur, grainDur);\r\n\t\tOut.ar(out, sig);\r\n\t}).add;\r\n\ts.sync;\r\n\tsynth = Synth(\\grains, [bufnum: buf]);\r\n};\r\n)",
   "name" : "Graphic illustration of granular time-stretching",
   "author" : "jamshark70",
   "description" : "Graphically represents a small set of granular windows, with variable offset. Move the \"ratio\" slider, and you can see and hear time stretching.",
   "ancestor_list" : []
}
