TIRand not in range

I’ve got 2 TIRand expression that are keeping providing values just outside of the range.

This is the trigger (recursive)

trig1=LocalIn.ar(1,0)+Impulse.ar(0);

The code:

// .. buffer and cue points choice
bufidx1=TIRand.ar(0,nbuf-1,trig1); // OK
bufnum1=Latch.ar(BufRd.ar(1,bBufnum,bufidx1).asInteger,trig1); // Latch and asInteger as attempts to solve the issue, not sure it is needed.
ncue1=Latch.ar(BufRd.ar(1,bBufnum,nbuf+bufidx1),trig1).poll(poll,"n_cue 1");
cueidx10=TIRand.ar(0,ncue1-2,trig1).poll(poll,"cue start 1");
// KO : when ncue1 = 3, the TIRand range should be in [0,1] but I end up with a value of 2.
cueidx11=TIRand.ar(cueidx10+1,ncue1-1,trig1).poll(poll,"cue end 1");
 // KO: when ncue1 = 3, the TIRand range should be in [cueidx10,2] but I end up with a value of 3. 

The 2 last TIRand don’t behaves like expected.
I tried with some separated POC and it works fine, but there, no, it doesn’t work fine.

I tried to limit with max : cueidx10=TIRand.ar(0,ncue1-2,trig1).max(ncue1-2) but it doesn’t help.

Any idea ?

The buffer reads like this

0.0
1.0
4.0
3.0
0.0
4.0

This code snippet, sorry, is opaque to me. Out of context it’s hard to guess what’s actually happening.

In any case, ‘asInteger’ is a pure language-side operation, server-side you’d want ‘round’ as the server doesn’t know the Integer class. Then, the signal from LocalIn is delayed by one block. I cannot see how this all works together, but these two things could be responsible for unexpected behavior.

‘asInteger’ was just an attempt to solve my issue. I removed it.

This is a simplified version of the code.
It is about a “self-triggering” loop player. An initial trigger. The Synth computes a duration. Releases the first loop and starts the second one. The Synth computes a duration. for that one too. Upon release restart the loop 1 with new randomly selected parameters.

// loop 1
trig1=LocalIn.ar(1,0)+Impulse.ar(0); // loop1's trigger is an initial trigger + the release of the loop2

// .. buffer and cue points choice
Poll.ar(poll,elapsed,"*****");
bufidx1=TIRand.ar(0,nbuf-1,trig1).poll(poll,";1_buff index"); // randomly select the buffer index for the loop1
bufnum1=BufRd.ar(1,bBufnum,bufidx1); // bufnum the choosen buffer
ncue1=BufRd.ar(1,bBufnum,nbuf+bufidx1); // # cue points for that buffer
// vvv Those are the ones behaving strangely
cueidx10=TIRand.ar(0,ncue1-2,trig1).poll(poll,";1_cue start");  // randomly select a start cuepoint
cueidx11=TIRand.ar(cueidx10+1,ncue1-1,trig1).poll(poll,";1_cue end"); // randomly select a end cuepoint (after the start cuepoint)
// ^^^ Those are the ones behaving strangely

offset1=Latch.ar(BufRd.ar(1,bBufnum,2*nbuf+bufidx1),trig1); // offset for the buffer in the cuepoint array/buffer
start1=Latch.ar(BufRd.ar(1,cBufnum,offset1+cueidx10),trig1).poll(poll,";1_start"); // real start cuepoint
end1=Latch.ar(BufRd.ar(1,cBufnum,offset1+cueidx11),trig1).poll(poll,";1_end"); // real end cue point

// .. gate
delay1=Latch.ar(((end1-start1)/BufSampleRate.kr(bufnum1)),trig1).poll(poll,";1_delay"); // duration between start and end
rel1=DelayN.ar(trig1,\maxdelaytime.ir(10),delay1); // delay the trigger to get the release trigger
gate1=SetResetFF.ar(trig1,rel1); // build a gate to be used in SelectX

