Pattern that lets you retrigger a pattern from another pattern

I want to retrigger a sequence of notes Pseq([0,5,3,12],inf) and its durs Pseq([1,0.5,0.25,0.25],inf)
with another pattern used as a trigger. When the note pattern is triggered it should plays the “note” and “dur” sequence from the start. Here is a reference

I tried Pn together with Pgate but that isn’t right. It keeps on playing one note before moving on to the next one in the sequence

I think the key here is “when the note pattern is triggered”… I’m not sure exactly what you mean. (I confess that I don’t have access to YouTube on this tablet… maybe it’s explained in the video.)

Do you mean, there is a high level rhythm, and every time there’s an event at this level, it plays the note pattern? (In this case, what happens if the high-level event’s duration is shorter than the note pattern? Truncate, or overlap?)

But that idea is active – there’s a rhythm driving it – which is where I get confused by the passive voice “when it’s triggered.” “To be triggered” is analog-style or Max-style thinking; SC runs on different principles.

Pspawner is probably the way to go here, because the child pattern is under control of the main pattern (hence, can be stopped). Pspawner essentially runs a Routine for higher level control. There’s also Pspawn which does more or less the same thing, but with events rather than routine-style imperative code. I think Pspawn is a bit idiosyncratic though, probably not as clear.

hjh

Sorry. Having a hard time explaining something I don’t know how to do. This I think is almost right but
the pattern gets stuck on one note before it is “retriggered” and proceeds to the next note. It would be nice if it plays through the sequence of notes and the “trigger” start the sequence from the beginning.
Hope this makes more sense…

> (
> Pdef(\part, Pdef(\part1,
> 	Ppar([
> 	Pbind(
> 		\instrument, \default,
> 		\freq, 40,
> 			// \buf, d['808bd'][1],
> 		\dur, Pseq([1/8,1/2,1/8],inf),
> 	   \amp,1,
> 		).collect({ |ev|
> 			~kickEvent = ev}),
> 
> 		Pbind(
> 		\instrument, \default,
> 			\ghoast, Pif(Pfunc({~kickEvent[\dur]== (1/8)}),1, Pn(1,inf,\trigny)),
> 			\degree,Pgate(Pseq([1,5,7,12,9,14,12
> 			],1),inf,\trigny),
> 			\octave,3,
> 			\timingOffset, Pseq([0,Pwhite(0.003,0.006,inf)],inf),
> 			\dur, 1/16,
> 		\sus,0.1,
> 	   \amp,0.05,
> 		)
> ],inf)
> )).play(~c);
> )

Sorry to be a stickler, but could you edit your post to use code tags instead of quote tags?

``` 
code block inside triple back ticks
```

quote tag, not the same

hjh

If you want to trigger sub-Patterns of events and simply wait for them to be finished before moving on, Pspawn is probably the only way to go here, as @jamshark70 suggested.

If you want your outer pattern to control duration itself (how often the inner patterns are triggered), and the inner patterns to run more or less on their own, then you can use recursive phrasing (check the documentation for “recursive phrasing”), or this \pattern event type:

Recursive phrasing requires that you put patterns into Pdefs - the \pattern type allows you to specify any sub-pattern in using the \instrument or \pattern key:

(
Pdef(\part, Pbind(
	\type, \pattern,
	\pattern, {
		Pbind(
			\dur, 1/12,
			\degree, Pseq([2, 4, 6, 5], inf),
		)
	},
	\dur, Prand([1/8, 1/4, 1], inf),
	\octave, Pseq([3, 4, 5, 4], inf),
	\legato, Prand([1, 2], inf)
)).play();
)

The \legato / \sustain keys in the outer pattern control how long the sub-pattern plays, more or less as if it were a single event. The sub-pattern inherits keys from the outer pattern - if you specify the sub-pattern in a function, it’s arguments are pulled from the outer event also (same as Plazy).

No, its me without the skills. Thanks!

I believe @sc_nils wants to call next on a Pbind - or something else which contains its own value pattern for dur and note data - from a separate pattern, thus controlling when next gets called. Essentially like a gate or clock driven analog step sequencer.

@sc_nils can you describe a bit what your intention in both of the Pbinds that you currently have inside of Ppar is? Is one meant to act as a potential trigger for the other?

Another consideration, is that the Live Stepper device in the video, it appears that all the notes which were input into the sequencer were likely the same duration, or did not specify a duration – meaning they would defer to whatever length the note in the MIDI clip was. If this is the case, or if you’re okay with this, you might be able to simplify things and run everything inside of one Pbind, with your note Pseq created externally:

a = Pseq([0, 5, 3, 12], inf).asStream;

(
Pdef(\sequencer,
	Pbind(
		\dur, Pseq([1, 0.5, 0.25, 0.25], inf),
		// \degrees, Prand([Rest(1), a.next], inf),
		\degrees, Pseq([Rest(), a.next, Rest(), a.next, a.next], inf),
	)
).play
)

