Live Coding, Pdef, Quantization and timming

Hello Synths,

I am trying to build my first live coding set and finding some issues. I am building a music with tempo = 64bpm, 3/4, and which is completely within the quadrature of 16 bars (48 beats). The main problem is that I would like to set a Quant which allows me to trigger patterns that starts on the next quadrature, independently of where I am in time grid. I have tried to build the following metronome, which visually seems to work. When I trigger the patterns

   (
   t = TempoClock.new((64)/60);
   t.schedAbs(t.nextBar, {t.beatsPerBar_(3)});
   ~postBeat = {postcs(["quadrature---------------",t.beats.div(16*3)]);1; postcs(["beats---------",t.beats]);1; postcs(["bar",t.bar]); 1;};
   t.schedAbs(t.nextBar, {~postBeat.value});
   )

Pdef(\fl).play(t, quant:4.0)
Pdef(\fl).play(t, quant:(48- (((t.beats - (t.beats.div(48) * 48))**2).sqrt)).round )

Pdef(\fl, Pbind(\instrument, \fl, \type, \midi, \midiout, ~b2, \db, -3, \degree, Pseq((1..48), inf) ,\dur, 1))

Apparently, the quadrature quantization seems to work visually when I post it:

((48- (((t.beats - (t.beats.div(48) * 48))**2).sqrt)).round).postln

But when I try it within the Pdef it does not work… What Is going on?

Any easier idea about how to do this ? Are there any quarks for this ? Specially one that allows me to see the time grid and the patterns there?

1 Like

I don’t know a GUI, but, set your TempoClock’s beatsPerBar to 3 and then use negative quant for a number of bars.

clock.setMeterAtBeat(3, clock.nextBar);

p = Pbind(...).play(quant: -16);

hjh

Thanks a lot!

Why exactly
t.schedAbs(t.nextBar, {t.beatsPerBar = 3});
does not work in this case?

Unfortunately I did not work… I’ve tried it in many different ways, but it did not work… Could it be a bug with Pdef? I’ve seen some people avoiding it.

What I am trying to do exactly is this:

(
//midi initialization
MIDIClient.init;
~b1 = MIDIOut(0);
~b2 = MIDIOut(1);

//converts a midi file into array of notes and durantions
~nativeMidi = SimpleMIDIFile.read(PathName(thisProcess.nowExecutingPath).parentPath +/+ "someMidiFile.MID");

~nativeMidi = ~nativeMidi.generatePatternSeqs;
~melody = ~nativeMidi.flatten.collect({ arg item, i; item[0] });
~meldur = ~nativeMidi.flatten.collect({ arg item, i; item[1] });


//Creates the metronome
t = TempoClock.new((60)/60);
//t.schedAbs(t.nextBar, {t.beatsPerBar = 3});
t.setMeterAtBeat(3, t.nextBar);
~postBeat = {postcs(["quadrature---------------",t.beats.div(16*3)]);1; postcs(["beats---------",t.beats]);1; postcs(["bars",t.bar]); 1;};
t.schedAbs(t.nextBar, {~postBeat.value});

t.beatsPerBar.postln;

//Defines the basic core of the Pbinds
Pdef(\fl, Pbind(\instrument, \fl, \type, \midi, \midiout, ~b1, \db, -3, \midinote, Pseq(~melodia, inf) ,\dur, Pseq(~meldur, inf)));

Pdef(\cl, Pbind(\instrument, \fl, \type, \midi, \midiout, ~b2, \db, -3, \midinote, Pseq(~melody, inf) ,\dur, Pseq(~meldur, inf)));

//Pdef(\fl).play(t, quant:[0, 0.0]);
//Pdef(\cl).play(t, quant:[0, 0.0]);

//Plays the Pbins
Pdef(\fl).play(t, quant:0);
Pdef(\cl).play(t, quant:-16);
)

I am running the full block of code at once. As I am sending midi from SC to Ableton, I am not even boot the server. Is there an issue with the order of execution within the block of code?

I have also checked the full duration of ~meldur and it lasts 48s, consistently with the proportions of bars and beats.

What is going on ??

Simplifying the example while keeping the core of it:

(
t = TempoClock.new;
t.setMeterAtBeat(3, t.nextBar.debug("setting meter at"));

Pdef(\x, Pbind(\type, \rest, \beat, Pfuncn({ thisThread.clock.beats.postln }, 3), \dur, 1)).play(t, quant: -16);
)