// loop 2
trig2=rel1; // loop2's trigger is loop1's release
// .. buffer and cue points choice
bufidx2=TIRand.ar(0,nbuf-1,trig2).poll(poll,";2_buff index");
bufnum2=BufRd.ar(1,bBufnum,bufidx2);
ncue2=BufRd.ar(1,bBufnum,nbuf+bufidx2);
// vvv Those are the ones behaving strangely
cueidx20=TIRand.ar(0,ncue2-2,trig2).poll(poll,";2_cue start");
cueidx21=TIRand.ar(cueidx20+1,ncue2-1,trig2).poll(poll,";2_cue end");
// ^^^ Those are the ones behaving strangely

offset2=Latch.ar(BufRd.ar(1,bBufnum,2*nbuf+bufidx2),trig2);
start2=Latch.ar(BufRd.ar(1,cBufnum,offset2+cueidx20),trig2).poll(poll,";2_start");
end2=Latch.ar(BufRd.ar(1,cBufnum,offset2+cueidx21),trig2).poll(poll,";2_end");

// .. gate
delay2=Latch.ar((end2-start2)/BufSampleRate.kr(bufnum2),trig2).poll(poll,";2_delay");// /BufRateScale.kr(bufnum2);
rel2=DelayN.ar(trig2,\maxdelaytime.ir,delay2);
gate2=SetResetFF.ar(trig2,rel2);
// gate2=(1-gate1)*EnvGen.kr(Env([0,0,1], [0.1, 0]));

Poll.ar(trig1,delay1, "----TRIG 1-- delay1");
Poll.ar(trig2,delay2, "----TRIG 2-- delay2");

// publish loop2's release, to be used as loop1's trigger in next iteration
LocalOut.ar([rel2]);

From time to time, one of the delays ‘delay1’ and ‘delay2’ gets negative.
The reason is that cueidx10/cueidx11 and/or cueidx20/cueidx20 are out-of-range.
The ‘hi’ limit of the TIRand is 2, and the TIRand outputs 3.
And then it is a cascade. The entire synth starts making non-sense.

Full code is here.

Phew, I’d need much more time to look through that and I haven’t (late here) –
maybe someone else can chime in …

My first guess is that it has to do with block calculation, you could check after server options set to blockSize 1 (and reboot).

From my notes on TIRand:

Note: Audio-rate inputs for lo and hi are currently broken in SuperCollider

So I guess you are running into that bug… You can try inserting A2K.kr for audio-rate inputs.

That’s why I didn’t post the full code initially :innocent:

I wrote it like this:

//bufidx1=TIRand.ar(0,nbuf-1,trig1)
bufidx1=K2A.ar(TIRand.kr(0,nbuf-1,trig1));

And although the random part seems to be done correctly, I end up loosing triggers. Probably due to the mix of Control and Audio rates… :pensive:

No positive impact. Neither on original version nor on the K2A version.

There is one thing I don’t get: how are initialized the variables ?
E.g.

cueidx20=K2A.ar(TIRand.kr(0,ncue2-2,trig2)).poll(poll,";2_cue start");

How does SC initialize the value of cueidx20 before trig2 occurs ? I expected it to be 0 or nil. But there is a value
“;2_cue start: 2
It seems that SC “play” the TIRand at the begining, ignoring default values.
Why ? Is it due to the bug I went into the other day ?

From cursory glance on your code (without understanding every detail), you may want to:

  • if you have an audio rate trigger, and want to use it for a kr signal, use T2K instead of A2K
  • if you read single values from a buffer, like looking up the cue numbers, better use Index instead of BufRd, also here I guess you can use Index.kr instead of .ar. I think all of your BufRd are probably easier as Index.
  • I think the kr extends to the other parts; do you need Latch.ar or just Latch.kr?
  • LocalIn / LocalOut stuff is always prone to errors, because of initialisation and delay. Easy to get it wrong and get glitches.

I hope that helps

1 Like

I’ll rewrite my code. With proper segregation between .ar and .kr

Solved.
Two causes I think:

  • a buggy TIRand.ar (I’ll try to make a mcve for this and report the bug on github if not already done)
  • Some confusion (read misconception) and unseeded use of Audio rates instead of Control Rates.

@Sciss, @dkmayer Thanks a lot for your support.

Search reveals this ticket: https://github.com/supercollider/supercollider/issues/1278

But it says it was closed with PR and tests (which apparently do not fix the issues). I re-opened the ticket.