Classes (extension) | GUI | Conductor | External Control > MIDI | External Control > OSC

CVCenter : Object
ExtensionExtension

CVCenter is a collection of CVs and provides a GUI for easy use in connection with external hard- and software like MIDI- and OSC-controllers.
Source: CVCenter.sc

Description

CVCenter lets you collect an arbitrary number of CVs (you need to have the Conductor-quark installed) and use them to easily create control-widgets for Synths, Ndefs or Pattern-keys. Or even more basically: any value that can be set at runtime. For each CV a widget will be created within the main CVCenter-GUI that combines an arbitrary number of TabbedView2s, filled with your widgets.

What's a CV?

A CV models a value constrained by a ControlSpec. The value can be a single Float or an array of Floats. Whenever the CV's value changes, it sends a changed message labeled 'synch'. This way dependants (such as GUI objects or server value) can be updated with SimpleControllers. A CV's value can be read with the 'value' message.

Class Methods

Basic usage

CVCenter.new(cvs ... setUpArgs)

Initializes CVCenter by creating its classvars all, cvWidgets and more private ones.

NOTE: It shouldn't be necessary to initialize CVCenter with an explicit call to *new. Rather use *use whenever possible as (will call *new internally)

Arguments:

cvs

an Array, a Dictionary or an Event, containing the widget name(s) and its/their internal CV(s):

... setUpArgs

An arbitrary number of key/value pairs. The following keys are currently supported:

midiMode
0MIDI-controllers that yield values from 0-127
1MIDI-controller that yield de-/incremental values

(for a detailed explanation see: *midiMode)

midiResolution
- only applies when midiMode is set to 1 - default: 1 (for a detailed explanation see: *midiResolution)
midiMean
- only applies when midiMode is set to 1 - default: 64 (for a detailed explanation see: *midiMean)
softwithin
- only applies when midiMode is set to 0 - default: 0.1 (for a detailed explanation see: *softWithin)
ctrlButtonBank
- default: nil (for a detailed explanation see: *ctrlButtonBank)

For a detailed description of the above listet arguments see the descriptions of the according classvar-methods.

Returns:

this (CVCenter)

CVCenter.front(tab ... cvs)

Creates and fronts the CVCenter GUI if it's closed or nil. Sends the GUI to front if it's in the background. If CVs are passed in as key/value pairs it adds them as widgets to the GUI under the given tab if given or the one that's currently in front.

Arguments:

tab

a String or a Symbol, indicating the label of a tab. If none is provided the widgets will be added to "default" or any other tab that's currently in front. If an event containing the keys \hi and \hi gets passed in the widget will be a CVWidget2D.

... cvs

key/value pairs: \wdgtName1, a CV, \wdgtName2, (lo: a CV, hi: a CV) ... \wdgtNameN, a CV

Returns:

this (CVCenter)

CVCenter.use(key, spec, value, tab, slot, svItems, connectS, connectTF)

Use an arbitrary name for creating a new CVWidget (a CVWidgetKnob or a CVWidget2D, depending on the argument slot).

NOTE: This is the recommended way for creating new widgets. This method should be convenient in most situations. It simply creates a new CVWidget and returns the CV. As CV inherits from Stream this method may directly be used in Patterns. Any reevaluation will leave a once created CV untouched.

You may omit the arguments slot and tab when creating a CVWidget2D if its first slot has already been created. However, if the widget is used within patterns you always must provide the slot as execution of the pattern depends on the right CV to be returned (if omitted the widget would return an event, composed of the \lo and \hi key).

Arguments:

key

a Symbol or a String - the resulting CV will be put into CVCenter.all under this name and the widget will be labeled the same. If the given key resembles to the name of an existing ControlSpec it can be used automatically for the resulting CV's spec (if 'spec' hasn't been given explictely as the second argument). If the key consists of valid a ControlSpec-name and numeric characters only (e.g. 'freq 1' but not 'my freq') the spec's name will be extracted automatically.

spec

a ControlSpec - default: ControlSpec.new

value

a default value for the CV - default: the ControlSpec's default value

tab

a String or a Symbol - the tab under which the widget will appear. If not provided the widget will be added under the tab that's currently in front.

slot

only needed if a CVWidget2D shall be created. Either \lo or \hi

Returns:

a CV

CVCenter.put( ... args)

Put new CVs into CVCenter

NOTE: It shouldn't be necessary to initialize CVCenter with an explicit call to *new. Rather use *use whenever possible as (*use uses *put internally)

Arguments:

... args

key/value pairs: \wdgtName1, a CV, \wdgtName2, (lo: a CV, hi: a CV), \wdgtNameN, a CVIf the number of arguments is odd and the last one is a Boolean this will indicate whether already contained values will be overwritten (default: false)

