SuperCollider 3.9dev Classes (extension) | Conductor | Control | GUI | Libraries | Quarks | Streams-Events-Patterns

Conductor
ExtensionExtension

Conductor provides a framework for defining interactive programs in terms of a collection of related components.

Description

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.

Class Methods

Conductor.new

Create a new Conductor object, containing a NullPreset and a ConductorPlayer instance.

Returns:

a Conductor

Conductor.make(func)

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 CVs 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;
)

Arguments:

func

a Function. See explanation above.

Returns:

a Conductor

Conductor.specs

Conductor.specs = value

Conductor.specs is a dictionary of ControlSpecs. When an argument in the make function is initialized to a CV, its identifier is looked up in this dictionary. If that does not provide a ControlSpec, the same identifier stripped of all of its numeric characters is used once again look-up.

The specs dictionary is provided with the following defaults: amp, audiobus, beats, bipolar, boostcut, controlbus, db, dbamp, delay, detune, dur, fadeTime, fin, freq, i_out, in, lofreq, longdelay, midfreq, midi, midinote, midivelocity, out, pan, phase, rate, ratio, rq, unipolar, widefreq

// define a conductor using the default controlspecs
(
Conductor.specs[\spT] = ControlSpec(-60, 700, 'linear', 0, 33);
a = Conductor.make{ | con, freq1, db, pan, dur, spT3, s3pT, sp3T|
    con.name_("example 1");
    con.pattern_(Pbind(*[freq: freq1, db: db, pan: pan, dur: dur]));
};

a.show;
)

(
a.play;
a[\freq].value = 700;
)

Returns:

Conductor.makeCV(name, value)

(describe method here)

Arguments:

name

(describe argument here)

value

(describe argument here)

Returns:

(describe returnvalue here)

Conductor.findSpec(name)

(describe method here)

Arguments:

name

(describe argument here)

Returns:

(describe returnvalue here)

Conductor.addSpec(name, spec)

(describe method here)

Arguments:

name

(describe argument here)

spec

(describe argument here)

Returns:

(describe returnvalue here)

Conductor.midiMonitor

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

(describe method here)

Returns:

(describe returnvalue here)

Conductor.postSpecs

(describe method here)

Returns:

(describe returnvalue here)

Conductor.initClass

(describe method here)

Returns:

(describe returnvalue here)

Inherited class methods

Instance Methods

Instance Variables

.gui

.gui = value

An instance of ConductorGUI that defines the Conductor's GUI presentation

Returns:

.player

.player = value

An instance of ConductorPlayer, which provides unified stop/play/pause/resume control for Patterns, Tasks, and, on the Servers, Synths, Groups, Buses, and Buffers. (These objects use a variant of Server: -sendBundle to guarantee correct order execution on the server.)

Returns:

.preset

.preset = value

An instance of CVPreset or CVInterpolator. This provides preset values for a user specified collection of CVs and Conductor's. The CVInterpolator allows interpolations between presets to be used for values

Returns:

.valueKeys

.valueKeys = value

An array of keys that determine the Conductor's response to value. (Typically includes both individual CVs and the CVPreset or CVInterpolator used by the Conductor.)

Returns:

an Array of Symbols

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.

// 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 ~pat, 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;
)

Arguments:

playFunc

define what shall be played the Conductor

stopFunc
pauseFunc
resumeFunc

.task_(func, 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.)

Arguments:

func

a Function

clock

a Clock: e.g. a TempoClock, AppClock or SystemClock instance

quant

a Quant object: a SimpleNumber, an Array or nil

(See also: Quant: Automatic%20instantiation)

.pattern_(pat, clock, event, quant)

Adds a PatternPlayer which plays the Pattern with the specified event, clock and quantization.

// 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;
)

Arguments:

pat

a Pattern

clock

a Clock: e.g. a TempoClock, AppClock or SystemClock instance

event

an Event

quant

a Quant object: a SimpleNumber, an Array or nil

(See also: Quant: Automatic%20instantiation)

.nodeProxy_(nodeProxy, args, bus, numChannels, group, multi: false)

Adds a NodeProxyPlayer, which uses the following bus, numChannels, and group if specified, otherwise uses default values. See NodeProxy for details.

(
a = Conductor.make({ | con, freq1, freq2, widefreq3, db|
    ~np = NodeProxy.audio(Server.default, 2);
    con.nodeProxy_(~np, [freq1: freq1, freq2: freq2, widefreq3: widefreq3, db: db]);

    ~np[0] = { | freq1, freq2, widefreq3, db|
        Mix(SinOsc.ar([freq1, freq2, widefreq3], 0, db.dbamp))
    };
});

a.show
)

Arguments:

nodeProxy

a NodeProxy

args

an Array denoting the arguments to the NodeProxy

bus

a Bus

numChannels

an Integer

group

a Group

multi

a Boolean, indicating whether the NodeProxy shall keep old playback links and add a new one or not

.synth_(ev, cvs)

Control a Synth through a Conductor

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

Arguments:

ev

an Event

cvs

an Array, denoting the controls used in the Conductor

.group_(ev, cvs)

CVs assigned to a group affect all of the synths within the group. In the following example the CV only alters playing synths, new synths use the default value:

(
SynthDef("pm1", { | out, freq, amp, pan, gate = 1,
ratio1 = 1, index1 = 1, a1 = 0.01, d1 = 0.2, s1 = 0.5, r1 = 1,
ratio2 = 1, index2 = 1, a2 = 0.01, d2 = 0.2, s2 = 0.5, r2 = 1
|
    var audio, env1, env2;

    env1 =    EnvGen.kr(Env.adsr(a1,d1,s1,r1), gate, doneAction: 2) * index1;
    env2 =    EnvGen.kr(Env.adsr(a2,d2,s2,r2), gate) * index2;
    audio = PMOsc.ar(freq * ratio1, freq * ratio2, env2, 0, env1);
    Out.ar(out, Pan2.ar(audio, pan, amp))
}).add;

Conductor.make{ | con, index2, ratio1, ratio2 |
    index2.sp(0,0,20);

    // create a whole bunch of groups in sequence
    con.group_(
        (id:[2,3,4,5,6,7,8,9], addAction: 1),
        [index2: index2, ratio1: ratio1, ratio2: ratio2]
    );

    // play a pattern in one
    con.pattern_(Pbind(*[
        instrument: \pm1,
        degree: Pwhite(0, 10), dur: 0.2, sustain: 2, group: 6,
        ratio1: ratio1, ratio2: ratio2, index2: index2
    ]))
}.show
)

The argument cvs 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]
alteredCV
[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).

Arguments:

ev

an Event, denoting the Groups to be created

cvs

an Array, denoting the controls used in the Conductor

.controlBus_(ev, cvs)

Arguments:

ev

an Event, specifiing

serveraServer (defaults to Server.default),
index(optional)
cvs

an Array of CVs that are used to determine the value of consecutive Buses

.buffer = ev

Arguments:

ev

an Event, designed primarily for small waveform buffers, specifiing:

serveraServer (defaults to Server.default)
cva CV that determines the values in the buffer
msga symbol that determines how the values are used to fill the buffer. Is is one of: \sine1, \cheby, \wave, or \signal
displayanotherCV. An optional CV used to display the contents of the buffer (as received from the server)
sizeinteger (defaults to 512 and should not exceed 1024)

Value related methods

.value

.value = kvs

If called as a getter it returns an Array of the values of all components identified by -valueKeys.

If called as a setter (with a trailing underscore) it iterates over the Array defined in the argument kvs assigning the value to be the value of the corresponding component identified by valueKeys

Arguments:

kvs

an Array of key/value pairs. E.g.:

[[\a, 0], [\b, 0], [\c, 0], [\d, 0], [\preset, [[], nil]]]

Returns:

an Array of key/value pairs

Settings, Preset and Interpolator related methods

A Conductor can load a set of initial settings for its contents from a file.

.noSettings

No file controls are displayed.

.useSettings

Allow a single set of settings to be saved to file and restored from file.

.path

.path = path

Stores the pathname of the file that saves the Conductor's settings and attempts to load those settings.

c = Conductor.make { | conductor, a, b, c, d |  };

// set the path
c.path_("~/Desktop".standardizePath);

// get the path
c.path;

Arguments:

path

a path to a location on the local harddisk.

Returns:

a String containing a path to a location on the local harddisk

Presets

A CVPreset saves 'presets' for an array of CVs or other objects that respond to input and input_.

.presetKeys_(keys, argPreset)

The objects at the keys will have their settings saved and restored by preset, which defaults to the object in the preset instance variable.

Arguments:

keys

an Array of Symbols

argPreset

a CVPreset

.usePresets

Creates a CVPreset, gives it valueKeys as its default presetKeys

Interpolator

A CVInterpolator will set a specified set of CVs to values that derived from interpolating between two presets.

.interpKeys_(keys, argPreset)

The objects at the keys (which must be a subset of the valueKeys of the preset) can have their settings interpolated between preset values.

Arguments:

keys

an Array of Symbols

argPreset

a CVPreset

.useInterpolator

Creates a CVInterpolator, sets valueKeys to be both presetKeys and interpKeys

An example:

