#lang scribble/manual @(require (for-label racket)) @title{Understanding Streams, Patterns and Events - Part 2} Patterns Introduction@section{related} Tutorials/Streams-Patterns-Events1, Tutorials/Streams-Patterns-Events3, Tutorials/Streams-Patterns-Events4, Tutorials/Streams-Patterns-Events5, Tutorials/Streams-Patterns-Events6, Tutorials/Streams-Patterns-Events7 @section{categories} Tutorials>Streams-Patterns-Events @section{section} Patterns Often one wants to be able to create multiple streams from a single stream specification. Patterns are just a way to make multiple Streams from a single specification, like a cookie cutter. A pattern can be any object that responds to the @racketblock[asStream:: message by creating a link::Classes/Stream::. Once again there is a default implementation in class link::Classes/Object:: of asStream that simply returns the receiver as its own stream. Thus any object is by default a pattern that returns itself as a stream when sent the asStream message. ] @racketblock[ ( a = 7.asStream; a.postln; a.next.postln; ) :: ] @section{section} Pattern and its subclasses There is a class named link::Classes/Pattern:: that provides more functionality for the concept of a pattern. A link::Classes/Pfunc:: is a Pattern that returns a link::Classes/FuncStream::. The same function arguments are supplied as are supplied to FuncStream. @racketblock[ ( var a, b; a = Pfunc.new({ #[1, 2, 3, 4].choose }); b = a.asStream; // make a stream from the pattern 5.do({ b.next.postln; }); // print 5 values from the stream ) :: A link::Classes/Prout:: is a Pattern that returns a link::Classes/Routine::. The same function argument is supplied as is supplied to Routine. ] @racketblock[ ( var a, b, c; a = Prout.new({ 3.do({ arg i; 3.rand.yield; }) }); // make two streams from the pattern b = a.asStream; c = a.asStream; 4.do({ b.next.postln; }); // print 4 values from first stream 4.do({ c.next.postln; }); // print 4 values from second stream ) :: A link::Classes/Pseries:: is a Pattern that generates an arithmetic series. ] @racketblock[ ( var a, b; a = Pseries.new(10, 3, 8); // stream starts at 10, steps by 3 and has length 8 b = a.asStream; 9.do({ b.next.postln; }); // print 9 values from stream ) :: link::Classes/Pgeom:: is a Pattern that generates a geometric series. ] @racketblock[ ( var a, b; a = Pgeom.new(10, 3, 8); // stream starts at 10, steps by factor of 3 and has length 8 b = a.asStream; 9.do({ b.next.postln; }); // print 9 values from stream ) :: ] @section{section} Math operations on Patterns Patterns also respond to math operators by returning patterns that respond to @racketblock[asStream:: with appropriately modified streams. Applying a unary operator to a pattern ] @racketblock[ ( var a, b, c; a = Pseries.new(0,1,10); // a is a pattern whose stream counts from 0 to 9 b = a.squared; // pattern b is a square of the pattern a c = b.asStream; 12.do({ c.next.postln; }); ) :: Using a binary operator on a pattern ] @racketblock[ ( var a, b, c; a = Pseries.new(0,1,10); // a is a pattern whose stream counts from 0 to 9 b = a + 100; // add a constant value to pattern a c = b.asStream; 12.do({ c.next.postln; }); ) :: ] @section{section} Filtering operations on patterns Patterns also respond to the messages @racketblock[collect::, ] @racketblock[select::, and ] @racketblock[reject:: by returning a new link::Classes/Pattern::. The ] @racketblock[collect:: message returns a Pattern whose link::Classes/Stream:: is modified by a function in the same way as the collect message sent to a Collection returns a modified Collection. ] @racketblock[ ( var a, b, c; // a is a pattern whose stream counts from 0 to 9 a = Pseries.new(0,1,10); // b is a pattern whose stream adds 100 to even values b = a.collect({ arg item; if (item.even, { item + 100 },{ item }); }); c = b.asStream; 6.do({ c.next.postln; }); ) :: The ] @racketblock[select:: message creates a pattern whose stream passes only items that return true from a user supplied function. ] @racketblock[ ( var a, b, c; // a is a pattern whose stream counts from 0 to 9 a = Pseries.new(0,1,10); // b is a pattern whose stream only returns the odd values b = a.select({ arg item; item.odd; }); c = b.asStream; 6.do({ c.next.postln; }); ) :: The ] @racketblock[reject:: message creates a pattern whose stream passes only items that return false from a user supplied function. ] @racketblock[ ( var a, b, c; // a is a pattern whose stream counts from 0 to 9 a = Pseries.new(0,1,10); // b is a pattern whose stream that only returns the non-odd values b = a.reject({ arg item; item.odd; }); c = b.asStream; 6.do({ c.next.postln; }); ) :: ] @section{section} Making Music with Patterns Here is a variation of the example given in part 1 that uses a link::Classes/Pattern:: to create two instances of the random melody stream. @racketblock[ ( s = Server.local; SynthDef(\help_SPE2, { arg i_out=0, sustain=1, freq; var out; out = RLPF.ar( LFSaw.ar( freq ), LFNoise1.kr(1, 36, 110).midicps, 0.1 ) * EnvGen.kr( Env.perc, levelScale: 0.3, timeScale: sustain, doneAction: Done.freeSelf ); //out = [out, DelayN.ar(out, 0.04, 0.04) ]; 4.do({ out = AllpassN.ar(out, 0.05, [0.05.rand, 0.05.rand], 4) }); Out.ar( i_out, out ); }).send(s); ) ( // streams as a sequence of pitches var pattern, streams, dur, durDiff; dur = 1/7; durDiff = 3; pattern = Prout.new({ loop({ if (0.5.coin, { #[ 24,31,36,43,48,55 ].do({ arg fifth; fifth.yield }); }); rrand(2,5).do({ // varying arpeggio 60.yield; #[63,65].choose.yield; 67.yield; #[70,72,74].choose.yield; }); // random high melody rrand(3,9).do({ #[74,75,77,79,81].choose.yield }); }); }); streams = [ (pattern - Pfunc.new({ #[12, 7, 7, 0].choose })).midicps.asStream, pattern.midicps.asStream ]; Routine({ loop({ Synth( \help_SPE2, [ \freq, streams.at(0).next, \sustain, dur * durDiff ] ); durDiff.do({ Synth( \help_SPE2, [ \freq, streams.at(1).next, \sustain, dur ] ); dur.wait; }); }) }).play ) :: To go to the next file: link::Tutorials/Streams-Patterns-Events3:: ]