Pattern Hack a la MIDI delay line

I was messing around with patterns trying to do a stuttering pattern and found a cool ‘hack’.

My original idea was to find a way to easily switch a pattern and the same pattern being ‘stuttered’ in subdivisions:

( // base pattern, subdivision = 1/16th notes
Pdef(\test,
	Pbind(
		\midinote, Pseq([50, 55, 60], inf),
		\dur, Pseq([0.75, 0.75, 0.5], inf)
	)
).play
)

( // 'stuttered' version, subdivision = 1/16th notes
Pdef(\test,
	Pbind(
		\midinote, Pstep(Pseq([50, 55, 60], inf), Pseq([0.75, 0.75, 0.5], inf)),
		\dur, 0.25
	)
).play
)
Pdef(\test).stop

But my patterns are pretty complex with lots of different keys and the chance of altering the patterns on the fly in different ways, so I gave up doing a key-by-key version. Then I came up with this cool Prout ‘hack’ - hack because the original pattern is muted and sort of taken over by the Prout. The super cool thing about this is that it allows the stuttering to be in any subdivision so it’s a bit like having two timelines going at once, where the stuttering is starting on a beat quantized to the first timeline but with independent control over the subsequent timing of the stuttered notes. Also, this is a add-on with no need to adjust the existing pattern which can be of any complexity, contain randomness etc. It is a bit like a ‘MIDI delay line with ducking’. Better shown than explained:

(
~stutNum = 6;
~stutter = 0;
~stutterAmpDecay = 1;
Pdef(\test,
	Pchain(
		Prout({|ev|
			while { ev.notNil }
			{
				var pb = ev.copy;
				var subD = ~stutNum.reciprocal;
				var num = pb.dur.div(subD).max(1);
				pb.dur = if (~stutter == 0) { Pseq([pb.dur]) } { Pseq([subD], num) };
				if (~stutterAmpDecay == 1) { pb.amp = Pseq(num.collect{|i|1/(i + 1) * ev.amp}, num) };
				Pbind(*pb.asPairs).play();
				ev.amp = \rest;
				ev = ev.yield
			}
		}),
		Pbind(
			\midinote, Pseq([50, 55, 60, 48, 53, 52], inf),
			\dur, Pseq([0.5, 0.5, 1, 0.75, 0.75, 0.5], inf),
			\amp, 0.3
		)
	)
).play() // plays normal at first
);

~stutter = 1; // turn on to hear the stutter effect
~stutNum = rrand(3, 8).debug(\stutNum); // try different values
~stutterAmpDecay = 0; // all notes will be same amp as the original
~stutterAmpDecay = 1; // decaying amplitudes
Pdef(\test).stop
4 Likes

Very cool! I have been doing a similar thing lately, except it is more of a traditional delay effect where the echoes can overlap the next note. I use the \lag key combined with multi-channel expansion of arrays to do this:

(
Pbind(
    \midinote, Pseq([50, 55, 60, 48, 53, 52], inf),
    \dur, Pseq([0.5, 0.5, 1, 0.75, 0.75, 0.5], inf),
    \amp, 0.3,
    \legato, 0.2,
    \echoTime, 0.25,
    \echoCoef, 0.5,
    \numEchoes, 7,
    \lag, Pfunc {|ev| Array.series(ev.numEchoes, 0, ev.echoTime)},
    \amp, Pfunc {|ev| Array.geom(ev.numEchoes, 1, ev.echoCoef) * ev.amp}
).play;
)

After doing this a bunch and scaling up the complexity, I eventually created an \echo event type which allows you to do some more cool stuff, like ping pong delays:

(
Event.addEventType(\echo, {
    var numNotes, echoTime, echoCoef, lag, echoPan;
    
    numNotes = (~numEchoes ? 0).asInteger.max(0) + 1;
    echoTime = ~echoTime ?? {thisThread.clock.beatDur / 2};
    echoCoef = ~echoCoef ? 0.5;
    echoPan = case
    {~echoPan.isKindOf(Array)} {~echoPan}
    {~echoPan.asSymbol == \rand} {{1.0.rand2} ! (numNotes - 1)}
    {~echoPan.asSymbol == \lr} {[-1, 1]}
    {~echoPan.asSymbol == \rl} {[1, -1]};
    echoPan = echoPan ? [0];
    echoPan = (~echoSpread ? 1) * echoPan;
    lag = ~lag ? 0;
    ~lag = lag.abs + Array.series(numNotes, 0, echoTime.abs);
    ~amp = ~amp.value * Array.geom(numNotes, 1, echoCoef);
    ~pan = [0] ++ echoPan.wrapExtend(numNotes - 1) + ~pan;
    
    ~eventTypes[\note].value(~server);
});

Pbind(
    \type, \echo,
    \midinote, Pseq([50, 55, 60, 48, 53, 52], inf),
    \dur, Pseq([0.5, 0.5, 1, 0.75, 0.75, 0.5], inf),
    \amp, 0.3,
    \legato, 0.2,
    \numEchoes, Pwhite(5, 12),
    \echoTime, Prand([1/8, 1/6, 1/4, 1/3], inf),
    \echoCoef, Pmeanrand(0.3, 0.8),
    \echoPan, Prand([\rand, \lr, \rl, \none], inf)
).play;
)
3 Likes

brilliant and so simple