Pbind to produce portamento on chords with different freqs

Hello,
I am trying to build a Pdef(Pbind) to produce chords that change frequency in time (portamento), each chord note going to a different frequency.
I tried this:

(
SynthDef(\portamentoPerc, {
|freq=440, endFreq=440, dur=1, portaDelai=0, portaDur=1, amp=0.4|
var sig, env, pitch;
env = EnvGen.kr(Env.perc(attackTime:0.01, releaseTime:dur-(dur/12)), doneAction:2);
pitch = EnvGen.kr(
Env(levels:[freq, freq, endFreq], times:[portaDelai, portaDur], curve:[\hold, \exp]));
sig = SinOsc.ar(freq:pitch, mul:env)*amp;
sig = Splay.ar(sig);
Out.ar(0, sig);
}).add;
)

Synth(\portamentoPerc);

(
Pdef(\portamentoSeq, Pbind(\instrument, \portamentoPerc,
\freq, Pseq([{Array.fill(size:10, function:{rrand(200,1000)})}],inf), // random freq
\endFreq, Pseq([Array.fill(size:10, function:{ rrand(Pkey(\freq)-20,Pkey(\freq)+20)})], inf),
// frequency end of portamento (relating to freq) - it does not work!
\dur, 2,
\portaDelai, Pkey(\dur)*0.1, //delay before portamento
\portaDur, Pkey(\dur)*0.7, // portamento duration
\amp, 0.1
));
)
c = Pdef(\portamentoSeq);
c.play;
c.pause;

But it does not work.
Do you think it’s possible?

The reason why this is not working is, that you cannot use the Pkey within a Function. As discussed here, Pfunc + specifying is the more flexible approach for taking over other key’s values within an Event.

The Pseq here is filled with an Array, but this will always be the same because the inner Function is evaluated before sequencing. If you want that you would not need the Pseq and could write

\freq, { rrand(200, 1000) } ! 10 // ! is for dup

But if you want different random chords, just write

\freq, Pfunc { { rrand(200, 1000) } ! 10 }

(
Pdef(
    \portamentoSeq, 
    Pbind(
        \instrument, \portamentoPerc,
        \freq, Pfunc { { rrand(200, 1000) } ! 10 }, // random start freqs
        \endFreq, { rrand(200, 500) } ! 10, // same end freqs
        \dur, 2,
        \portaDelai, Pkey(\dur)*0.1, //delay before portamento
        \portaDur, Pkey(\dur)*0.7, // portamento duration
        \amp, 0.05
    )
);
)

c = Pdef(\portamentoSeq);
c.trace.play;
c.pause;
1 Like

Your original idea with ± 20 Hz could be written like this:

(
Pdef(
    \portamentoSeq_2, 
    Pbind(
        \instrument, \portamentoPerc,
        \freq, Pfunc { { rrand(200, 1000) } ! 10 }, // random start freqs
        \endFreq, Pfunc { |e| { bilinrand(20) } ! 10 + e[\freq] }, 
        \dur, 2,
        \portaDelai, Pkey(\dur)*0.1, //delay before portamento
        \portaDur, Pkey(\dur)*0.7, // portamento duration
        \amp, 0.05
    )
);
)

c = Pdef(\portamentoSeq_2);
c.trace.play;
c.pause;

Note that in this case the portamento intervals in the higher register are much smaller than in the low, thus you might want to detune with midisteps and recalculate, or multiply frequencies.
E.g. here min just 4th below (* 0.75) and max just major third above (* 1.25)

(
Pdef(
    \portamentoSeq_2, 
    Pbind(
        \instrument, \portamentoPerc,
        \freq, Pfunc { { rrand(200, 1000) } ! 10 }, // random start freqs
        \endFreq, Pfunc { |e| { 1 + bilinrand(0.25) } ! 10 * e[\freq] }, 
        \dur, 2,
        \portaDelai, Pkey(\dur)*0.1, //delay before portamento
        \portaDur, Pkey(\dur)*0.7, // portamento duration
        \amp, 0.05
    )
);
)

c = Pdef(\portamentoSeq_2);
c.trace.play;
c.pause;
1 Like

Thank you very much, it works very well… and thanks also for .trace … it’s really very useful to realize what’s going on.
However, it seems that with .trace (c.trace.play), c.pause does not work:
ERROR: Message ‘pause’ not understood.

I will try to study the page on Pfunc that you sent me:

Thanks again :slight_smile:

Ah, yes, in the case of Pdef the inner Pbind should be traced, otherwise not the Pdef but the Ptrace is played:

(
Pdef(
    \portamentoSeq_2, 
    Pbind(
        \instrument, \portamentoPerc,
        \freq, Pfunc { { rrand(200, 1000) } ! 10 }, // random start freqs
        \endFreq, Pfunc { |e| { 1 + bilinrand(0.25) } ! 10 * e[\freq] }, 
        \dur, 2,
        \portaDelai, Pkey(\dur)*0.1, //delay before portamento
        \portaDur, Pkey(\dur)*0.7, // portamento duration
        \amp, 0.05
    ).trace
);
)

c = Pdef(\portamentoSeq_2);
c.play;
c.pause;

The habit of appending .trace comes from my main usage of rather taking a pure Pbind than a Pdef:

(
p = Pbind(
        \instrument, \portamentoPerc,
        \freq, Pfunc { { rrand(200, 1000) } ! 10 }, // random start freqs
        \endFreq, Pfunc { |e| { 1 + bilinrand(0.25) } ! 10 * e[\freq] }, 
        \dur, 2,
        \portaDelai, Pkey(\dur)*0.1, //delay before portamento
        \portaDur, Pkey(\dur)*0.7, // portamento duration
        \amp, 0.05
);
)

x = p.trace.play;
x.pause;

x.play;

Oui, je vois :slight_smile:

In your example (which works), there are no parentheses to Pfunc, only a pair of braces. Are parentheses not necessary?

Not if you don’t want to pass a second arg to Pfunc (the ResetFunc). There are several possibilities to save parenthesis like this, e.g. with if, while etc. However in certain situations it might be clearer to use them,
e.g. when applying methods, polymorphism convenience is sometimes cofusing

Pfunc { { |i| i-1 } ! 10 }.squared

-> a Punop

is totally equivalent to

(Pfunc { { |i| i-1 } ! 10 }).squared

-> a Punop


While here the order of operation is reversed

Pfunc ({ { |i| i-1 } ! 10 }.squared)

-> a Pfunc


The result however is the same as the operations can be exchanged

(Pfunc { { |i| i-1 } ! 10 }).squared.iter // the result of the Pfunc stream is squared
-> an UnaryOpStream

(Pfunc { { |i| i-1 } ! 10 }).squared.iter.next  
-> [ 1, 0, 1, 4, 9, 16, 25, 36, 49, 64 ]


(Pfunc ({ { |i| i-1 } ! 10 }.squared)).iter // Pfunc's func is already a UnaryOpFunction (squared)
-> a FuncStream

(Pfunc ({ { |i| i-1 } ! 10 }.squared)).iter.next  
-> [ 1, 0, 1, 4, 9, 16, 25, 36, 49, 64 ]
1 Like

thanks for the clarification.
I’m still pretty beginner and do not understand everything, but thanks to your help, I advance little by little :slight_smile:

I would like to indicate that my problem is solved, but I can not find the small checkbox in this post.
Where is he?