Returns:

this (CVCenter)

Connecting Nodes (Groups, Synths) and NodeProxies

It is possible to create GUIs from running Synths, NodeProxies (Ndefs as well as ProxySpaces) with one simple method-call. You can find thes methods documented in Synth: -cvcGui resp NodeProxy: -cvcGui.

(
SynthDef(\simpleSine, { |freq=220, amp=0.3|
    Out.ar(0, SinOsc.ar([freq, freq+1], mul: amp))
}).add // SynthDefs *must* be 'added' or 'stored'
)

x = Synth(\simpleSine).cvcGui;
x.free;
CVCenter.removeAll; // remove widgets from CVCenter again

For more complex SynthDefs the cvcGui-method will take care for special requirements like arrayed controls and treat them adequately.

If the user has already created widgets in CVCenter and wants to connect controls of a running Synth or a NodeProxy to these the following method might be suitable:

CVCenter.connectToNode(node, kvArray, environment)

Arguments:

node

mandatory - a Group, a Synth, a NodeProxy, an Ndef or a slot in a ProxySpace (pushed or not) whose controls shall be connected to the widgets in CVCenter. The objects can be given as variables (a, b, c, d...), environment-variables (~mySynth). Also variables can be given as strings/symbols (e.g. a as "a" or \a).

kvArray

mandatory - an Array consisting of pairs of control-name(s) / widget-name(s):

a 1-dimensional control:
control-name: ["widgetName"]
an arrayed control:
control-name: ["widget-name1", "widget-name2",... "widgetNameN"]
2 controls combined in a CVWidget2D:
#[control-name1, control-name2]: ["widget2D-name"]
a 2-dimensional control in a single control-name:
2D-control-name: ["widget2D-name"]
environment

optional - if the object is given as slot in an array or dictionary the method will try to look it up in the given environment.

Returns:

this (CVCenter)

A simple example

(
SynthDef(\connectTest, { |freq=#[220, 222, 227, 238], ampLeft=0.3, ampRight=0.3|
    var son = SinOsc.ar(freq);
    Out.ar(0, Splay.ar(son) * [ampLeft, ampRight]);
}).add
)

(
#[lo, hi].do({ |slot| CVCenter.use(\amp, slot: slot) }); // a 2D-widget for 'amp'
4.do({ |i| CVCenter.use("freq"++(i+1)) }); // 4 single widgets for the freq-control (4 slots)
)

x = Synth(\connectTest);

(
CVCenter.connectToNode(x, [
    freq: ["freq1", "freq2", "freq3", "freq4"],
    #[ampLeft, ampRight]: ["amp"]
])
)

x.free;
CVCenter.removeAll;
NOTE: If the user doesn't provide the object in a variable or the variable-name can't be determined fallback-actions will be created ( s.sendBundle(s.latency, [...])). However, these will address the object's nodeID. If the object gets removed and recreated with a new nodeID the actions will stop working.

Setup options (classvar getters and setters)

CVCenter.midiMode

CVCenter.midiMode = value

Different MIDI-devices may have different output modes: either values from 0-127 or an in-/decremental value (e.g. -1 or +1). These modes may be taken in account as follows:

0the device outputs a values 0-127
1the device outputs in-/decremental values

midiMode can be set at runtime and will be reflected in all widgets, i.e. all currently connectected devices will be switched to the new mode immediately.

default: 0

Returns:

this (CVCenter)

CVCenter.midiResolution

CVCenter.midiResolution = value

When midiMode has been set to 1, this method allows to set the resolution (= stepsize) of the connected hardware MIDI-sliders.

standard-value: 1 lower values -> higher resolution (lower stepsize) higher values -> lower resolution (bigger stepsize)

default: 1

Returns:

this (CVCenter)

CVCenter.ctrlButtonBank

CVCenter.ctrlButtonBank = value

Some MIDI-devices provide several banks of sliders. I.e. a device may be equipped with 16 hardware-sliders and 4 banks that can be switched. So, slider 1 in bank 2 is slider nr. 17, slider 3 in bank 3 is slider nr. 35. By default these sliders would have to be addressed in a CCResponder as 16 (slider 17) and 34 (slider 35) which makes it hard to immediately get the right slider from what is displayed within the GUI. ctrlButtonBank translates the hardware-layout in a way that makes it easy to see the slider's bank and number: slider 17 becomes 2:1 (bank 2, nr. 1).

default: nil

Returns:

this (CVCenter)

CVCenter.midiMean

CVCenter.midiMean = value

Devices which output a in-/decremtal may output a standard value + in-/decrement. midiMean gets automatically subtracted from this value, so in-/decrement remains. Applies only if midiMode is set to 1.

