Stream : AbstractFunction : Object

Stream is the base class for classes that define streams
Source: Stream.sc

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.

Overview

A Stream represents a sequence of values that are obtained incrementally by repeated next messages. A Stream can be restarted with a reset message. (Not all streams actually implement reset semantics.)

The class Object defines 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.

FuncStream(nextFunction, resetFunction)

A Function defines a stream consisting of the Function itself, a FuncStream defines a stream that consists of evaluations of its nextFunction.

// Example 1: a Function vs. a FuncStream
(
    f = { 33.rand };
    x = FuncStream(f);
    10.do({ [f.next, x.next].postln });
)
// 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 });
)

Routine(nextFunction, stacksize)

In a FuncStream, the nextFunction runs through to completion for each element of the stream. In a Routine, the nextFunction returns values with yield and resumes execution (when it receives a 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.

// 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 do or while) can be used within the nextFunction in a manner analogous to repeat marks in a score.

// example
(
    x = Routine({
        4.do({
            [1,2,3,4].do({ arg i; i.yield; });
        });
    });
    17.do({ x.next.postln });
)

Playing streams

Because streams respond like functions to the value message, they can be used as a scheduling task.

// 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 numbers can be played directly with the play message.

// 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 Events need to be wrapped in an EventStreamPlayer. The Event's delta (can also be set by dur) is used as a scheduling beats value:

// play at the next beat, with offset 0.4
(
Routine({ loop {
    "///".postln; (delta:0.5).yield;
    "_/_".postln; (delta: 0.1).wait;
} }).asEventStreamPlayer.play;
)

Iteration

The method -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: -collect, -reject, -select, -dot, -interlace, -appendStream, -embedInStream, -trace.

Composite Streams

Routines can be embedded in each other, using -embedInStream :

// 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 concatenated just like Streams:

(
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 combined with the composition operator <>

(
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.

Unary messages

Streams support most of the unary messages defined in AbstractFunction :

(
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;
)

Binary messages

Streams support the following binary messages defined in AbstractFunction :

(
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, '<!', \rrand, \exprand
]
.do({ arg msg;
    postf("\n msg: % \n", msg);
    b.reset.perform(msg).do({ arg v; v.post; " ".post; });
});
nil;
)

Class Methods

Inherited class methods

Instance Methods

.play(clock, quant)

Streams that return numbers can be played directly with the play message. Streams that return events need to be wrapped in an EventStreamPlayer. See -asEventStreamPlayer.

Arguments:

clock

a clock. TempoClock by default.

quant

either a number n (quantize to n beats), or an array [n, m] (quantize to n beats, with offset m).

.do(function, inval)

iterate until a nil is encountered.

WARNING: Applying do to an endless stream will lock up the interpreter!

.collect(argCollectFunc)

iterate indefinitely.

.reject(function)

return only those elements for which function.value(element) is false.

.select(function)

return only those elements for which function.value(element) is true.

.dot(function, stream)

return function.value(this.next, stream.next) for each element.

.interlace(function, stream)

iterate all of stream for each element of this. Combine the values using function.

.appendStream(stream)

append stream after this returns nil. The same like ++

.embedInStream(inval)

iterate all of this from within whatever Stream definition it is called.

.trace(key, printStream, prefix: "")

print out the results of a stream while returning the original values.

Arguments:

key

when streaming events, post only this key.

printStream

printOn this stream (default: Post).

prefix

string added to the printout to separate different streams.

Inherited instance methods

Undocumented instance methods

++(stream)

<>(obj)

.all(inval)

.asEventStreamPlayer(protoEvent)

.buildForProxy

From extension in /usr/local/share/SuperCollider/SCClassLibrary/JITLib/ProxySpace/wrapForNodeProxy.sc

.collate(stream)

.composeBinaryOp(argSelector, argStream, adverb)

.composeNAryOp(argSelector, anArgList)

.composeUnaryOp(argSelector)

.fastForward(by, tolerance: 0, inevent)

From extension in /usr/local/share/SuperCollider/SCClassLibrary/JITLib/Patterns/extRoutine.sc

.generate(function, item)

.iter

.next

.parent

.proxyControlClass

From extension in /usr/local/share/SuperCollider/SCClassLibrary/JITLib/ProxySpace/wrapForNodeProxy.sc

.put(item)

.putAll(aCollection)

.putN(n, item)

.repeat(repeats: inf)

.reverseComposeBinaryOp(argSelector, argStream, adverb)

.streamArg

.subSample(offset: 0, skipSize: 0)

.value(inval)

.valueArray