Prints:

setting meter at: 0.0
-> Pdef(‘x’)
0.0
1.0
2.0

… which is correct. Since you’re creating the clock and immediately scheduling, the clock beats == 0. 0 is a 16-bar boundary.

I can’t quite see what your code is doing, that you don’t want. Can you be more specific?

hjh

The main goal is to extract a main melody from a midi file and transform it using list patterns.

The extraction works fine and in this case the whole melody is in 3/4 with a total duration of 48 beats ( or 16 bars).

My first option is to schedule many instruments which will begin at multiples of 48 beats, e.g. Pdef(\fl) beggins at beat 0
Pdef(\cl) beggins at beat 48
Pdef(\bs) beggins at beat 482
Pdef(\vi) beggins at beat 48
3. … and so on

My second option is to do the last thing on the fly: idependently of when I evaluate the code, the new instrument will always beggins at multiples of 48 beats but without any offset to the 0 reference beat. It is just the same of playing a clip on the fly in ableton live.

Thanks a lot!!!

OK, so, this is something we can talk about. (I have to admit, “begin at multiples of 48 beats but without any offset to the 0 reference beat” makes no sense to me… there must always be a reference beat. If there’s no reference, then quant means nothing, and neither do your quadratures.)

Taking a guess here – if you have a 48-beat clip in Ableton, and you put the cursor at beat 12 and start playing, then it starts playing right now, but in the middle of the clip.

That detail might seem too obvious to mention, but it’s the key to the whole thing.

In SC, to my knowledge, there is no built-in way to “fast forward” a pattern to some point in the middle. But it should be possible to write one.

Time is short for me this morning – I don’t have time to write code at the moment, maybe later. But I would suggest a Prout where the function body does something like this:

  1. What beat is it now, relative to the last 16-bar boundary? Call this pos.

  2. Get events from the pattern’s stream, adding up delta until the total is >= pos.

  3. This total is the onset time of the first note at or after pos. So, yield a rest event (dur: Rest(pos - total)).

  4. Then embed the remainder of the stream: inval = stream.embedInStream(inval). That should take you out to the next 48-beat boundary.

hjh

There is a fastForward method on Stream - I think this is used in JITLib for Pdef and Pdefn when you use the outset property. You should already be able to do this via e.g. Pdef(\abletone).outset = 1, and then giving the Pdef a value. This would give you a 1 second slop /after/ the beat where re-setting your Pdef would be in sync. If you can, it would be good to leverage this before re-inventing the wheel.

Oh, good tip. That makes it easy, then:

(
~alignPattern = { |pattern, cycle = 48|
	Prout { |inval|
		var clock = thisThread.clock,
		phaseNow = ((clock.beats - clock.baseBarBeat) % cycle),
		stream = pattern.asStream,
		remaining = stream.fastForward(phaseNow, 0.001, inval);
		// stream is now ready for the first event after 'phaseNow'
		// but there is probably some time remaining; so, rest
		inval = Event.silent(remaining).yield;
		// return value (next inval) may be given back to the caller
		stream.embedInStream(inval)
	}
};
)

// test it
(
p = ~alignPattern.(
	Pbind(
		\dur, 1,
		\a, Pn(Pseries(0, 1, 4), 1),
		\beats, Pfunc { thisThread.beats },
		\beatInBar, Pfunc { thisThread.clock.beatInBar }
	).trace,
	4
);
)

// you can do this many times
// and the relationship between \beats and \a
// will be consistent
q = p.play;

// e.g. one run
-> an EventStreamPlayer
( 'beats': 618.162651085, 'a': 0, 'dur': 1, 'beatInBar': 2.162651085 )
( 'beats': 618.162651085, 'a': 1, 'dur': 1, 'beatInBar': 2.162651085 )
( 'beats': 618.162651085, 'a': 2, 'dur': 1, 'beatInBar': 2.162651085 )
( 'beats': 619.0, 'a': 3, 'dur': 1, 'beatInBar': 3.0 )

// another
-> an EventStreamPlayer
( 'beats': 620.873896849, 'a': 0, 'dur': 1, 'beatInBar': 0.87389684900006 )
( 'beats': 621.0, 'a': 1, 'dur': 1, 'beatInBar': 1.0 )
( 'beats': 622.0, 'a': 2, 'dur': 1, 'beatInBar': 2.0 )
( 'beats': 623.0, 'a': 3, 'dur': 1, 'beatInBar': 3.0 )