This way you’re able to control exactly when the new note gets selected by referencing the sequence of notes, held in a, or whatever variable you assign them into.

If you’d like independent dur for both your triggering pattern and your note sequence pattern, then, hmm, perhaps check out Ppatlace it will you interleave Pbinds together, though, essentially it’s “monophonic” in the sense that it will either allow for whatever your triggering Pbind is doing or the note Pbind.

Just to clarify @sc_nils - are you trying to emulate the step-upon-trigger sequencer behavior that’s found in the SH-101 and lots of sequencer modules like in the Mutable Instruments Anshruti? In that case, I think I can describe the behavior for those who can’t watch the video in the first post. With the classic “acid house” setup, you basically have a predetermined pattern of note values programmed on the SH-101 and an external trigger source coming from a drum machine. Every time the SH-101 receives a trigger, the note is triggered and the pattern is advanced by one step. This means that the note values are controlled by the SH-101, but the rhythm is controlled externally by the drum machine. If the amount of notes in a bar and the length of the note pattern are not equal, the note pattern “rotates” around every bar, which gives the resulting sequence a pleasing feeling of constant variation.

Some musical examples:

The bassline in Mr. Fingers - Waterfall (note the variations)

A Guy Called Gerald using two SH-101s in parallel (bassline and arp pattern). The 808 has three trigger outs (clap/cowbell/accent), which makes it possible to trigger both SH-101s separately using their external clock inputs.

Quick Gerald remake:

(
a = Pseq([3,3,-4,-6,-2,-2,-4,-6,-9,-6,-11,-9,-6,-4,-14,-11], inf).asStream;

Pdef(\sequencer,
	Pbind(
		\dur, Pseq([0.75, 0.75, 0.75, 0.5, 0.25, 0.5, 0.5], inf),
		\note, Pfunc({a.next}),
		\octave, 4,
		\sustain, 0.01,
		\tempo, 127/60
	)
).play;
)
1 Like

Oh, I think I get it now: note data come from one source, timing comes from a totally different source.

With any pattern, you can make a stream from it and get data from it on demand. It isn’t strictly required to play a pattern.

~noteSource = Pbind(
	\midinote, Pseq([50, 52, 59], inf),
	\sustain, 0.3
).asStream;  // 'asStream' is important!


// manual trigger
(
b = Button(nil, Rect(800, 200, 100, 25)).front
.states_([["note"]])
.action_({
	~noteSource.next(Event.new).play;
});
)

TempoClock.tempo = 124/60;

// step sequencer trigger
(
~step = Pbind(
	\switch, Pseq([1, 0, 0, 1,  0, 0, 1, 0,  0, 0, 1, 1,  1, 0, 0, 0], inf),
	\dur, 0.25,
).collect { |ev|
	if(ev[\switch] > 0) {
		ev = ~noteSource.next(ev);
	} {
		ev.put(\type, \rest)
	};
	ev
}.play;
)

~step.stop;

But that’s not quite the same as the first post, I think…

… so it isn’t individual notes being triggered, but the whole sequence. (However, this requirement is also different from what is shown in the video… so I’m not sure @sc_nils has a clear definition of the problem. Seems that there are two ideas happening at once.)

For the OP problem, I’d still use Pspawn:

(
~noteSeq = Pbind(
	\degree, Pseq([0, 5, 3, 12], 1),
	\dur, Pseq([1, 0.5, 0.25, 0.25], 1)
);

p = Pspawn(
	// here is the "other pattern used as a trigger"
	Pbind(
		\dur, Pwhite(2, 6, inf),
		\method, \par,
		\pattern, Ref(~noteSeq)
	)
).play;
)

hjh

3 Likes

Exactly.

To be honest, I skipped the explanation in the OP and went straight to the video, because I was already familiar with the classic SH-101 technique and I assumed that the explanation was just a sort of misinterpretation of the technique… But yes, they are definitely two distinct ideas requiring different approaches.

I really like your step seq trigger approach!

Sorry for the late reply. Been sick. Thanks to all of you trying to sort this out. @bovil43810 is right about my misinterpretation of the classic technique. I think this is the clearest showcase of the technique

with @jamshark70 suggestion it sounds something like this in SC:

(
~noteSource = Pbind(
	\instrument,\default,
	\midinote, Pseq([33,40, 45], inf),
   
).asStream;  // 'asStream' is important!
)


// step sequencer trigger
(
 Pdef(\part, Pbind(
	\switch, Pseq([0, 0, 1, 0,  0, 0, 1, 0,  0, 1, 0, 1,  1, 1, 0, 0], inf),
	\dur, 0.125,
).collect { |ev|
	if(ev[\switch] > 0) {
		ev = ~noteSource.next(ev);
	} {
		ev.put(\type, \rest)
	};
	ev}
).play;
)

