Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

add PpatRewrite + help

git-svn-id: file:///home/jakob/programming/sc-quarks-mirror/dewdrop_lib/ddwPatterns@1890 93058666-9291-4804-9e73-1e07a4327beb
  • Loading branch information...
commit 8768284b5ac3bccac01def0675edc7f4b425bfb8 1 parent 11b1da6
authored April 26, 2011
132  Help/PpatRewrite.html
... ...
@@ -0,0 +1,132 @@
  1
+<html>
  2
+<head>
  3
+  <title>PpatRewrite</title>
  4
+</head>
  5
+
  6
+<body>
  7
+<h2>PpatRewrite</h2>
  8
+<h3>superclasses: FilterPattern : Pattern : AbstractFunction : Object</h3>
  9
+
  10
+<p>An extension of the rule-based stream-rewriting implementation in Prewrite.</p>
  11
+
  12
+<p>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.</p>
  13
+
  14
+<p>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.</p>
  15
+
  16
+<p>PpatRewrite specifies rules as an array of associations. The first matching rule applies; rules given earlier in the list take priority.</p>
  17
+
  18
+<p>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).</p>
  19
+
  20
+
  21
+<p><strong>*new(pattern, levelPattern, rules, defaultRule, autoStreamArrays = false, reuseLevelResults = false)</strong></p>
  22
+
  23
+<ul>
  24
+  <li><strong>pattern:</strong> The seed pattern.</li>
  25
+  <li><strong>levelPattern:</strong> Returns the desired number of levels of recursion. 0 will output the seed pattern with no modification. 1 applies the rules to the values from the seed pattern only. 2 applies the rules to the seed pattern and then applies the rules again to the first-level rule results, and so on.</li>
  26
+  <li><strong>rules:</strong> An array of associations: matchObject -> rule. See below.</li>
  27
+  <li><strong>defaultRule:</strong> A single association, applied only if nothing matches from 'rules'.</li>
  28
+  <li><strong>autoStreamArrays:</strong> If true, any time a rule returns an array of values, the array will be wrapped in Pseq so that the array items are output one by one. This is simply a convenience to avoid writing Pseq(array, 1) in every rule.</li>
  29
+  <li><strong>reuseLevelResults:</strong> If true, the first time the rewriting is done at a given level, the results are saved internally. Then, if the level is used again, the saved results are returned again. Useful if it's important that the results at different levels be recognizable.</li>
  30
+</ul>
  31
+
  32
+<p><strong>Rules:</strong></p>
  33
+
  34
+<pre>[
  35
+	matchingObject -> rule,
  36
+	matchingObject -> rule,
  37
+	matchingObject -> rule,
  38
+	...
  39
+]</pre>
  40
+
  41
+<p>matchingObject may be:</p>
  42
+
  43
+<ul>
  44
+  <li><strong>nil:</strong> matches everything</li>
  45
+  <li>an <strong>Object:</strong> matches if the value being tested is identical</li>
  46
+  <li>an <strong>Array/List:</strong> matches if any item in the array is identical to the test value (array.includes(x))</li>
  47
+  <li>a <strong>Function:</strong> The function is passed the test value and should return true or false</li>
  48
+</ul>
  49
+
  50
+<p>If the incoming values are MIDI notes:</p>
  51
+
  52
+<pre>[
  53
+	60 -> { ... return a pattern to play instead of middle C },
  54
+	[62, 65, 67, 68 72] -> { ... pattern for D, F, G, A and C above middle C ... },
  55
+	{ |mnote| mnote.odd } -> { ... pattern for odd numbered notes ... },
  56
+	nil -> { |item| item }  // everything else, no change
  57
+]</pre>
  58
+
  59
+<p>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.</p>
  60
+
  61
+
  62
+<p><strong>Example</strong></p>
  63
+
  64
+<pre>// Recursively ornament a melody
  65