(
a = Conductor.make { | con, cv1, cv2, cv3 |
    // set the value range of cv1
    cv1.sp(440, 20, 20000, 0, 'exp');
    // set the value range of cv2
    cv2.sp(0.1, 0.01, 1, 0, 'exp');

    // we want to use an interpolator
    con.useInterpolator;

    // we  want to save  cv3 only
    con.presetKeys_(#[cv3]);

    // we only want to interpolate cv3
    con.interpKeys = #[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
)

Adding new CVs

.addCV(name, val, argGui)

Creates a CV inserts it at key in the Conductor and appends it to valueKeys and, if it exists, preset items

Arguments:

name

a Symbol, denoting the name under which the CV will be added to the Conductor

val

a Float or an Integer, setting the initial value of the CV

argGui

a Symbol, denoting the name of the view in which the CVshall appear. The view can be determined by calling Conductor: -gui.

c = Conductor.make { | conductor, a, b, c, d |  };
c.gui // -> ConductorGUI[ (win -> a Window) ]

Returns:

a CV

MIDI related methods

.useMIDI(argKeys)

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

This will add a set of controls to enable linking individual CVs to MIDI continuous controllers

Arguments:

argKeys

an Array of Symbols

.midiKBD_(noteOnFunction, midiChan)

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

The function receives a key number and velocity value and returns an object that responds to release (typically a Synth). If midiChan is nil, it responds to MIDI key commands from all channels in "omni" mode. See also: Using MIDI.

Arguments:

noteOnFunction

a Function, triggered when a key is played. E.g.:

{ |veloc, num, chan, src|
    notes[num] = Synth(\default, [\freq, num.midicps,
        \amp, veloc * 0.00315]);
}
midiChan

an Integer, denoting a MIDI channel

GUI related methods

.show(argName, x: 128, y: 64, w: 900, h: 160)

Draw the Conductor within a window named argName at x, y with size w, h.

Arguments:

argName

a Symbol or a String, denoting the name of the Window to be drawn

x

an Integer, denoting the x-position on screen

y

an Integer, denoting the y-position on screen

w

an Integer, denoting the width of the GUI to be drawn

h

an Integer, denoting the height of the GUI to be drawn

Returns:

.draw(win, name, conductor)

Draw the Conductor within the specified window.

Arguments:

win

a Window within which the representation of the Conductor shall be drawn

name

a Symbol or a String

conductor

the Conductor object

More undocumented instance methods

.addActions(kv)

Arguments:

kv

(describe argument here)

Returns:

(describe returnvalue here)

.load(argPath)

(describe method here)

Arguments:

argPath

(describe argument here)

Returns:

(describe returnvalue here)

.make(func)

(describe method here)

Arguments:

func

(describe argument here)

Returns:

(describe returnvalue here)

.save(path)

(describe method here)

Arguments:

path

(describe argument here)

Returns:

(describe returnvalue here)

.name

.name = name

(describe method here)

Arguments:

name

(describe argument here)

Returns:

(describe returnvalue here)

.add(name, obj, guiSpec)

(describe method here)

Arguments:

name

(describe argument here)

obj

(describe argument here)

guiSpec

(describe argument here)

Returns:

(describe returnvalue here)

.simpleGUI

(describe method here)

Returns:

(describe returnvalue here)

.addCVs(kv)

(describe method here)

Arguments:

kv

(describe argument here)

Returns:

(describe returnvalue here)

.getFile(argPath)

(describe method here)

Arguments:

argPath

(describe argument here)

Returns:

(describe returnvalue here)

.putFile(vals, argPath)

(describe method here)

Arguments:

vals

(describe argument here)

argPath

(describe argument here)

Returns:

(describe returnvalue here)

.synthDef_(function, cvs, ev)

(describe method here)

Arguments:

function

(describe argument here)

cvs

(describe argument here)

ev

(describe argument here)

Returns:

(describe returnvalue here)

.makeArgs(func)

(describe method here)

Arguments:

func

(describe argument here)

Returns:

(describe returnvalue here)

.init

(describe method here)

Returns:

(describe returnvalue here)

.stop

(describe method here)

Returns:

(describe returnvalue here)

.input

.input = kvs

(describe method here)

Arguments:

kvs

(describe argument here)

Returns:

(describe returnvalue here)

.addCon(name, func)

(describe method here)

Arguments:

name

(describe argument here)

func

(describe argument here)

Returns:

(describe returnvalue here)

.addCursor(key: 'cursor')

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

(describe method here)

Arguments:

key

(describe argument here)

Returns:

(describe returnvalue here)

Conductor Internals

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

    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. CVs may be given a single value or an Array of values as default value. See examples below.

Inherited instance methods

Examples

(
a = Conductor.new; // create the conductor
a.addCV(\cv1);    // add some CVs
a.addCV(\cv2);
a.addCV(\cv3);

w = a.show;  // display it
// following line will throw an Error if
// not using Cocoa or SC version < 3.7
// defer( { Document.current.front}, 0.05);
)

(
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

defer({
    a[\cv3].value = 1/(1..128);    // and change cv3 to represent an array
    w = a.show
}, 0.1); // and show it now
)

(
w.close;
defer({
    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);
    w = a.show;

    // following line will throw an Error if
    // not using Cocoa or SC version < 3.7
    // 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(AppClock);
)

w.close;

// Changing CV ranges
(
c = Conductor.make { |conductor, a, b, c, d |
    a.spec_(\freq);
    b.spec_(\freq, 880);
    c.sp(1, 0, 15, 1);
    // a spec with an array given as default value
    // will result in a MultiSlider in the GUI
    d.spec_(\unipolar, 1/(1..128));
};

c.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) )
    }).add;

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