class:: Stream summary:: Stream is the base class for classes that define streams related:: Classes/Routine, Classes/FuncStream, Classes/EventStreamPlayer categories:: Streams-Patterns-Events description:: Stream is an abstract class that is not used directly. The following attempts to document some aspects of the use of Streams for music generation. subsection::Overview A Stream represents a sequence of values that are obtained incrementally by repeated strong::next:: messages. A Stream can be restarted with a strong::reset:: message. (Not all streams actually implement reset semantics.) The class link::Classes/Object:: defines strong::next:: to return the object itself. Thus every object can be viewed as a stream and most simply stream themselves. In SuperCollider, Streams are primarily used for handling text and for generating music. subsection::FuncStream(nextFunction, resetFunction) A link::Classes/Function:: defines a stream consisting of the Function itself, a link::Classes/FuncStream:: defines a stream that consists of emphasis::evaluations:: of its nextFunction. code:: // Example 1: a Function vs. a FuncStream ( f = { 33.rand }; x = FuncStream(f); 10.do({ [f.next, x.next].postln }); ) :: code:: // Example 2: the reset function ( f = { 33.rand }; x = FuncStream(f, {thisThread.randSeed_(345)}); x.reset; 10.do({ [f.next, x.next].postln }); x.reset; 10.do({ [f.next, x.next].postln }); ) :: subsection::Routine(nextFunction, stacksize) In a link::Classes/FuncStream::, the nextFunction runs through to completion for each element of the stream. In a link::Classes/Routine::, the nextFunction returns values with strong::yield:: and resumes execution (when it receives a strong::next:: message) at the expression following the yield. This allows a sequence of expressions in the function definition to represent a sequence of distinct events, like a musical score. code:: // example ( x = Routine({ 1.yield; 2.yield; 3.yield; }); 4.do({ x.next.postln }); ) :: Once the nextFunction completes execution, the Routine simply yields nil repeatedly. Control structures (such as strong::do:: or strong::while::) can be used within the nextFunction in a manner analogous to repeat marks in a score. code:: // example ( x = Routine({ 4.do({ [1,2,3,4].do({ arg i; i.yield; }); }); }); 17.do({ x.next.postln }); ) :: subsection::Playing streams Because streams respond like functions to the value message, they can be used as a scheduling task. code:: // compare: // a function, returning 0.5 ( SystemClock.sched(0.0, { "***".postln; 0.5 } ); ) // a stream, returning 0.5 and 0.1 ( SystemClock.sched(0.0, Routine({ loop { "***".postln; 0.5.yield; "_*_".postln; 0.1.yield; } }); ); ) // this is the reason why 'wait' works the same (for numbers) like 'yield' ( SystemClock.sched(0.0, Routine({ loop { "***".postln; 0.5.wait; "_*_".postln; 0.1.wait; } }); ); ) :: Streams that return strong::numbers:: can be played directly with the strong::play:: message. code:: // play at the next beat, with offset 0.4 ( Routine({ loop { "***".postln; 0.5.wait; "_*_".postln; 0.1.wait; } }).play(quant:[1, 0.4]); ) :: Streams that return strong::Events:: need to be wrapped in an link::Classes/EventStreamPlayer::. The Event's strong::delta:: (can also be set by strong::dur::) is used as a scheduling beats value: code:: // play at the next beat, with offset 0.4 ( Routine({ loop { "///".postln; (delta:0.5).yield; "_/_".postln; (delta: 0.1).wait; } }).asEventStreamPlayer.play; ) :: subsection::Iteration The method link::#-do:: effectively 'plays' a stream by iterating all of its contents. And the following messages create a stream by filtering another stream in some way: link::#-collect::, link::#-reject::, link::#-select::, link::#-dot::, link::#-interlace::, link::#-appendStream::, link::#-embedInStream::, link::#-trace::. subsection::Composite Streams Routines can be strong::embedded:: in each other, using link::#-embedInStream:: : code:: // example ( x = Routine({ 2.do({ [1,2,3,4].do({ arg i; i.yield; }); }); }); y = Routine({ 100.yield; 30.yield; x.embedInStream; 440.yield; 1910.yield; }); 17.do({ y.next.postln }); ) :: Routines can be strong::concatenated:: just like Streams: code:: ( x = Routine({ 2.do({ [1,2,3,4].do({ arg i; i.yield; }); }); }); y = Routine({ 100.yield; 30.yield; }); z = x ++ y; 17.do({ z.next.postln }); ) :: Routines can be strong::combined:: with the composition operator <> code:: ( x = Routine({ arg inval; 2.do({ [1,2,3,4].do({ arg i; if(inval.isNil) { nil.alwaysYield }; inval = (i * inval).yield; }); }); }); y = Routine({ 100.yield; 30.yield; 4.do { 1.0.rand.yield }; }); z = x <> y; 17.do({ z.value.postln }); // call .value here, as this is a function. ) :: Composite Streams can be defined as combinations of Streams using the unary and binary messages. subsection::Unary messages Streams support most of the unary messages defined in link::Classes/AbstractFunction:: : code:: ( a = Routine({ 20.do({ 33.rand.yield }) }); b = Routine({ [-100,00,300,400].do({ arg v; v.yield}) }); c = b.neg; // define a composite stream // enumerate and perform all of the unary messages: [ \neg, \reciprocal, \bitNot, \abs, \asFloat, \asInteger, \ceil, \floor, \frac, \sign, \squared, \cubed, \sqrt, \exp, \midicps, \cpsmidi, \midiratio, \ratiomidi, \ampdb, \dbamp, \octcps, \cpsoct, \log, \log2, \log10, \sin, \cos, \tan, \asin, \acos, \atan, \sinh, \cosh, \tanh, \rand, \rand2, \linrand, \bilinrand, \sum3rand, \distort, \softclip, \coin, \even, \odd, \isPositive, \isNegative, \isStrictlyPositive ] .do({ arg msg; postf("\n msg: % \n", msg); b.reset.perform(msg).do({arg v; v.post; " ".post;}); }); nil; ) :: subsection::Binary messages Streams support the following binary messages defined in link::Classes/AbstractFunction:: : code:: ( a = Routine({ 20.do({ 33.rand.yield }) }); b = Routine({ [-100,00,300,400].do({ arg v; v.yield}) }); [ '+' , '-' , '*', '/', \div, '%', '**', \min, \max, '<', '<=', '>', '>=', '&', '|', \bitXor, \lcm, \gcd, \round, \trunc, \atan2, \hypot, '>>', '+>>', \ring1, \ring2, \ring3, \ring4, \difsqr, \sumsqr, \sqrdif, \absdif, \amclip, \scaleneg, \clip2, \excess, '