default: 64

Returns:

this (CVCenter)

CVCenter.softWithin

CVCenter.softWithin = value

If midiMode has been set to 0 moving a widget-slider will set the CV to a new value. However, if a MIDI-slider is connected to that widget, moving the MIDI-slider will set the CV's value immediatly to the value that is stored in the MIDI-slider i.e. a "jump" will happen. softWithin will ease this behavior by setting the CV's value only if the slider gets within softWithin/2. Applies only if midiMode is set to 0.

default: 0.1

Returns:

this (CVCenter)

Convenience methods

CVCenter.at(key)

Return the CV at the given key. If the key belongs to a CVWidget2D an Event will rather be returned, containing 2 CVs: \lo and \hi. If your code relies on a CV being returned by this method, make sure you call the right slot (e.G.: CVCenter.at(\myKey).lo).

Arguments:

key

a Symbol or a String

Returns:

a CV resp. an Event

CVCenter.removeAt(key)

Remove a given widget including its CV(s). . Existing OSC- or MIDI-connections will be resolved automatically.

Arguments:

key

a Symbol or String

Returns:

this (CVCenter)

CVCenter.removeAll( ... keys)

Remove given CVWidgets including their CVs or all. Existing OSC- or MIDI-connections will be resolved automatically.

Arguments:

... keys

widget-keys, given as Strings or Symbols. If no keys are given all widgets will be removed.

Returns:

this (CVCenter)

CVCenter.removeAtTab(label)

Remove CVWidgets under a given tab and remove the tab.

Arguments:

label

The name of the tab as String or Symbol

Returns:

this (CVCenter)

CVCenter.removeTab(label)

Remove CVWidgets under a given tab and remove the tab.

Arguments:

label

The name of the tab as String or Symbol

Returns:

this (CVCenter)

CVCenter.addActionAt(key, name, action, slot, active: true)

Add an action given in action to a widget's CV given in key under the name name

Arguments:

key

a Symbol or String - representing a CVWidget resp. its CV

name

a Symbol or a String - the name under which the action will be added. The action may be removed or deactivated again by referring to this name.

action

a Function or a String that compiles to a function - if one or more arguments are added, the first will automatically be a representation of the CV. The function can also be provided as a string that compiles to a function when you call interpret on it.

slot

a String or Symbol - either \hi or \lo. Only needed if the widget is a CVWidget2D

active

a Boolean - activate or deactivate the action. default: true

Returns:

this (CVCenter)

CVCenter.removeActionAt(key, name, slot)

Remove an action from a widget's CV given in key under the name name

Arguments:

key

a Symbol or String - representing a CVWidget resp. its CVs

name

a Symbol or a String - the name under which the action is stored.

slot

a String or Symbol - either \hi or \lo. Only needed if the widget is a CVWidget2D

Returns:

this (CVCenter)

CVCenter.activateActionAt(key, name, activate, slot)

Activates or deactivates an action at key (representing the widget) and name (the name under which the action is stored)

Arguments:

key

a Symbol or String - representing a CVWidget resp. its CVs

name

a Symbol or a String - the name under which the action is stored.

activate

a Boolean

true
- activate the action
false
- deactivate the action
slot

a String or Symbol - either \hi or \lo. Only needed if the widget is a CVWidget2D

Returns:

this (CVCenter)

CVCenter.snapShots

Returns currently saved snapshots - an Event holding snapshots under the timestamp or custom name when they were saved as key and an IdentityDictionary of keys and values of all current CVWidgets resp. their internal CV(s) as values

Returns:

an Event

CVCenter.saveSnapshot(dialog: false)

Save a snapshot of all values of all CVWidgets currently held in CVCenter. The snapshot will immediatly appear in the 'select snapshot' dropdown at the bottom of the CVCenter GUI. Selecting a snapshot will set the CVWidgets contained in the snapshot to its coresponding value in the snapshot.

Arguments:

dialog

