Compare commits

..

No commits in common. "main" and "feature-refactor-osc" have entirely different histories.

13 changed files with 401 additions and 673 deletions

1
.gitignore vendored
View File

@ -1 +0,0 @@
.DS_Store

View File

@ -1,53 +1,21 @@
# TODO
TODO
- quantise playback speed to rational values
- display the playback and harmonics when quantised
- Convert the granulator (including the buffer stuff) to a class
- multiple grain buffers - TouchOsc interface to select which to send to
- play back all buffers?
- Convert the TouchOSC interface library to a class
- refactor for multitrack
- which controls are per-track and which are global? sort these out in the UI
- encapsulate a grainstrack in an object?
These will involve some disintermingling of code
- auto-mix: base the mix level on how loud the incoming signal is so that tracks don't fade out
- fancier playback:
- intertwine different rates and directions
Ideally I want to be able to use the granulator, TouchOSC and the
midi controller as part of the same session, so do stuff like
- rhythm controls
- number of steps in step granulator
- modulate grain level in time with playback
- sync LFOs to playback
t = TouchOSC("192.168.0.30")
TODO list - touchosch
g = Granulator()
URL TO SC
grains/buflen Y Y
grains/reset Y Y
grains/record0..3 Y Y
grains/mode0..3 Y Y
grains/speed0..3 Y Y
grains/dust Y Y
grains/slope Y Y
grains/back Y Y
grains/trigger Y Y
grains/speedlock
grains/speedquant
grains/mix0 Y
grains/mix1 Y
grains/mix2 Y
grains/mix3 Y
k = MidiKnobs();
trackselect
t.bind('/grain', 0, 1, 0.5, { |self| g.set("amp", self.v) });
track/record
track/mode
track/speed
track/size
track/blur
track/mix
track/pan
track/track
track/jitter
t.bind(

44
TODO.md
View File

@ -1,27 +1,37 @@
TODO
====
Monday-Tuesday to-do
## Basic interface stuff
Use server.sync to speed up booting
Write default settings to the interface on startup <-- done
- re-route the effects so that it goes
input -> filter -> delay -> granulator -> reverb
not input -> granulator -> effects
Synchronise speeds across granulators
Quantise speeds
that's enough!
Try to get all the common interfaces on one page
--
Later:
## Musical
vibrato and tremolo
Test things like really rapid playback
more playback modes
Pitch-shifting (tuned and untuned)
LFO Modulate the filter <-- done
LFO Modulate the granulator settings
Separate panel for input effects: distort and overdrive
Sync timining of granule playback to buffer length / speed
Timing based on beat detection
## Advanced interface
Save current patch / load patch <-- Done
Save the current buffer! - if this is incorporated with current settings, it's a way to save how the granulator is playing, and then resume. Which is good for live stuff and also for overdubbing
SuperCollider seems to have the ability to read and write files, but not scan directories, so the patch-saver will have to maintain its own index file
patch = file with settings, including a link to the buffer sample

View File

@ -1,32 +0,0 @@
(
//input mixing, effects and lfos
~inputb = Bus.audio(s, 1); // bypassing this one for now
~infxb = Bus.audio(s, 1);
~inmixer = SynthDef(
\input_null,
{
arg in1 = 2, in2 = 3, out = 4;
Out.ar(out, In.ar(in1) + In.ar(in2));
}
).play(s, [\in1, ~usbinput1, \in2, ~usbinput2, \out, ~infxb]);
// LFO buses and synths
~lfoab = Bus.control(s, 1);
~lfobb = Bus.control(s, 1);
~lfocb = Bus.control(s, 1);
~lfoa = Synth(\lfo, [\out, ~lfoab ]);
~lfob = Synth(\lfo, [\out, ~lfobb ]);
~lfoc = Synth(\lfo, [\out, ~lfocb ]);
"LFOs running".postln;
)

View File

@ -1,76 +0,0 @@
// this is just output effects now
(
~outfxb = Bus.audio(s, 2);
~filterb = Bus.audio(s, 2);
~delayb = Bus.audio(s, 2);
~reverbb = Bus.audio(s, 2);
~grainmixer = SynthDef(
\grain_mixer,
{
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, ~infxb, \grainb, ~grainsb, \out, ~outfxb ], \addToTail);
// filter is after grains again
~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, ~outfxb, \out, ~filterb, \mod, ~filtermodb, \amp, 0.5], \addToTail);
// delay always passes through 100% of its input + amp % of the delay
~delay = SynthDef(
\delay, {
arg in, out, maxdelay=1, delaytime=0.2, decaytime=0.1, amp=0.5;
var sig = In.ar(in, 2), del;
del = CombC.ar(sig, maxdelay, delaytime, decaytime, amp);
Out.ar(out, sig + del);
}
).play(s, [ \in, ~filterb, \out, ~delayb ], \addToTail);
~reverb = SynthDef(
\reverb, {
arg in, out, mix=0.33, room=0.5, damp=0.5, amp=0.25;
var input = In.ar(in, 2);
Out.ar(out, input + FreeVerb2.ar(input[0], input[1], mix, room, damp, amp));
}
).play(s, [ \in, ~delayb, \out, 0 ], \addToTail);
"Effects running".postln;
)

