Doing something "every x"

I’m trying to figure out how to do something every x happens. Like transpose a scale up an octave every 4 bars. or Pstutter a \dur every time “0.5” is chosedn from a Prand.

So for example I though this would work to make the last beat or a 7 beat sound quadruple in Frequency:

(
t = TempoClock(120/60).permanent_(true);
t.tempo_(120/60);
t.schedAbs(t.nextBar, {t.beatsPerBar_(7)});
~metro = {
	("beat :" +((t.beats.floor)%t.beatsPerBar+1)).postln;
	("bar :" +(t.bar)).postln;
		"".postln;
		1;
	};
)


(
t.schedAbs(t.nextBar, {
	~metro.value;
})
)

(
SynthDef(\hh, {arg freq=220, out=0, amp=1.0, res=0.5, pan=0;
		var sig, env;
		sig = WhiteNoise.ar(1);
		sig = RHPF.ar(sig, freq*40*Line.ar(0.5, 1, 0.1), res);
	    env = EnvGen.ar(Env.perc(0.001, 0.1));
		sig = (sig * env * amp * 2).softclip;
		sig = Pan2.ar(sig, pan);
		Out.ar(out, sig);
	}).add;
)

(
c = t.beatInBar.round.postln %7;
Pbind(
	\instrument, \hh,
	\freq, 220,
	\dur, Pif(c == 0, Pkey(\freq)*4, 1),
).play;
)

But it doesn’t work. I am not attached to this weird thing I got going on here, and expect there to be some super simple Pevery, or something of the sort I just don’t know about yet…but I have been looking for a while and can’t find one.

If you’ve ever used tidal cycles, you’d probably agree that doing a function every x cycles can make algorithmic composition super dynamic and awesome very easily. Since tidal is just sending OSC to SC, I imagine there’s a way to do this pretty easily.

OK, let’s take this one apart step by step.

(
c = t.beatInBar.round.postln %7;

Here, you’re asking the clock for its beat within the bar, right now, and assigning this specific number to c.

It looks like you’re expecting c to be a function that will be evaluated every time you use c anywhere.

SC does not work this way.

If you wanted c to be a function, then you have to write it as a function. If you don’t write it as a function, then it will be a concrete, unchanging value.

(Tidal is in Haskell, and in Haskell, assignment statements do create functions [oversimplifying]. Perhaps this is where you got the idea that SC variables will behave functionally. But it simply isn’t the case. It will be important for you to get out of this habit.)

Pbind(
	\instrument, \hh,
	\freq, 220,
	\dur, Pif(c == 0, Pkey(\freq)*4, 1),
).play;
)

… and this will evaluate the condition for the moment in time when c was assigned, but not for “now”.

Then, the other tricky thing is that == is always a “right-now” operation. It isn’t lazy. In Pif, == is not meaningful unless it’s lazy.

The development branch includes a lazy == operator. So, if you’re using a development version, you should be able to do it like this:

(
c = Pfunc { t.beatInBar.round.postln % 7 };

Pbind(
	\instrument, \hh,
	\freq, 220,
	\dur, Pif(c |==| 0, Pkey(\freq)*4, 1),
).play;
)

(Although I expect you don’t really want to \dur to be potentially 880 beats.)

Unfortunately |==| didn’t make it into 3.11.x. In the meantime: Pbinop('==', c, 0)

I guess |==| will go into 3.12…? Nobody seems to know. It was added in September last year… unfortunate that it slipped through the cracks for release.

Pstutter a \dur every time “0.5” is chosen from a Prand

If you mean to choose the next value based on the condition, that’s Pclutch. (Admittedly it’s quite hard to find these!)

hjh

Patterns by way of Events have a nice model for notes but don’t really provide a readymade model for musical structures. If you don’t use Patterns then the logic for musical structures can be a bit more intuitive but you lose a lot of other nice capabilities of Patterns. I don’t think it is possible to do things in bars in a single Pbind. You will have to become familiar with Patterns that can provide outer logic like Plazy and Pspawner where you can do what amounts to nested looping.

Here are two examples - the first one transposes up an octave every 4 bars and the second “stutters” whenever the dur is “0.5”.

There may be terser way to do this but for myself I haven’t been able to figure that out.

(
Pdef(\a, {
    
    var octave = 3;
    var bar = 0;
    var beatsperbar = 4;
    
    Plazy({
        
        if (bar.mod(4) == 0) {
            octave = octave + 1;
        };
        bar = bar + 1;
        
        Pbind(
            \degree, Pwhite(0, 4),
            \octave, octave,
            \dur, Prand([0.5, 1, 0.25], inf)
        ).finDur(beatsperbar)
        
    }).repeat
})
)

Pdef(\a).play
Pdef(\a).stop
(
Pdef(\b, 
    
    Pspawner({|sp|
        
        var pattern = Pbind(
            \degree, Pwhite(0, 4),
            \dur, Prand([1, 0.5, 0.25, 2], inf)
        ).asStream;
        
        inf.do({|i|
            
            var evt = pattern.next(Event.default);
            var dur = evt[\dur];
            
            if (dur == 0.5) {
                sp.seq(
                    Pbind(\dur, 0.125, \legato, 0.1).finDur(dur) <> evt
                )
            }{
                sp.seq(
                    Pbind().finDur(dur) <> evt
                )
            }
        })
    })
)
)

Pdef(\b).play
Pdef(\b).stop
1 Like

Here’s a different approach (similar to what I use to implement a version of “every” in my Bacalao) live coding stuff). It is not exactly the same as TidalCycles’ every (as @droptableuser mentions it’s complicated with SC’s Events in general), but under the right circumstances it can behave similarly (although the main and alternate pattern can get out of sync if they’re not of the same duration, e.g. a bar).

TempoClock.default.tempo = 2
a = Pbind(\degree, Pseq((0..3)), \dur, 0.25)
b = Pbind(\degree, Pseq((7..4)), \dur, 0.5)

p = Pn(Pswitch1([a, b], Pfunc{ thisThread.clock.bar % 4 == 0 })).play(quant: 4)
p.stop

Glen.

Very interesting. I thought he == operator is only in the development version? @jamshark70 Perhaps I misunderstood. That’s pretty much a simpler, better version of what I was trying to do.

@droptableuser Thank you also, I will try playing with that tonight.

That’s exactly what I said… It’s in development but not in an official 3.x release yet.

hjh

I might not understand how releases or git works… I am on “HEAD” 3.11.2 “9a34118” on Windows 10, and the above code is working for me with the double equals. Again I apologize if I misunderstand.

== has been in SC forever. It’s the “right-now” equality check. You can use it with numbers, strings, arrays but it does not work the way you expect with patterns or UGens.

|==| is a new operator that composes meaningfully with patterns and UGens. This is the one that hasn’t been officially released yet.

hjh

I see, sorry for my confusion, and thanks everyone all for the different methods.

According to the changelog for 3.12, |==| has gone into 3.12.x. :+1:

hjh

1 Like