One additional observation:

If the current ‘phase’ (beat within the cycle) is after the last event in the pattern, then fastForward will skip forward through an entire cycle. That looks like a bug… but on the other hand, the only way to know if the current phase is after that last event is to iterate over all of the events (to total up the deltas). From that standpoint, the behavior is correct.

You could perhaps optimize it by saying “if the next cycle boundary is less than, oh, 0.1 beats away, then there’s a very low chance of another event happening before the boundary, in which case you could skip the fast-forward.” But I don’t think it’s really necessary.

hjh

Just to clarify what I wanted. First I evaluate this pattern:

Pdef(\bz, Pbind(\amp, 0.125, \dur, 1/4, \degree, Pseq((1..16), inf))).play

And I would like to evaluate the following at any time, so that it would start to be played exactly when the \bz start its cycle.

Pdef(\qt, Pbind(\amp, 0.125, \dur, 1/4,\octave, 3, \degree,Pseq((1..16), inf))).play;

Therefore, both patterns would be paralell melodies separated by one octave.

I have been trying to realize how .outset work, but nothing that I’ve tried worked properly. I was setting the arguments within the play method for patterns, but with no result…

I’ve found in the EventPatternProxy some info about how to use it, but there is nothing in the Quant helpfile. Does it really works ?

b) using as EventStreamPlayer

.play(argClock, protoEvent, quant, doReset: false)

starts the EventPatternProxy and creates a player. if you want to play multiple instances, use -fork.

Arguments:

argClock play on a certain clock, e.g. a TempoClock.
protoEvent an event to be used as a first input to the chain
quant can be an array of [quant, phase, offset, outset], or an instance of Quant.
doReset if set to true, play will restart the stream if already running (a Boolean).

Pardon me for being a little confused about your requirement… first you had said you wanted something that sounded to me like ‘quant’, and then you said it didn’t work… so I thought you wanted something different from ‘quant’ and I took a guess as to what that might be.

But the behavior you’re describing is what ‘quant’ already does :wink: So I don’t understand what “it didn’t work” means.

(Based on your example, outset is not what you want, btw.)

Anyway… the typical SC solution to this problem is to start both patterns with reference to a common time grid (maintained in the clock).

// quant: 4 bc 16 notes * 1/4 beat/note = 4 beats
Pdef(\bz, Pbind(\amp, 0.125, \dur, 1/4, \degree, Pseq((1..16), inf))).play(quant: 4);

Pdef(\qt, Pbind(\amp, 0.125, \dur, 1/4,\octave, 3, \degree,Pseq((1..16), inf))).play(quant: 4);

In this example, when you run the Pdef(\qt…) line, at any time, it waits for the next 4-beat barline. Pdef(\bz) was already playing with reference to 4-beat barlines, so they are in sync – “parallel melodies separated by [two] octave[s].”

hjh

Sorry for the mess, actually there are two questions:
1 - Evaluating two code lines at different user time in order to get pattern execution synced.
2 - Evaluating two code lines simultaneously in order to get pattern execution synced.

I moved to the second because it is easier, but I am still having lots of problem with it.

I am partially achieving the sound result that I want when I evaluate this two lines simultaneously:

(
a = Pdef(\bz, Pbind(\amp, 0.125, \dur, 1/4, \degree, Pseq((1..16), inf))).play(quant: 0);

b = Pdef(\qt, Pbind(\amp, 0.125, \dur, 1/4,\octave, 3, \degree,Pseq((1..16), inf))).play(quant: 4);
)

But when I stop them and then re-evaluate, they sometimes get out of sync (no more paralel octave, but phased canon)…

I’ve stopped them using the hard kill (cmd+ .) and with the following:
(
a.stop;
b.stop;
)

With both all the methods I get random results (sometimes octaves synced sometimes not)… I’ve tried rebooting the interpreter, rebooting the server, but I was all useless… What I am missing here? Is this asynchrony related to the relation beetween Quant and the default tempo clock that is running on the background ?

Thanks a lot!!!

I don’t understand how those are starting at the same time at all. (Well, it’s possible if you always start a new TempoClock at that time… but that would ensure sync only the first time, on that clock. Subsequent runs on the same clock would be out of sync.)

quant feeds into the clock’s nextTimeOnGrid method, so we can see the behavior of the two different quant values this way.

