PIdev pattern searching for numbers with integer distance from a source pattern, optionally avoiding repetitions within a span
Part of: miSCellaneous
Inherits from: Pattern
DIdev / PIdef / PLIdev search for numbers with integer distance from a source signal / pattern up to a given deviation. Repetitions within a lookback span are avoided, DIdev / PIdef / PLIdev randomly choose from possible solutions. Intended for search within integer grids (pitches, indices etc.), however applications with non-integer sources are possible, see examples.
NOTE: It's the user's responsibility to pass a combination of deviation and lookback values that allows a possible choice, see examples.
NOTE: In contrast to DIdev, PIdev and PLIdev do *not* need to know maximum deviations (minLoDev, maxHiDev) beforehand. Thus the order of arguments is different (here loDev and hiDev before lookBack).
See also: Idev suite, PLIdev, DIdev
Creation / Class Methods
Creates a new PIdev object.
*new (pattern, maxLookBack = 3, loDev = -3, hiDev = 3, lookBack, thr = 1e-3, length = inf)
pattern - The source value pattern to start search from.
maxLookBack - Integer, the maximum lookback span. Fixed, defaults to 3.
loDev - Determines the current low deviation for the search. Defaults to -3.
hiDev - Determines the current high deviation for the search. Defaults to 3.
lookBack - Determines the current lookback span for avoiding repetitions.
Can be modulated but must not exceed maxLookBack.
If no value is passed, then maxLookBack is taken.
thr - Threshold for equality comparison. Can be modulated, defaults to 1e-3.
length - Number of repeats. Defaults to inf.
Examples
(
s = Server.local;
Server.default = s;
s.boot;
)
Ex.1) Basic usage: random choice within region without repetitions
// constant source (72), max deviation +/- 3
// no repetition within 5 pitches
(
p = Pbind(
\dur, 0.2,
\midinote, PIdev(72, 5, -3, 3).trace(prefix: "midi "),
).play;
)
p.stop
Ex.2) Variable deviations and lookBack
(
~loDev = -6;
~hiDev = 5;
~lookBack = 2;
p = Pbind(
\dur, 0.2,
\midinote, PIdev(72, 11, Pfunc { ~loDev }, Pfunc { ~hiDev }, Pfunc { ~lookBack }).trace(prefix: "midi "),
).play;
)
// change on the fly
// as lookBack equals 2, this defines a fixed sequence (up or down anyway)
(
~loDev = -1;
~hiDev = 1;
)
// widen range
(
~loDev = -6;
~hiDev = 5;
)
// force a twelve-tone row
~lookBack = 11;
// contradictory input, lookBack 11 not possible within range, causes repetitions
(
~loDev = -3;
~hiDev = 2;
)
p.stop
Ex.3) Moving source signal
(
~loDev = -1;
~hiDev = 1;
~lookBack = 2;
p = Pbind(
\dur, 1/7,
\midinote, PIdev(
Pseg(Pseq([65, 90], inf), 5, \sin).round,
11,
Pfunc { ~loDev },
Pfunc { ~hiDev },
Pfunc { ~lookBack }
).trace(prefix: "midi "),
).play;
)
// widen range and increase lookBack
(
~loDev = -6;
~hiDev = 5;
~lookBack = 10;
)
p.stop;
Ex.4) Dynamic deviation range and lookBack
// lookBack and deviations coupled here
// maxLookBack must be large enough
(
~loDev = -1;
~hiDev = 1;
~lookBack = 2;
p = Pbind(
\dur, 1/7,
\midinote, PIdev(
78,
10,
Pn(Plazy { ~loDev }),
Pn(Plazy { ~hiDev }).trace(prefix: "absolute deviation "),
Pn(Plazy { ~lookBack })
).trace(prefix: "midi ");
).play
)
// start parameter movement on the fly
(
~loDev = Pseg(Pseq([2, 5].neg, inf), 5, \sin);
~hiDev = Pseg(Pseq([2, 5], inf), 5, \sin).trace(prefix: "absolute deviation and lookBack ");
~lookBack = Pseg(Pseq([2, 5], inf), 5, \sin);
)
p.stop
Ex.5) Non-integer source
(
~loDev = -6;
~hiDev = 5;
~lookBack = 3;
~thr = 1;
p = Pbind(
\dur, 1/7,
\midinote, PIdev(
Pseg(Pseq([65, 90], inf), 5, \sin),
5,
Pfunc { ~loDev },
Pfunc { ~hiDev },
Pfunc { ~lookBack },
Pfunc { ~thr }
).trace(prefix: "midi "),
).play;
)
// close floats can occur here
~thr = 0.01
// not here
~thr = 2
p.stop
Ex.6) Multichannel expansion
// larger pitch range and lookBack in upper voice,
// as with most patterns, no automatic array expansion of pattern arguments
(
p = Pbind(
\dur, 1/7,
\src, Pseg(Pseq([65, 85], inf), 5, \sin).round,
\midinote, Ptuple([
PIdev(Pkey(\src), 1, -1, 1),
PIdev(Pkey(\src) + 8.5, 3, -5, 5)
]).trace(prefix: "midi "),
).play;
)
p.stop
Ex.7) Application to other params: rhythm
// if we have indexed data of whatever, we can slide over it,
// groups of durations as items to be streamed by PIdev
(
~rhythmBase = [
[1, 1],
[2, 1, 1],
[1, 1, 2]
].collect(_.normalizeSum);
~rhythms = ~rhythmBase *.x [1, 2];
~rhythmNum = ~rhythms.size;
~rhythms = ~rhythms.scramble;
SynthDef(\noise_grain, { |out = 0, freq = 400, att = 0.005, rel = 0.1,
rq = 0.05, pan = 0, amp = 0.1|
var sig = { WhiteNoise.ar } ! 2;
sig = BPF.ar(sig, freq, rq) *
EnvGen.ar(Env.perc(att, rel, amp), doneAction: 2) *
(rq ** -1) * (250 / (freq ** 0.8));
OffsetOut.ar(out, Pan2.ar(sig, pan));
}).add;
)
(
// rhythmic variation and partial pitch repetition
// play for a while to note slow sliding caused by Pseg
~loDev = -1;
~hiDev = 1;
p = Pbind(
\instrument, \noise_grain,
\rel, Pexprand(0.05, 0.15),
\dur, PIdev(
// be careful not to exceed index bounds
Pseg(Pseq([~loDev.abs, ~rhythmNum - ~hiDev - 1], inf), 10, \sin, inf).round,
2, // lookBack span, no repetition within 3 items
~loDev,
~hiDev
).trace(prefix: "rhythm type: ").collect(~rhythms[_]).flatten * 0.3,
\midinote, Pstutter(Pwhite(1, 2), Pclump(3, Pxrand((50..100), inf))).flatten + [0, 12],
\pan, Pstutter(Pwhite(1, 2), Pclump(3, Pwhite(-1.0, 1))).flatten
).play;
)
p.stop
Ex.8) Proof of concept
(
// Function to check an array for repetitions within a maximum test span
~repetitionCheck = { |array, maxTestSpan|
maxTestSpan.do { |i|
var result = (array.drop(i+1) - array).drop((i+1).neg).includes(0).not;
("no repetitions within a span of " ++ (i+2).asString ++ " items: ").post;
result.postln;
}
}
)
// test case
// no repetitions within a maximum span of 6 (lookBack == 5)
(
p = PIdev(Pbrown(0, 20, 0.3).round.asInteger, 5, -7, 7).iter;
a = p.nextN(10000);
a.plot;
~repetitionCheck.(a, 10);
)