PpatRewrite

superclasses: FilterPattern : Pattern : AbstractFunction : Object

An extension of the rule-based stream-rewriting implementation in Prewrite.

Prewrite matches values based on equality (Dictionary) or identity (IdentityDictionary). PpatRewrite uses the matchItem interface to support identity, array lookup and arbitrary functions for more flexible rule lookup.

Prewrite does not support non-deterministic rewriting rules. In PpatRewrite, the rule may be a function, and this function may return a pattern that will be embedded in place of the incoming value.

PpatRewrite specifies rules as an array of associations. The first matching rule applies; rules given earlier in the list take priority.

The level number may also be a pattern. PpatRewrite will obtain the level number and then apply the rules recursively to each value from 'pattern', going that number of levels deep. When this cycle runs out of output values, another level number will be chosen and the seed pattern will be repeated with a new application of the rules (at the new level).

*new(pattern, levelPattern, rules, defaultRule, autoStreamArrays = false, reuseLevelResults = false)

Rules:

[
	matchingObject -> rule,
	matchingObject -> rule,
	matchingObject -> rule,
	...
]

matchingObject may be:

If the incoming values are MIDI notes:

[
	60 -> { ... return a pattern to play instead of middle C },
	[62, 65, 67, 68 72] -> { ... pattern for D, F, G, A and C above middle C ... },
	{ |mnote| mnote.odd } -> { ... pattern for odd numbered notes ... },
	nil -> { |item| item }  // everything else, no change
]

The rule's function is passed the incoming item, level number, and the input value passed by 'next'. When used in Pbind, the rule can use previously calculated items in the event-in-progress.

Example

// Recursively ornament a melody
(
var	intervals = Pseries(0, Pwrand(#[-2, -1, 1, 2], #[0.1, 0.2, 0.4, 0.3], inf), { rrand(2, 5) });
p = Pbind(
	\root, 7,
	\degree, PpatRewrite(
		Pseq(#[2, 5, 4], 1),
		Pseries(0, 1, 5),
		[
			// intervals is biased upward, so this should tend to rise
			{ |item| item <= 0 } -> { |item| item + intervals },
			// and '-', applied to a higher note, should tend to fall
			{ |item| item > 0 } -> { |item| item - intervals },
		]
	),
	\dur, 0.5
).play;
)

p.stop;


/*
We can also attach the level number to the output degree,
and use the level to highlight the tree-like structure.

In each rewriting operation, the first output value comes directly from the parent level.
So the original level associated with the item is attached.
Subsequent values acquire the current level.
*/

(
var	intervals = Pseries(
	0,
	Pwrand(#[-2, -1, 1, 2], #[0.1, 0.2, 0.4, 0.3], inf),
	Pwrand(#[2, 3, 4, 5], #[0.4, 0.3, 0.2, 0.1], inf).asStream
);
p = Pbind(
	\root, 7,
	[\degree, \level], PpatRewrite(
		Ptuple([Pseq(#[2, 5, 4], 1), 0]),
		Pseries(0, 1, 5),
		[
			// intervals is biased upward, so this should tend to rise
			{ |item| item[0] <= 0 } -> { |item, level|
				Ptuple([
					item[0] + intervals,
					Pseq([item[1], Pn(level, inf)])
				])
			},
			// and '-', applied to a higher note, should tend to fall
			{ |item| item[0] > 0 } -> { |item, level|
				Ptuple([
					item[0] - intervals,
					Pseq([item[1], Pn(level, inf)])
				])
			},
		]
	),
	// 2**(1-0) = 2, 2**(1-1) = 1, 2**(1-2) = 0.5 etc.
	\dur, 2 ** (1 - Pkey(\level)),
	\amp, (Pkey(\level) + 1).reciprocal * 0.5,
	\octave, Pkey(\level) + 3
).play;
)

p.stop;