363
grains.scd 100644
View File

@ -0,0 +1,363 @@
Server.killAll
// Execute this before booting the server
Server.default.options.inDevice_("Scarlett 2i2 USB");
// TODO - FIXME
// [X] mode switching isn't working
// [ ] playback speed isn't working
// [ ] lfo filter modulation isn't working
// [ ] setting parameters from defaults at startup like passthrough
s.sampleRate
(
~to = TouchOSC("192.168.0.209", 9000);
~usbinput = 2;
~buflen = 4.0;
~beatsperbar = 4;
// trying setting the playback LFOs before the controls
// granulator playback modes
// each of these is a control bus with a synth that drives the pattern
// the granulator mode control switches between them
// more ideas for modules: scramble - do a permutation of ABCDEFGH slots
// todo - encapsulate these in a class
~grainsinb = Bus.control(s, 1);
~grainsin = SynthDef(
\grainsin,
{
arg out=5, speed=1;
Out.kr(out, 0.5 + SinOsc.kr(speed, 0, 0.5));
}
).play(s, [\out, ~grainsinb, \speed, 1]);
~grainsawb = Bus.control(s, 1);
~grainsaw = SynthDef(
\grainsaw,
{
arg out=5, speed=1;
Out.kr(out, 0.5 + LFSaw.kr(speed, 0, 0.5));
}
).play(s, [\out, ~grainsawb, \speed, 1]);
~grainreverseb = Bus.control(s, 1);
~grainreverse = SynthDef(
\grainreverse,
{
arg out=5, speed=1;
Out.kr(out, 0.5 - LFSaw.kr(speed, 0, 0.5));
}
).play(s, [\out, ~grainreverseb, \speed, 1]);
~graintrib = Bus.control(s, 1);
~graintri = SynthDef(
\graintri,
{
arg out=5, speed=1;
Out.kr(out, 0.5 + LFTri.kr(speed, 0, 0.5));
}
).play(s, [\out, ~graintrib, \speed, 1]);
~grainrandb = Bus.control(s, 1);
~grainrand = SynthDef(
\grainrand,
{
arg out=5, speed=1;
Out.kr(out, 0.5 + WhiteNoise.kr(0.5));
}
).play(s, [\out, ~grainrandb, \speed, 1]);
~modes = [
[ ~grainsaw, ~grainsawb, "saw" ],
[ ~grainreverse, ~grainreverseb, "reverse", ],
[ ~grainsin, ~grainsinb, "sine" ],
[ ~graintri, ~graintrib, "triangle" ],
[ ~grainrand, ~grainrandb, "random" ]
];
~playbacklfo = ~modes[0][0];
~playbacklfob = ~modes[0][1];
// audio buses
// recordb = input to bufrecorder
// granulatorb = output from granulator
~recordb = Bus.audio(s, 1);
~infilter = SynthDef(
\input_null,
{
arg in = 2, out = 4;
Out.ar(out, In.ar(in));
}
).play(s, [\in, ~usbinput, \out, ~recordb]);
~granulatorb = Bus.audio(s, 2);
// LFO bus and synth used to modulate the filter
~lfob = Bus.control(s, 1);
~lfo = SynthDef(
\lfo,
{
arg out=5, freq=1, amp=0;
Out.kr(out, SinOsc.kr(freq, 0, amp));
}
).play(s, [\out, ~lfob, \freq, 1, \amp, 0]);
// buffer recorder
~frippbuffer = Buffer.alloc(s, s.sampleRate * ~buflen, 1);
~bufrecorder = SynthDef(
\fripp_record,
{
arg in = 2, fb = 4, buffer = 0, mix = 0.25, record = 0.0, feedback = 0.0;
var insig, fbsig;
insig = record * In.ar(in, 1);
fbsig = feedback * Mix.ar(In.ar(fb, 2));
RecordBuf.ar(insig + fbsig, buffer, 0, mix, 1 - mix, loop: 1)
}
).play(s, [\in, ~recordb, \record, 1.0, \fb, ~granulatorb, \out, 0, \buffer, ~frippbuffer], \addToTail);
// the main granulator synth
// todo - different styles of trigger
~granulator = SynthDef(
\grainsynth,
{
arg out=0, modb, trate=120, size=12, rate=1, posb=5, amp=1.0, freq=10000, rq=0.3, pan=0, track=0.25, jitter=0, chorus=0.0, blur=0.0, dust = 0, buffer;
var dur, blen, clk, chor, pos, pans, grains, filtfreq;
dur = size / trate;
clk = (Impulse.kr(trate) * (1 - dust)) + (Dust.kr(trate) * dust);
chor = chorus * 2.pow((LFNoise0.kr(trate) + 0.5).floor) + (1 - chorus);
blen = BufDur.kr(buffer);
pos = Wrap.kr(In.kr(posb, 1) + WhiteNoise.kr(blur), 0, 1);
pans = pan + WhiteNoise.kr(jitter) + (track * (In.kr(posb, 1) - 1));
filtfreq = (In.kr(modb, 1) * freq * 0.5) + freq;
grains = TGrains.ar(2, clk, buffer, chor * rate, pos * blen, dur, pans, amp);
Out.ar(out, RLPF.ar(grains, filtfreq, rq));
}
).play(s, [\out, ~granulatorb, \buffer, ~frippbuffer, \posb, ~grainsawb, \modb, ~lfob]);
~mixerb = Bus.audio(s, 2); // this is what we will record from
~mixer = SynthDef(
\mixer_synth,
{
arg in = 2, gbus = 4, out = 0, amp = 1.0, passthrough = 0.0;
//Out.ar(out, In.ar(gbus, 2));
Out.ar(out, (amp * In.ar(gbus, 2)) + (passthrough * In.ar(~recordb, 1) ! 2));
}
).play(s, [\in, ~usbinput, \out, ~mixerb, \gbus, ~granulatorb, \amp, 1.0, \passthrough, 0.0], \addToTail);
~monitor = SynthDef(
\monitor_synth,
{
arg in=2, out=0;
Out.ar(out, In.ar(in, 2))
}
).play(s, [\in, ~mixerb, \out, 0 ], \addToTail);
// sync the server so that all the synths are ready for the touchosc stuff
)
// s.sync(); // this needs to be done in a routine because it calls yield
// sidebar -
ServerMeter.new(s, 8, 8);
~mixer.set(\in, 0);
~recordb;
~monitor.set(\in, ~usbinput);
~mixer.set(\in, 0);
(
OSCdef.freeAll;
~to.button('/record', 1, { | v | ~bufrecorder.set(\record, v) });
~to.button('/reset', 0, { | v |
if( v > 0, {
var sp = ~to.v('/grains/speed')[0];
~buflen = ~to.v('/grains/buflen');
[ "resetting buffer to", ~buflen ].postln;
~newbuffer = Buffer.alloc(s, s.sampleRate * ~buflen, 1);
~granulator.set(\buffer, ~newbuffer);
~bufrecorder.set(\buffer, ~newbuffer);
if( ~frippbuffer.isNil.not, { ~frippbuffer.free });
~frippbuffer = ~newbuffer;
~playbacklfo.set(\speed, sp / ~buflen);
});
});
~to.slider('/mix', 0.25, TouchOSCScale(0, 1), { |v| ~bufrecorder.set(\mix, v) } );
~to.slider('/gain', 0.5, TouchOSCScale(0, 1), { |v| ~granulator.set(\amp, v) } );
~to.slider('/passthrough', 0.5, TouchOSCScale(0, 1), { |v| ~mixer.set(\passthrough, v) } );
~to.slider('/feedback', 0, TouchOSCScale(0, 0.25), { |v|
~bufrecorder.set(\feedback, v) } );
~to.button('/grains/bpm', "~", {});
~tapper = TapBeats();
~to.button('/grains/tap', 0, { | v, t |
if( v > 0, {
~tapper.tap(t);
if( ~tapper.bpm.isNil.not, {
~to.v_('/grains/bpm', ~tapper.bpm.round(1));
})
})
});
~to.button('/grains/bpmsend', 0, { | v |
if( ~tapper.bpm.isNil.not, {
var bl = ~beatsperbar * ~tapper.bpm;
~to.v_('/grains/buflen', bl);
~to.v_('/reset', 1);
});
});
// 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), {});
// this is a write-only control to display where the buffer playback is at
~to.slider('/grains/buffer', 0, TouchOSCScale(0, 1), {});
~to.xy('/grains/speed', [ 1, 40 ], TouchOSCScale(0, 2), TouchOSCScale(0, 120), { | v |
~playbacklfo.set(\speed, v[0] / ~buflen);
~granulator.set(\trate, v[1] / ~buflen);
});
~to.button('/grains/mode', 0, { |v|
var mode = ~modes[v];
"mode".postln;
[ v, mode ].postln;
if( mode.isNil.not, {
~granulator.set(\posb, mode[1]);
~playbacklfo = mode[0];
~playbacklfob = mode[1];
~playbacklfo.set(\speed, ~to.v('/grains/speed')[0]);
}, {
[ "Bad mode index", v ].postln;
});
});
~to.button('/grains/dust', 0, { |v| ~granulator.set(\dust, v) });
~to.slider('/grainf/blur', 0, TouchOSCScale(0, 1), { |v| ~granulator.set(\blur, v) });
// todo vvv quantise speed should be swappable
// var trate, qspeed;
// qspeed = 2.pow(v[0].floor);
// ~playbacklfo.set(\speed, qspeed / ~buflen);
// [ "speed", v[0], qspeed, qspeed / ~buflen ].postln;
// trate = 2.pow(v[1].floor) / ~buflen;
// ~granulator.set(\trate, trate);
~to.slider('/grains/size', 12, TouchOSCScale(0, 20),{ |v| ~granulator.set(\size, v) });
// Page 2: grainfx
~to.slider('/grains/blur', 0, TouchOSCScale(0, 1), { |v| ~granulator.set(\blur, v) });
~to.button('/grainfx/back', 0, { |v| ~granulator.set(\rate, if( v > 0, { -1 }, { 1}))});
~to.button('/grainfx/slope', 1, { |v| });
~to.button('/grainfx/chorus', 0, { |v| ~granulator.set(\chorus, v) });
~to.slider('/grainfx/pan', 0, TouchOSCScale(-1, 1), { |v| ~granulator.set(\pan, v) });
~to.slider('/grainfx/track', 0, TouchOSCScale(-1, 1), { |v| ~granulator.set(\track, v) });
~to.slider('/grainfx/jitter', 0, TouchOSCScale(0, 1), { |v| ~granulator.set(\jitter, v) });
// pitch gets quantised to octaves from 3 below to 3 above.
// NOTE: the pitch TouchOSC control is -1 to 1, not 0 to 1
// min/max gets ignored because I'm overloading the ctrlset/get
// TODO: fixme,
// ~to.slider('/grainfx/pitch', -1, 1, 1,
// { |self| ~granulator.set("rate", self.v) },
// { |self, ctrlv | self.v = 2.pow((ctrlv * 3).floor) },
// { |self| self.v.log2.floor / 3; }
// );
~to.xy(
'/fx/filter',
[ 10000, 0.3 ],
TouchOSCScale(200, 10000),
TouchOSCScale(0.1, 1),
{ |v|
~granulator.set(\freq, v[0]);
~granulator.set(\res, v[1]);
}
);
~to.slider('/fx/lfofreq', 0.5,TouchOSCScale(0.001, 4), { |v| ~lfo.set(\freq, v) } );
~to.slider('/fx/lfoamp', 0, TouchOSCScale(0, 1), { |v| ~lfo.set(\amp, v) });
)
(
~posdisplay = Task.new({
{
~playbacklfob.get({ | v |
~to.v_('/grains/buffer', v)
});
0.02.wait;
}.loop;
});
~posdisplay.start;
)
~to.slider('/grains/blur', 0, TouchOSCScale(0, 1), { |v| ~granulator.set(\blur, v) });
~posdisplay.stop;
~playbacklfob;
~posb;

View File

@ -1,98 +0,0 @@
(
~modes = [
[ "saw", \pos_saw ],
[ "reverse", \pos_reverse ],
[ "sine", \pos_sine ],
[ "step", \pos_step ],
];
~outputDir = Platform.recordingsDir +/+ "GrainBuffers";
~grainsb = Bus.audio(s, 2);
~granulators = Array.new(4);
~grainmodes = [ 0, 0, 0, 0 ]; // keep track of mode so don't swap if not needed
~speeds = [ 1, 1, 1, 1 ]; // hacky speed quantisation
~posb = Array.new(4);
~rectriggerb = Array.new(4);
~patterns = [ nil, nil, nil, nil ];
~players = [ nil, nil, nil, nil ];
~loopsynths = [ nil, nil, nil, nil ];
// create the control busses
(0..3).do({
~posb.add(Bus.control(s, 1));
~rectriggerb.add(Bus.control(s, 1));
});
// start the granulators
(0..3).do({ |i|
var pb = ~posb[i], rtb = ~rectriggerb[i];
~granulators.add(Granulator.new(~buflen, ~infxb, ~grainsb, pb, rtb));
});
// set up the Patterns which drive the position synths
// (0..3).do({ |i|
// ~patterns.add(~makePattern.value(i, 0, ~speeds[i]));
// });
//
// (0..3).do({|i|
// ~players.add(~patterns[i].play(~tc, quant: ~beatsperbar))
// });
~setmode = {
arg track, mode;
~grainmodes[track] = mode;
if(~players[track].isNil.not,{
~players[track].stop;
~patterns[track].free;
});
~patterns[track] = ~makePattern.value(track, mode, ~speeds[track]);
~players[track] = ~patterns[track].play(~tc, quant: ~beatsperbar);
};
~setspeed = {
arg track, speed;
if( ~speeds[track] != speed, {
~speeds[track] = speed;
~players[track].stop;
~patterns[track].free;
~patterns[track] = ~makePattern.value(track, ~grainmodes[track], ~speeds[track]);
~players[track] = ~patterns[track].play(~tc, quant: ~beatsperbar);
});
};
~makePattern = {
arg track, mode, speed;
var ptrig, ppos, synth = ~modes[mode][1];
// note: trigger is going off the base tempoclock, not the playback speed - I think this is
// the right thing to do but I'm not sure yet
ptrig = Pbind(
\instrument, \trigger,
\dur, ~beatsperbar,
\out, ~rectriggerb[track]
);
ppos = Pbind(
\instrument, synth,
\dur, ~beatsperbar / speed,
\length, ~buflen / speed,
\out, ~posb[track]
);
Ppar([ptrig, ppos]);
};
~dumpbuffers = { |prefix|
(0..3).do({|i|
var filename = ~outputDir +/+ prefix ++ 'buffer' ++ i.asString ++ '.aiff';
~granulators[i].buffer.write(filename);
});
}
)

View File

@ -1,53 +0,0 @@
(
~fuzzbox = SynthDef(
\fuzzbox,
{
arg in=2, out=4, distort=0.1, decay=0.999;
var raw, cross, pf;
raw = In.ar(in, 1).softclip;
cross = CrossoverDistortion.ar(raw, 0.5, 0.5);
pf = PeakFollower.ar(raw, decay);
Out.ar(out, ((1 - distort) * raw) + (distort * pf * cross));
}
).play(s, [\in, ~usbinput, \out, ~recordb, \distort, 0 ]);
// ~decimator = SynthDef(
// \decimator,
// {
// arg in=2, out=4, modb, rate=10000, smooth=0.5;
// var raw, mod, decimated;
// raw = In.ar(in, 1);
// mod = In.kr (modb, 1);
// decimated = SmoothDecimator.ar(raw, rate + (0.2 * rate * mod), smooth);
// Out.ar(out, decimated);
// }
// ).play(s, [\in, ~usbinput, \out, ~recordb, \modb, ~lfob, \rate, 10000 ]);
//
// ~localmax = SynthDef(
// \localmax,
// {
// arg in=2, out=4, threshold=25;
// var chain;
// chain = FFT(LocalBuf(2048), In.ar(in, 1).distort);
// chain = PV_LocalMax(chain, threshold);
// Out.ar(out, IFFT.ar(chain));
// }
// ).play(s, [\in, ~usbinput, \out, ~recordb, \threshold, 25 ]);
//
// ~scramble = SynthDef(
// \scramble,
// {
// arg in=2, out=4, shift=1;
// var chain;
// chain = FFT(LocalBuf(2048), In.ar(in, 1).distort);
// chain = PV_BinScramble(chain, 0.5, 0.2, Impulse.kr(shift));
// Out.ar(out, IFFT.ar(chain));
// }
// ).play(s, [\in, ~usbinput, \out, ~recordb, \shift, 1 ]);
//
)

View File

@ -1,200 +0,0 @@
(
~to = TouchOSC(~touchosc_ip, 9000);
~tracknum = 0;
~granulator = ~granulators[0];
~speedlock = 0;
~speedquant = 0;
OSCdef.freeAll;
~to.button('/grains/reset', 0, { | v |
if( v > 0, {
~buflen = ~to.v('/grains/buflen');
(0..3).do({|i|
~granulators[i].reset(~buflen);
});
});
});
~quantspeed = { |v| 2.pow((v * 4 + 0.5).round - 5) };
~quantharmonics = {
arg f, quantise=0;
if(quantise != 0, {
var fraction = f.asFraction(7, false);
fraction[0] / fraction[1];
},
{ f }
);
};
// 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);
if(v == 0, {
~mixlevel[track] = ~granulators[track].mix;
~granulators[track].mix_(0);
},
{
~granulators[track].mix_(~mixlevel[track]);
});
if( track == ~tracknum, { ~to.v_('/track/mix', ~granulators[track].mix); })
};
// 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/mode0', 0, { |v| ~setmode.value(0, v) });
~to.button('/grains/mode1', 0, { |v| ~setmode.value(1, v) });
~to.button('/grains/mode2', 0, { |v| ~setmode.value(2, v) });
~to.button('/grains/mode3', 0, { |v| ~setmode.value(3, v) });
// needs work
~to.slider('/grains/speed0', 1, TouchOSCScale(0, 2), { |v| ~setspeed.value(0, ~quantspeed.value(v)) });
~to.slider('/grains/speed1', 1, TouchOSCScale(0, 2), { |v| ~setspeed.value(1, ~quantspeed.value(v)) });
~to.slider('/grains/speed2', 1, TouchOSCScale(0, 2), { |v| ~setspeed.value(2, ~quantspeed.value(v)) });
~to.slider('/grains/speed3', 1, TouchOSCScale(0, 2), { |v| ~setspeed.value(3, ~quantspeed.value(v)) });
~to.slider('/grains/passthrough', 0.75, TouchOSCScale(0, 1), { |v| ~grainmixer.set(\passthrough, v) });
~to.slider('/grains/mix0', 0.5, TouchOSCScale(0, 1), { | v | ~granulators[0].gain_(v) });
~to.slider('/grains/mix1', 0.5, TouchOSCScale(0, 1),{ | v | ~granulators[1].gain_(v) });
~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/lock', 0, { |v| ~speedlock = v });
~to.button('/grains/quant', 1, { |v| ~speedquant = v });
~to.button('/grains/metronome', 0, { |v|
if( v == 1, {
~bps = ~beatsperbar / ~buflen;
~tc.tempo_(~bps);
});
~metromix.set(\amp, v)
});
// Page 2: track
~to.xy('/track/triggersize', [ 100, 0.125 ], TouchOSCScale(0, 200), TouchOSCScale(0, 1), { |v|
~granulator.trigger_(v[0]);
~granulator.size_(v[1]);
});
~to.slider('/track/blur', 0, TouchOSCScale(0, 1.0), { |v| ~granulator.blur_(v) });
~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/chorus', 0, { |v| ~granulator.chorus_(v) });
~to.slider('/track/harmonics', 2, TouchOSCScale(0.5, 3), { |v|
~granulator.harmonics_(~quantharmonics.value(v, 1))
});
~to.slider('/track/detune', 0, TouchOSCScale(0, 0.059), { |v| ~granulator.detune_(v) });
~to.slider('/track/pitch', 0, TouchOSCScale(-2, 2), { |v| ~granulator.pitch_(v.round) });
~to.slider('/track/mix', 0.25, TouchOSCScale(0, 1), { |v| ~granulator.mix_(v); });
~to.slider('/track/pan', 0, TouchOSCScale(-1, 1), { |v| ~granulator.pan_(v) });
~to.slider('/track/track', 0.5, TouchOSCScale(-1, 1), { |v| ~granulator.track_(v) });
~to.slider('/track/jitter', 0.25, TouchOSCScale(0, 1), { |v| ~granulator.jitter_(v) });
~to.button('/trackselect', 0, { |v|
~tracknum = v.asInteger;
~granulator = ~granulators[~tracknum];
~to.v_('/track/triggersize', [~granulator.trigger, ~granulator.size]);
~to.v_('/track/blur', ~granulator.blur);
~to.v_('/track/mix', ~granulator.mix);
~to.v_('/track/pan', ~granulator.pan);
~to.v_('/track/track', ~granulator.track);
~to.v_('/track/jitter', ~granulator.jitter);
~to.v_('/track/dust', ~granulator.dust);
~to.v_('/track/slope', ~granulator.slope);
~to.v_('/track/back', ~granulator.back);
~to.v_('/track/chorus', ~granulator.chorus);
~to.v_('/track/harmonics', ~granulator.harmonics);
~to.v_('/track/detune', ~granulator.detune);
~to.v_('/track/pitch', ~granulator.pitch);
});
// set up the record buttons on the front page now because /track/mix has been defined
~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.slider(
'/fx/filterfreq',
10000, TouchOSCScaleExp(100, 10000), { |v| ~filter.set(\freq, v) }
);
~to.slider('/fx/grainmix', 1.0, TouchOSCScale(0, 1), { |v| ~grainmixer.set(\grains, v) } );
~to.slider('/fx/filtermix', 0.8, TouchOSCScale(0, 1), { |v| ~filter.set(\amp, v) } );
~to.button('/fx/filtermoda', 1, { |v| ~filtermod.set(\a, v) });
~to.button('/fx/filtermodb', 0, { |v| ~filtermod.set(\b, v) });
~to.button('/fx/filtermodc', 0, { |v| ~filtermod.set(\c, v) });
~to.slider('/fx/delay', 0.2,TouchOSCScale(0, 1), { |v| ~delay.set(\delaytime, v) } );
~to.slider('/fx/decay', 1, TouchOSCScale(0, 5), { |v| ~delay.set(\decaytime, v) } );
~to.slider('/fx/delaymix', 0.2, TouchOSCScale(0, 1), { |v| ~delay.set(\amp, v) } );
~to.slider('/fx/reverbwet', 0.33,TouchOSCScale(0, 1), { |v| ~reverb.set(\mix, v) } );
~to.slider('/fx/reverbroom', 0.5,TouchOSCScale(0, 1), { |v| ~reverb.set(\room, v) } );
~to.slider('/fx/reverbdamp', 0.5,TouchOSCScale(0, 1), { |v| ~reverb.set(\damp, v) } );
~to.slider('/fx/reverbmix', 0.2, TouchOSCScale(0, 1), { |v| ~reverb.set(\amp, v) } );
// note - the three LFOs have different rate ranges
~to.slider('/lfos/afreq', 0.5,TouchOSCScale(0.001, 2), { |v| ~lfoa.set(\freq, v) } );
~to.slider('/lfos/aamp', 0, TouchOSCScale(0, 1), { |v| ~lfoa.set(\amp, v) });
~to.slider('/lfos/bfreq', 0.5,TouchOSCScale(0.01, 20), { |v| ~lfob.set(\freq, v) } );
~to.slider('/lfos/bamp', 0, TouchOSCScale(0, 1), { |v| ~lfob.set(\amp, v) });
~to.slider('/lfos/cfreq', 0.5,TouchOSCScale(0.1, 200), { |v| ~lfoc.set(\freq, v) } );
~to.slider('/lfos/camp', 0, TouchOSCScale(0, 1), { |v| ~lfoc.set(\amp, v) });
)