a Boolean denoting whether the snapshot name can be set in a dialog (default: false - the snapshot's name will be the current timestamp)

Returns:

this (CVCenter)

CVCenter.deleteSnapshots

Deletes all currently saved snapshots

Returns:

this (CVCenter)

Setting GUI-properties

CVCenter.bounds

CVCenter.bounds = rect

Get or set the current bounds of the CVCenter GUI.

Arguments:

rect

A Rect.

Returns:

this (CVCenter)

CVCenter.alwaysOnTop

CVCenter.alwaysOnTop = bool

Determine whether CVCenter resp. its childviews should always be on top of other views.

Arguments:

bool

a Boolean

Returns:

this (CVCenter)

CVCenter.numMsSlotsPerColumn

CVCenter.numMsSlotsPerColumn = value

A special method that allows the user to determine how many slots a multislider-widget shall display in 1 columns at max (even though this value refers to the maximum number od slots in one column a multislider-widget will always stretch over at least 2 colums). If this number gets exceeded a multislider widget will automatically stretch over 3 columns. default: 15.

Arguments:

(value)

an Integer indicating how many slots shall be displayed within one column. default: 15

Returns:

this (CVCenter)

CVCenter.isClosed

Returns true if no childviews exists (i.e. *childViews is empty) and the main window is closed (closing the main window should automatically close all childviews as well).

Returns:

CVCenter.guiMoveTo(point)

Moves the GUI to a given position on screen.

Arguments:

point

a Point

Returns:

this (CVCenter)

CVCenter.guiMoveTo(point)

Moves the GUI to a given position on screen.

Arguments:

point

a Point

Returns:

this (CVCenter)

CVCenter.guiChangeDimensions(point)

Set the GUI's dimension to the given value.

Arguments:

point

a Point

Returns:

this (CVCenter)

CVCenter.renameTab(oldName, newName)

Set a tab-name from oldName to newName

Arguments:

oldName

a String or a Symbol

newName

a String or a Symbol

Returns:

this (CVCenter)

CVCenter.widgetsAtTab(label)

Get an Array containing all widgets (names) under the give tab.

Arguments:

label

a String or a Symbol representing the name of the selected tab

Returns:

an Array

CVCenter.widgetConnectGUI(key, connectSliders, connectTextFields)

Drawing sliders in a widget while they're moved from outside (e.g. when they're set from some other application outside SuperCollider via OSC) may cost a lot of CPU power. Via this method the connection to sliders and number boxes can be suspended and hence CPU power may be freed for other processes.

Arguments:

key

a String or Symbol denoting the widget whose sliders, numberboxes shall be disconnected

connectSliders

a Boolean or nil, telling CVCenter to connect or disconnect the slider(s) for the widget denoted by the argument key

connectTextFields

a Boolean or nil, telling CVCenter to connect or disconnect the number box(es) for the widget denoted by the argument key

Returns:

this (CVCenter)

Saving and loading setups

CVCenter.saveSetup(path)

Save all widgets and tabs resp. their actions, connected MIDI- and OSC-responders into a setup-file on disk. A once stored setup can be loaded again with loadSetup and all its responders can get reinitialized automatically.

Arguments:

path

optional - expects the full path to the location where the setup shall be stored disk. If given no file-save dialog will be displayed.

Returns:

this (CVCenter)

CVCenter.loadSetup(path, addToExisting: false, autoConnectOSC: true, oscConnectToIP: true, oscRestrictToPort: false, activateCalibration: false, resetCalibration: false, autoConnectMIDI: true, midiConnectSrc: false, midiConnectChannel: false, midiConnectCtrl: true, loadActions: true, midiSrcID, oscIPAddress, loadShortcuts: true, loadSnapshots: true, connectSliders, connectNumBoxes)

Loads a saved setup and creates the CVCenter GUI with all widgets stored in the setup. For the user's convenience arguments for this method are defined explicitely, allowing the IDE to automatically list their names when the method's name gets typed. Also one may use the dialog provided by CVCenterLoadDialog that allows selecting options by checkboxes and dropdown-lists.

Arguments:

path

optional - expects the full path to the location where the setup shall be stored to disk. If given no file-open dialog will be displayed.

addToExisting

If CVCenter GUI has already been initialized and contains some widgets and addToExisting is set to true the setup will rather add widgets to the already existing ones. default: false

autoConnectOSC

If set to true OSC-responders will be initialized automatically. default: true

oscConnectToIP

If set to true OSC-responders will be initialized with a NetAddr bound to the IP-address stored with the setup up (if the setup has been saved with an IP-address). If set to false the OSC-responders will listen to commands coming from any IP-address. However, if responders should listen to an explicit IP-address that is not the same as the one that was saved with the setup the CVCenterLoadDialog still allows the user to detect a valid IP-address to be used with all OSC-responders. By default oscConnectToIP is set to true

oscRestrictToPort

If set to true all OSC-responders loaded from the setup will listen to the expliciit port stored with the setup (if a port has been saved with the setup). However, it is usually not recommended to restrict listening to a specific port as ports may easily change between sessions. Hence, this argument is set to false by default.

activateCalibration

If set to true the regarding widget will try to determine the range of incoming (OSC) values and adjust itself to it if necessary. However, this might not be the desired behaviour if the widget has already been calibrated when the setup was first saved. Hence, this argument is set to false by default.

resetCalibration

If set to true the calibration from invoming (OSC) values will start anew from 0, 0 (low constraint, high constraint). default: false.

autoConnectMIDI

