Skip to content

Instantly share code, notes, and snippets.

@antonhornquist
Created July 30, 2019 22:31

Revisions

  1. antonhornquist created this gist Jul 30, 2019.
    234 changes: 234 additions & 0 deletions Engine_Glut.scd
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,234 @@
    (
    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];
    )