I guess the actual sound is comming from the ~noteSource Pbind… a quess it is also here I specify my \out argument? When I try updating the ~noteSource Pbind value it doesn’t seem to pile up in the NodeTree which Is strange considering it is is a Pbind and not a Pdef.

Regarding my original question I have no better way of explaining it by showing it like this.
I am trying to force myself to work in the syntax Pdefs and not sure how to apply Pspawn in this context…

(
~c = TempoClock.new(120/60).permanent_(true);
~c.tempo_(120/4/60);
);

(
Pdef(\part,
	Ppar([
        //pattern that triggers
	Pbind(
		\instrument, \default,
		\degree, 1,
		\legato,0.02,
		\sus,0.002,
		\octave, 3,
			\dur, Pseq([1/8,1/2,Rest(1/8),Rest(1/8),1/8],inf),
	   \amp,0.05,
		).collect({ |ev|
			~kickEvent = ev}),
Pbind(
// pattern that listens
		\instrument, \default,
			\ghoast, Pif(Pfunc({~kickEvent[\dur]== (1/8)}),Pn(1,inf,\trigny),1),
			\degree,Pgate(Pseq([1,5,7,9
			],1),inf,\trigny),
			\octave,5,
			\timingOffset, Pseq([0,Pwhite(0.003,0.006,inf)],inf),
			\dur, 1/16,
		\sus,0.1,
	   \amp,0.01,
		)

],inf)
).play(~c);
)

But as I’ve pointed out earlier the Pgate keeps on playing the same note in the sequence before it gets the next 1/8, then moving on to the next note in the pattern.
I rather want it to cycle through the Pseq’s 1,5,7,9 for as long as it takes before the next 1/8, and only then make i start from the begining of the sequence 1,5, etc , whichever the step in the pattern is currently on. Sorry can’t explain it better. Hope it helps

It isn’t strange at all.

If you do a = Pbind(...) (note: without play!) then… you hear nothing. If you then do a = Pbind(... something else...), you… still hear nothing. No pileup.

So it’s not quite right to think of it like “multiple assigning an event pattern = pileup.”

Pileup occurs when you have played an event pattern (a = Pbind(...).play) and then play another pattern without stopping the first (a = Pbind(... something else...).play). The pattern players pile up.