View File

@ -1,46 +0,0 @@
// Execute this before booting the server
(
Server.default.options.inDevice_("Scarlett 2i2 USB");
Server.default.options.hardwareBufferSize_(1024);
Server.default.options.outDevice_("Scarlett 2i2 USB");
//Server.default.options.outDevice_("External Headphones");
)
Server.killAll;
(
Routine.run({
~usbinput = 2;
~usbinput1 = 2;
~usbinput2 = 3;
~bpm = 90;
~bps = ~bpm / 60;
~beatsperbar = 4;
~buflen = ~beatsperbar / ~bps;
[ "bpm", ~bpm ].postln;
[ "buffer length", ~buflen ].postln;
~tc = TempoClock.new(~bps);
~touchosc_ip = "192.168.0.209";
("./synths.scd").loadRelative;
Granulator.init(s);
s.sync;
("./control.scd").loadRelative;
s.sync;
("./granulator.scd").loadRelative;
s.sync;
("./effects.scd").loadRelative;
s.sync;
("./sequencer.scd").loadRelative;
s.sync;
"please wait for the interface to load...".postln;
~buflen.sleep;
("./interface.scd").loadRelative;
"ok go!".postln;
});
)

View File

@ -1,36 +0,0 @@
~frippbuffers[~currentfripp].write("/Users/mike/Music/SuperCollider Recordings/test.aiff");
~frippbuffer.write("/Users/mike/Music/SuperCollider Recordings/slow.aiff");
~frippbuffer.isNil;
(
~monitor = SynthDef(
\monitor_synth,
{
arg in=2, out=0;
Out.ar(out, In.ar(in, 2))
}
).play(s, [\in, ~fxb, \out, 0 ], \addToTail);
)
~monitor.set(\in, ~reverbb)
~monitor.free
~pitchb.scope()
~frippbuffers.plot;
~bufrecorder.set(\buffer, ~frippbuffers[1]);
~granulators[0].get(\buffer, {|v| v.postln});
(
~frippbuffers.do({
|b, i|
b.write("/Users/mike/Music/SuperCollider Recordings/buffer" ++ i.asString ++ ".aiff");
});
)

