remove a crufty folder that's done nothing for quite a while...
[supercollider.git] / build / SCClassLibrary / JITLib / Patterns / Pdef.sc
blobd638d2299332ba3539c48f6ca55879d09827d171
2 // contains numerical / value patterns
4 PatternProxy : Pattern {
5         var <source, <pattern, <>envir;
6         var >clock, quant, <>condition=true, reset;
7         
8                                 // quant new pattern insertion. can be [quant, phase, timingOffset]
9                                 // in EventPatternProxy it can be [quant, phase, timingOffset, onset]
10                                 
11         classvar <>defaultQuant, defaultEnvir;
12         
13         *new { arg source;
14                 ^super.new.source_(source)
15         }
16                 
17         *default { ^1 } // safe for duration patterns
18         *defaultValue { ^1 }
19         
20         clock { ^clock ? TempoClock.default }
21         
22         quant { ^quant ??  { this.class.defaultQuant } }
23         quant_ { arg val; quant = val }
24         
25         constrainStream { arg stream;
26                 if(quant.isNil or: { stream.isNil }) { ^pattern.asStream };
27                 ^Pseq([PfinQuant(EmbedOnce(stream), quant, clock), pattern]).asStream
28         }
29         
30         source_ { arg obj;
31                 var pat = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
32                 if (obj.isNil) { pat = this.class.default }; 
33                 pattern = pat;
34                 source = obj; // keep original here.
35         }
36                 
37         defaultEvent {
38                 if(envir.isNil) { envir = this.class.event };
39                 ^if(envir[\independent] === true) { (parent:envir) } { envir }
40         }
41         
42         convertFunction { arg func;
43                         ^Prout {
44                                 var inval = func.def.prototypeFrame !? { inval = this.defaultEvent };
45                                 func.value( inval ).embedInStream(inval)
46                         };
47         }
48         
49         *parallelise { arg list; ^Ptuple(list) }
50         
51         pattern_ { arg pat; this.source_(pat) }
52         
53         timingOffset_ { arg val; quant = quant.instill(2, val) }
54         timingOffset { arg val; ^quant.obtain(2) }
55         phase_ { arg val; quant = quant.instill(1, val) }
56         phase { arg val; ^quant.obtain(1) }
57         quantBeat_ { arg val; quant = quant.instill(0, val) }
58         quantBeat { arg val; ^quant.obtain(0) }
59         
60         set { arg ... args; 
61                 if(envir.isNil) { this.envir = this.class.event };
62                 args.pairsDo { arg key, val; envir.put(key, val) };
63         }
64         unset { arg ... args;
65                 if(envir.notNil) { args.do { arg key; envir.removeAt(key) } };
66         }
67         
68         get { arg key;
69                 ^if(envir.notNil) { envir[key] } { nil };
70         }
71         
72         place { arg ... args; 
73                 if(envir.isNil) { this.envir = this.class.event };
74                 args.pairsDo { arg key, val; envir.place(key, val) }; // place is defined in the event.
75         }
76         
77         isEventPattern { ^false }
78         
79         receiveEvent { ^nil }
80         
81         embed { |val|
82                 ^if(val.notNil) { Pchain(this, val) } { this }.embedInStream
83         }
84         
85         embedInStream { arg inval;
86                 
87                 var outval, count = 0;
88                 var pat = pattern;
89                 var test = condition;
90                 var resetTest = reset;
91                 var stream = pattern.asStream;
92                 
93                 while {
94                         this.receiveEvent(inval); // used in subclass
95                         
96                         if(
97                                 (reset !== resetTest) 
98                                 or: { pat !== pattern and: { test.value(outval, count) } }
99                         ) {
100                                                 pat = pattern;
101                                                 test = condition;
102                                                 resetTest = reset;
103                                                 count = 0;
104                                                 stream = this.constrainStream(stream);
105                         };
106                         outval = stream.next(inval);
107                         count = count + 1;
108                         outval.notNil
109                 }{
110                         inval = outval.yield;
111                 };
112                 ^inval
113         }
114         
115         endless {
116                 
117                 ^Proutine { arg inval;
118                         
119                         var outval, count = 0;
120                         var pat = pattern;
121                         var test = condition;
122                         var resetTest = reset;
123                         var stream = pattern.asStream;
124                         var default = this.class.defaultValue;
125                         
126                         loop {
127                                 this.receiveEvent(inval); // used in subclass
128                                 
129                                 if(
130                                         (reset !== resetTest) 
131                                         or: { pat !== pattern and: { test.value(outval, count) } }
132                                 ) {
133                                                         pat = pattern;
134                                                         test = condition;
135                                                         resetTest = reset;
136                                                         count = 0;
137                                                         stream = this.constrainStream(stream);
138                                 };
139                                 outval = stream.next(inval) ? default;
140                                 count = count + 1;
141                                 inval = outval.yield;
142                         }
143                 }
144                 
145         }
146         
147         count { arg n=1;
148                 condition = { |val,i| i % n == 0 }
149         }
150         
151         reset { reset = reset ? 0 + 1 }
152         
153         sched { arg func;
154                 if(quant.isNil) 
155                         { func.value } 
156                         { this.clock.schedAbs(quant.nextTimeOnGrid(this.clock), { func.value; nil }) }
157         }
158         
159         storeArgs { ^[pattern] }
160         
161         
162         // these following methods are factored out for the benefit of subclasses
163         // they only work for Pdef/Tdef/Pdefn.
164         
165                 
166         *removeAll { 
167                 this.clear; 
168                 this.all.makeEmpty; 
169         }
170         
171         *clear { 
172                 this.all.do { arg pat; pat.clear } 
173         }
174         
175         clear { 
176                 this.stop;
177                 this.source = nil;
178                 ^nil 
179         }
180         
181         remove {
182                 this.class.all.removeAt(this.key);
183                 this.clear;
184         }
185         
186         // backward compatibility
187         *basicNew { arg source;
188                 ^super.new.init(source)
189         }
190         
191         // global storage
192         
193         *at { arg key;
194                 ^this.all.at(key)
195         }
196         
197         repositoryArgs { ^[this.key, this.source] }
198         
199         *postRepository { arg keys, stream;
200                 keys = keys ?? { this.all.keys };
201                 stream = stream ? Post;
202                 keys.do { arg key; 
203                         var item;
204                         item = this.all[key];
205                         if(item.notNil and: { item.source !== this.default }) {
206                                 stream << item.class.name << "(" <<<* item.repositoryArgs << ")";
207                                 if(item.envir.notNil and: { item.envir.notEmpty }) {
208                                         stream << ".set(" <<<* item.envir.asKeyValuePairs << ")"
209                                 };
210                                 
211                                 stream << ";\n"
212                         };
213                 };
214         }
215         
216         *event { arg proxyClass = PatternProxy;
217                 var event, res;
218                 if(defaultEnvir.isNil) {
219                         defaultEnvir = Event.default;
220                         defaultEnvir.parent = defaultEnvir.parent.copy.putAll(
221                                 (
222                                 forward: #{ 1 },
223                                 proxyClass: proxyClass,
224                                 get: {|e, key| 
225                                         var x = e.at(key); 
226                                         if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x); };
227                                         x
228                                 }, 
229                                 place: { |e, key, val|
230                                         var x = e.at(key); 
231                                         if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x) };
232                                         x.source_(val)
233                                 }
234                                 )
235                         );
236                 };
237                 event = defaultEnvir.copy;
238         //      event[\self] = event; // this risks a crash on storeOn.
239                 ^event 
240         }
241         
242         ////////////////
243         
246 Pdefn : PatternProxy {
247         var <key;
248         classvar <>all;
249         
250         *initClass { 
251                 all = IdentityDictionary.new;
252         }
253         *new { arg key, item;
254                 var res = this.at(key);
255                 if(res.isNil) {
256                         res = super.new(item).prAdd(key);
257                 } {
258                         if(item.notNil) { res.source = item }
259                 }
260                 ^res
261         
262         }
263         
264         map { arg ... args;
265                 if(envir.isNil) { this.envir = () };
266                 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
267         }
268         
269         storeArgs { ^[key] } // assume it was created globally
270         
271         prAdd { arg argKey;
272                 key = argKey;
273                 all.put(argKey, this);
274         }
278 // contains time patterns (tasks)
280 TaskProxy : PatternProxy {
281         var <player, <>playQuant;
282         classvar <>defaultQuant=1.0;
283         
284         storeArgs { ^[source] }
285         
286         source_ { arg obj;
287                         pattern = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
288                         if (obj.isNil) { pattern = this.class.default; source = obj; }; 
289                         this.wakeUp;
290                         source = obj;
291         }
292         
293         convertFunction { arg func;
294                         ^Prout { |inevent|
295                                         var inval = func.def.prototypeFrame !? { this.defaultEvent };
296                                         if(inevent.isNumber or: {inevent.isNil} or: { inval.isNil }) { 
297                                                 inevent = inval
298                                         } {
299                                                 inevent.copy.parent_(inval);
300                                         };
301                                         func.value(inevent)
302                         }
303         }
304         
305         
306         isEventPattern { ^true }
307         
308         *default { ^Pn(this.defaultValue,1) }
309         
310         constrainStream { arg str;
311                 ^if(this.quant.notNil and: { str.notNil }) {
312                         Pseq([
313                                 EmbedOnce(Pconst(thisThread.clock.timeToNextBeat(this.quant), str, 0.001)),
314                                 pattern
315                         ])
316                 } { pattern }.asStream
317         }
318         
319         align { arg argQuant;
320                 quant = argQuant;
321                 this.source = this.source.copy;
322         }
323         
324         
325         ////////// playing interface //////////
326         
327         playOnce { arg argClock, doReset = (false), quant;
328                 var clock = argClock ? this.clock;
329                 ^PauseStream.new(this.asProtected.asStream).play(clock, doReset, quant ? this.quant)
330         }
331         
332         play { arg argClock, doReset=false, quant;
333                 playQuant = quant ? this.quant;
334                 if(player.isNil) { 
335                         player = this.playOnce(argClock, doReset, playQuant);
336                 } {
337                                 // resets  when stream has ended or after pause/cmd-period:
338                         if (player.streamHasEnded or: { player.wasStopped }) { doReset = true };
339                         
340                         if(player.isPlaying.not) { 
341                                 player.play(argClock, doReset, playQuant);
342                         } { 
343                                 if (doReset) { player.reset };
344                         }
345                 }
346         }
347         wakeUp {        
348                         if(this.isPlaying) { this.play(quant:playQuant) }       }
349         asProtected {
350                 ^Pprotect(this, { if(this.player.notNil) { this.player.streamError } })
351         }
352         
353         // check playing states:
354         isPlaying { ^player.notNil and: { player.wasStopped.not } }
355         isActive { ^this.isPlaying and: { player.streamHasEnded.not } }
356         hasSource { ^source.notNil }
357         hasEnvir { ^envir.notNil }
358         hasPlayer { ^player.notNil }
359         hasEnded { ^player.isNil or: { player.streamHasEnded } }
360         isPaused { ^player.isNil or: { player.wasStopped  } }
361         canPause { ^player.notNil and: { player.canPause } }
362         
363         fork { arg clock, quant, event;
364                 ^Routine { this.embedInStream(event) }.play(clock ? thisThread.clock, quant)
365         }
366         
367         stop { player.stop; player = nil; }
368         
369         pause { if(player.notNil) { this.sched { player.pause } } }
370         resume { arg clock, quant; 
371                 player !? { player.resume(clock ? this.clock, quant ? this.quant) } 
372         }
373         
376 Tdef : TaskProxy {
377         var <key;
378         classvar <>all;
379         
380         
381         *initClass { 
382                 all = IdentityDictionary.new;
383         }
384         
385         *new { arg key, item;
386                 var res = this.at(key);
387                 if(res.isNil) {
388                         res = super.new(item).prAdd(key);
389                 } {
390                         if(item.notNil) { res.source = item }
391                 }
392                 ^res
393         
394         }
396         storeArgs { ^[key] }
397         
398         prAdd { arg argKey;
399                 key = argKey;
400                 all.put(argKey, this);
401         }
407 // contains event patterns
409 EventPatternProxy : TaskProxy {
410         var <>fadeTime;
411         classvar <>defaultQuant=1.0;
412         
413         storeArgs { ^[source] }
414         
415         source_ { arg obj;
416                 if(obj.isKindOf(Function)) // allow functions to be passed in
417                         { pattern = PlazyEnvirN(obj) } 
418                         { if (obj.isNil) 
419                                 { pattern = this.class.default }
420                                 { pattern = obj }
421                         };
422                 envir !? { pattern = pattern <> envir };
423                 this.wakeUp;
424                 source = obj;
425         }
426         
427         envir_ { arg dict;
428                 envir = dict;
429                 this.source = source;
430         }
431         
432         *defaultValue { ^Event.silent }
434         embedInStream { arg inval, cleanup;
435                 
436                 var outval, count=0;
437                 var pat = pattern;
438                 var test = condition;
439                 var resetTest = reset;
440                 var stream = pattern.asStream;
441                 
442                 cleanup ?? { cleanup = EventStreamCleanup.new };
443                 
444                 while {
445                         this.receiveEvent(inval);       
446                         if(
447                                 (reset !== resetTest) 
448                                 or: { pat !== pattern and: { test.value(outval, count) } }
449                         ) {
450                                                 pat = pattern;
451                                                 test = condition;
452                                                 resetTest = reset;
453                                                 count = 0;
454                                                 // inval is the next event that will be yielded
455                                                 // constrainStream may add some values to it so IT MUST BE YIELDED
456                                                 stream = this.constrainStream(stream, inval, cleanup);
457                                                 cleanup = EventStreamCleanup.new;
458                         };
459                         outval = stream.next(inval);
460                         count = count + 1;
461                         outval.notNil
462                 }{
463                         outval = cleanup.update(outval);
464                         inval = outval.yield;
465                         if(inval.isNil) { ^nil.alwaysYield }            
466                 };
467                 ^inval
468                 
469         }
470         
471         endless {
472                 
473                 ^Proutine { arg inval;
474                         
475                         var outval;
476                         var cleanup = EventStreamCleanup.new;
477                         var count = 0;
478                         var pat = pattern;
479                         var test = condition;
480                         var resetTest = reset;
481                         var stream = pattern.asStream;
482                         var default = this.class.defaultValue;
483                         
484                         loop {
485                                 this.receiveEvent(inval);       
486                                 if(
487                                         (reset !== resetTest) 
488                                         or: { pat !== pattern and: { test.value(outval, count) } }
489                                 ) {
490                                                         pat = pattern;
491                                                         test = condition;
492                                                         resetTest = reset;
493                                                         count = 0;
494                                                         // inval is the next event that will be yielded
495                                                         // constrainStream may add some values to it so IT MUST BE YIELDED
496                                                         stream = this.constrainStream(stream, inval, cleanup);
497                                                         cleanup = EventStreamCleanup.new;
498                                 };
499                                 outval = stream.next(inval) ? default;
500                                 count = count + 1;
501                                 outval = cleanup.update(outval);
502                                 inval = outval.yield;
503                         }
504                 }
505                 
506         }
508         
509         constrainStream { arg str, inval, cleanup;
510                 var delta, tolerance, new;
511                 var quantBeat, catchUp, deltaTillCatchUp, forwardTime, quant = this.quant;
512                 
513                 ^if(quant.notNil) {
514                         quantBeat = this.quantBeat ? 0;
515                         catchUp = this.outset;
516                         
517                         delta = thisThread.clock.timeToNextBeat(quant);
518                         tolerance = quantBeat % delta % 0.125;
519                         if(catchUp.notNil) {
520                                 deltaTillCatchUp = thisThread.clock.timeToNextBeat(catchUp);
521                                 new = pattern.asStream;
522                                 forwardTime = quantBeat - delta + deltaTillCatchUp;
523                                 delta = new.fastForward(forwardTime, tolerance) + deltaTillCatchUp;
524                         } {
525                                 new = pattern
526                         };
528                         if(fadeTime.isNil) {
529                                 if(delta == 0) {
530                                         cleanup.exit(inval);
531                                         new 
532                                 } {
533                                         Pseq([EmbedOnce(Pfindur(delta, str, tolerance).asStream(cleanup)), new])
534                                 }
535                         }{
536                                 
537                                 Ppar([
538                                         EmbedOnce(PfadeOut(str, fadeTime, delta, tolerance).asStream(cleanup)),
539                                         PfadeIn(new, fadeTime, delta, tolerance)
540                                 ])
541                         }
542                 } { 
543                         cleanup.exit(inval); 
544                         pattern 
545                 }.asStream
546         }
548         *parallelise { arg list; ^Ppar(list) }
549         
550         outset_ { arg val; quant = quant.instill(3, val) }
551         outset { arg val; ^this.quant.obtain(3) }
552         
553                 
554         // branching from another thread
555         
556         fork { arg argClock, quant, protoEvent; // usual fork arg order: clock, quant, ...
557                 argClock = argClock ? thisThread.clock;
558                 ^EventStreamPlayer(this.asStream, protoEvent).play(argClock, true, quant)
559         }
560         
561         // playing one instance //
562         
563         playOnce { arg argClock, protoEvent, quant;
564                 ^this.fork(argClock ? this.clock, quant ? this.quant, protoEvent)
565         }
566         
567         
568         ////////// playing interface //////////
569         
570         // start playing //
572         play { arg argClock, protoEvent, quant, doReset=false;
573                 playQuant = quant ? this.quant;
574                 if(player.isNil) { 
575                         player = EventStreamPlayer(this.asProtected.asStream, protoEvent);
576                         player.play(argClock, doReset, playQuant);
577                 } {
578                                 // resets  when stream has ended or after pause/cmd-period:
579                         if(player.streamHasEnded or: { player.wasStopped }) { doReset = true };
580                         protoEvent !? { player.event = protoEvent };
581                         if(player.isPlaying.not) {
582                                 player.play(argClock, doReset, playQuant);
583                         } { 
584                                 if(doReset) { player.reset };
585                         }
586                 }
587         }
588         
589         
592 Pdef : EventPatternProxy {
593         var <key;
594         
595         classvar <>all; 
596                                 
597         storeArgs { ^[key] }
598                 
599         *new { arg key, item;
600                 var res = this.at(key);
601                 if(res.isNil) {
602                         res = super.new(item).prAdd(key);
603                 } {
604                         if(item.notNil) { res.source = item }
605                 }
606                 ^res
607         
608         }
609         
610         map { arg ... args;
611                 if(envir.isNil) { this.envir = () };
612                 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
613         }
614         
615         prAdd { arg argKey;
616                 key = argKey;
617                 all.put(argKey, this);
618         }
619         
620         *initClass {
621                 var phraseEventFunc;
622                 
623                 all = IdentityDictionary.new; 
624                 Class.initClassTree(Event);
625                 
626                 phraseEventFunc = {
627                         var pat, event, outerEvent, recursionLevel, instrument, embeddingLevel, freq, rest;
628                         
629                                 embeddingLevel = ~embeddingLevel ? 0; // infinite recursion catch
630                                 freq = ~freq.value;
631                                 rest = freq.isKindOf(Symbol); // check for outer rests
632                                 if(rest) { ~freq = freq };
633                                 pat = (~repository ? all).at(~instrument);
634                                 
635                                 if(pat.notNil and: { embeddingLevel < 8 })
636                                 {
637                                         pat = pat.pattern; // optimization. outer pattern takes care for replacement
638                                         // preserve information from outer pattern, but not delta.
639                                         
640                                         recursionLevel = ~recursionLevel;
641                                         if(~transparency.isNil or: 
642                                                 { ~transparency > (recursionLevel ? 0) }
643                                         ) {
644                                                 outerEvent = currentEnvironment.copy
645                                         } {
646                                                 outerEvent = Event.default;
647                                                 outerEvent.use {
648                                                         ~type = \phrase;
649                                                         ~recursionLevel = recursionLevel;
650                                                 }
651                                         };
652                                         
653                                         if(recursionLevel.notNil) {
654                                                 if(recursionLevel > 0) {
655                                                         // in recursion, some inner values have to be overridden
656                                                         instrument = ~instrument;
657                                                         pat = pat.collect { |inval|
658                                                                         inval.use {
659                                                                                         ~instrument = instrument;
660                                                                                         ~parent = outerEvent;
661                                                                                         ~recursionLevel = recursionLevel - 1;
662                                                                                 };
663                                                                                 inval
664                                                         };
665                                                 } {
666                                                         // play pattern in the ordinary way
667                                                         ~type = \note;
668                                                 };
669                                         } {     // avoid recursion, if instrument not set.
670                                                 outerEvent.put(\embeddingLevel, embeddingLevel + 1);
671                                                 outerEvent.parent_(Event.parentEvents.default);
672                                         };
673                                         // maybe add a Pprotect here.
674                                         // pat.asProtected
675                                         pat = Pfindur(~sustain.value, pat);
676                                         outerEvent.put(\delta, nil); // block delta modification by Ppar
677                                         outerEvent.put(\instrument, ~synthDef ? \default);
678                                 
679                                         pat.play(thisThread.clock, outerEvent, 0.0);
680                                 } {
681                                         ~type = \note;
682                                         ~play.value;
683                                 }
684                 
685                 };
686                 
687                 Event.addEventType(\phrase, phraseEventFunc);
688         }
689         
694 PbindProxy : Pattern {
695         var <>pairs, <source;
696         
697         *new { arg ... pairs;
698                 ^super.newCopyArgs(pairs).init
699         }
700         init {
701                 forBy(0, pairs.size-1, 2) { arg i;
702                         pairs[i+1] = PatternProxy(pairs[i+1])
703                 };
704                 source = EventPatternProxy(Pbind(*pairs));
705         }
706         embedInStream { arg inval;
707                 ^source.embedInStream(inval)
708         }
709         find { arg key;
710                 pairs.pairsDo { |u,x,i| if(u === key) { ^i } }; ^nil
711         }
712         quant_ { arg val;
713                 pairs.pairsDo { arg key, item; item.quant = val }; // maybe use ref later
714                 source.quant = val;
715         }
716         quant { ^source.quant }
717         envir { ^source.envir }
718         envir_ { arg envir; source.envir_(envir) }
719         
720         at { arg key; var i; i = this.find(key); ^if(i.notNil) { pairs[i+1] } { nil } }
721         
722         // does not yet work with adding arrayed keys/values
723         set { arg ... args; // key, val ...
724                 var changedPairs=false, quant;
725                 quant = this.quant;
726                 args.pairsDo { |key, val|
727                         var i, remove;
728                         i = this.find(key);
729                         if(i.notNil)
730                         { 
731                                 if(val.isNil) {
732                                         pairs.removeAt(i);
733                                         pairs.removeAt(i);
734                                         changedPairs = true;
735                                 }{
736                                         pairs[i+1].source = val
737                                 };
738                         }{ 
739                                 pairs = pairs ++ [key, PatternProxy(val).quant_(quant)];
740                                 changedPairs = true;
741                         };
742                 
743                 };
744                 if(changedPairs) { source.source =  Pbind(*pairs) };
745                 
746         }
747         
748         storeArgs { ^pairs.collect(_.source) }
752 Pbindef : Pdef {
753         *new { arg ... pairs;
754                 var key, pat, src;
755                 key = pairs.removeAt(0);
756                 pat = super.new(key);
757                 src = pat.source;
758                 if(pairs.isEmpty.not) {
759                         if(src.class === PbindProxy) {
760                                 src.set(*pairs);
761                                 pat.wakeUp;
762                         } {
763                                 if(src.isKindOf(Pbind)) 
764                                 {
765                                         src.patternpairs.pairsDo { |key, pat|
766                                                 if(pairs.includes(key).not) { 
767                                                         pairs = pairs.add(key); 
768                                                         pairs = pairs.add(pat);
769                                                 }
770                                         }
771                                 };
772                                 
773                                 src = PbindProxy.new(*pairs).quant_(pat.quant);
774                                 pat.source = src
775                         };
776                 };
777                 
778                 ^pat
779                 
780         }
781         
782         storeArgs { ^[key]++pattern.storeArgs }
783         repositoryArgs { ^this.storeArgs }
784         quant_ { arg val; super.quant = val; source.quant = val }
790 // general purpose lookup stream
792 Pdict : Pattern {
793         var <>dict, <>which, <>repeats, <>default;
794         *new { arg dict, which, repeats=inf, default;
795                 ^super.newCopyArgs(dict, which, repeats, default);
796         }
797         storeArgs { ^[dict,which,repeats,default ] }
798         embedInStream { arg inval;
799                 var keyStream, key;
800                 keyStream = which.asStream;
801                 repeats.value.do({
802                         key = keyStream.next(inval);
803                         if(key.isNil) { ^inval };
804                         inval = (dict.at(key) ? default).embedInStream(inval);
805                 });
806                 ^inval
807         }