Advancing patterns by hand (or: unfixed pattern duration)

This perennial question made me realize that there are really THREE ways to do pattern-based composition, where your next event or section is triggered manually, e.g. by user interaction.

1. "I want instantaneous, zero-latency transitions: when I hit the button on my controller, I want my playing event to immediately end and the next one to start. I don’t care about note durs / deltas at all."

This case is addressed in the linked question, and some other places. If you want full manual control simply pull notes from your event stream and play them yourself:

~stream = Pdef(\notes).asStream;

~stream.next(()).play; // next event
~stream.next(()).play; // next event
~stream.next(()).play; // next event


One gotcha: you must have (\sendGate, false) in your event, else Event:play will automatically end the Event after \dur beats.

2. "I want some Events to hold until I signal the pattern to move on, but I also want to use regular fixed-duration Events as well"

This requires a hacked / custom EventStreamPlayer. I want to note, very pragmatically, that this behavior sounds cool af but feels like it could be a nightmare to compose with and perform… It might be worth considering whether 1 or 3 are really more suitable.

3. "I want my pattern to wait for a signal it to move on, and I want it to advance in a musically meaningful way (e.g. on beat)

The idea here is that you want to keep your pattern running until you signal it, but you want the transition to the next pattern to happen e.g. on a beat interval, or after some sub-pattern finishes. This behavior is already implemented extensively by Pdef and EventPatternProxy. If you aren’t composing with “many Events” but really just want single monophonic-ish Event’s like #1 above, you can get this behavior easily by using Pmono or PmonoArtic.

This last case is more interesting to me, so I hacked around on an example to clarify how it can be done, and show off a few tricks that make it much more usable:

