CV
superclass: Stream
related classes: SV, Conductor
A CV models a single floating point value or an array of such values constrained to a specific numerical
range by a ControlSpec. [SV] is a derived class that defines an index into an array.
CV is derived from Stream so it can be used directly in Stream and Pattern definitions.
CV:connect(view) will connect a CV to a GUI ControlView in both Cocoa and Swing.
GUI representations. A similar set of methods have been defined that connect
argument arrays consisting of keys and CVs to nodes, buses, buffers, and NodeProxys.
An SV is a CV that models an index into an array of Symbols. The array is held in the instance
variable items. The symbol corresponding to the CV's current value can be accessed with the
method item.
Creation
*new( spec, initialValue)
spec - Any object that responds to asSpec (Nil, Symbol, ControlSpec, Array) with a ControlSpec
initialValue - the initialValue is constrained to lie within the range of the spec
Note: some common ControlSpecs include:
unipolar, bipolar, freq, lofreq, midfreq, widefreq, phase, rq,
audiobus, controlbus, midi, midinote, midivelocity,
db, amp, boostcut, pan, detune, rate, beats, delay
Setting the range of values on existing CVs
spec_(spec, default value)
spec - Any object that responds to asSpec (Nil, Symbol, ControlSpec, Array) with a ControlSpec
defaultValue - the initial value of the CV
sp( val, min, max, step, warp)
val inital value
min smallest possible value
max largest possible value
step smallest incremental change in GUI
warp either 'linear' or 'exponential'
In the case of exponential warp,
min and max must be non-zero and the same sign.
Examples:
a = CV(\freq, 480);
b = CV.new.spec_(\bipolar, -1);
c = CV.new.sp(0, 0 , 100);
connect (view)
Connects a CV to a view.
Array defines connect to allow a set of CVs to be connected to a view with multiple control values (i.e., Slider2D.
Accessing
value
Answer the current value of the control. This is how the control is read within tasks and patterns
input
a number ranging form 0 to 1 that corresponds to postion of the current value between min and max
input_ (inputValue)
an inputValue ranging form 0 to 1 results in this.value ranging from min to max.
This method is typically used by external controls (such as MIDI) and GUI views to alter the CV.
value_ (newValue)
Set the current value of the control, irrespective of the range settings.
windex
Interprets the value of the CV as a probability table and returns a weighted random value
indexedBy (key)
For use in Patterns: uses the value of key in the current Event as an index and returns the
indexed value
CV connections using SimpleControllers
When a CV's value is changed a changed message (with 'synch' as the identifier) is send to update
any dependant objects. For example, action_(function) creates a SimpleController which is added to
the dependants of the CV and evaluated whenever the CV changes value. This same basic mechanism
is used to connect the CV to GUI's, server, objects, and some other objects in the language. Most of
this is more or less hidden from view.
Under normal circumstances, CV connections are automatically removed when the targeted Control,
Bus, or View is deleted. If there is a program error, it is possible that connections will persist and will
need to be explicitly removed.
action_ (function)
Create a dependant SimpleController that evaluates the function whenever the CV's value is altered.
release
Remove all dependants. (This is actually a method of Object..)
GUI connections
The following methods establish connections Views and CVs.
SCSlider-connect(aCV)
SCNumberBox-connect(aCV)
SC2DSlider-connect([xCV, yCV])
SCRangeSlider-connect([loCV, hiCV])
SCMultiSliderView-connect(aCV) // for CVs with multiple values
SCPopupMenu-connect(aCV) // for SVs, displays SV-items
SCListView-connect(aCV) // for SVs
One CV can be connected to many views but each view is connected to only one CV.
When a CV's value changes, it is relayed to all of its dependants including the source of the
the change. That way, the GUI accurately reflects the new value of the CV. See the behavior
of 'b' in the following example.
The following example provides a generic graphic interface to two CVs.
Subsequent examples depend on that window, so leave it open until you are finished
working through the help file. (The interpreter variables 'a' and 'b' contain the CVs
used by the examples, so they should be left unaltered.)
(
a = CV(\freq); // create a couple of CVs
b = CV.new.sp(-10,-100,20, 10);
w = SCWindow("CV Demo", Rect(64, 0, 400, 300)).front; // make a window
w.view.decorator = FlowLayout(w.view.bounds);
// CVs can be connected to SCSlider and SCNumberBox.
z = SCNumberBox(w, Rect(0, 0, 50, 20));
a.connect(z);
y = SCSlider(w, Rect(50, 0, 150, 20));
a.connect(y);
w.view.decorator.nextLine;
b.connect( SCNumberBox(w, Rect(0, 0, 50, 20)) );
b.connect( SCSlider(w, Rect(50, 0, 150, 20)) );
w.view.decorator.nextLine;
// Pairs of CVs can be connected to SCRangeSlider, SC2DSlider .
[a,b].connect( SCRangeSlider(w, Rect(0, 0, 200, 20)) );
w.view.decorator.nextLine;
[a,b].connect( SC2DSlider(w, Rect(00, 0, 200, 200)) );
)
Server connections
OSC commands (i.e., /n_set, /s_new) specify initial values of parameters as a flat array of pairs
consisting of a name and its initial value:
[frequency: 440, amplitude: 1, ...]
"Extended argument arrays" allow CVs to be used in place of the initial value. This is the standard
syntax for establishing connections between CVs and a server. In an extended argument array,
the CV's value can be altered before being sent, multiple CV's can determine the value to be sent,
and the value to be sent can be determined by a function:
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 }]
For example, the method Synth-controls is identical to Synth-new except the args parameter is
extended:
Synth-controls(defName, extendedArgArray, target, addAction)
( // basic CV connection
Synth.controls("default", [
freq: a
]);
)
( // modified CV connection
Synth.controls("default", [
amp: [b, b.dbamp],
freq: a
]);
)
(// multiple modified connection
Synth.controls("default", [
freq: [
[a,b], a + b,
],
amp: [
[a,b], (a.cpsmidi.neg/4 + b).dbamp
]
]);
)
In the previous two examples, the modifyin expression is actually a combination of Streams altered
with binary and unary operators. This is concise, but in some cases, it may be necessary
to use a Function to define the modification. Notice that within a Function definition it is necessary
to explicitly extract the value of the CV using a value message.
(// a Function modifying the CV connection
Synth.controls("default", [
freq: [b, { var index; (index = b.value + 100 /12 ).asInteger;
[100,200,300,400,500, 600].mirror.at(index)
}],
amp: [
[a,b], {(a.value.cpsmidi.neg/4 + b.value).dbamp }
]
]);
)
Summary of Node related connection methods
Array
connectToNode(server,nodeID)
connectToNodeProxy(server,nodeID)
the receiver is a flat Array of name/value pairs
connectToBuffer(server,bufnum)
connectToBus(server, index)
the receiver is an Array of CVs.
Node
setControls(extendedArgArray)
NodeProxy
setControls(extendedArgArray) - connects CVs to the named controls
(
c = CV.new.sp(-20, -100, 120);
p = NodeProxy.audio(s, 2);
p.play; //play to hardware output, return a group with synths
p.setControls([f: a, c: [b, { \freq.asSpec.map(b.input)}] ]);
b.input_(0.5);
p.source = { arg f=400, c = 400; PMOsc.ar(f , c, 4, 0, 0.1) };
)
Synth
*controls(synthDef, extendedArgArray, target, addAction) Thihe same as *new, but allows CV's to be used in the args array.
Bus
*controls(arrayOfCVs,server)
(
c = { arg f = 400, a = -20, pan = 0; Pan2.ar(SinOsc.ar(f, 0, a.dbamp), pan) }.play;
d = Bus.controls([a,b],s);
[a,b].connectToBus(d.server, d.index);
Routine({ // need to make sure the synth has been created...
s.sync;
c.map(\f,d.index,\a,d.index + 1);
}).play;
)
setControls(arrayOfCVS) - connects CVs to consecutive buses
(
c = { arg f = 400, a = -20, pan = 0; Pan2.ar(SinOsc.ar(f, 0, a.dbamp), pan) }.play;
d = Bus.control(s,3);
d.setControls([a,b]);
[a,b].connectToBus(d.server, d.index);
Routine({ // need to make sure the synth has been created...
s.sync;
c.map(\f,d.index,\a,d.index + 1);
}).play;
)
Reading and writing CVs in functions and patterns
Tasks
Within a function, CVs are accessed using value and input and altered using value_ and input_.
(
Task({
10.do({
a.input.post; " ".post; // print the value scaled to the range [0,1]
a.value.postln; // print the actual value
a.input_(1.0.rand); // select a new value at random
// the weighting of values will conform to the warp
// with \exp, values fall uniformly in each octave
wait(0.1);
})
}).play(AppClock)
)
Patterns
Within a pattern definition, a CV can directly substitute for a Pattern:
(
SynthDescLib.global.read;
Pbind( \instrument, \default,
\freq, a,
\db, b,
\dur, 0.2
).play(quant: 0);
)
Pfunc can be used to change the CV from within a Pattern.
(
SynthDescLib.global.read;
Pbind( \instrument, \default,
\freq, Pwhite(100, 1000).round(100),
\placeholder, Pfunc({ arg ev; a.value_(ev.at(\freq)) }),
\db, b,
\dur, 0.2
).play;
)
CVs and external input
The input_(in) method makes it easy to connect a CV to any control source:
( // MIDI
MIDIClient.init;
MIDIIn.connect(0,0);
MIDIIn.control = { arg src, chan, num, val; a.input_(val/127)
}
)
The methods input and input_ are also used by ConductorPreset to allow interpolations
between settings that follow warp of the CV's ControlSpec.