scopesupercollidersound-synthesis

Supercollider: scope of variables and arguments for additive synthesis using Mix.fill


I'm quite confused on how to implement parameters in additive synthesis.

I'm trying to implement a system where I can sequence the following parameters: arbitrary number of partials, base frequency. I'm not sure of the feasibility of the arbitrary number of partials, but sequencing the base frequency should be indeed totally possible in my opinion.

Here is the code I'm working on:

(
    z = {
        arg index, freq, nn;
        var finalFreq, ff, amp, nn;
        ff = index*freq;
        amp = 0.5/nn;
        finalFreq = freq + ff;
        finalFreq.postln;
        s = SinOsc.ar(finalFreq, 0, amp);
    };
)

(
SynthDef.new('additive',{
    arg freq, nn;
    var sig, env;
    env = Line.kr(1,0,0.2);
    sig = Mix.fill(nn, z) * env;
    Out.ar(0,sig);
}).add
)

(p = Pbind(
    \instrument, \additive,
    \dur, Pseq([0.1,0.2,0.3,0.4],inf),
    \freq, Pseq([100,440,880,55],inf),
    \nn, Pseq([1,5,10,100],inf)
).play;
)

This fails with Index not an Integer. And I'm not even sure on how to send parameters to z.

An alternative formulation, which I envisioned exploiting variable scope is the following:

(
SynthDef.new('additive',{
    arg freq, nn;
    var sig, env;
    env = Line.kr(1,0,0.2);
    z = {
        arg index;
        var finalFreq, ff, amp, nn;
        ff = index*freq;
        amp = 0.5/nn;
        finalFreq = freq + ff;
        finalFreq.postln;
        s = SinOsc.ar(finalFreq, 0, amp);
    };

    sig = Mix.fill(nn, z) * env;
    Out.ar(0,sig);
}).add
)

It's also not working, failing with Index not an Integer.

How would you tackle this problem?


Solution

  • Mix.fill creates an array one time, when the Synth is created, so you can't dynamically change the size of the array by using a Synth argument.

    Your bottom example also declares nn as a variable inside the z function, which means that amp = 0.5/nil

    One possible solution is make many SynthDefs. Let's say that you know that you that minimum number of SinOscs you want is 2 and the maximum is 25.

    (
    (2..25).do({ arg nn;
        
        SynthDef.new('additive'++nn,{
            arg freq;
            var sig, env;
            env = Line.kr(1,0,0.2);
            z = {
                arg index;
                var finalFreq, ff, amp;
                ff = index*freq;
                amp = 0.5/nn;
                finalFreq = freq + ff;
                finalFreq.postln;
                s = SinOsc.ar(finalFreq, 0, amp);
            };
            
            sig = Mix.fill(nn, z) * env;
            Out.ar(0,sig);
        }).add
    });
    )
    

    This will give you SynthDefs named additive2, additive3, additive4, ...additive25.

    When you want to change the size of the array, you'll have to change which SynthDef you're playing. This is a Pbind which randomly picks from between your additive SynthDefs:

    Pbind(
        \instrument, Pfunc({ 'additive' ++ 2.rrand(25) })
    ).play
    

    Or, to use the pbind from your top example:

    (
    n = [1,5,10,100];
    
    n.do({ arg nn;
        
        SynthDef.new('additive'++nn,{
            arg freq;
            var sig, env;
            env = Line.kr(1,0,0.2);
            z = {
                arg index;
                var finalFreq, ff, amp;
                ff = index*freq;
                amp = 0.5/nn;
                finalFreq = freq + ff;
                finalFreq.postln;
                s = SinOsc.ar(finalFreq, 0, amp);
            };
            
            sig = Mix.fill(nn, z) * env;
            Out.ar(0,sig);
        }).add
    });
    )
    
    Pbind(
        \instrument, Pfunc({ 'additive' ++ n.choose }),
        \dur, Pseq([0.1,0.2,0.3,0.4],inf),
        \freq, Pseq([100,440,880,55],inf)
    ).play