pabloriera / lode

Live coding with Ordinary Differential Equations
5 stars 1 forks source link

Working example of patching/routing with buses (the ugly way) #9

Open pabloriera opened 5 years ago

pabloriera commented 5 years ago

This example shows a viable way of doing the patching by adding SynthDefs an instantiating them. This was thought to be possible to do from outside as FoxDot does. It sends the path of a SynthDef file an then it creates nodes in the server by OSC. This way has the side effect of needing to mucho buses, every ODE SynthDef needs a bus for every parameter, to allow them to be connected with other buses.

In this example, the ODEs has 4 input buses, no matter if they use only 2. The connect SynthDef connects the output of the second Hopf ODE to the first. The key was using the Synth.after(), but I am don't know exactly what this means, but it is probably related to the execution order.

Is there a way of doing this, but without needing so many buses? By default there are 128 available, I think they are enough.

(
~global_input_offset = 20;
~global_output_offset = 10;
~global_n_inputs = 4;

SynthDef.new(\Hopf,
    {|input_offset=0, output_offset=0|
        var osc;
        var inputs;
        inputs = ~global_n_inputs.collect({|in| In.ar(in+input_offset+~global_input_offset );});
        inputs.do({arg item,i; item.source.inputs.postln});
        osc = Oderk4.ar("Hopf",*inputs);
        OffsetOut.ar(output_offset+~global_output_offset , osc[0]);
        OffsetOut.ar(output_offset+1+~global_output_offset , osc[1]);
}).add;

SynthDef.new(\param,
    {| val=0, out=10|
        val.postln;
        Out.ar(out,DC.ar(1)*val);
}).add;

SynthDef.new(\connect,
    {| in=0, out=0|
        Out.ar(out+~global_input_offset,In.ar(in+~global_output_offset,1));
}).add;

SynthDef.new(\output,
    {|bus=10, amp=1|
        Out.ar(0,Splay.ar(In.ar(bus,1),1,amp));
}).add;
)

(
~ode1_input_offset = 0;
~x = Synth(\Hopf);
~px1 = Synth(\param);
~px2 = Synth(\param);
~px1.set(\val,0.5,\out,0+~global_input_offset );
~px2.set(\val,330,\out,1+~global_input_offset );
~out = Synth(\output,~global_output_offset,1,1);
)

(
~ode2_input_offset = ~ode1_input_offset+~global_n_inputs;
~ode2_output_offset = 2;
~y = Synth(\Hopf,[\input_offset,~ode2_input_offset,\output_offset,~ode2_output_offset  ]);
~py1 = Synth(\param);
~py2 = Synth(\param);
~c1 = Synth.after(~y,\connect);
~py1.set(\val,1.0,\out,~ode2_input_offset + ~global_input_offset );
~py2.set(\val,10,\out,~ode2_input_offset + 1 +~global_input_offset );
~c1.set(\in,~ode2_output_offset,\out,~ode1_input_offset)
)
munshkr commented 5 years ago

The key was using the Synth.after(), but I am don't know exactly what this means, but it is probably related to the execution order.

Yes, nodes in the server are ordered. By default, when you create a synth node with Synth.new, it adds them at the head of the list. If you want to connect the output of ~ode1 to the input of ~ode2, ~ode2 must be after ~ode1, so you'd need to create it with ~ode2 = Synth.after(~ode1, ...). The order is a parameter of the \s_new OSC command.

osc = Oderk4.ar("Hopf",*inputs); OffsetOut.ar(output_offset+~global_output_offset , osc[0]); OffsetOut.ar(output_offset+1+~global_output_offset , osc[1]);

You can rewrite this with a single OffsetOut.ar(~global_output_offset + output_offset, osc) thanks to multichannel expansion, and this will work regardless of the number of outputs that Oderk4 returns (which is the number of ODE equations, right?).

I feel like this could be written without setting a maximum number of busses, but you'd need to write a Synthdef with the specific number of parameters, and if they change (because you added/removed parameters/equations in the yaml), you'd have to rewrite and send it to the server again, which may cause problems with the currently running synth nodes (not sure about this).

pabloriera commented 5 years ago

Yes, nodes in the server are ordered. By default, when you create a synth node with Synth.new, it adds them to the head of the list. If you want to connect the output of ~ode1 to the input of ~ode2, ~ode2 must be after ~ode1, so you'd need to create it with ~ode2 = Synth.after(~ode1, ...). The order is a parameter of the \s_new OSC command.

Ok, I understand. The thing I couldn't accomplish is to connect the ode1 back to the ode2. That creates a loop in the node tree, but I was hoping that SC will add the necessary block dealy to cope with that, but it didn't allow the creation of that connection.

You can rewrite this with a single OffsetOut.ar(~global_output_offset + output_offset, osc) thanks to multichannel expansion, and this will work regardless of the number of outputs that Oderk4 returns (which is the number of ODE equations, right?).

Perfect, yes, that is the case

I feel like this could be written without setting a maximum number of buses, but you'd need to write a Synthdef with the specific number of parameters, and if they change (because you added/removed parameters/equations in the yaml), you'd have to rewrite and send it to the server again, which may cause problems with the currently running synth nodes (not sure about this).

Yes, that is probably the most coherent of the alternatives. To rewrite the SynthDef and relaunch the node with a crossfade, whenever the ODE changed in the number of parameters/equations. I will try to do a test and try to listen if it sounds ok.

munshkr commented 5 years ago

Ok, I understand. The thing I couldn't accomplish is to connect the ode1 back to the ode2. That creates a loop in the node tree, but I was hoping that SC will add the necessary block dealy to cope with that, but it didn't allow the creation of that connection.

About this, I've just found out about InFeedback, have you tried with it instead of In? There are some notes about synth node order too.

pabloriera commented 5 years ago

Yes, I tried that but I didn't succeed, I believe I did something wrong. I have an example of a FM7 synth that allows feedback and recurrent connections, so I should check how it is done there.

El mar., 27 nov. 2018 a las 11:57, Damián Silvani (notifications@github.com) escribió:

Ok, I understand. The thing I couldn't accomplish is to connect the ode1 back to the ode2. That creates a loop in the node tree, but I was hoping that SC will add the necessary block dealy to cope with that, but it didn't allow the creation of that connection.

About this, I've just found out about InFeedback http://doc.sccode.org/Classes/InFeedback.html, have you tried with it instead of In? There are some notes about synth node order too.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/pabloriera/lode/issues/9#issuecomment-442088238, or mute the thread https://github.com/notifications/unsubscribe-auth/ABifvTFBbq2iykAc8CRktNIp2UZYdEjqks5uzVL3gaJpZM4Yz1EY .

-- ♫

pabloriera commented 5 years ago

The patching of the output of one node to another is already working. Creating groups for each type of synthdef and assigning an order was needed, although I am not sure it is completely done.