+(
  66
+var	intervals = Pseries(0, Pwrand(#[-2, -1, 1, 2], #[0.1, 0.2, 0.4, 0.3], inf), { rrand(2, 5) });
  67
+p = Pbind(
  68
+	\root, 7,
  69
+	\degree, PpatRewrite(
  70
+		Pseq(#[2, 5, 4], 1),
  71
+		Pseries(0, 1, 5),
  72
+		[
  73
+			// intervals is biased upward, so this should tend to rise
  74
+			{ |item| item <= 0 } -> { |item| item + intervals },
  75
+			// and '-', applied to a higher note, should tend to fall
  76
+			{ |item| item > 0 } -> { |item| item - intervals },
  77
+		]
  78
+	),
  79
+	\dur, 0.5
  80
+).play;
  81
+)
  82
+
  83
+p.stop;
  84
+
  85
+
  86
+/*
  87
+We can also attach the level number to the output degree,
  88
+and use the level to highlight the tree-like structure.
  89
+
  90
+In each rewriting operation, the first output value comes directly from the parent level.
  91
+So the original level associated with the item is attached.
  92
+Subsequent values acquire the current level.
  93
+*/
  94
+
  95
+(
  96
+var	intervals = Pseries(
  97
+	0,
  98
+	Pwrand(#[-2, -1, 1, 2], #[0.1, 0.2, 0.4, 0.3], inf),
  99
+	Pwrand(#[2, 3, 4, 5], #[0.4, 0.3, 0.2, 0.1], inf).asStream
  100
+);
  101
+p = Pbind(
  102
+	\root, 7,
  103
+	[\degree, \level], PpatRewrite(
  104
+		Ptuple([Pseq(#[2, 5, 4], 1), 0]),
  105
+		Pseries(0, 1, 5),
  106
+		[
  107
+			// intervals is biased upward, so this should tend to rise
  108
+			{ |item| item[0] <= 0 } -> { |item, level|
  109
+				Ptuple([
  110
+					item[0] + intervals,
  111
+					Pseq([item[1], Pn(level, inf)])
  112
+				])
  113
+			},
  114
+			// and '-', applied to a higher note, should tend to fall
  115
+			{ |item| item[0] > 0 } -> { |item, level|
  116
+				Ptuple([
  117
+					item[0] - intervals,
  118
+					Pseq([item[1], Pn(level, inf)])
  119
+				])
  120
+			},
  121
+		]
  122
+	),
  123
+	// 2**(1-0) = 2, 2**(1-1) = 1, 2**(1-2) = 0.5 etc.
  124
+	\dur, 2 ** (1 - Pkey(\level)),
  125
+	\amp, (Pkey(\level) + 1).reciprocal * 0.5,
  126
+	\octave, Pkey(\level) + 3
  127
+).play;
  128
+)
  129
+
  130
+p.stop;</pre>
  131
+</body>
  132
+</html>
63  ddwFilterPatterns.sc
@@ -242,3 +242,66 @@ Pdelay : FilterPattern {
242 242
 		^inval
243 243
 	}
244 244
 }
  245
+
  246
+
  247
+PpatRewrite : FilterPattern {
  248
+	var	<>levelPattern, <>rules, <>defaultRule,
  249
+		<>autoStreamArrays = true,
  250
+		<>reuseLevelResults = false;
  251
+
  252
+	*new { |pattern, levelPattern, rules, defaultRule,
  253
+		autoStreamArrays = true, reuseLevelResults = false|
  254
+		^super.new(pattern).levelPattern_(levelPattern)
  255
+			.rules_(rules)
  256
+			.defaultRule_(defaultRule ?? { nil -> { |in| in } })
  257
+			.autoStreamArrays_(autoStreamArrays).reuseLevelResults_(reuseLevelResults)
  258
+	}
  259
+
  260
+	embedInStream { |inval|
  261
+		var	levelStream = levelPattern.asStream,
  262
+			level, outputs = List.new;
  263
+		while { (level = levelStream.next(inval)).notNil } {
  264
+			inval = this.recurse(inval, pattern.asStream, level, outputs);
  265
+		};
  266
+		^inval
  267
+	}
  268
+
  269
+	recurse { |inval, inStream, level, outputs|
  270
+		var	rule;
  271
+		if(reuseLevelResults and: { outputs[level].notNil }) {
  272
+			^Pseq(outputs[level], 1).embedInStream(inval)
  273
+		} {
  274
+			// mondo sucko that I have to hack into the List
  275
+			outputs.array = outputs.array.extend(max(level+1, outputs.size));
  276
+			outputs[level] = List.new;
  277
+			if(level > 0) {
  278
+				r { |inval| this.recurse(inval, inStream, level-1, outputs) }
  279
+				.do { |item|
  280
+					case
  281
+					// matched a rule, use it
  282
+					{ (rule = rules.detect { |assn| assn.key.matchItem(item) }).notNil }
  283
+						{ inval = this.rewrite(item, rule, inval, level, outputs) }
  284
+					// matched the default rule
  285
+					{ defaultRule.key.matchItem(item) }
  286
+						{ inval = this.rewrite(item, defaultRule, inval, level, outputs) }
  287
+					// no match, just spit out the item unchanged
  288
+					{ outputs[level].add(item); inval = item.embedInStream(inval) };
  289
+				};
  290
+			} {
  291
+				inval = inStream.collect { |item|
  292
+					outputs[level].add(item);
  293
+					item
  294
+				}.embedInStream(inval);
  295
+			};
  296
+		};
  297
+		^inval
  298
+	}
  299
+
  300
+	rewrite { |item, rule, inval, level, outputs|
  301
+		var	result = rule.value.value(item, level, inval);
  302
+		if(autoStreamArrays and: { result.isSequenceableCollection }) {
  303
+			result = Pseq(result, 1);
  304
+		};
  305
+		^result.asStream.collect { |item| outputs[level].add(item); item }.embedInStream(inval);
  306
+	}
  307
+}

0 notes on commit 8768284

Please sign in to comment.
Something went wrong with that request. Please try again.