If set to true MIDI-responders will be initialized automatically. default: true

midiConnectSrc

If set to true MIDI-responders will only listen to the ID stored with the setup (if an ID has been stored with the setup). default: false.

midiConnectChannel

If set to true MIDI-responders will only listen to a channel stored with the setup (if a channel has been stored with the setup). default: false.

midiConnectCtrl

If set to true MIDI-responders will only listen to a controller-nr. stored with the setup. default: true.

loadActions

If set to true actions will be initialized automatically. default: true

midiSrcID

Optional - all MIDI-responders will be initialized using this ID as an identification of the MIDI-device that is going to be used. Replaces any ID that has been saved with the setup.

oscIPAddress

Optional - all OSC-responders will be initialized using this IP-address. Replaces any IP-address stored with the setup.

loadShortcuts

If set to true currently active shortcuts in CVCenter, CVWidgets and CVWidgetEditors will be replaced by the shortcuts saved with the setup. By default saving a setup will always save all currently active shortcuts. default: true

Returns:

this (CVCenter)

More classvars and internal values

CVCenter.setup

returns the current setup:

Returns:

an Event

CVCenter.all

Returns:

an IdentityDictionary, containing all CVs added to CVCenter

CVCenter.cvWidgets

all widgets (currently either CVWidgetKnobs or CVWidget2Ds) displayed within the main GUI of CVCenter

Returns:

an IdentityDictionary, containing all CVWidgets

CVCenter.window

The main window of CVCenter

Returns:

an instance of Window

CVCenter.tabs

A TabbedView2, containing the widgets

Returns:

CVCenter.childViews

An IdentityDictionary keeping the parent windows of detached tabs: Any tab may be detached in this version of CVCenter as the GUI is now based on TabbedView2 instead of TabbedView. Moreover, tabs can be dragged between childviews (or the main window and a childview) by clicking its label and holding cmd (Mac) or ctrl (Linux, Windows). The structure of childViews is the following:

IdentityDictionary[
    parent window 1 (a TopView) -> (
        tabs: (
            tab 1 (a TabbedViewTab): (
                widgets: [widget-name 1, widget-name 2, ... widget-name n]
            ),
            tab 1 (a TabbedViewTab): (
                widgets: [widget-name 1, widget-name 2, ... widget-name n]
            ),
            ...
            tab n (a TabbedViewTab): (
                widgets: [widget-name 1, widget-name 2, ... widget-name n]
            )
        )
    ),
    parent window 2 (a TopView) -> (
        tabs: (
            tab 1 (a TabbedViewTab): (
                widgets: [widget-name 1, widget-name 2, ... widget-name n]
            ),
            tab 1 (a TabbedViewTab): (
                widgets: [widget-name 1, widget-name 2, ... widget-name n]
            ),
            ...
            tab n (a TabbedViewTab): (
                widgets: [widget-name 1, widget-name 2, ... widget-name n]
            )
        )
    ),
    ...
    parent window n (a TopView) -> (
        tabs: (
            tab 1 (a TabbedViewTab): (
                widgets: [widget-name 1, widget-name 2, ... widget-name n]
            ),
            tab 1 (a TabbedViewTab): (
                widgets: [widget-name 1, widget-name 2, ... widget-name n]
            ),
            ...
            tab n (a TabbedViewTab): (
                widgets: [widget-name 1, widget-name 2, ... widget-name n]
            )
        )
    )
]

Returns:

CVCenter.shortcuts

CVCenter.shortcuts = value

An IdentityDictionary containing all currently defined shortcuts for CVCenter. Though this is a getter/setter it is recomended to add/remove shortcuts via the GUI within the preferences-interface (changes will become active after recompilation of the class-library) or the shortcuts-interface (changes become active immediatly but get reset after recompilation of the class-library).

See KeyDownActions for more information about the internal structure of the shortcuts dictionary resp. the workings of shortcuts in general. Also have a look at the list of default shortcuts that can be modified by the user at any time.

Returns:

CVCenter.scv

An Event containing various objects that do not necessarily belong to CVCenter but are related to it. E.g. when a shortcut opens a window that window may be added to scv so other shortcuts can refer to it later. Basically scv has been introduced as the concept of configurable shortcuts seperate shortcut-actions from CVCenter. Hence, variables in these actions can't get initialized belonging to CVCenter automatically. However, a classvar holding a dictionary that can be filled up later solves the problem.

Returns:

an Event

CVCenter.guix

CVCenter.guix = value

The x-position at which the GUI will be created - has no direct effect besides when being set ahead from initializing CVCenter.

Returns:

this (CVCenter)

CVCenter.guiy

CVCenter.guiy = value

