rsc3/doc-schelp/HelpSource/Classes/Stream.scrbl

358 lines
8.1 KiB
Racket

#lang scribble/manual
@(require (for-label racket))
@title{Stream}
Stream is the base class for classes that define streams@section{related}
Classes/Routine, Classes/FuncStream, Classes/EventStreamPlayer
@section{categories}
Streams-Patterns-Events
@section{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.
@section{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.
@section{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.
@racketblock[
// Example 1: a Function vs. a FuncStream
(
f = { 33.rand };
x = FuncStream(f);
10.do({ [f.next, x.next].postln });
)
::
]
@racketblock[
// 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 });
)
::
]
@section{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.
@racketblock[
// 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.
]
@racketblock[
// example
(
x = Routine({
4.do({
[1,2,3,4].do({ arg i; i.yield; });
});
});
17.do({ x.next.postln });
)
::
]
@section{subsection}
Playing streams
Because streams respond like functions to the value message, they can be used as a scheduling task.
@racketblock[
// 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.
]
@racketblock[
// 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:
]
@racketblock[
// play at the next beat, with offset 0.4
(
Routine({ loop {
"///".postln; (delta:0.5).yield;
"_/_".postln; (delta: 0.1).wait;
} }).asEventStreamPlayer.play;
)
::
]
@section{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::.
@section{subsection}
Composite Streams
Routines can be strong::embedded:: in each other, using link::#-embedInStream:: :
@racketblock[
// 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:
]
@racketblock[
(
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 <>
]
@racketblock[
(
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.
]
@section{subsection}
Unary messages
Streams support most of the unary messages defined in link::Classes/AbstractFunction:: :
@racketblock[
(
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;
)
::
]
@section{subsection}
Binary messages
Streams support the following binary messages defined in link::Classes/AbstractFunction:: :
@racketblock[
(
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;
)
::
]
@section{InstanceMethods}
@section{method}
play
Streams that return strong::numbers:: can be played directly with the strong::play:: message. Streams that return strong::events:: need to be wrapped in an link::Classes/EventStreamPlayer::. See link::#-asEventStreamPlayer::.
@section{argument}
clock
a clock. link::Classes/TempoClock:: by default.
@section{argument}
quant
either a number strong::n:: (quantize to strong::n:: beats), or an array strong::[n, m]:: (quantize to n beats, with offset m).
@section{method}
do
iterate until a nil is encountered.
@section{warning}
Applying do to an endless stream will lock up the interpreter!
::
@section{method}
collect
iterate indefinitely.
@section{method}
reject
return only those elements for which function.value(element) is false.
@section{method}
select
return only those elements for which function.value(element) is true.
@section{method}
dot
return function.value(this.next, stream.next) for each element.
@section{method}
interlace
iterate all of stream for each element of this. Combine the values using function.
@section{method}
appendStream
append stream after this returns nil. The same like ++
@section{method}
embedInStream
iterate all of this from within whatever Stream definition it is called.
@section{method}
trace
print out the results of a stream while returning the original values.
@section{argument}
key
when streaming events, post only this key.
@section{argument}
printStream
printOn this stream (default: link::Classes/Post::).
@section{argument}
prefix
string added to the printout to separate different streams.