[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[sc-dev] [APPROVE?] SynthDef and non-float args

Ongoing discussion of the changing envelopes problem on the sc forum led me to a couple of class library extensions that I think would be very valuable to commit.

1. For people who are using the Control.names workaround to pass an envelope into a running synth, the following methods might help with creating the synth nodes:

+ Synth {
*playWithEnv { |defName, args, envArgs, target, addAction = \addToHead|
var synth, server, addNum, inTarget;
inTarget = target.asTarget;
server = inTarget.server;
addNum = addActions[addAction];
synth = this.basicNew(defName, server);
if((addNum < 2), { synth.group = inTarget; }, { synth.group = inTarget.group; });
server.listSendBundle(nil, synth.playWithEnvBundle(args, envArgs, inTarget, addNum));

playWithEnvBundle { |args, envArgs, inTarget, addNum|
var envMsg;
envMsg = [\n_setn, nodeID];
envArgs.pairsDo({ |key, env|
envMsg = envMsg ++ [key, env.asArray.size] ++ env.asArray;
^[ [\s_new, defName, nodeID, addNum, inTarget.nodeID] ++ args,

2. I finally understood what Philippe has been asking for: a way to maintain a library of synthdefs without having to hard code all the envelopes into every def. My stock answer has been to use Instr/Patch, but I've come to see that felix's system of specs and default controls actually takes some sophistication to understand, and it's not immediately approachable for new users.

So, this is a first draft at a happy medium: more flexibility than SynthDef, but close enough to the SynthDef syntax that it's easier to learn. It's also not limited to envelopes--any object that can't be passed as a UGen graph function argument can be put in the environment this way.

One musical possibility this raises is to sequence a pattern with randomly generated envelopes for every note. I know of at least one problem with this that this code doesn't solve, but I think I can fix it without too much trouble.

The class name is open for debate, It's not quite ready for prime time (couple of details to iron out), but I wanted to put it out there for comments and suggestions.



\sine2, { |out = 0, freq = 440, amp = 0.5|
   var sig;
   sig = SinOsc.ar(freq, 0, amp) * EnvGen.kr(~env, doneAction:2);
   Out.ar(out, [sig, sig])

\sine2, nil, [\env, Env.sine(1)]);
\sine2, nil, [\env, Env.sine(5)], latency:1);
\sine2, [\freq, 200, \amp, 0.3], [\env, Env.sine(1)]);


SynthDefLib {
var <environment, <name, ugenGraphFunc, rates, prependArgs,

*new { |name, ugenGraphFunc, rates, prependArgs|
var new;
name = name.asSymbol;
ugenGraphFunc.notNil.if({ // function supplied, making a new one
new = Environment.new, name, ugenGraphFunc, rates, prependArgs);
\synthdeflib, name, new);
}, {
^\synthdeflib, name) // no func supplied, retrieve from library

*play { |name, args, envArgs, target, addAction = \addToHead, latency|
var def;
(def = \synthdeflib, name)).notNil.if({
^def.play(args, envArgs, target, addAction, latency);

play { |args, envArgs, target, addAction = \addToHead, latency|
var inTarget, server, newSynth;
inTarget = target.asTarget;
server = inTarget.server;
(envArgs.isNil and: { synthDef.notNil }).if({
newSynth = Synth.basicNew(name, server);
server.sendBundle(latency, newSynth.newMsg(inTarget, args, addAction));
}, {
newSynth = this.asSynthDef(envArgs).play(inTarget, args, addAction);
}, {
(envArgs.notNil or: { synthDef.isNil }).if({
newSynth = Synth.basicNew(name, server);
server.sendBundle(latency, newSynth.newMsg(inTarget, args, addAction))

setEnvArgs { |envArgs|
(envArgs.size > 0).if({
envArgs.pairsDo({ |key, value|
environment.put(key, value)
synthDef = nil // purge cached def

asSynthDef { |envArgs|
(envArgs.notNil or: { synthDef.isNil }).if({
synthDef = SynthDef(name, ugenGraphFunc, rates, prependArgs);
}, {
^synthDef // for speed, use last synthdef if nothing changed


H. James Harkins /// dewdrop_world

"If attacked by a lion, thrust your arm down his throat.
This takes some practice." -- Cyril Connolly