Classes (extension) | Conductor | Control | Libraries | Quarks | Streams-Events-Patterns

CV : Stream : AbstractFunction : Object
ExtensionExtension

A single floating point value or an array of such values constrained to a specific numerical range by a ControlSpec.
Source: CV.sc
Subclasses: EV, SV, TV

Description

A CV models a single Float or an Array of such values constrained to a specific numerical range by a ControlSpec.

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 Qt (as well as 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 SV: -item.

Class Methods

Creation

CV.new(spec: 'unipolar', default)

Arguments:

spec

Any object that responds to asSpec (nil, a Symbol, a ControlSpec, an Array) with a ControlSpec. 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
default

The initial value is constrained to lie within the range of the spec.

Returns:

a CV

CV.viewDictionary

CV.viewDictionary = value

An IdentityDictionary, holding the various view-classes for various gui-elements that may be used with CV as its keys and the class that syncs the view's value to the CV's value as its values:

// Qt
IdentityDictionary[
    (RangeSlider -> CVSyncProps.new),
    (EZGui -> CVSyncValue),
    (NumberBox -> CVSyncValue),
    (Slider -> CVSyncInput),
    (PopUpMenu -> SVSync),
    (ListView -> SVSync),
    (Knob -> CVSyncInput),
    (Slider2D -> CVSyncProps.new),
    (Button -> CVSyncValue),
    (MultiSliderView -> CVSyncMulti)
]

CV.initClass

Calls *buildViewDictionary on startup.

CV.buildViewDictionary

As the name suggests this method builds the viewDictionary and prepares the various connectable view-elements (sliders, number-boxes etc.) for the use with CV. The method also takes care of the different GUI-schemes (Cocoa, Qt, Swing).

Inherited class methods

Instance Methods

Setting the range of values on existing CVs

.spec

.spec_(s, v)

Set the ControlSpec and (optionally) a default value - see also *new

a = CV(\freq);
a.spec;
a.spec_(\bipolar, -1);
a.spec;

Arguments:

s

any object that responds to asSpec (nil, a Symbol, a ControlSpec or an Array) with a ControlSpec

v

the initial value of the CV

.sp(default: 0, lo: 0, hi: 0, step: 0, warp: 'lin')

Set the values of the CV's ControlSpec explicitely.

a = CV(\freq);
a.spec;
a.sp(0, 0, 100);
a.spec;

Arguments:

default

inital value

lo

smallest possible value

hi

highest possible value

step

smallest incremental change

warp

either 'linear' or 'exponential'

In the case of exponential warp, min and max must be non-zero and the same sign.

.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).

Arguments:

view

a View, usable with a CV. See *viewDictionary to for an overview of qualified views.

Accessing

.value

.value = val

Set or get the current value of the CV.

Arguments:

val

Usually a Float or an Integer. If the CV's ControlSpec's minval, maxval, step and/or default is an array it may also be an Array of numeric values.

Returns:

if used as a getter the current value of the CV

.input

.input = in

Similar as -value but requires or returns a number ranging form 0 to 1 that corresponds to postion of the current value between min and max.

Arguments:

in

a number between 0 and 1

Returns:

if used as a getter the current value of the CV normalized to a value between 0 and 1

.windex(key)

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

Arguments:

key

a Symbol

Returns:

the value indexed by key

CV connections using SimpleControllers

When a CV's value is changed a changed message (identified by the symbol \synch) 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 GUIs, 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.

NOTE: Though the method seems to be a setter and getter it is not in terms of sclang. calling action as getter will throw an error!
a = CV(\freq);
// action_ returns a SimpleController
// for an explicit removal it may be stored in a variable
c = a.action_({ |cv| "the value of a is now %\n".postf(cv.value) });
a.input_(0.7); // try with different values between 0 and 1
a.value_(456); // or set the value explicitely
// only remove c
c.remove;
a.input_(0.3); // no posts anymore
// add the SimpleController again
c = a.action_({ |cv| "the value of a is now %\n".postf(cv.value) });
// and another one
d = a.action_({ |cv| "the input of a is now %\n".postf(cv.input) });
a.value_(456); // both, c and d's functions are triggered
a.release; // remove all dependants at once

Arguments:

function

an arbitrary Function

Returns:

GUI connections

The following methods establish connections Views and CVs.

// pseudo-code
Slider-connect(aCV)
NumberBox-connect(aCV)
Slider2D-connect([xCV, yCV])
RangeSlider-connect([loCV, hiCV])
MultiSliderView-connect(aCV)    // for CVs with multiple values
PopupMenu-connect(aCV)    // for SVs, displays SV-items
ListView-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.

