Skip to content

Instantly share code, notes, and snippets.

@antonhornquist
Last active November 28, 2019 15:48
Show Gist options
  • Save antonhornquist/9e55b8f6f46ee5c52acc0d0b348149d8 to your computer and use it in GitHub Desktop.
Save antonhornquist/9e55b8f6f46ee5c52acc0d0b348149d8 to your computer and use it in GitHub Desktop.
(
Norns.spawn('glut') { |engine|
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;
});
});
});
};
buffers = Array.fill(nvoices, { arg i;
Buffer.alloc(
s,
s.sampleRate * 1,
);
});
~buffers = buffers; // TODO
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(engine.server); });
levels = Array.fill(nvoices, { arg i; Bus.control(engine.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;
engine.addCommand('reverb_mix', "f", { arg value; effect.set(\mix, value); });
engine.addCommand('reverb_room', "f", { arg value; effect.set(\room, value); });
engine.addCommand('reverb_damp', "f", { arg value; effect.set(\damp, value); });
engine.addCommand('read', "is", { arg voice, path; [voice, path].debug; readBuf.value(voice - 1, path); });
engine.addCommand('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);
});
});
engine.addCommand('gate', "ii", { arg argVoice, gate;
var voice = argVoice - 1;
voices[voice].set(\gate, gate);
});
engine.addCommand('speed', "if", { arg argVoice, speed;
var voice = argVoice - 1;
voices[voice].set(\speed, speed);
});
engine.addCommand('jitter', "if", { arg argVoice, jitter;
var voice = argVoice - 1;
voices[voice].set(\jitter, jitter);
});
engine.addCommand('size', "if", { arg argVoice, size;
var voice = argVoice - 1;
voices[voice].set(\size, size);
});
engine.addCommand('density', "if", { arg argVoice, density;
var voice = argVoice - 1;
voices[voice].set(\density, density);
});
engine.addCommand('pitch', "if", { arg argVoice, pitch;
var voice = argVoice - 1;
voices[voice].set(\pitch, pitch);
});
engine.addCommand('spread', "if", { arg argVoice, spread;
var voice = argVoice - 1;
voices[voice].set(\spread, spread);
});
engine.addCommand('volume', "if", { arg argVoice, volume;
var voice = argVoice - 1;
voices[voice].set(\volume, volume);
});
engine.addCommand('envscale', "if", { arg argVoice, envscale;
var voice = argVoice - 1;
voices[voice].set(\envscale, envscale);
});
nvoices.do({ arg i;
engine.addPoll(
('phase_' ++ (i+1)).asSymbol,
{
var val = phases[i].getSynchronous;
val
}// , false // TODO: should be periodic
);
engine.addPoll(
('level_' ++ (i+1)).asSymbol,
{
var val = levels[i].getSynchronous;
val
}
);
});
seek_tasks = Array.fill(nvoices, { arg i;
Routine {}
});
engine.onFree = {
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;
pg.free;
};
}
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment