Skip to content

Instantly share code, notes, and snippets.

@antonhornquist
Created September 6, 2019 11:16
Show Gist options
  • Save antonhornquist/b3f0de19fecbeb873745df4023108a07 to your computer and use it in GitHub Desktop.
Save antonhornquist/b3f0de19fecbeb873745df4023108a07 to your computer and use it in GitHub Desktop.
(
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(s, path, 0, -1, [0], {
voices[i].set(\buf, newbuf);
buffers[i].free;
buffers[i] = newbuf;
});
});
});
};
var alloc = {
buffers = Array.fill(nvoices, { arg i;
Buffer.alloc(
s,
s.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;
s.sync;
// mix bus for all synth outputs
mixBus = Bus.audio(s, 2);
effect = Synth.new(\effect, [\in, mixBus.index, \out, 0]);
phases = Array.fill(nvoices, { arg i; Bus.control(context.server); });
levels = Array.fill(nvoices, { arg i; Bus.control(context.server); });
pg = ParGroup.before(effect);
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);
});
s.sync;
NornsCmddef('reverb_mix', "f", { arg value; effect.set(\mix, value); });
NornsCmddef('reverb_room', "f", { arg value; effect.set(\room, value); });
NornsCmddef('reverb_damp', "f", { arg value; effect.set(\damp, value); });
NornsCmddef('read', "is", { arg voice, path; readBuf.value(voice - 1, path); });
NornsCmddef('seek', "if", { arg argVoice, argPos;
var voice = argVoice - 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 = argPos;
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 = argPos;
voices[voice].set(\pos, pos);
voices[voice].set(\t_reset_pos, 1);
voices[voice].set(\freeze, 0);
});
});
NornsCmddef('gate', "ii", { arg argVoice, gate;
var voice = argVoice - 1;
voices[voice].set(\gate, msg[2]);
});
NornsCmddef('speed', "if", { arg argVoice, speed;
var voice = argVoice - 1;
voices[voice].set(\speed, speed);
});
NornsCmddef('jitter', "if", { arg argVoice, jitter;
var voice = argVoice - 1;
voices[voice].set(\jitter, jitter);
});
NornsCmddef('jitter', "if", { arg argVoice, jitter;
var voice = argVoice - 1;
voices[voice].set(\jitter, jitter);
});
NornsCmddef('size', "if", { arg argVoice, size;
var voice = argVoice - 1;
voices[voice].set(\size, size);
});
NornsCmddef('density', "if", { arg argVoice, density;
var voice = argVoice - 1;
voices[voice].set(\density, density);
});
NornsCmddef('pitch', "if", { arg argVoice, pitch;
var voice = argVoice - 1;
voices[voice].set(\pitch, pitch);
});
NornsCmddef('spread', "if", { arg argVoice, spread;
var voice = argVoice - 1;
voices[voice].set(\spread, spread);
});
NornsCmddef('volume', "if", { arg argVoice, volume;
var voice = argVoice - 1;
voices[voice].set(\volume, volume);
});
NornsCmddef('envscale', "if", { arg argVoice, envscale;
var voice = argVoice - 1;
voices[voice].set(\envscale, envscale);
});
nvoices.do({ arg i;
NornsPolldef(
"phase_" ++ (i+1),
{
var val = phases[i].getSynchronous;
val
}
);
NornsPolldef(
"level_" ++ (i+1),
{
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