Chucking process classes

Pattern prototype classes:

PR(\symbol) -- PRocess prototype

A process is an AdhocClass that defines functions needed to maintain a musical stream. The process architecture will be described in another document.

AdhocClass({

~asPattern = #{

SynthDescLib.global.read; // other initialization may take place here too

Pbind(\degree, Pn(Plazy({ Pseq((0..7).scramble, 1) }), inf), \delta, 0.125,

\sustain, 0.16, \instrument, \default);

};

}) => PR(\simplePbind);

Event.default => PR(\simplePbind); // set the event prototype

Now, this example is a bit stupid because you could just write the Pbind and play it as is. PRs have more flexibility, though, because the pattern may be defined in terms of other functions held in the AdhocClass. You can change the behavior of the process dramatically by changing environment variables within the AdhocClass, even while playing. See the scwork/chucklib/startup##.rtf files for one architecture that does this. and the [ChuckExamples] helpfile for some sample usage scenarios.

BP(\symbol) -- Bound Process

The process prototype (PR) defines functionality without any musical data. When you bind musical material to a process prototype, you have a bound process (BP).

PR's cannot be played. BP's can.

PR(\simplePbind) => BP(\pbind1);

BP(\pbind1).play(4); // start it on the next multiple of 4 beats

BP(\pbind1).stop(4);

BP(\pbind1).free; // you ought to clean up after yourself

Objects that can be chucked into a bound process:

Processes:

BP

PR

AdhocClass

These prepare the BP to receive data. If the BP already holds a process, chucking another BP, PR or AdhocClass will wrap the current process inside the new one, based on the adverb:

=>.wrap -- default action if no adverb. If the process is already wrapped, remove the current wrapper first. Then add the new wrapper.

=>.nest -- wrap the process, whether it's already wrapped or not.

=>.replace -- replace the current bound process with the new one.

It is supported to chuck fully realized BPs into other BPs. The receiving BP gets a copy of the incoming process's AdhocClass. This, for instance, is a way to save the state of a process to reuse.

BP(\ch1).unwrap => BP(\saveWrapper);

// or:

BP(\ch1).v.child => BP(\saveChild);

// later:

BP(\saveWrapper) =>.wrap BP(\ch2); // apply earlier wrapper to another chord process

Quantization factors:

nil (converts to NilTimeSpec)

SimpleNumber (converts to BasicTimeSpec)

Array (converts to QuantOffsetTimeSpec)

TimeSpec

Chucking one of these into a BP defines the default quantization for that process, or for all processes if chucked into the class BP.

TempoClock -- set the clock for process. May be done only when the process is not running. You can set a default clock for all processes by chucking into the BP class.

Material from MIDI:

MIDIRecBuf -- binds the buffer as it is. Use the adverb to determine where it should go. The process will define how it responds to different adverbs.

MBM (MIDIBufManager) -- binds the current buffer as shown in the GUI.

You can also index a MBM directly: MBM(0)[0] returns the first midi buffer stored in the MBM at index 0. This may then be chucked into the BP. The index may be an integer or symbolic name.

Support patterns:

Pattern

MacRh or subclasses

Symbol

Some processes depend on patterns for various parameters (most importantly, MacRh, MicRh, ArpegPat, Func). Use an adverb to determine which pattern you're sending in.

// choose arpeggiation types at random from the list

Prand([\up, \down, \random], inf) =>.arpeg BP(\ch1);

A Symbol may be used to retrieve a predefined pattern from the Pdefn class; if the Pdefn doesn't exist, the Symbol will be turned into a pattern that outputs itself: Pn(symbol, inf).

Pdefn(\randomArpeg, Prand([\up, \down, \random], inf)); // in your setup code; then...

\randomArpeg =>.arpeg BP(\ch1);

// to use only one arpeggiation pattern:

\down =>.arpeg BP(\ch1); // BP receives Pn(\down, inf)

BP scheduling:

Chucklib scheduling is compatible with pattern scheduling. The quant argument helps compute the absolute time (in beats) when the first event should occur. The event stream player thread will wake up at exactly that time. Events use the server's latency settings to delay execution of OSC messages to the server just slightly, preventing timing jitter due to inconsistencies in the amount of time it takes to transmit each message over the network. (See the server timing help file for more information.) Since all events delay by the same amount, everything remains in sync.

For the quant argument, you may use:

1 (any SimpleNumber) -- begin playing at the next multiple of this number

[a, b] (where a and b are simple numbers) -- begin playing at b + the next multiple of a

NilTimeSpec() -- begin playing exactly now

DelayTimeSpec(number) -- begin playing exactly now + number

AbsoluteTimeSpec(number) -- begin playing at exactly the time specified

This object is not valid after the given time passes.

BasicTimeSpec(number) -- begin playing at the next multiple of this number

QuantOffsetTimeSpec(a, b) -- begin playing at b + the next multiple of a

Note that BasicTimeSpec is the same thing as using a simple number, and QuantOffsetTimeSpec is the same as a two element array.

Each BP also has a leadTime property, which is normally zero (in which case it has no effect on the above scheme). Values greater than zero shift the thread scheduling earlier by that number of beats. At the same time, events are delayed by the same number of beats. Processes with different values of leadTime should sound exactly in sync, but processes with higher values will execute earlier on the client. This is helpful if one process supplies information to another process -- the supplier must always execute earlier.

You may not change the leadTime while the BP is playing. Negative values are not permitted.

Using BP:

Methods:

free -- Stop playing now and clear references to objects for garbage collection.

play(quant, clock, doReset = false)

reset -- Rebuild the event stream.

stop(quant)

stopNow -- stop this and all child processes immediately.

Useful internal methods:

prepareForPlay(quant, clock, doReset = false) -- populate objects in the adhoc class needed for play.

eventSchedTime(quant) -- populate the absolute number of beats on the clock for scheduling, based on the given quantization factor. If none is given, fall back to the quantization factor in the adhoc class, and lastly to the classwide default.

asPattern -- request a pattern from the adhoc class.

asStream -- call asPattern, and wrap the resulting stream in a CleanupStream to handle early termination.

asEventStreamPlayer -- produce the task that will play the process.