Conductor
Superclass: Environment
related classes: ConductorPlayer, ConductorGUI,
CV, SV, CVPreset, CVInterpolator,
Overview
Conductor provides a framework for defining interactive programs in terms of a collection of related components. The Conductor is a kind of Dictionary and its components are stored and accessed as dictionary entries (i.e., key/value pairs). This allows the Conductor to be configured either when it is first defined or in separate code.
The basic components of a Conductor are:
1. ConductorPlayer, which can play, stop, pause, and resume an arbitrary collection of Tasks, Synths, Patterns, and NodeProxys as well as allocate and deallocate resources such s Buffers and Buses. By default a Conductor has a single player, but it can be readily configured to have several.
2. CV, which defines a control value or array of control values constrained to a specific range of values by a ControlSpec. The value of a CV can be set directly or as an input ranging from 0 to 1 rescaled to the CV's range. CVs provide methods to connect to GUI elements, server nodes, buses, and buffers and may be used directly in Pattern definitions. SV (symbolic value), EV (envelope value), and TV (text value) are classes derived from CV which provide similar interfaces.
Typically, a Conductor has multiple CV's.
3. ConductorGUI, which defines the GUI representation of the Conductor
4. CVPreset, which saves the values of a user specified collection of components into selectable presets. CVInterpolator is a preset
that also provides the ability to interpolate between presets.
5. Conductor, one Conductor can be a component of another, providing hierarchical control.
Conductor has the instance variables player, gui, and preset which respectively default to a ConductorPlayer, a ConductorGUI, and a CVPreset.
Class Methods
*new
Example 1: Defining a Conductor using 'new':
( a = Conductor.new; // create the conductor
a.addCV(\cv1); // add some CVs
a.addCV(\cv2);
a.addCV(\cv3);
w = a.show; defer( { Document.current.front}, 0.05); // display it
)
( a[\cv1] .sp(440, 20, 20000, 0, 'exp'); // set the range of cv1
a[\cv2] .sp(0.1, 0.01, 1, 0, 'exp'); // now cv2
)
( w.close; // close the display window
a[\cv3].value = 1/(1..128); // and change cv3 to represent an array
defer( { w = a.show; Document.current.front}, 0.1); // and show it now
)
( w.close;
a.useInterpolator; // add the use of an interpolator
a[\cv3].value = 1/(1..128); a.preset.addPreset; // save some presets
a[\cv3].value = 1/(1..128).reverse; a.preset.addPreset;
a.preset.presetCV.value_(0);
defer( { w = a.show; Document.current.front}, 0.1);
)
Task { // interpolate between the presets
loop {
a.preset.targetCV.value_(1);
100.do {| i | a.preset.interpCV.value_(i/100); 0.01.wait };
a.preset.targetCV.value_(0);
100.do {| i | a.preset.interpCV.value_(i/100); 0.02.wait };
}
}.play;
w.close;
*make (function)
The first argument of the function is set to the Conductor being constructed.
Subsequent arguments are initalized as CVs; arguments with default values are
set to instances of the class the default value specifies.
Below, the first line defines a Conductor with four CV's assigned to the arguments a,b,c,d.
The second line displays that Conductor.
(
c = Conductor.make { | conductor, a, b, c, d | };
c.show;
)
( // here the CV d is initialized to an array of values.
c = Conductor.make { | conductor, a, b, c, d | d.value_(1/(1..128)) };
c.show;
)
using 'make' to define example 1 above
( a = Conductor.make { | con, cv1, cv2, cv3 |
cv1 .sp(440, 20, 20000, 0, 'exp'); // set the value range of cv1
cv2 .sp(0.1, 0.01, 1, 0, 'exp'); // set the value range of cv1
con.useInterpolator; // we want to use an interpolator
con.presetKeys_(#[cv3]); // we want to save cv3 only
con.interpKeys = #[cv3]; // we only want to interpolate cv3
cv3.value = 1/(1..128); // save some presets
con.preset.addPreset;
cv3.value = 1/(1..128).reverse;
con.preset.addPreset;
con.preset.presetCV.value = 1; // select a preset
con.task_({ // this task is added to the player object
loop { // once the player has something to play,
// its start button will appear in the GUI
con.preset.targetCV.value_(0);
100.do {| i | con.preset.interpCV.value_(i/100); cv2.value.wait };
con.preset.targetCV.value_(1);
100.do {| i | con.preset.interpCV.value_(i/100); (cv2.value * 2).wait } ;
}
});
}.show
)
Instance Variables
gui - an instance of [ConductorGUI] .
preset - an instance of [ConductorPreset], which provides preset values for a user specified collection of CV's and Conductor's.
player - an instance of [ConductorPlayer], which provides unified stop/play/pause/resume control for Patterns, Tasks, and, on the server, synths, groups, buses, and buffers. (These objects use a variant of Server-sendBundle to guarantee correct order execution on the server.)
path - stores the pathname of the file that saves the Conductor's settings and attempts to load those settings
valueKeys - an array of keys that identify which components should be saved. This usually includes both indvidual CVs and the Conductor's preset.
Instance Methods
GUI related methods:
show (argName, x, y, w, h)
Draw the Conductor within a window named argName at x,y with size w,h.
draw (window, name, conductor)
Draw the Conductor within the specified window
See ConductorGUI for more details
Settings related methods:
path_ (filePath)
Load the settings stored in the file identified by filePath.
value
Returns an array of the values of all components identified by valueKeys
value_ (valueArray)
Iterates over the the array assigning the value to be the value of the corresponding component identified by valueKeys
noSettings no file controls are displayed
useSettings allow a single set of settings to be saved to file and restored from file
Preset related methods:
presetKeys_ (keys, preset)
The objects at the keys will have their settings saved and restored by preset, which
defaults to the object in the preset instance variable.
interpKeys_ (keys, preset)
The objects at the keys (which must be a subset of the valueKeys of the preset) can
have their settings interpolated between preset values.
usePresets creates a CVPreset, gives it valueKeys as its default presetKeys
useInterpolator creates a CVInterpolator, sets valueKeys to be both presetKeys
and interpKeys
Player related methods:
stop
play
pause
resume
action_ (playFunc, stopFunc, pauseFunc, resumeFunc)
Adds an ActionPlayer which responds to play, stop, pause, and resume by evaluating
the corresponding function with the Conductor as currentEnvironment.
task_ (function, clock, quant)
Adds a TaskPlayer which plays the function within a task scheduled by the specified clock
and quantization. (On stop, tasks that block on a message port are also be deleted.)
pattern_ (pattern, clock, event, quant)
Adds a PatternPlayer which plays the pattern with the specified event, clock and quantization.
group_ ( event, args)
The event is assigned CVEvent-groupEvent as its parent.
synth_ ( event, args)
The event is assigned CVEvent-synthEvent as its parent.
The argument args is an interleaved array of keys and CVs (or value).
CVs can also be altered before being sent to the server and combinations of CVs can
determine the value to be sent:
value [freq: 440 ]
CV [freq: aCV ]
altered CV [freq: [ aCV, aCV.midicps ] ]
combination [freq: [ [aCV, bCV], aCV.midicps + bCV] ]
function [freq: [ aCV, { aCV.midicps.value + 33.rand }]
The events use the same keys as note events in patterns. The keys server, group, and
addAction and, for synths, instrument determine the group or synth. As in patterns, the
default values for these keys are
server: Server.default,
group: 1,
addAction: 0,
instrument: 'default'
Usually the node ID of the group or synth is dynamically allocated, but the key id can be set
to set the id directly. For group events, a new group or collection of groups is created with the
specified id(s). For synth events, no synths are created, but the control values determined by
the event are sent to the specified id(s).
controlBus_ ( event, cvs)
The event can specify
server: aServer (defaults to Server.default)
index: (optional)
CVs is an array of CVs that are used to determine the value of consecutive buses
buffer_ ( event)
This event is designed primarily for small waveform buffers, it specifies:
server: aServer (defaults to Server.default)
cv: a CV that determines the values in the buffer
msg: A symbol that determines how the values are used to fill the buffer.
Is is one of: \sine1, \cheby, \wave, or \signal
display: anotherCV
An optional CV used to display the contents of the buffer (as received from the server)
size: integer (defaults to 512 and should not exceed 1024).
Using Conductor
The messages spec_(specName, default) and sp(default, lo, hi, step, warp) can be used to set
the range of values a CV can assume (see [CV] for details). In this example, the CV assigned to
d is given an array as a default value.
(
// Changing CV ranges
c = Conductor.make { |conductor, a, b, c, d |
a .spec_(\freq);
b .spec_(\freq, 880);
c .sp(1, 0, 15, 1);
d .spec_(\unipolar,1/(1..128));
};
c.show;
)
(
// action_can control any kind of user program,
c = Conductor.make { |conductor, freq, db, dur |
freq .spec_(\freq);
db .spec_(\db, -10);
dur .sp(0.2, 0.05, 1, 0, 'exp');
// add a pattern using actions,
// notice the use of ~player, an environment variable
// within the Conductor
conductor.action_(
{ ~pat = Pbind(*[freq: freq * 2, db: db, dur: dur/2])
.play(quant: 0);
},
{ ~pat.stop },
{ ~pat.pause},
{ ~pat.resume}
);
conductor.name_("test");
};
c.show;
)
(
// but convenience methods such as pattern_ are more concise
c = Conductor.make { |conductor, freq, db, dur |
freq .spec_(\freq);
db .spec_(\db, -10);
dur .sp(0.2, 0.05, 1, 0, 'exp');
conductor.pattern_(
Pbind(*[freq: freq * 2, db: db, dur: dur/2]),
quant: 0
)
};
c.show;
)
(
// Controlling a synth
Conductor.make({ arg conductor, freq, volIndB;
freq .spec_(\freq);
volIndB .sp(-20,-100,20);
conductor.synth_(
( instrument: \default, // this Event is explicitly specifying all the default values
addAction: 1,
group: 1,
server: Server.default
),
[freq: freq, amp: [volIndB, volIndB.dbamp], pan: -1 ]);
}).show
)
(
// Controlling a Pattern and a Group
// 'vol' is assigned to both the Pattern and the Pattern's group
// this provides continuous control of the pattern's notes as they are sounding
b = Conductor.make({ arg conductor, freq, vol;
freq .spec_(\freq);
vol .sp(-20,-100,20);
conductor.name_( "a group used to control a pattern's synths");
conductor.group_( (id: [22], group: 0, addAction: 1), [amp: [vol, vol.dbamp], freq: freq ]);
conductor.pattern_(Pbind
(\freq, Prand([1, 3/2, 5/4, 7/4], inf) * freq + Ptuple([Pwhite(-1.0,1),Pwhite(-1.0,1)]),
\db, vol,
\dur, Prand([0.2, 1.2],inf),
\group, 22,
\legato, 10),
quant:0 );
} );
b.show;
)
(
// Using a Buffer and interpolation
c = Conductor.make({ arg conductor, freq, vol, overtones, waveform;
var buf;
freq .spec_(\freq);
vol .sp(-20,-100,20);
overtones .sp(1/(1..64),0, 1);
waveform .spec_(\bipolar, Array.fill(512,0));
SynthDef ("osc", { |out = 0, freq = 200, amp = 0.1, bufnum |
Out.ar(out, Osc.ar(bufnum, freq, 0, amp) )
}).store;
conductor.name_("synth");
conductor.buffer_(buf = (msg: \sine1, cv: overtones, display: waveform) );
b = buf;
conductor.synth_(a = (instrument: \osc), [
freq: [freq, freq * 3/2], amp: [vol, vol.dbamp], bufnum: { buf.bufnum } ])
});
c.valueKeys_(#[overtones]);
c.useInterpolator;
// c.usePresets;
d = c.show;
)