|  | // SVNVimStart
s = Server.local;
s.waitForBoot {
    var module, msg, modules;
    // Connect to OSC
    thisProcess.openUDPPort(5005);
    n = NetAddr.new("0.0.0.0", 5005);
    o = OSCFunc({ arg msg, time, addr, recvPort; [msg, time, addr, recvPort].postln; }, '/radio', n);
    // Create the synth definition and load it
    module = SynthDef.new(\module, { 
        arg hue, saturation, value, pan, gain, octave, notefreq;
        var oscillator, noise, filter, panner, mixer, frequency, qfactor, lagtime, output, amplitude;
        // Dynamic time of the module
        lagtime = 10 / (2**(octave/8));
        // Oscillator/filter frequency
        frequency = (notefreq * (1.0 + (hue*0.02))) * (2**(octave-1));
        //frequency = (130 + (hue * 130)) * (2 ** octave);
        //frequency = 130 * Scale.major.ratios[(hue * 12).asInteger];
        //frequency = (130 * frequency) * (2**octave);
        frequency = Lag.kr(frequency, lagtime/10);
        
        // Filtered saw oscillator
        oscillator = Mix.ar([SawDPW.ar(frequency), SawDPW.ar(frequency/2+value*(1-saturation)*100)]);
        filter = DFM1.ar(oscillator, frequency, saturation/2, 1.0, 0.0, 0.0006);
        // Noise
        qfactor = Lag.kr((1 - saturation)**4, lagtime);
        noise = Crackle.ar(1.99, 1.0);
        noise = BPF.ar(noise, frequency, qfactor);
        // Mix noise and saw
        mixer = Mix.ar([filter * saturation.sqrt(), 2 * noise*(1-saturation)]);
        // Apply pan
        panner = LinPan2.ar(mixer, pan);
        // Apply dynamics
        amplitude = Lag.kr(HPF.kr(value, 4 * hue / (lagtime)), lagtime);
        output = panner * amplitude;
        // Compress
        output = Compander.ar(output, output, 0.5, 0.3, 0.3, 0.1, lagtime);
        // Crank everything down
        output = output * 0.2; 
        Out.ar(0, output);
    });
    module.load(s);
    s.sync;
    // Create multiple sound generators
    modules = Array.fill(12, 
        {
            arg index; 
            var pan, octave;
            pan = 0 - ((index % 4) - 1.5)/1.5;
            octave = (2 - (index / 4).floor);
            "Module %: Pan %, octave %\n".postf(index, pan.round(1e-1), octave.round(1e-1));
            Synth.new(\module, 
                [\hue, 0.5, \saturation, 0.1, \value, 0.5, \pan, pan, \gain, 0.9, \octave, octave, \notefreq, Scale.major.degreeToFreq(index, 48.midicps, octave)]
            )
        }
    );
    // Hook up OSC
    f = { |msg, time, addr|
        if(msg[0] == '/radio') {
            if(msg[1]<modules.size){
                modules[msg[1]].set(\hue, msg[2]);
                modules[msg[1]].set(\saturation, msg[3]);
                modules[msg[1]].set(\value, msg[4]);
            }
        }
    };
    thisProcess.addOSCRecvFunc(f);
};
 |