From a83b8be362b44729d595ea3926d77e5d4acfd6ca Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Mon, 24 Apr 2023 16:49:36 +1000 Subject: [PATCH 1/7] Separated input from output effects --- control.scd | 37 +++++++++++++++++++++++++++++++------ effects.scd | 42 ++++++++---------------------------------- granulator.scd | 7 +++++-- main.scd | 10 +++++----- 4 files changed, 49 insertions(+), 47 deletions(-) diff --git a/control.scd b/control.scd index f130326..b0a62af 100644 --- a/control.scd +++ b/control.scd @@ -1,11 +1,10 @@ ( -// audio buses and other control stuff -// recordb = input to bufrecorder +//input mixing, effects and lfos - -~recordb = Bus.audio(s, 1); +~inputb = Bus.audio(s, 1); +~infxb = Bus.audio(s, 1); ~inmixer = SynthDef( \input_null, @@ -13,9 +12,8 @@ arg in1 = 2, in2 = 3, out = 4; Out.ar(out, In.ar(in1) + In.ar(in2)); } -).play(s, [\in1, ~usbinput1, \in2, ~usbinput2, \out, ~recordb]); +).play(s, [\in1, ~usbinput1, \in2, ~usbinput2, \out, ~inputb]); -~grb = Bus.audio(s, 2); // LFO buses and synths @@ -29,4 +27,31 @@ "LFOs running".postln; +// filter is now before grains + +~filtermodb = Bus.control(s, 1); + +~filtermod = SynthDef( + \filtermod, { + arg out, a = 1.0, b = 0.0, c = 0.0; + var siga, sigb, sigc; + siga = In.kr(~lfoab) * a; + sigb = In.kr(~lfobb) * b; + sigc = In.kr(~lfocb) * c; + Out.kr(out, Wrap.kr(siga + sigb + sigc, -1, 1)); + } +).play(s, [\out, ~filtermodb, \a, 1, \b, 0, \c, 0 ]); + + +~filter = SynthDef( + \filter, { + arg in, out, mod, freq=10000, res=0.3, amp=1.0; + var filt, lfo; + lfo = LinExp.kr(In.kr(mod, 1), -1, 1, freq * 0.5, freq * 2); + filt = RLPF.ar(In.ar(in, 2) * amp, lfo, res); + Out.ar(out, filt); + } +).play(s, [ \in, ~inputb, \out, ~infxb, \mod, ~filtermodb, \amp, 0.5], \addToTail); + + ) diff --git a/effects.scd b/effects.scd index 55a242c..9ddcfed 100644 --- a/effects.scd +++ b/effects.scd @@ -1,11 +1,10 @@ +// this is just output effects now ( -// output effects mixing and effects -~fxb = Bus.audio(s, 2); -~filterb = Bus.audio(s, 2); -~grainsb = Bus.audio(s, 2); + +~outfxb = Bus.audio(s, 2); ~delayb = Bus.audio(s, 2); ~reverbb = Bus.audio(s, 2); @@ -14,36 +13,10 @@ ~grainmixer = SynthDef( \grain_mixer, { - arg in=2, gbus=4, out=0, passthrough=0.75, grains=0.75; - Out.ar(out, (grains * In.ar(gbus, 2)) + (passthrough * In.ar(~recordb, 1) ! 2)); + arg in=2, grainb=4, out=0, passthrough=0.75, grains=1; + Out.ar(out, (grains * In.ar(grainb, 2)) + (passthrough * In.ar(in, 1) ! 2)); } -).play(s, [\in, ~usbinput, \gbus, ~grb, \out, ~fxb ], \addToTail); - -~filtermodb = Bus.control(s, 1); - -~filtermod = SynthDef( - \filtermod, { - arg out, a = 1.0, b = 0.0, c = 0.0; - var siga, sigb, sigc; - siga = In.kr(~lfoab) * a; - sigb = In.kr(~lfobb) * b; - sigc = In.kr(~lfocb) * c; - Out.kr(out, Wrap.kr(siga + sigb + sigc, -1, 1)); - } -).play(s, [\out, ~filtermodb, \a, 1, \b, 0, \c, 0 ]); - - -~filter = SynthDef( - \filter, { - arg in, out, mod, freq=10000, res=0.3, amp=1.0; - var filt, lfo; - lfo = LinExp.kr(In.kr(mod, 1), -1, 1, freq * 0.5, freq * 2); - filt = RLPF.ar(In.ar(in, 2) * amp, lfo, res); - Out.ar(out, filt); - } -).play(s, [ \in, ~fxb, \out, ~filterb, \mod, ~filtermodb, \amp, 0.5 ], \addToTail); - - +).play(s, [\in, ~infxb, \grainb, ~grainsb, \out, ~outfxb ], \addToTail); @@ -58,7 +31,7 @@ del = CombC.ar(sig, maxdelay, delaytime, decaytime, amp); Out.ar(out, sig + del); } - ).play(s, [ \in, ~filterb, \out, ~delayb ], \addToTail); + ).play(s, [ \in, ~outfxb, \out, ~delayb ], \addToTail); // try taking out the reverb because I think it causes noises @@ -72,3 +45,4 @@ "Effects running".postln; ) + diff --git a/granulator.scd b/granulator.scd index 7326b64..f10d263 100644 --- a/granulator.scd +++ b/granulator.scd @@ -8,6 +8,8 @@ [ "random", \pos_random ] ]; +~grainsb = Bus.audio(s, 2); + ~granulators = Array.new(4); ~posb = Array.new(4); ~possynths = Array.new(4); @@ -22,7 +24,7 @@ (0..3).do({ |i| var pb = ~posb.at(i); - ~granulators.add(Granulator.new(~buflen, ~recordb, ~fxb, pb, ~triggerb, ~pitchb)); + ~granulators.add(Granulator.new(~buflen, ~infxb, ~grainsb, pb, ~triggerb, ~pitchb)); }); ~setmode = { @@ -37,4 +39,5 @@ "Granulator running".postln; -) \ No newline at end of file +) + diff --git a/main.scd b/main.scd index 79ea333..47661a5 100644 --- a/main.scd +++ b/main.scd @@ -12,16 +12,16 @@ Routine.run({ ("./synths.scd").loadRelative; Granulator.init(s); - s.sync; - - "Synths loaded".postln; - ("./control.scd").loadRelative; - ("./effects.scd").loadRelative; + s.sync; ("./granulator.scd").loadRelative; s.sync; + ("./effects.scd").loadRelative; + s.sync; ("./interface.scd").loadRelative; }); ) + + From f880ac583184045476a733bb0b2e6313ea016a17 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sun, 17 Sep 2023 10:11:31 +1000 Subject: [PATCH 2/7] Added a trigger to sync the recorder with the playback buffer Also turns off granulator input mix when recording stops --- granulator.scd | 31 ++++++++++++++++++----- interface.scd | 69 +++++++++++++++++++++++++++++++++++++++++--------- main.scd | 15 ++++++++++- synths.scd | 12 ++++++--- 4 files changed, 104 insertions(+), 23 deletions(-) diff --git a/granulator.scd b/granulator.scd index f10d263..b92d226 100644 --- a/granulator.scd +++ b/granulator.scd @@ -12,32 +12,49 @@ ~granulators = Array.new(4); ~posb = Array.new(4); +~rectriggerb = Array.new(4); ~possynths = Array.new(4); +~triggersynths = Array.new(4); + +// create the control busses (0..3).do({ - var pb = Bus.control(s, 1), ps; - ~posb.add(pb); - ps = Synth(\pos_saw, [ \out, pb, \speed, 1 / ~buflen ]); - ~possynths.add(ps); + var ps; + ~posb.add(Bus.control(s, 1)); + ~rectriggerb.add(Bus.control(s, 1)); }); +// start the granulators (0..3).do({ |i| - var pb = ~posb.at(i); - ~granulators.add(Granulator.new(~buflen, ~infxb, ~grainsb, pb, ~triggerb, ~pitchb)); + var pb = ~posb[i], rtb = ~rectriggerb[i]; + ~granulators.add(Granulator.new(~buflen, ~infxb, ~grainsb, pb, rtb)); }); +"Granulators running".postln; + +// launch the pos synths and triggers to sync the buffer recorders + +(0..3).do({ |i| + var pb = ~posb[i], rtb = ~rectriggerb[i]; + ~possynths.add(Synth(\pos_saw, [ \out, pb, \speed, 1 / ~buflen ])); + ~triggersynths.add(Synth(\trigger, [ \out, rtb ])); +}); + +// TODO - retrigger the buffer records when changing the length etc + ~setmode = { arg track, mode; var synth = ~modes[mode][1]; ~possynths[track].get(\speed, { | speed | ~possynths[track].free; + ~triggersynths[track].free; ~possynths[track] = Synth(synth, [\out, ~posb[track], \speed, speed]); + ~triggersynths[track] = Synth(\trigger, [ \out, ~rectriggerb[track] ]); }); ~granulators[track].mode_(mode); }; -"Granulator running".postln; ) diff --git a/interface.scd b/interface.scd index e4dece2..fbc1742 100644 --- a/interface.scd +++ b/interface.scd @@ -1,5 +1,6 @@ -// FIXME: the touchosc stuff is crashing on intialisation + +~quantspeed.value(2); ( ~to = TouchOSC("192.168.0.209", 9000); @@ -8,10 +9,13 @@ ~tracknum = 0; ~granulator = ~granulators[0]; +~speedlock = 0; +~speedquant = 0; + OSCdef.freeAll; -~to.button('/reset', 0, { | v | +~to.button('/grains/reset', 0, { | v | if( v > 0, { var sp = ~to.v('/grains/speed')[0]; ~buflen = ~to.v('/grains/buflen'); @@ -23,26 +27,65 @@ OSCdef.freeAll; }); }); +~quantspeed = { |v| 2.pow((v * 4).round - 5) }; + +// control ganging is hella laggy, do it in TouchOSC + +~setspeed = { | track, v | + var speed, qv = if(~speedquant > 0, { ~quantspeed.value(v) }, { v }); + speed = qv / ~buflen; + if(~speedlock > 0, { + (0..3).do({|n| + ~possynths[n].set(\speed, speed); + if( n != track, { + var url = ("/grains/speed" ++ n).asSymbol; + ~to.s_(url, v); + }); + }); + }, + { ~possynths[track].set(\speed, speed) }); +}; + +// setrecord: toggles record on (1) or off (0) for a track, and also sets the +// track's input mix to 0 so that it doesn't start fadeing out. If the track +// is the currently selected track, set its touchosc control to 0. + +~setrecord = { | track, v | + ~granulators[track].record_(v); + [ v, track, ~tracknum ].postln; + if(v == 0, { + ~granulators[track].mix_(0); + if( track == ~tracknum, { ~to.v_('/track/mix', 0); }); + }); +}; + + // note: ~buflen is the variable for buffer length, which only gets set to // ~to.v('/grains/buflen') when the buffer is reset with the clear button ~to.slider('/grains/buflen', ~buflen, TouchOSCScale(0.1, 10.0), {}); -~to.button('/grains/record0', 0, { | v | ~granulators[0].record_(v) }); -~to.button('/grains/record1', 0, { | v | ~granulators[1].record_(v) }); -~to.button('/grains/record2', 0, { | v | ~granulators[2].record_(v) }); -~to.button('/grains/record3', 0, { | v | ~granulators[3].record_(v) }); + + + +~to.button('/grains/record0', 0, { | v | ~setrecord.value(0, v) }); +~to.button('/grains/record1', 0, { | v | ~setrecord.value(1, v) }); +~to.button('/grains/record2', 0, { | v | ~setrecord.value(2, v) }); +~to.button('/grains/record3', 0, { | v | ~setrecord.value(3, v) }); + + ~to.button('/grains/mode0', 0, { |v| ~granulators[0].mode_(v); ~setmode.value(0, v) }); ~to.button('/grains/mode1', 0, { |v| ~granulators[1].mode_(v); ~setmode.value(1, v) }); ~to.button('/grains/mode2', 0, { |v| ~granulators[2].mode_(v); ~setmode.value(2, v) }); ~to.button('/grains/mode3', 0, { |v| ~granulators[3].mode_(v); ~setmode.value(3, v) }); -~to.slider('/grains/speed0', 1, TouchOSCScale(0, 2), { |v| ~possynths[0].set(\speed, v / ~buflen); }); -~to.slider('/grains/speed1', 1, TouchOSCScale(0, 2), { |v| ~possynths[1].set(\speed, v / ~buflen); }); -~to.slider('/grains/speed2', 1, TouchOSCScale(0, 2), { |v| ~possynths[2].set(\speed, v / ~buflen); }); -~to.slider('/grains/speed3', 1, TouchOSCScale(0, 2), { |v| ~possynths[3].set(\speed, v / ~buflen); }); +~to.slider('/grains/speed0', 1, TouchOSCScale(0, 2), { |v| ~setspeed.value(0, v) }); +~to.slider('/grains/speed1', 1, TouchOSCScale(0, 2), { |v| ~setspeed.value(1, v) }); +~to.slider('/grains/speed2', 1, TouchOSCScale(0, 2), { |v| ~setspeed.value(2, v) }); +~to.slider('/grains/speed3', 1, TouchOSCScale(0, 2), { |v| ~setspeed.value(3, v) }); + ~to.slider('/grains/passthrough', 0.75, TouchOSCScale(0, 1), { |v| ~grainmixer.set(\passthrough, v) }); @@ -51,13 +94,15 @@ OSCdef.freeAll; ~to.slider('/grains/mix2', 0.5, TouchOSCScale(0, 1),{ | v | ~granulators[2].gain_(v) }); ~to.slider('/grains/mix3', 0.5, TouchOSCScale(0, 1),{ | v | ~granulators[3].gain_(v) }); -~to.button('/grains/speedquant', 0, { |v| }); +~to.button('/grains/lock', 0, { |v| ~speedlock = v }); + +~to.button('/grains/quant', 0, { |v| ~speedquant = v }); // Page 2: track -~to.xy('/track/triggersize', [ 120, 0.1 ], TouchOSCScale(0, 640), TouchOSCScale(0, 0.5), { |v| +~to.xy('/track/triggersize', [ 320, 0.25 ], TouchOSCScale(0, 640), TouchOSCScale(0, 0.5), { |v| ~granulator.trigger_(v[0]); ~granulator.size_(v[1]); }); diff --git a/main.scd b/main.scd index 47661a5..60094f0 100644 --- a/main.scd +++ b/main.scd @@ -3,7 +3,8 @@ ( Server.default.options.inDevice_("Scarlett 2i2 USB"); Server.default.options.hardwareBufferSize_(1024); -//Server.default.options.outDevice_("Scarlett 2i2 USB"); +Server.default.options.outDevice_("Scarlett 2i2 USB"); +//Server.default.options.outDevice_("External Headphones"); ) Server.killAll; @@ -25,3 +26,15 @@ Routine.run({ + + + +("./synths.scd").loadRelative; +Granulator.init(s); +("./control.scd").loadRelative; +("./granulator.scd").loadRelative; +("./effects.scd").loadRelative; +("./interface.scd").loadRelative; + + + diff --git a/synths.scd b/synths.scd index 2ff256a..9940ecd 100644 --- a/synths.scd +++ b/synths.scd @@ -14,12 +14,12 @@ SynthDef(\pos_sine, { SynthDef(\pos_saw, { arg out, speed=1; - Out.kr(out, 0.5 + LFSaw.kr(speed, 0, 0.5)); + Out.kr(out, 0.5 + LFSaw.kr(speed, 0, 0.5, 0.5)); }).add; SynthDef(\pos_reverse, { arg out, speed=1; - Out.kr(out, 0.5 - LFSaw.kr(speed, 0, 0.5)); + Out.kr(out, 0.5 - LFSaw.kr(speed, 0, 0.5, 0.5)); }).add; SynthDef(\pos_step, { @@ -33,9 +33,15 @@ SynthDef(\pos_random, { Out.kr(out, 0.5 + WhiteNoise.kr(0.5)); }).add; -SynthDef( \lfo, { +SynthDef(\lfo, { arg out, freq=1, amp=0; Out.kr(out, SinOsc.kr(freq, 0, amp)); }).add; +SynthDef(\trigger, { + arg out=1; + Out.kr(out, Impulse.kr(0)) +}).add; + + ) From 932c04717a9cef0e252d22b4b070b110ed1c5c60 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sat, 23 Sep 2023 15:05:08 +1000 Subject: [PATCH 3/7] Added routine to dump all four buffers --- granulator.scd | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/granulator.scd b/granulator.scd index b92d226..017fabb 100644 --- a/granulator.scd +++ b/granulator.scd @@ -1,4 +1,5 @@ + ( ~modes = [ [ "saw", \pos_saw ], @@ -8,6 +9,9 @@ [ "random", \pos_random ] ]; +~outputDir = Platform.recordingsDir +/+ "GrainBuffers"; + + ~grainsb = Bus.audio(s, 2); ~granulators = Array.new(4); @@ -31,7 +35,6 @@ ~granulators.add(Granulator.new(~buflen, ~infxb, ~grainsb, pb, rtb)); }); -"Granulators running".postln; // launch the pos synths and triggers to sync the buffer recorders @@ -55,6 +58,10 @@ ~granulators[track].mode_(mode); }; - +~dumpbuffers = { |prefix| + (0..3).do({|i| + var filename = ~outputDir +/+ prefix ++ 'buffer' ++ i.asString ++ '.aiff'; + ~granulators[i].buffer.write(filename); + }); +} ) - From 3ac1c3cd47f5293e1b46092461c139222637a984 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sat, 23 Sep 2023 15:05:33 +1000 Subject: [PATCH 4/7] Fixed quantspeed --- interface.scd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface.scd b/interface.scd index fbc1742..e63c94f 100644 --- a/interface.scd +++ b/interface.scd @@ -27,13 +27,15 @@ OSCdef.freeAll; }); }); -~quantspeed = { |v| 2.pow((v * 4).round - 5) }; +~quantspeed = { |v| 2.pow((v * 4 + 0.5).round - 5) }; // control ganging is hella laggy, do it in TouchOSC + ~setspeed = { | track, v | var speed, qv = if(~speedquant > 0, { ~quantspeed.value(v) }, { v }); speed = qv / ~buflen; + [ "setspeed", v, qv, speed ].postln; if(~speedlock > 0, { (0..3).do({|n| ~possynths[n].set(\speed, speed); From 3d08975c3fe5e12a9fd95033f8c5b89071f2d253 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sat, 23 Sep 2023 15:06:05 +1000 Subject: [PATCH 5/7] Made the record button save and restore level --- interface.scd | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/interface.scd b/interface.scd index e63c94f..796b8a1 100644 --- a/interface.scd +++ b/interface.scd @@ -51,14 +51,24 @@ OSCdef.freeAll; // setrecord: toggles record on (1) or off (0) for a track, and also sets the // track's input mix to 0 so that it doesn't start fadeing out. If the track // is the currently selected track, set its touchosc control to 0. +// keeps the level for each track in an array and resets it + +~mixlevel = Array.new(4); + +(0..3).do({~mixlevel.add(0.25)}); + ~setrecord = { | track, v | ~granulators[track].record_(v); [ v, track, ~tracknum ].postln; if(v == 0, { + ~mixlevel[track] = ~granulators[track].mix; ~granulators[track].mix_(0); - if( track == ~tracknum, { ~to.v_('/track/mix', 0); }); + }, + { + ~granulators[track].mix_(~mixlevel[track]); }); + if( track == ~tracknum, { ~to.v_('/track/mix', ~granulators[track].mix); }) }; From 31172455cc9cea917b03b3f8c0ef38693d95fcc7 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sat, 23 Sep 2023 15:06:26 +1000 Subject: [PATCH 6/7] Made slope default to off --- interface.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface.scd b/interface.scd index 796b8a1..8e7e6ef 100644 --- a/interface.scd +++ b/interface.scd @@ -123,7 +123,7 @@ OSCdef.freeAll; ~to.button('/track/dust', 0, { |v| ~granulator.dust_(v) }); ~to.button('/track/back', 0, { |v| ~granulator.back_(v)}); -~to.button('/track/slope', 1, { |v| ~granulator.slope_(v) }); +~to.button('/track/slope', 0, { |v| ~granulator.slope_(v) }); ~to.button('/track/chorus', 0, { |v| ~granulator.chorus_(v) }); ~to.slider('/track/detune', 0, TouchOSCScale(0, 0.059), { |v| ~granulator.detune_(v) }); From 49133e06c6c9e86e447f162646b6ccd923bf1834 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Sat, 23 Sep 2023 15:06:41 +1000 Subject: [PATCH 7/7] removed quantspeed guff --- interface.scd | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface.scd b/interface.scd index 8e7e6ef..fb9b69c 100644 --- a/interface.scd +++ b/interface.scd @@ -1,7 +1,5 @@ -~quantspeed.value(2); - ( ~to = TouchOSC("192.168.0.209", 9000);