The y-position at which the GUI will be created - has no direct effect besides when being set ahead from initializing CVCenter.

Returns:

this (CVCenter)

CVCenter.guiwidth

CVCenter.guiwidth = value

The GUI-width - has no direct effect besides when being set ahead from initializing CVCenter.

Returns:

this (CVCenter)

CVCenter.guiheight

CVCenter.guiheight = value

The GUI-height - has no direct effect besides when being set ahead from initializing CVCenter.

Returns:

this (CVCenter)

Inherited class methods

Instance Methods

Inherited instance methods

Examples

// create a couple of CVWidgets under the tab "test"
// and add an action
CVCenter.use(\knobTest, tab: \test);
CVCenter.addActionAt(\knobTest, \testFunc, { |cv| "knobTest: %\n".postf(cv.value) }, active: false);
CVCenter.use(\test2D, tab: \test, slot: \lo);
// arguments 'tab' and 'slot' may be omitted when creating the 'hi' slot as the widget knows under which
// tab it's living and which slot has already been created. Also the operation doesn't depend
// on a CV being returned
CVCenter.use(\test2D);
CVCenter.addActionAt(\test2D, \testFunc, { |cv| "test2D lo: %\n".postf(cv.value) }, \lo);
CVCenter.addActionAt(\test2D, \testFunc, { |cv| "test2D hi: %\n".postf(cv.value) }, \hi);

// action activation and deactivation
// watch the "actions"-button at the bottom of the widget
// turn the knob to see the effect
CVCenter.activateActionAt(\knobTest, \testFunc, true);
CVCenter.activateActionAt(\knobTest, \testFunc, false);
CVCenter.activateActionAt(\knobTest, \testFunc, true);

// some sound
Server.default.waitForBoot{ a = { |freq1, freq2, amp| SinOsc.ar([freq1, freq2]) * amp }.play };

// set suitable specs for synth's controls
CVCenter.cvWidgets[\knobTest].setSpec(\amp);
#[lo, hi].do({ |slot| CVCenter.cvWidgets[\test2D].setSpec(\freq, slot) });

// add actions that set the controls in a
CVCenter.addActionAt(\knobTest, \setAmp, { |cv| a !? { a.set(\amp, cv.value) }});
CVCenter.addActionAt(\test2D, \setFreq1, { |cv| a !? { a.set(\freq1, cv.value) }}, \lo);
CVCenter.addActionAt(\test2D, \setFreq2, { |cv| a !? { a.set(\freq2, cv.value) }}, \hi);

// CVCenter will not open another gui if one is already present
// if the GUI is in the background it will be sent to front
CVCenter.front;

// close the GUI and open it again
// all widgets should just work as before
CVCenter.window.close;
CVCenter.front;

// free the synth and remove all widgets
a.free;
CVCenter.removeAll;

 

Auto-generating CVWidgets within CVCenter GUI from running Synths, NodeProxies, Ndefs and ProxySpaces and Instr/Patch

// a SynthDef, including some arrayed controls
(
SynthDef('dynKlank', { |impFreq1, impFreq2, freqs1=#[800, 1071, 1153, 1723], freqs2=#[789, 1067, 1150, 1720], ringtimes1=#[1, 1, 1, 1], ringtimes2=#[1, 1, 1, 1]|
    var signal1, signal2;
    signal1 = DynKlank.ar(`[freqs1, nil, ringtimes1 ], Impulse.ar(impFreq1, 0, 0.1));
    signal2 = DynKlank.ar(`[freqs2, nil, ringtimes2 ], Impulse.ar(impFreq2, 0, 0.1));
    Out.ar(0, [signal1, signal2]);
},
// specs, defined within the SynthDef's metadata will be considered when auto-creating the GUI
metadata: (
    specs: (
        freqs1: \freq,
        freqs2: \freq,
        impFreq1: [0.1, 5],
        impFreq2: [0.1, 5],
        ringtimes1: [1, 20],
        ringtimes2: [1, 20]
    )
)).add; // must use 'add' or 'store' if the GUI shall be auto-generated
)

