// title: crude ringbuffer recording // author: LFSaw // description: // loop-record into two buffers with record-once control // code: ( s.waitForBoot{ q = q ? (); q.ringInputChannels = [0, 1]; q.ringBufLength = 30; q.ringBufs = Buffer.allocConsecutive(q.ringInputChannels.size, s, q.ringBufLength * s.sampleRate, ); }; ) ( Ndef(\inRing).addSpec( \gain0, [0, 5], \gain1, [0, 5], \recEnable0, [0, 1, \lin, 1, 0], \recEnable1, [0, 1, \lin, 1, 0], \recLevel0, [0, 1], \recLevel1, [0, 1], \preLevel1, [0, 1, \lin], \preLevel0, [0, 1, \lin] ); Ndef(\inRing, { var inputs, gains, recEnables, recLevels, preLevels, resets; inputs = SoundIn.ar(q.ringInputChannels); gains = [\gain0.kr(1) , \gain1.kr(1)]; recEnables = [\recEnable0.kr(0), \recEnable1.kr(0)]; recLevels = [\recLevel0.kr(1) , \recLevel1.kr(1)]; preLevels = [\preLevel0.kr(1) , \preLevel1.kr(1)]; resets = [\reset0.tr(0) , \reset1.tr(0)]; // a RecordBuf for each input with independant controls [inputs, gains, recEnables, recLevels, preLevels, resets].flop.collect{|args, i| var input, gain, recEnable, recLevel, preLevel, reset; var buf = q.ringBufs[i]; var wrPhasor, rdPhasor; #input, gain, recEnable, recLevel, preLevel, reset = args; wrPhasor = Phasor.ar( trig: reset, rate: BufRateScale.kr(buf), start: 0, end: BufFrames.kr(buf), resetPos: s.options.blockSize ); rdPhasor = Phasor.ar( trig: reset, rate: BufRateScale.kr(buf), start: 0, end: BufFrames.kr(buf), resetPos: 0 ); BufWr.ar( inputArray: Mix([ input * recLevel * recEnable * gain, BufRd.ar(1, buf, wrPhasor, 1) * ((recEnable * preLevel) + (1- recEnable)) ]), bufnum: q.ringBufs[i], phase: wrPhasor, loop: 1 ); // playback BufRd.ar( numChannels: 1, bufnum: buf, phase: rdPhasor, loop: 1); }; }); ( q.argToWhich = {|q, argument, which| "%%".format(argument, which).asSymbol }; q.resetRingBufs = {|q| s.bind{ q.ringBufs.do{|buf| buf.zero; }; Ndef(\inRing).set(\reset0, 1, \reset1, 1); } }; q.resetRingBuf = {|q, which| s.bind{ q.ringBufs[which].zero; Ndef(\inRing).set(q.argToWhich(\reset, which)); } }; q.recordOnce = {|q, which, overdub = true| q.isRecording = q.isRecording ? [false, false]; q.isRecording[which].if({ "ringBuf[%] rec — already recording\n".postf(which); "" },{ Routine{ q.isRecording[which] = true; overdub.not.if{ q.resetRingBuf(which); s.sync; }; Ndef(\inRing).set(q.argToWhich(\recEnable, which), 1); "ringBuf[%] rec — started\n".postf(which); q.ringBufLength.wait; Ndef(\inRing).set(q.argToWhich(\recEnable, which), 0); "ringBuf[%] rec — stopped\n".postf(which); q.isRecording[which] = false; }.play; }); }; ); ) q.ringBufs[1].plot // reset ringbuffers q.resetRingBufs; // record once into channel 0, reset first q.recordOnce(0, false); // record once into channel 0, overdub q.recordOnce(0, true); // record once into channel 1 q.recordOnce(1, false); Ndef(\inRing).edit