When you play a pattern, it creates an object called EventStreamPlayer. This object gets an event from the source stream (by next(prototypeEvent), plays the event (sound happens here), and waits for the event’s delta value.

In my step sequencer example, when the step pattern encounters a positive trigger, it gets an event from the source stream, plays the event, and waits for the step duration. The only difference between this and EventStreamPlayer is the timing! So the step pattern is the player… and there is only one player, running continuously. Reassigning ~noteSource does not add new players – so, no pileup.

Pgate is a sample-and-hold pattern. It’s designed to repeat the same value until the condition releases the hold. So it’s definitely not the object you want here.

I think I understand the requirement but I need to think about the best way. I suspect that it will involve stopping and repeating the note sequence but I haven’t worked out the details in my head (writing on a tablet now). There’s not much of a way for a pattern to reset a stream in the middle, but if a stream stops and is repeated, then it would appear to have reset. The catch is that you also want the timing to run independently, so you wouldn’t reset the entire arpeggiator. I’d like to test it a bit later before saying more.

hjh

I think this does the trick.

I replaced ~kickEvent[\dur] == (1/8) in the listening pattern with a more abstract ~trigger flag. You don’t necessarily have to do this. But, when the arpeggiator stops and cycles back to the beginning, it’s necessary to clear the trigger flag. If you don’t, then the next Pn iteration will find that the trigger is on, and reset again, and find again that the trigger is on = SC hangs. IMO it’s messier to extract the condition from an event, and then have to modify the event (which might be used somewhere else) – better for the pattern producing ~kickEvent to pull the condition out into other storage.

Pchain is because there is nothing to stop the kick rhythm from having an odd number of 16ths. In that case, the \timingOffset swing would get out of sync with the barline. To handle that, the arpeggiator rhythm runs continuously (never resets), and Pchain inserts the note data into that. (BTW if you meant \timingOffset to be swing, then the Pwhite should have length 1 instead of inf.)

None of this is particularly obvious btw.

(
~c = TempoClock.new(120/60).permanent_(true);
~c.tempo_(120/4/60);
)

(
~trigger = false;

Pdef(\part,
	Ppar([
		// pattern that triggers
		Pbind(
			\instrument, \default,
			\degree, 1,
			\legato, 0.02,
			\sus, 0.002,
			\octave, 3,
			\dur, Pseq([1/8, 1/2, Rest(1/8), Rest(1/8), 1/8], inf),
			\amp, 0.05,
		).collect({ |ev|
			if(ev[\dur] == (1/8)) {
				~trigger = true;
			};
			ev  // return event
		}),
		Pchain(
			// add note data into timing data
			Pn(Pbind(
				// pattern that listens
				\ghoast, Pif(
					Pfunc({ ~trigger }),
					// here, we must un-do the trigger being set
					// otherwise Pn will infloop
					Pfunc({
						~trigger = false;
						nil  // return nil to stop the Pbind!
					}),
					\dummy
				),
				\degree, Pseq([1, 5, 7, 9], inf),  // changed `1` to `inf`
				\octave, 5
			), inf),
			Pbind(
				\instrument, \default,
				\timingOffset, Pseq([0, Pwhite(0.003, 0.006, 1)], inf),
				\dur, 1/16,
				\sus, 0.1,
				\amp, 0.01
			)
		)
	], inf)
).play(~c);
)

hjh

PS There are a couple of sticky problems. Some of them, I don’t have good answers for (not yet anyway).

  • ~trigger is not well encapsulated – an outside actor could tamper with it and cause the arpeggiator to reset at the wrong time. One way to address that would be to write Penvir((trigger: false), Ppar(...)) in place of the Ppar, effectively creating a local environment scope for the Ppar.

  • Here, the illusion of sync depends on the fact that the arpeggiator’s rhythm evenly divides the kick rhythm values, so an arpeggiator note will always line up with a kick note. If the arpeggiator had an arbitrary rhythm that wasn’t in sync, with this code structure there’s no good way to truncate an arpeggiator note so that the next note would hit the trigger exactly. It’s tricky to cancel the old stream.

  • A Boolean trigger is ok for one listener pattern. If you have two, with different rhythms, then you can’t just clear the trigger (because any subsequent listeners wouldn’t see the trigger at all). I think you’d need to be aware of all the streams that are looking for a trigger and remove them from a collection, one by one; when the collection is empty, then it’s back to neutral state. Getting access to those streams is the tricky part; the pattern interface is designed to keep the streams internally without exposing them.

  • This one, I really don’t have a good answer for – it’s more theoretical though, no need to lose sleep over it. Data sharing between patterns in SC depends on side effects, breaking patterns’ ideal of stateless functional design, so it always looks a little awkward to me. I’m not deep enough into functional programming to see an alternative though. I guess it doesn’t really matter – as long as the side effects are treated carefully, it will all work. It’s just an “I wonder if there’s a better way” thing for me.

hjh

Thanks @jamshark70! Most of this is beyond my understanding. It will take some time for me to digest I can see that it works and that is great!
I tried to apply this concept to @hemiketal great post on My Pdef workflow
where he uses an added \isRest key to the default keys in order to get a nice sequencer going.
So I thought I could apply ~trigger koncept to the \isRest. But that doesn’t seem to work. Maybe I am asking to much. Nevertheless I am extremely thankful for your help and giving me useful solutions to what I wanted to accomplish!

  var addisRest = Event.parentEvents.default;
         addisRest[\play] = addisRest[\play] <> { ~isRest.notNil.if { ~isRest.if { ~type = \rest } } };

~c = TempoClock.new(120/60).permanent_(true);
~c.tempo_(120/4/60);


(
~trigger = false;
Pdef(\part, Pdef(\part1,
	Ppar([
	Pbind(
		\instrument, \default,
		\isRest, Pseq([
			1,0,1,0,
			1,0,0,0,
			1,0,0,0,
			1,0,1,1,
			1,0,1,1,
			1,0,1,0,
			1,0,1,0,
			1,0,0,1,
			].rotate(0),inf).coin.not,
		\octave,3,
		\dur, 1/16,
	   \amp,1,
		\legato, 0.005,
		).collect({ |ev|
			if(ev[\isRest]) {
				~trigger = false}
			{~trigger = true};
			ev  // return event
		}),



		Pchain(
			// add note data into timing data
			Pn(Pbind(
				// pattern that listens
				\ghoast, Pif(
					Pfunc({ ~trigger }),
					// here, we must un-do the trigger being set
					// otherwise Pn will infloop
					Pfunc({
						~trigger = false;
						nil  // return nil to stop the Pbind!
					}),
					\dummy
				),
				\degree, Pseq([1,5,7,9,12
			],1),  // changed `1` to `inf`
				\octave, 6
			), inf),

			Pbind(
				\instrument, \default,
				\timingOffset, Pseq([0, 0.009], 1),
				\dur, 1/16,
				\sus, 2,
				\amp, 0.1
			)
		),




	],inf)

)).play(~c);
)

1 Like

This…

… will stop the arpeggiator after two events, and it won’t recover because the timing pattern has nothing to repeat it.

Why did you change this to length = 1?

hjh

1 Like

Worked!!! Amazing! I don’t know why I changed that