Skip to content

Instantly share code, notes, and snippets.

@antonhornquist
Created July 30, 2019 22:31
(
var nvoices = 7;
var pg;
var effect;
var buffers;
var voices;
var mixBus;
var phases;
var levels;
var seek_tasks;
// disk read
var readBuf = { arg i, path;
if(buffers[i].notNil, {
if (File.exists(path), {
// TODO: load stereo files and duplicate GrainBuf for stereo granulation
var newbuf = Buffer.readChannel(context.server, path, 0, -1, [0], {
voices[i].set(\buf, newbuf);
buffers[i].free;
buffers[i] = newbuf;
});
});
});
};
var alloc = { |engine|
buffers = Array.fill(nvoices, { arg i;
Buffer.alloc(
context.server,
context.server.sampleRate * 1,
);
});
SynthDef(\synth, {
arg out, phase_out, level_out, buf,
gate=0, pos=0, speed=1, jitter=0,
size=0.1, density=20, pitch=1, spread=0, gain=1, envscale=1,
freeze=0, t_reset_pos=0;
var grain_trig;
var jitter_sig;
var buf_dur;
var pan_sig;
var buf_pos;
var pos_sig;
var sig;
var env;
var level;
grain_trig = Impulse.kr(density);
buf_dur = BufDur.kr(buf);
pan_sig = TRand.kr(trig: grain_trig,
lo: spread.neg,
hi: spread);
jitter_sig = TRand.kr(trig: grain_trig,
lo: buf_dur.reciprocal.neg * jitter,
hi: buf_dur.reciprocal * jitter);
buf_pos = Phasor.kr(trig: t_reset_pos,
rate: buf_dur.reciprocal / ControlRate.ir * speed,
resetPos: pos);
pos_sig = Wrap.kr(Select.kr(freeze, [buf_pos, pos]));
sig = GrainBuf.ar(2, grain_trig, size, buf, pitch, pos_sig + jitter_sig, 2, pan_sig);
env = EnvGen.kr(Env.asr(1, 1, 1), gate: gate, timeScale: envscale);
level = env;
Out.ar(out, sig * level * gain);
Out.kr(phase_out, pos_sig);
// ignore gain for level out
Out.kr(level_out, level);
}).add;
SynthDef(\effect, {
arg in, out, mix=0.5, room=0.5, damp=0.5;
var sig = In.ar(in, 2);
sig = FreeVerb.ar(sig, mix, room, damp);
Out.ar(out, sig);
}).add;
context.server.sync;
// mix bus for all synth outputs
mixBus = Bus.audio(context.server, 2);
effect = Synth.new(\effect, [\in, mixBus.index, \out, context.out_b.index], target: context.xg);
phases = Array.fill(nvoices, { arg i; Bus.control(context.server); });
levels = Array.fill(nvoices, { arg i; Bus.control(context.server); });
pg = ParGroup.head(context.xg);
voices = Array.fill(nvoices, { arg i;
Synth.new(\synth, [
\out, mixBus.index,
\phase_out, phases[i].index,
\level_out, levels[i].index,
\buf, buffers[i],
], target: pg);
});
context.server.sync;
engine.addCommand("reverb_mix", "f", { arg msg; effect.set(\mix, msg[1]); });
engine.addCommand("reverb_room", "f", { arg msg; effect.set(\room, msg[1]); });
engine.addCommand("reverb_damp", "f", { arg msg; effect.set(\damp, msg[1]); });
engine.addCommand("read", "is", { arg msg;
readBuf.value(msg[1] - 1, msg[2]);
});
engine.addCommand("seek", "if", { arg msg;
var voice = msg[1] - 1;
var lvl, pos;
var seek_rate = 1 / 750;
seek_tasks[voice].stop;
// TODO: async get
lvl = levels[voice].getSynchronous();
if (false, { // disable seeking until fully implemented
var step;
var target_pos;
// TODO: async get
pos = phases[voice].getSynchronous();
voices[voice].set(\freeze, 1);
target_pos = msg[2];
step = (target_pos - pos) * seek_rate;
seek_tasks[voice] = Routine {
while({ abs(target_pos - pos) > abs(step) }, {
pos = pos + step;
voices[voice].set(\pos, pos);
seek_rate.wait;
});
voices[voice].set(\pos, target_pos);
voices[voice].set(\freeze, 0);
voices[voice].set(\t_reset_pos, 1);
};
seek_tasks[voice].play();
}, {
pos = msg[2];
voices[voice].set(\pos, pos);
voices[voice].set(\t_reset_pos, 1);
voices[voice].set(\freeze, 0);
});
});
engine.addCommand("gate", "ii", { arg msg;
var voice = msg[1] - 1;
voices[voice].set(\gate, msg[2]);
});
engine.addCommand("speed", "if", { arg msg;
var voice = msg[1] - 1;
voices[voice].set(\speed, msg[2]);
});
engine.addCommand("jitter", "if", { arg msg;
var voice = msg[1] - 1;
voices[voice].set(\jitter, msg[2]);
});
engine.addCommand("size", "if", { arg msg;
var voice = msg[1] - 1;
voices[voice].set(\size, msg[2]);
});
engine.addCommand("density", "if", { arg msg;
var voice = msg[1] - 1;
voices[voice].set(\density, msg[2]);
});
engine.addCommand("pitch", "if", { arg msg;
var voice = msg[1] - 1;
voices[voice].set(\pitch, msg[2]);
});
engine.addCommand("spread", "if", { arg msg;
var voice = msg[1] - 1;
voices[voice].set(\spread, msg[2]);
});
engine.addCommand("volume", "if", { arg msg;
var voice = msg[1] - 1;
voices[voice].set(\gain, msg[2]);
});
engine.addCommand("envscale", "if", { arg msg;
var voice = msg[1] - 1;
voices[voice].set(\envscale, msg[2]);
});
nvoices.do({ arg i;
engine.addPoll(("phase_" ++ (i+1)).asSymbol, {
var val = phases[i].getSynchronous;
val
});
engine.addPoll(("level_" ++ (i+1)).asSymbol, {
var val = levels[i].getSynchronous;
val
});
});
seek_tasks = Array.fill(nvoices, { arg i;
Routine {}
});
};
var free = {
voices.do({ arg voice; voice.free; });
phases.do({ arg bus; bus.free; });
levels.do({ arg bus; bus.free; });
buffers.do({ arg b; b.free; });
effect.free;
mixBus.free;
};
[alloc, free];
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment