Arguments inside a Tdef?

I’m working with Tdefs a bit and trying to get flexible using them in live coding. I haven’t been able to get my head around using arguments inside a Tdef in order to control it from outside. In the following example I’ve got a Synth making Grains, and a “control” Tdef controlling these grains. I’d then like to generate a number of these control Tdefs and feed them different values. But at the moment I can’t even get the Tdef I’m working with to accept arguments. Not sure if this is a bug or if I’m the problem. Any help appreciated!

Cheers,
Jordan

(
SynthDef(\grain, {
        var env, sig;

        env = Env.sine(\dur.kr(1) * \overlap.kr(2)).ar(Done.freeSelf);
        sig = SinOsc.ar(\freq.kr(440) * 2) * env * Rand(200, 2000);
        sig = SinOsc.ar(\freq.kr(440) + sig + [0, 1]);
        sig = sig.tanh;
        sig = sig * env * \amp.kr(-10.dbamp);
        //sig = Pan2.ar(sig, \pan.kr(0));
        Out.ar(\out.kr(0), sig);

}).add;
)
// this works
(
Tdef(\a, {

    loop {
        rrand(10, 80).do {
        Synth(\grain, [freq: (1..15).choose * 50, amp: -20.dbamp, overlap: 2]);
        0.1.wait;
                };
        rrand(5, 7).wait;

    };
}).play;
)

// this doesn't work
(
Tdef(\a, {
|freq = 50|
    loop {
        rrand(10, 80).do {
        Synth(\grain, [freq: (1..15).choose * freq, amp: -20.dbamp, overlap: 2]);
        0.1.wait;
                };
        rrand(5, 7).wait;

    };
}).play;
)

you can use a global variable to pass values in:

(
~freq=50;
Tdef(\a, {
    loop {
        rrand(10, 80).do {
        Synth(\grain, [freq: (1..15).choose * ~freq, amp: -20.dbamp, overlap: 2]);
        0.1.wait;
                };
        rrand(5, 7).wait;

    };
}).play;
)
~freq=60;

When you play a Tdef or Task or Routine, the argument is reserved for the clock time. You can’t use it for another purpose.

semiquaver’s recommendation is good.

hjh

The first argument to your Tdef is an Event. This can be set with Tdef:envir_ or by setting individual keys via Tdef:set.

Tdef(\waiter, {
  |e|
  inf.do {
    "Waiting".postln;
    e[\dur].wait;
  }
}).set(\dur, 1)

Ha! That’s what I get for not RTFM-ing. I got that one wrong.

Routines and Tasks do use the argument for time:

Task { |a| [a, thisThread.beats].postln }.play;
-> [ 1502.626000243, 1502.626000243 ]

I don’t use Tdef in my own workflow, so I guessed it was parallel to Task, but it isn’t.

I found, however, that it isn’t supported to replace the Tdef environment using envir_ while the Tdef is playing. This might be a bug, or it might be an unavoidable limitation (perhaps Tdef should throw an error if you call envir_ while it’s playing).

Tdef(\x, { |event|
	loop {
		event.postln;
		1.0.wait;
	}
}).play;

// prints:
(  )
(  )
...

Tdef(\x).envir = (a: 1);

(  )  // huh? where's 'a: 1'?
(  )

^^ The problem here is that event is populated once, at the beginning of the function, and never updated again. Even though you set the object’s environment, the new information doesn’t make it into the function.

If you try to update, according to “Streams’ input values (inval, inevent)”, then you get the time instead of the new event.

Tdef(\x, { |event|
	loop {
		event.postln;
		event = 1.0.wait;
	}
}).play;

(  )
2059.0  // oh... now you don't have the envir anymore
2060.0

set should be fine.

hjh

Thanks everyone for the help. This question sent me down quite a rabbit hole, I ended up (finally) using a custom event function instead of arguments directly inside the Tdef to give me more control. Here’s the finished code if anyone’s interested. Would also be keen for feedback if anyone thinks there are better ways of doing this!


(
SynthDef(\grain, {
        var env, sig;

        env = Env.sine(\dur.kr(1) * \overlap.kr(2)).ar(Done.freeSelf);
        sig = SinOsc.ar(\freq.kr(440) * Rand(2, 5).round(0.5)) * env * Rand(200, 2000);
        sig = SinOsc.ar(\freq.kr(440)+ sig + [0, 1]);
        sig = sig.tanh;
        sig = sig * env * \amp.kr(-10.dbamp);
        //sig = Pan2.ar(sig, \pan.kr(0));
        Out.ar(\out.kr(0), sig);

}).add;

SynthDef(\fx, {
    var sig;

    sig = In.ar(\in.kr(0), 2);
    sig = sig + (PitchShift.ar(sig, 0.2, 2) * -10.dbamp);

    sig = sig + (NHHall.ar(sig, \rt60.kr(4, 2)) * -5.dbamp);
    sig = HPF.ar(sig, 30);
    sig = LPF.ar(sig, 8000);
    ReplaceOut.ar(\out.kr(0), sig);


}).add;

)

x = Synth.tail(nil, \fx);




~howmany = 10;

(
~freqs = Array.fill(~howmany, { exprand(30, 500)});
~overlaps = Array.fill(~howmany, {exprand(1, 5)});
~waitTimes = Array.fill(~howmany, {exprand(0.1, 0.5)});
~amps = Array.fill(~howmany, {|i| (rrand(-18, -25) - i).dbamp});
)

(
~gran_texture = {
    |freq, amp, overlap, waitTime|
    Routine({
        rrand(10, 80).do {
            Synth(\grain, [
                freq: (1..15).choose * freq,
                amp: amp,
                overlap: overlap
            ]);
        waitTime.wait;
        }
    }).play;
}
)

(
~tdefs = ~howmany.collect { |i|
    Tdef("s%".format(i).asSymbol, {
        loop {
         ~gran_texture.value(~freqs[i], ~amps[i], ~overlaps[i], ~waitTimes[i]);
        rrand(5, 7).wait;
        };
    });

};
)

(
~howmany.do {
    |i|
    Tdef("s%".format(i).asSymbol).play;
}
)