NOTE: 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.)
(
// create a couple of CVs
a = CV(\freq);
b = CV.new.sp(-10,-100,20, 10);

// make a window
w = Window("CV Demo", Rect(64, 0, 400, 300)).front;
w.view.decorator = FlowLayout(w.view.bounds);

// CVs can be connected to Slider and NumberBox.
z = NumberBox(w, Rect(0, 0, 50, 20));
a.connect(z);

y = Slider(w, Rect(50, 0, 150, 20));
a.connect(y);

w.view.decorator.nextLine;

b.connect(NumberBox(w, Rect(0, 0, 50, 20)));
b.connect(Slider(w, Rect(50, 0, 150, 20)));

w.view.decorator.nextLine;

//    Pairs of CVs can be connected to RangeSlider, Slider2D
[a,b].connect(RangeSlider(w, Rect(0, 0, 200, 20)));

w.view.decorator.nextLine;
[a,b].connect(Slider2D(w, Rect(0, 0, 200, 200)));
)

An example connecting a MultiSliderView

(
// create a CV with a 'multi-dimensional' ControlSpec
c = CV([20, 20000, \exp, 0.0, { 20.rrand(20000) }!7, " hz"].asSpec);

x = Window("CV MultiSlider Demo", Rect(100, 100, 400, 300)).front;
x.view.decorator = f = FlowLayout(x.view.bounds);
m = MultiSliderView(x, f.innerBounds.width@f.innerBounds.height);
c.connect(m);
c.action_({ |cv| "the CV's value is now %\n".postf(cv.value) });
)

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 (here freq is just meant as an example key):

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:

// 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 modifying 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.

NOTE: 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
Array: -connectToNode: the receiver is a flat array of name/value pairs

Array: -connectToBuffer: the receiver is an array of CVs

Array: -connectToBus: the receiver is an array of CVs.

Node
Node: -setControls: connects CVs to named controls
NodeProxy
NodeProxy: -setControls: connects CVs to 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
Synth: *controls: the same as Synth: *new but allows CVs to be used in the args array
Bus
Bus: *controls:
(
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;
)

Bus: -setControls: 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 value and altered using value_ and input_.
(
Task({
    10.do({
        // print the value scaled to the range [0, 1]
        a.input.post; " ".post;
        // print the actual value
        a.value.postln;
        // select a new value at random
        a.input_(1.0.rand);
        // 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 CVPreset to allow interpolations between settings that follow warp of the CV's ControlSpec.

more undocumented methods

.put(index, val)

(describe method here)

Arguments:

index

(describe argument here)

val

(describe argument here)

Returns:

(describe returnvalue here)

.asInput(val)

(describe method here)

Arguments:

val

(describe argument here)

Returns:

(describe returnvalue here)

.db(default: 0, lo: -100, hi: 20, step: 1, warp: 'lin')

(describe method here)

Arguments:

default

(describe argument here)

lo

(describe argument here)

hi

(describe argument here)

step

(describe argument here)

warp

(describe argument here)

Returns:

(describe returnvalue here)

.cvWidgetConnect(view)

From extension in /home/stefan/.local/share/SuperCollider/Extensions/CVCenter/CVCenter/extCV.sc

(describe method here)

Arguments:

view

(describe argument here)

Returns:

(describe returnvalue here)

.embedInStream

(describe method here)

Returns:

(describe returnvalue here)

.asOSCArgEmbeddedArray(array)

(describe method here)

Arguments:

array

(describe argument here)

Returns:

(describe returnvalue here)

.reset

(describe method here)

Returns:

(describe returnvalue here)

.draw(win, name: ">")

(describe method here)

Arguments:

win

(describe argument here)

name

(describe argument here)

Returns:

(describe returnvalue here)

.setValue(v)

From extension in /home/stefan/.local/share/SuperCollider/Extensions/CVCenter/Conductor/classes/CV/extCVForTouch.sc

(describe method here)

Arguments:

v

(describe argument here)

Returns:

(describe returnvalue here)

.split

(describe method here)

Returns:

(describe returnvalue here)

.connect(view)

(describe method here)

Arguments:

view

(describe argument here)

Returns:

(describe returnvalue here)

.cvWidgetDisconnect(object)

From extension in /home/stefan/.local/share/SuperCollider/Extensions/CVCenter/CVCenter/extCV.sc

(describe method here)

Arguments:

object

(describe argument here)

Returns:

(describe returnvalue here)

.touch(dur, delta)

From extension in /home/stefan/.local/share/SuperCollider/Extensions/CVCenter/Conductor/classes/CV/extCVForTouch.sc

(describe method here)

Arguments:

dur

(describe argument here)

delta

(describe argument here)

Returns:

(describe returnvalue here)

.asControlInput

(describe method here)

Returns:

(describe returnvalue here)

.next

(describe method here)

Returns:

(describe returnvalue here)

.windex(key)

(describe method here)

Arguments:

key

(describe argument here)

Returns:

(describe returnvalue here)

.size

(describe method here)

Returns:

(describe returnvalue here)

.at(index)

(describe method here)

Arguments:

index

(describe argument here)

Returns:

(describe returnvalue here)

Inherited instance methods

Examples

(some example code)