(
Server.default.waitForBoot {
    a = Synth(\dynKlank);
    // GUI creation - note that actions within widgets representing arrayed controls will
    // rather use setn to set the regarding controls
    // have a look at the Synth helpfile to see all possible arguments for cvcGui
    a.cvcGui(pairs2D: (impFreqs: #[impFreq1, impFreq2]));
};
)


/////////////////////////////////// CVCenter and Ndefs /////////////////////////////////////////////////

Ndef(\sawTest, { |damping| SinOsc.ar(damping * VarSaw.ar(\sawFreq.kr([100, 101]))).tanh * \amp.kr(0.5) });

// Ndefs don't have metadata but you may add some specs ahead from creation
Spec.add(\sawFreq, \freq);
Spec.add(\damping, #[200, 20000, \exp, 0.0, 1000, " Hz"]);

// create the GUI
// have a look at the NodeProxy helpfile to see all possible arguments for cvcGui
Ndef(\sawTest).cvcGui;

// play the Ndef (turn the knobs if you don't hear anything immediately)
Server.default.waitForBoot{ Ndef(\sawTest).play };

// clean up
CVCenter.removeAll;
Ndef(\sawTest).clear(10);


/////////////////////////////////// the same thing in NodeProxy-style /////////////////////////////////

a = NodeProxy.new.play; // play to hardware output.
// set the source
a.source = { |damping| SinOsc.ar(damping * VarSaw.ar(\sawFreq.kr([100, 101]))).tanh * \amp.kr(0.5) };

// specs should already be declared within the above example
a.cvcGui

a.clear(10)

CVCenter.removeAll


/////////////////////////////////// as ProxySpace, pushed //////////////////////////////////////////////

p = ProxySpace.push(s);
~sawTest = { |damping| SinOsc.ar(damping * VarSaw.ar(\sawFreq.kr([100, 101]))).tanh * \amp.kr(0.5) };

Server.default.waitForBoot{ ~sawTest.play };

// specs should already be there
~sawTest.cvcGui;

~sawTest.clear(10);
p.clean;
p.pop;
CVCenter.removeAll;


/////////////////////////////////// as ProxySpace, not pushed //////////////////////////////////////////

p = ProxySpace.new;

p[\sawTest] = { |damping| SinOsc.ar(damping * VarSaw.ar(\sawFreq.kr([100, 101]))).tanh * \amp.kr(0.5) };

Server.default.waitForBoot{ p[\sawTest].play };

p[\sawTest].cvcGui;

p[\sawTest].clear(10);
p.clean;
p.pop;
CVCenter.removeAll;


/////////////////////////////////// Instr/Patch support ////////////////////////////////////////////////

(
Instr(\saw, { |amp = 0.5|
    Splay.ar(LFSaw.ar(NamedControl.kr(\freq, [ 110, 112, 222, 334, 556 ]), 0, amp));
});

Instr(\rlpf, { |in, ffreq = 1000, famp = 1, rq = 0.1|
    RLPF.ar(in, ffreq, rq) * famp;
});
)

i = Instr(\saw) <>> Instr(\rlpf);
p = i.play;
p.cvcGui;

// clean up when done
p.stop;
CVCenter.removeAll;

CVCenter and Patterns

// declare some SynthDef first
(
SynthDef(\hihat, { |out=0, atk=0.01, rel=0.1, curve=(-4.0), amp=0.5, freq=300, rq=0.5, pan=0|
    var env, son;
    env = Env.perc(atk, rel, curve:curve);
    son = BPF.ar(WhiteNoise.ar, freq, rq);
    Out.ar(out, Pan2.ar(son * EnvGen.ar(env, doneAction: 2) * amp, pos:pan));
}).add;
)

// something to change the tempo
t = TempoClock.default;
t.tempo_(2);
CVCenter.use(\tempo, #[1.0, 3.0], t.tempo, tab: \hihat);
CVCenter.addActionAt(\tempo, \setTempo, { |cv| t.tempo_(cv.value) });

// the pattern
// no .action_ needed here - CVs can be embedded in patterns just like any other pattern
// you can at any time change the Pdef, add new widgets to CVCenter by calling CVCenter.use(\someKey)
// NOTE: If you're adding 2D-widgets (CVWidget2D) you *must* explicitly declare their slots ('hi' or 'lo')
// as the output depends on what is returned by CVCenter:use
(
Pdef(\hiHat,
    Pbind(*[
        instrument: \hihat,
        dur: Pwrand([1/4, Pwrand([Pn(1/4, 4), Pn(1/8, 2)], [2, 5].normalizeSum, 1)], [16, 9].normalizeSum, inf),
        freq: 20000,
        atk: 0.0001,
        rel: Pgauss(0.04, Pwhite(0.02, CVCenter.use(\rel, value: 0.2, tab: \hihat))).abs,
        curve: Pgauss(-4.0, 1.0),
        amp: CVCenter.use(\amp, \amp, tab: \hihat),
        rq: Pwhite(0.1, 1.0),
        pan: Pgauss(0.0, 1.0),
    ])
)
)

Pdef(\hiHat).play;
Pdef(\hiHat).stop;

Using CVWidgetMS in Patterns

multi-slider widgets can be used in patterns as well. However, their usage differs.

// a synth, utilizing an arrayed control
(
SynthDef(\poly, { |atk=0.02, level=0.7, rel=1.5, curve=(-4), freqMod=1, gate=1, amp=1.0|
    var env, son;
    env = EnvGen.kr(Env.perc(atk, rel, level, curve), gate, doneAction: 2);
    son = LeakDC.ar(SinOsc.ar(\freq.kr({ |i| i }!5)).cubed.cubed.scaleneg(freqMod) * env);
    Out.ar(0, Splay.ar(son).tanh);
}).add;
)

// for the arrayed control create a CVWidgetMS *ahead* from using it in the pattern
// otherwise we get a conflict due to the different clocks for GUI related things
// (AppClock - the widget creation in CVCenter GUI) and the pattern playing on TempoClock
// setting freq - where going to use the 'note'-event.
// Hence, we're calling the widget degree rather than freq.
CVCenter.use(\degree, #[0, 11, \lin, 1, 2]!5);
// a CVWidgetMS for setting rythmic sequences
CVCenter.use(\dur, #[0.1, 1, \lin, 0.1, 0.3]!10);

// the pattern
(
Pdef(\poly,
    Pbind(*[
        instrument: \poly,
        // rather than setting freq in hz we determine the degree in a scale
        // instead of Prout{} the shortcut p{} can be used
        note: PdegreeToKey( Prout({ loop {
            CVCenter.at(\degree).embedInStream
        }}), Scale.dorian),
        // dur cannot be set individually for each channel
        dur: Pseq(CVCenter.cvWidgets[\dur].split, inf),
        curve: CVCenter.use(\curve, #[-4.0, 4.0].asSpec),
        rel: Pwhite(0.8, 2.0),
        atk: Pwhite(0.01, 0.03),
        freqMod: Pwhite(
            CVCenter.use(\freqModLo, \pan, slot: \lo),
            CVCenter.use(\freqModLo, \pan, slot: \hi)
        ),
        level: CVCenter.use(\amp),
    ])//.trace
)
)

Pdef(\poly).play;
Pdef(\poly).stop;

Shortcuts

When CVCenter GUI is sent to front a number of shortcuts apply. Most of these shortcuts will open new windows. However, if the regarding window is already open (and maybe hidden under another window) it will rather be brought to front again than opened in another window.

NOTE: All shortcuts are configurable by the user. Handling shortcuts is basically handled by a class named KeyDownActions. They can be altered (actions as well as the action-triggering key-combinations), removed or new shortcuts can be added. This can be done manually by editing *shortcuts (not recommended) or using the KeyDownActionsEditor in the preferences-interface (persistent) or editing short-term shortcuts in the shortcuts-editor (shortcuts will be valid until next recompilation of the class-library). However, this is still an experimental feature. Especially in detached tabs shortcuts may not work as expected

arrow right
Navigate through the tabs of the CVCenter GUI - ascending. If the end has been reached the selection will wrap to the first tab.
arrow left
Navigate through the tabs of the CVCenter GUI - descending. If the beginning has been reached the selection will wrap to the last tab.
alt arrow right
Select and focus first widget in alphabetical order, activating widget-shortcuts. Repeating the same shortcut will select and focus the next widget in alphabetical order.
alt arrow left
Select and focus last widget in alphabetical order, activating widget-shortcuts. Repeating the same shortcut will select and focus the previous widget in alphabetical order.
0-9
Select tabs and focus them. The numbers represent the order of the tabs.
s
Save the current CVCenter setup to disk.
l
Load a CVCenter setup from disks.
p
Open the CVCenter prferences dialog.
d
Detach the tab currently focused tab in the CVCenter GUI.
shift c
Activate OSC calibration for all widgets.
alt shift c
Deactivate OSC calibration for all widgets.
shift r
Reset and restart OSC calibration for all widgets.
alt c
Open the OSCCommands GUI. This will also trigger OSCCommands: *collect
o
See a list of already connected OSC-controllers (see also: CVCenterControllersMonitor).
m
See a list of already connected MIDI-controllers (see also: CVCenterControllersMonitor).
alt s
Open the shortcuts-editor. Shortcuts edited here will persist until the next library-recompilation (unless the option "write to preferences" is selected).
shift esc
Close all open CVWidget(MS)Editors.
e
If MasterEQ from the Quark wslib is installed, open it.
h
Start the History and open its GUI.
shift h
End the History, close its GUI a and (if Patform.ideName != "scqt") open a new Document containing the recorded History.
NOTE: History.document will only work with GUI-systems that support Document. When using the new SC-IDE consider using History.saveCS ( History.saveCS(path, forward))

n
Open an NdefMixer for the default Server
shift p
Open a new PdefAllGui.
alt p
Open a new PdefnAllGui.
t
Open a new TdefAllGui.
a
If the Quark AllGui is installed open a new AllGui.