{
   "name" : "crude ringbuffer recording",
   "author" : "LFSaw",
   "ancestor_list" : [],
   "description" : "loop-record into two buffers with record-once control",
   "labels" : [
      "looper",
      "building block"
   ],
   "code" : "(\r\ns.waitForBoot{\r\n\r\n\tq = q ? ();\r\n\tq.ringInputChannels = [0, 1];\r\n\tq.ringBufLength = 30;\r\n\tq.ringBufs = Buffer.allocConsecutive(q.ringInputChannels.size, s, q.ringBufLength * s.sampleRate, );\r\n};\r\n)\r\n\r\n\r\n(\r\nNdef(\\inRing).addSpec(\r\n\t\\gain0, [0, 5],\r\n\t\\gain1, [0, 5],\r\n\r\n\t\\recEnable0, [0, 1, \\lin, 1, 0], \r\n\t\\recEnable1, [0, 1, \\lin, 1, 0], \r\n\r\n\t\\recLevel0, [0, 1],\r\n\t\\recLevel1, [0, 1],\r\n\r\n\t\\preLevel1, [0, 1, \\lin],\r\n\t\\preLevel0, [0, 1, \\lin]\r\n);\r\n\r\nNdef(\\inRing, {\r\n\tvar inputs, gains, recEnables, recLevels, preLevels, resets;\r\n\tinputs = SoundIn.ar(q.ringInputChannels);\r\n\tgains      = [\\gain0.kr(1)     , \\gain1.kr(1)];\r\n\trecEnables = [\\recEnable0.kr(0), \\recEnable1.kr(0)];\r\n\trecLevels  = [\\recLevel0.kr(1) , \\recLevel1.kr(1)];\r\n\tpreLevels  = [\\preLevel0.kr(1) , \\preLevel1.kr(1)];\r\n\tresets  = [\\reset0.tr(0) , \\reset1.tr(0)];\r\n\r\n\t// a RecordBuf for each input with independant controls\r\n\t[inputs, gains, recEnables, recLevels, preLevels, resets].flop.collect{|args, i|\r\n\t\tvar input, gain, recEnable, recLevel, preLevel, reset;\r\n\t\tvar buf = q.ringBufs[i];\r\n\t\tvar wrPhasor, rdPhasor;\r\n\t\t#input, gain, recEnable, recLevel, preLevel, reset = args;\r\n\t\t\r\n\t\twrPhasor = Phasor.ar(\r\n\t\t\ttrig: reset, \r\n\t\t\trate: BufRateScale.kr(buf), \r\n\t\t\tstart: 0, \r\n\t\t\tend: BufFrames.kr(buf),\r\n\t\t\tresetPos: s.options.blockSize\r\n\t\t);\r\n\t\trdPhasor = Phasor.ar(\r\n\t\t\ttrig: reset, \r\n\t\t\trate: BufRateScale.kr(buf), \r\n\t\t\tstart: 0, \r\n\t\t\tend: BufFrames.kr(buf),\r\n\t\t\tresetPos: 0\r\n\t\t);\r\n\t\tBufWr.ar(\r\n\t\t\tinputArray: Mix([\r\n\t\t\t\tinput * recLevel * recEnable * gain,\r\n\t\t\t\tBufRd.ar(1, buf, wrPhasor, 1) * ((recEnable * preLevel) + (1- recEnable))\r\n\t\t\t]),\r\n\t\t\tbufnum: q.ringBufs[i],\r\n\t\t\tphase: wrPhasor,\r\n\t\t\tloop: 1\r\n\t\t);\r\n\t\t// playback\r\n\t\tBufRd.ar(\r\n\t\t\tnumChannels: 1, \r\n\t\t\tbufnum: buf, \r\n\t\t\tphase: rdPhasor, \r\n\t\t\tloop: 1);\r\n\t};\t\r\n});\r\n\r\n(\r\nq.argToWhich = {|q, argument, which|\r\n\t\"%%\".format(argument, which).asSymbol\r\n};\r\n\r\nq.resetRingBufs = {|q|\r\n\ts.bind{\r\n\t\tq.ringBufs.do{|buf|\r\n\t\t\tbuf.zero;\r\n\t\t};\r\n\t\tNdef(\\inRing).set(\\reset0, 1, \\reset1, 1);\r\n\t}\r\n};\r\nq.resetRingBuf = {|q, which|\r\n\ts.bind{\r\n\t\tq.ringBufs[which].zero;\r\n\r\n\t\tNdef(\\inRing).set(q.argToWhich(\\reset, which));\r\n\t}\r\n};\r\nq.recordOnce = {|q, which, overdub = true|\r\n\tq.isRecording = q.isRecording ? [false, false];\r\n\t\r\n\tq.isRecording[which].if({\r\n\t\t\"ringBuf[%] rec — already recording\\n\".postf(which);\r\n\t\t\"\"\r\n\t},{\r\n\t\tRoutine{\r\n\t\t\tq.isRecording[which] = true;\r\n\t\t\toverdub.not.if{\r\n\t\t\t\tq.resetRingBuf(which);\r\n\t\t\t\ts.sync;\r\n\t\t\t};\r\n\t\t\tNdef(\\inRing).set(q.argToWhich(\\recEnable, which), 1);\r\n\t\t\t\"ringBuf[%] rec — started\\n\".postf(which);\r\n\t\t\tq.ringBufLength.wait;\r\n\t\t\tNdef(\\inRing).set(q.argToWhich(\\recEnable, which), 0);\r\n\t\t\t\"ringBuf[%] rec — stopped\\n\".postf(which);\r\n\t\t\tq.isRecording[which] = false;\r\n\t\t}.play;\r\n\t});\r\n};\r\n);\r\n)\r\nq.ringBufs[1].plot\r\n// reset ringbuffers\r\nq.resetRingBufs;\r\n\r\n// record once into channel 0, reset first\r\nq.recordOnce(0, false);\r\n// record once into channel 0, overdub\r\nq.recordOnce(0, true);\r\n\r\n\r\n// record once into channel 1\r\nq.recordOnce(1, false);\r\n\r\n\r\nNdef(\\inRing).edit",
   "id" : "1-5ch",
   "is_private" : null
}
