Fact(\symbol) -- Factory -- automatic creation

A factory creates an object that depends on other support objects. For example, a voicer may use buffers as wavetables or samples. The factory for this voicer is responsible for creating the buffers and returning the voicer.

Requires an Environment or Event containing:

make: A function to create the Factory's output object and its support objects.

free: For Voicer factories, a function to free supporting objects.

type: A symbol specifying whether this is a \voicer or \bp factory.

keys: Variables from the current environment to import into the factory during instantiation.

Voicer factories:

The ~target environment variable is reserved for the target on which the Voicer will play. Usually this will be a MixerChannel; if not, it should be a Group or Server.

Including keys: [\master] means that if there is a master MixerChannel stored in the environment variable ~master in the current environment, it will be available inside the factory environment. Thus the target MixerChannel can direct its output to the master, and adapt to whichever environment is current at the time it's asked to produce its object.

(

// note: I define my Instr's in the global Instr library

// (.rtf files held in ./Instr/), then reference them by name here.

// for this example, I can't assume you have this Instr.

Instr("analog.ghostly", { arg freq, gate, freqlag, attacktime, decaytime, env, vsense, mul;

var amp;

amp = Sensitivity.kr(1, Latch.kr(gate, gate), vsense);

Mix.ar(Formlet.ar(PinkNoise.ar([0.2, 0.2]), freq*2, attacktime, decaytime, mul))

* EnvGen.kr(env, gate, doneAction:2, levelScale:amp*2.5)

}, [\freq, [0, 1], [0, 2], [0.001, 2, \exponential], [0.001, 2, \exponential],

EnvSpec(Env.adsr(0.01, 1, 0.75, 0.1)), [0, 1], [0, 1]]);

(keys: [\master], make: {

// master is used as the target mixer's output

~target = MixerChannel("ghostly", s, 1, 2, level:0.1, outbus: ~master);

Voicer(10, Instr("analog.ghostly"), [\attacktime, `0.002, \decaytime, `0.9186, \vsense, `0.787, \mul, `0.535], target:~target).latency_(0.5)

},

free: {

~target.free

}, type: \voicer) => Fact(\ghost);

)

// This line creates both the MixerChannel and Voicer.

Fact(\ghost) => VC(\ghost);

// When you free the VC, it remembers the MixerChannel and frees it according to the free function.

VC(\ghost).free;

Passing parameters to Voicer factories:

Use entries in the Factory's environment to declare parameters. The declaration should be outside the 'make' function; if it's inside, 'make' will overwrite any values that you try to pass in.

Any parameters specified in the parms dictionary at chuck time will replace the default values.

This example uses a parameter to alter the amplitude envelope.

(

Instr("analog.ghostly", { arg freq, gate, freqlag, attacktime, decaytime, env, vsense, mul;

var amp;

amp = Sensitivity.kr(1, Latch.kr(gate, gate), vsense);

Mix.ar(Formlet.ar(PinkNoise.ar([0.2, 0.2]), freq*2, attacktime, decaytime, mul))

* EnvGen.kr(env, gate, doneAction:2, levelScale:amp*2.5)

}, [\freq, [0, 1], [0, 2], [0.001, 2, \exponential], [0.001, 2, \exponential],

EnvSpec(Env.adsr(0.01, 1, 0.75, 0.1)), [0, 1], [0, 1]]);

// this is the default envelope, simple adsr

// declared as a pre-existing value in the environment

(envelope: Env.adsr,

make: { |name|

~target = MixerChannel(name, s, 1, 2, level: -10.dbamp);

Voicer(10, Instr("analog.ghostly"), [\attacktime, `0.002, \decaytime, `0.9186,

\vsense, `0.787, \mul, `0.535,

// parameter is used here, by environment variable

\env, ~envelope], target:~target);

}, free: { ~target.free }, type: \voicer) => Fact(\parameterized);

)

// use all default values

Fact(\parameterized) => VC(\defaults);

// use a different envelope

Fact(\parameterized).chuck(VC(\slow), parms: (

envelope: Env.adsr(5, 3, 0.7, 4) // new envelope

));

VC(\defaults).v.gate(440, 2, 0.5);

VC(\slow).v.gate(440, 9, 0.5);

VC(#[defaults, slow]).free;

BP factories:

Since a BP manages its own resources, there is no need for the factory to include a free function.

When creating the BP, the factory must know the name of the output BP. This is passed as the first argument to the make function. So, the general form of a BP factory should be as follows:

(keys: [... any import variables here, as above...],

make: { |name| // |name| receives name of target BP

PR(\myPrototype) => BP(name);

// set other parameters of BP(name) here

// make sure to return BP(name) as the last thing in the function

BP(name)

}, type: \bp) => Fact(\bpFactory);

// Fact => BP passes in the name of the target BP

Fact(\bpFactory) => BP(\myBP);

// does not refer to the Factory; uses its own freeCleanup

BP(\myBP).free;