(
t = TempoClock.default;

"Quant 0 = %; Quant 4 = %\n".postf(t.nextTimeOnGrid(0), t.nextTimeOnGrid(4));
)

If you run this repeatedly, you’ll find that quant 0 is some floating-point value with a lot of digits after the point, and quant 4 is always an exact multiple of 4. These are the pattern onset times. As they are not the same number, it means they will not start at the same time.

If you want them to start at the same time, don’t use different quant values. It’s like going to the fruit market, asking for an apple and an orange, and then saying “but these don’t taste alike.”

But when I stop them and then re-evaluate, they sometimes get out of sync (no more paralel octave, but phased canon)…

This is also expected behavior with Pdef.

If you write a pattern (Pbind) directly and play it, e.g. p = Pbind(...); q = p.play(quant: 4), then p.play creates a new stream from the pattern, which naturally starts from the beginning.

Pdef maintains its own stream – with Pdef, you should think of stop as pause, and then play is more like resume.

If you’re quantizing the start time, then you should also reset at play time.

Pdef(\qt).play(doReset: true, quant: 4);

Is this asynchrony related to the relation beetween Quant and the default tempo clock that is running on the background ?

No. Every clock has its own independent metric grid. If you ask clock B for a nextTimeOnGrid (which is what play with quant does), it doesn’t know or care what clock B A is doing. (fixed typo there)

Not resetting the Pdefs is a big problem – make sure you do that. And use the same quant value.

hjh

1 Like

I dont want them to start at the same time, I want them to be synced to the 4 bars (16 beats) grid. I want the second line to start playing after one whole cycle of execution of Pseq((1…16), inf)) in the first line.

If I still could ask the second question… If I want to evaluate the second Pdef code line (at any point in time) – while the first Pdef code line is running – and get both Pdef synced related to the beggining of the cycle of the first line’s Pseq((1…16)), what do I need to do?

I want to be free to evaluate the second Pdef line code at any point in the marked red line in the drawing.

I tried it in many ways using .reset and doReset, but could figure out how it should be…

Thanks again!

The behavior you’re describing is exactly what quant is supposed to do. It’s exactly the way I have been using quant for over 15 years.

When you specify, for instance, quant: 4, the meaning of this is: no matter what time you hit the command to play the pattern (in your terminology, “on-the-fly second line evaluation”), it will wait until the next 4-beat boundary to begin playing (“second line synced”).

For simplicity, let’s go to your 4-beat example from post 12, but modified as suggested above (same quant for all patterns, and using doReset: true).

Pdef(\a, Pbind(\degree, Pn(Pseries(0, 1, 16), inf), \dur, 0.25)).play(doReset: true, quant: 4);

// run this **any time** after you hear \a
Pdef(\b, Pbind(\degree, Pn(Pseries(0, 1, 16), inf), \dur, 0.25, \octave, 4)).play(doReset: true, quant: 4);

// do these at different times
// i.e. create the worst-case scenario:
// their streams are stopped at different time points
Pdef(\a).stop;
Pdef(\b).stop;

// restart with quant
Pdef(\a).play(doReset: true, quant: 4);

// again, run this later
Pdef(\b).play(doReset: true, quant: 4);

When I do the above, I get precise sync, exactly as you describe.

Can you try the above exactly as written and confirm the result? (I wonder if we have a situation here where I’m saying “do it this way,” and you’re saying “I’m doing something slightly different and getting a different result” – which would be not quite surprising.)

The one thing I can imagine you might mean is, maybe you want \a to start right now (without reference to any time grid) and \b to sync with that. One way to do that would be to reset the time grid itself.

(
TempoClock.setMeterAtBeat(TempoClock.beatsPerBar, TempoClock.beats.debug("resetting time base to"));
Pdef(\a, Pbind(\degree, Pn(Pseries(0, 1, 16), inf), \dur, 0.25)).play(doReset: true, quant: 4);
)

// later, any time
Pdef(\b, Pbind(\degree, Pn(Pseries(0, 1, 16), inf), \dur, 0.25, \octave, 4)).play(doReset: true, quant: 4);

Be careful that nothing else is playing at the time you setMeterAtBeat. I personally don’t favor this strategy because processes could get out of sync if you accidentally reset the time base while something is playing – I think it’s safer to keep the clock’s meter as it is, and conform the processes to the existing bar line (rather than try to conform the barline to the processes). But the above should be OK if done with care.

hjh

1 Like

Thanks a lot!!! I will do more tests here.