(

////////////////////////////////////////////////////////////////
// A synth for continuous playback
SynthDef(\monoSynth, {
var sig, env, lpf, res, detune, freq;

env = Env.adsr(0.1, 0.8, 0.2, 3).kr(gate:\gate.kr(1));
env = env + Env.perc(0.01, 0.3).kr(gate:Impulse.kr(2/3));

res 	= \res.kr(0.5).lag(0.25);
detune 	= \sawDetune.kr(0.05).lag(0.25);
lpf		= \lpf.kr(1200);
freq	= \freq.kr.lag(0.1);

lpf = env.linlin(
0, 1,
100, lpf
);
lpf = lpf.lag(0.01);

freq = freq * [0, detune].midiratio;

sig = LFSaw.ar(freq);
sig = RLPF.ar(sig, lpf, res);
sig = \amp.kr(1) * env * sig;

Out.ar(\out.kr(0), sig);

////////////////////////////////////////////////////////////////
// A simple delay
SynthDef(\delay, {
var sig, feed;

feed = -6.dbamp * LocalIn.ar(2);
feed = LeakDC.ar(feed);
feed = DelayC.ar(feed, 4, [5.08/4, 6/4]);
feed = feed - FreqShift.ar(feed, SinOsc.kr([1, 1.1]).range(0, 0.15));
feed = Rotate2.ar(feed[0], feed[1], -0.1);

sig = In.ar(\in.kr, 2);

LocalOut.ar(
DelayN.ar(sig, 1, [4/4, 1/4]) + feed
);

sig = sig + feed;

Out.ar(\out.kr(0), sig);

////////////////////////////////////////////////////////////////
// The most recently played event
~lastEvent = nil;

////////////////////////////////////////////////////////////////
// A base pattern for a mono synth (e.g. one event played continuously)
Pdef(\base, Pmono(
\monoSynth,
\dur, 		1/8,
\octave, 	4,
\callback,	{
|event|
event[\isPlaying] = true;	// bug in Pmono! this is never set.
~lastEvent = event			// capture our lastEvent
}.inEnvir						// this is executed with the Event as the envir, but we need ~lastEvent...
));

////////////////////////////////////////////////////////////////
// Four variations on the \base, with different notes and filter values
// Since \base is a Pmono, these only set values of the running \monoSynth.

Pdef(\a, Pbind(
\degree, 	[0, 3],
\lpf,		200
) <> Pdef(\base));

Pdef(\b, Pbind(
\lpf, 		Prand([800, 3200, 400], inf),
\degree, 	[-2, 2] + Prand([0, 0.2], inf),
) <> Pdef(\base));

Pdef(\c, Pbind(
\lpf, 		Prand([800, 5200, 700], inf),
\degree, 	[-3, 8] + Prand([0, 0.3], inf),
) <> Pdef(\base));

Pdef(\d, Pbind(
\lpf, 		Prand([800, 5200, 700], inf),
\degree, 	[1, 9] + Prand([-0.3, 0], inf),
) <> Pdef(\base));

////////////////////////////////////////////////////////////////
// Pdef(\section) will hold one of \a, \b, \c, \d - we'll swap them out
// when we want to change sections. Quant controls which beats we'll
// swap patterns on - so 2 means e.g. [108, 110, 112, ...]
Pdef(\section, nil).quant = 2;

////////////////////////////////////////////////////////////////
// Finally, we chain our section pattern with our delay via Pfx.
Pdef(\chain, Pfx(
Pdef(\section),
\delay
));

////////////////////////////////////////////////////////////////
// A simple routine to encapsulate stepping
// through each section \a...\d
~sequence = Routine({

// First, play the chain.
Pdef(\chain).play(quant:2);

loop {
[\a, \b, \c, \d].do {
|name|

// yield until we call next() on the routine to advance it again
"Playing %".format(name).postln;
Pdef(\section, Pdef(name)).yield;
}
}
});
)


With this setup, you have four sections (each of which is really just two monophonic notes), and you advance through them in your own sweet time using a Routine.

You capture the most recent Event played by your sequence in ~lastEvent, so you can set values on it manually (e.g. with code or with a controller). This is not the best way to control a running synth, but I think it illustrates something useful. You can control the above with code like:

// Start and advance the sequence.
~sequence.next();

// ~lastEvent can be used to set values on the synth also!
~lastEvent.set(\sawDetune, 0.4, \res, 0.6);


Some gotchas:

1. quant controls when playback starts and when patterns are swapped in after a change. It’s more powerful than it looks - the documentation for Pdef:quant and Quant have more details.
2. The overall granularity of WHEN your patterns swap out is still bound to the \dur of the events. If you specify an Event (dur: 4), playing it still “stops the world” for 4 beats. In general, you want \dur's that are significantly smaller than the quant you’re using.
3. However, note that Pdef(\a) has short \durs and is simply sending the same parameters to the already-running monophonic synth: I have no problems with granularity here, but I’ve still basically got one continuous & unchanging note playing.
4. If your music is not beat-gridded, and quant is not meaningful, keep in mind you can always have a quant of 0, and varying \dur values. This ends up being a kind of quantization factor - for example, if you had: Pmono(\synth, \dur, Pseq([ 4, Pseq([ 1/8 ], inf) ]), then the event will sustain a minimum of 4 beats (the first \dur is 4…), after which you’ll be able to transition with a granularity of 1/8 beats.
6 Likes

If you want full manual control simply pull notes from your event stream and play them yourself:

Hmm… I’m in a situation where I’m trying this on a Ppar, and things seem a little more complicated. The call to next() only seems to return a single event, even if two should be started simultaneously. Am I doing it wrong?

When Par yields two events at the same point in time, it yields the first with a \delta (e.g. time to wait until the next event) of 0, so the next one is played immediately. If you wanted to keep the “same-time-ness” of a Ppar, while still handling the exact timing between events manually, you should probably just keep pulling events as long as their deltas are 0.

Ok, suppose I have a list of events extracted from stream until \delta is nonzero.

I expected I could put that list in a Ppar again to play them all at once, but is that the right way to do it? The reason I’m asking is that when I tried just now, it froze, playing only the first of the events and I had to kill the interpreter (system didn’t respond to control-. anymore).

I guess Ppar is not correct since it expects a list of patterns, and not a list of events.

You should be able to do something like:

(
~r = Routine({
var events;
var nextEvent;

while {
nextEvent = myStream.yield(Event.parentEvents.delta));
nextEvent.notNil;
} {
if (nextEvent.delta > 0.00001) {    // some small threshold, or == 0
events.yield();
events = [];
}
}
});

~r.next.do(_.play}; // play all of them at once
~r.next.do(_.play);
~r.next.do(_.play);
~r.next.do(_.play);


This seems to work (not sure it 's the best possible approach):

~chord_player = EventStreamPlayer(Pseq(events).asStream, ()).play;

where events is the list of events extracted from the Ppar until \delta is non-zero

You can definitely do this, but if you know events are all at the same time (e.g. \delta == 0), then you can just call play yourself on all of them, all at once - events.do(_.play).

Ok, but do I then lose the opportunity to force stopping the playing before the event finishes?

In the code that I posted, the Event(s) only play when you pull the next events with ~r.next, and play them - there’s no clock playing, nothing advancing on its own. It’s up to you to drive fetching with next and playing, on a clock or on MIDI note or UI events - you code would effectively take the role of EventStreamPlayer.

If you mean controlling the playback of the Event’s you’ve play-ed - those only end automatically if their \sendGate key is true - the default. If you want them to play indefinitely until you stop them yourself, just set events.do(_.put(\sendGate, false)) before you play them.

Ok, thanks. I think it’s clear now. But along the way, I realized I could simplify my design to the point where I don’t need the manual pulling events from streams anymore.

Now when an event comes in, I call a function that calculates a new pattern and starts playing that pattern instead of the previously running one. For my use case, that should suffice for now.

If it becomes important later - it’s also not SO tough to keep track of the Event’s you play, and send e.g. \gate, 0 messages to them when you’re stopping the pattern that played them. That way, you could immediately end even in-progress events from a Pattern you’re stopping.

1 Like

Edit: 1. Damn tablet posted prematurely again. Maybe time to dump Firefox. 2. Maybe I misunderstood the question, but there’s a common requirement here, so…

If there’s one event, “wait until the event’s side effect (the synth) is finished before pulling the next event” is easy to define.

If there are multiple events at the same time point (delta 0), then at minimum there’s “wait until any one of the event’s synths is finished” or “wait until all of them have finished.” There may be other options too.

The “any” case would require firing on the first completion and discarding the remaining responders.

The “all” case works beautifully with a Condition. I’m not at the computer but something like:

// inside your loop
// also assuming event is pre-filled with the previous nonzero delta event
var events = Array.new, event;
var cond;
while {
event.notNil and: { event.delta <= 0 }
} {
event = stream.next(());
};
cond = Condition({ events.size > 0 });

events.do { |ev|
ev.play;
// here, set up the responder
// its function should do:
// events.remove(ev);
// cond.signal;
};

cond.wait;


It’s a quite useful pattern that isn’t obvious – maybe doesn’t apply to this specific case anymore though.

hjh

1 Like