View File

@ -1,26 +0,0 @@
(
~metrob = Bus.audio(s, 2);
~metromix = SynthDef(\metromix, {
arg in=1, out=0, amp=1;
Out.ar(out, amp * In.ar(in, 2));
}).play(s, [\in, ~metrob, \out, 0, \amp, 0]);
~metronome = Pbind(
\instrument, \metronome,
\dur, ~beatsperbar,
\amp, 0.5,
\pan, 0,
\out, ~metrob
).play(~tc);
)

View File

@ -1,45 +0,0 @@
(
SynthDef(\pos_saw, {
arg out, length=1;
Out.kr(out, EnvGen.kr(Env([0, 1], length), doneAction: Done.freeSelf))
}).add();
SynthDef(\pos_sine, {
arg out, length=1;
Out.kr(out, EnvGen.kr(Env.sine(length, 1), doneAction: Done.freeSelf))
}).add();
SynthDef(\pos_reverse, {
arg out, length=1;
Out.kr(out, EnvGen.kr(Env([1, 0], length), doneAction: Done.freeSelf))
}).add();
SynthDef(\pos_step, {
arg out, length=1;
var levels = (0..8) / 8, times = (length / 8) ! 7;
Out.kr(out, EnvGen.kr(Env(levels: levels, times: times, curve: \hold), doneAction: Done.freeSelf));
}).add();
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, EnvGen.kr(Env.perc(0.001, 0.2, 2), levelScale:2.0, levelBias:-1,doneAction:Done.freeSelf));
}).add;
SynthDef(\metronome, {
arg out=0, amp=1, pan=0, filter=1000, atk=0.01, rel=0.1;
var sig, env;
env = EnvGen.kr(Env.perc(atk, rel, amp), doneAction: Done.freeSelf);
sig = HPF.ar(WhiteNoise.ar(), filter);
Out.ar(out, Pan2.ar(sig * env, pan));
}
).add;
)