VarGui object for creating a slider gui to set environmental variables and synth controllers


Part of: miSCellaneous


Inherits from: Object


SC setups may contain discrete and continuous control, e.g. using combinations of Pbinds and Synths. There are many possible ways of interaction between such elements, sometimes there is the alternative to control things by server or language (e.g. Pbinds versus Demand Ugens) but often one needs to have both. VarGui is a simple multi purpose slider GUI, intended for indentifying parameters, which may then be used again in another setup.

Pbinds can easily refer to environmental variables - e.g. via index streams (and collect) or Pfunc - so VarGui can control environmental variables and synth controllers. VarGui was not intended for performance, there is a lack of run / pause / stop features etc.


Works with Cocoa and SwingOSC.



Some Important Issues Regarding VarGui


When using arrayed synth controls it is better to take Synths derived from SynthDefs and pass these Synth objects (instead of nodeIDs) to synthIDs  - otherwise setting a single component of an arrayed control also causes setting of all components below.


Creation / Class Methods


*new (varCtr, synthCtr, synthIDs, varCtrGroups, synthCtrGroups, environment, players, server)

Create a new VarGui object, which holds control specifications.

varCtr - Collection of the form [ var1, spec1, ... varN, specN ], where

var must be the symbol of the environmental variable to be set and

spec a collection of the form [ minval, maxval, warp, step, default ],

defining the corresponding ControlSpec, or a collection of specs of this form, 

if the environmental variable has the value of a collection itself.

Defaults to [].

synthCtr - Collection of the form [ synthAlias1, specPairs1, ... synthAliasN, specPairsN ], where

synthAlias is a symbol that names the synth in the gui and

specPairs a collection of the form [ arg1, spec1, ... , argN, specN ] where

arg must the symbol of the control arg to be set and

spec a collection of the form [ minval, maxval, warp, step, default ] 

defining the corresponding ControlSpec, or a collection of specs of this form

for an arrayed control.

Must be set together with synthIDs.

Defaults to [].

synthIDs - Collection of Synths or nodeIDs corresponding to synthCtr.

Defaults to [].

varCtrGroups -  Color grouping for environmental variables.

Expects collection of collections of indices, whereby 

components of a collection are counted as seperate indices.

Per default different colors are chosen for different variables but

the same color is used for all components of a collection.

synthCtrGroups - Color grouping for synth controllers.

Expects collection of collections of indices, whereby 

components of a collection are counted as seperate indices

Per default different colors are chosen for different synths but

the same color is used for all args of a synth.

environment - Environment of the variables to be set.

Defaults to currentEnvironment.

players - Collection of EventStreamPlayers to be stopped by the gui stop button.

Defaults to [].

server - The server to send node messages to. 

Defaults to the local server.

(

s = Server.local;

s.boot;

)

// control of one variable

(

~midi = 50;

p = Pbind(\midinote, Pfunc({ ~midi })).play; // Pbind which refers to an environmental variable

)

// VarGui to set this variable - works with Swing or Cocoa as current GUI scheme.

// After the gui method has opened the window, the sound does not change. 

// All values update to gui values after pushing the update button, a single value after moving the slider.

VarGui([\midi, [50, 70, \linear, 0.1, 70]], players: [p]).gui;

// as the player was given, the Pbind can be stopped via the stop button.

//////////////////////////////////////////////////// 


// control of some synth args

(

~s = { |freq = 400, devFactor = 0.1, devFreq = 10| 

SinOsc.ar(SinOsc.ar(devFreq, 0, devFactor * freq, freq), mul: 0.1) }.play;

)

// VarGui to set the synth args - works with Swing or Cocoa as current GUI scheme.

// After the gui method has opened the window, the sound does not change. 

// All values update to gui values after pushing the update button, a single value after moving the slider.

(

VarGui([], // no varCtr - may also be nil

[\aSynth, [\freq, [300, 3000, \exponential, 1, 700],

\devFactor, [0, 1, \linear, 0.01, 0.5],

\devFreq, [0, 50, \linear, 0.1, 20]]], 

[~s]).gui;

)


// the same, collecting the frequency deviation params in a separate color group


(

VarGui([], 

[\aSynth, [\freq, [300, 3000, \exponential, 1, 700],

\devFactor, [0, 1, \linear, 0.01, 0.5],

\devFreq, [0, 50, \linear, 0.1, 20]]], 

[~s], [], // no varCtr - may also be nil

[[1,2]]

).gui;

)


 

// The stop button frees all nodes.



*load (pathname, filename, synthIDs, varCtrGroups, synthCtrGroups, environment, players, server)

Create a VarGui, that loads varCtr and synthCtr data from the specified file

(probably saved before via dialog),

pathname - Pathname string.

filename - Filename string.

synthIDs - Collection of of Synths or nodeIDs to be controlled by the loaded synth control data, therefore  

it must fit the loaded specifications.

Defaults to [].

varCtrGroups -  Color grouping for environmental variables.

Expects collection of collections of indices, whereby 

components of a collection are counted as seperate indices.

Per default different colors are chosen for different variables but

the same color is used for all components of a collection.

synthCtrGroups - Color grouping for synth controllers.

Expects collection of collections of indices, whereby 

components of a collection are counted as seperate indices

Per default different colors are chosen for different synths but

the same color is used for all args of a synth.

environment - Environment of the variables to be set.

Defaults to currentEnvironment.

players - Collection of EventStreamPlayers to be stopped by the gui stop button.

Defaults to [].

server - The server to send node messages to. 

Defaults to the local server.

Creating a gui from the VarGui object


gui (sliderHeight, font, colorsLo, colorsHi, fontHeight, buttonFontFactor, buttonHeightFactor)

Create a gui window.

sliderHeight - Gui slider height. Defaults to 24. 

font - Gui font as string. Defaults to "Arial".

colorsLo - Low RGB value for color space, in which distinct slider and background colors are chosen by random. 

Must be between 0 and 1. Defaults to 0.25. 

colorsHi - High RGB value for color space, in which distinct slider and background colors are chosen by random. 

Must be between 0 and 1. Defaults to 0.7. 

fontHeight - Normally this doesn't have to be set, adapts to sliderHeight if value nil is given (default).  

buttonHeightFactor - Proportion of button height to slider height. Defaults to 1.2.  

buttonFontFactor - Proportion of button font height to slider font height. Defaults to 1.

(

~s = { |freq = 400, devFactor = 0.1, devFreq = 10| 

SinOsc.ar(SinOsc.ar(devFreq, 0, devFactor * freq, freq), mul: 0.1) }.play;

)

(

VarGui([], // more colors, try several times

[\aSynth, [\freq, [300, 3000, \exponential, 1, 700],

\devFactor, [0, 1, \linear, 0.01, 0.5],

\devFreq, [0, 50, \linear, 0.1, 20]]], 

[~s], [], [[0],[1],[2]]).gui(16, "Monaco", 0.0, 1);

)

 

(

VarGui([], // dark

[\aSynth, [\freq, [300, 3000, \exponential, 1, 700],

\devFactor, [0, 1, \linear, 0.01, 0.5],

\devFreq, [0, 50, \linear, 0.1, 20]]], 

[~s], [], [[0],[1],[2]]).gui(16, "Courier", 0.0, 0.5);

)

 

(

VarGui([], // bright

[\aSynth, [\freq, [300, 3000, \exponential, 1, 700],

\devFactor, [0, 1, \linear, 0.01, 0.5],

\devFreq, [0, 50, \linear, 0.1, 20]]], 

[~s], [], [[0],[1],[2]]).gui(20, "Geneva", 0.5, 1);

)


// if there are many sliders, it may be necessary to set font params explicitely

(

v = VarGui( // only making sliders small will not do for cocoa -

[\collVarOne, [300, 3000, \exponential, 1, 700] ! 15,

\collVarTwo, [0, 1, \linear, 0.01, 0.5] ! 15,

\collVarThree, [0, 50, \linear, 0.1, 20] ! 20], 

[~s]).gui(9);

)

 

(

v = VarGui( // better with setting additional layout params 

[\collVarOne, [300, 3000, \exponential, 1, 700] ! 15,

\collVarTwo, [0, 1, \linear, 0.01, 0.5] ! 15,

\collVarThree, [0, 50, \linear, 0.1, 20] ! 20], 

[~s]).gui(9, fontHeight: 7, buttonHeightFactor: 3, buttonFontFactor: 1.5);

)



update

Update variables and controllers to current slider values. Like update button action.


updateVarSliders(key, val, indexOffset, updateVar)

Update variable sliders.

key - Variable name given as symbol.  

val - Variable value. Maybe also be collection, then a sequence of variable sliders is set. 

indexOffset - Integer. Causes updating to start indexOffset below the slider determined by key. Defaults to 0.

updateVar - Boolean. Determines if variables should be updated with sliders. Defaults to true.


updateSynthSliders(synthKey, key, val, indexOffset, updateSynth)

Update synth sliders.

synthKey - Synth identifier given as symbol (gui synth label) or integer (gui synth number).  

key - Synth control name given as symbol.  

val - Synth control value. Maybe also be collection, then a sequence of synth sliders is set. 

indexOffset - Integer. Causes updating to start indexOffset below the slider determined by key. Defaults to 0.

updateSynth - Boolean. Determines if synth controls should be updated with sliders. Defaults to true.


font (font, height, buttonFontFactor)

Set gui font.

font - Font as string. Defaults to "Arial".

height - Font height. Defaults to 16.

buttonFontFactor - Proportion of button font height to slider font height. Defaults to 1.

(

v = VarGui([\midi, [50, 70, \linear, 0.1, 70]]).gui;

)

(

v.font("Verdana", 10, 1)

)





Examples



// running a Pbind and a control synth.

// The control synth controls the pitches of all Pbind-driven synths.



(

s = Server.local;

s.boot;

)


( 

// control synth


~cb = Bus.control(s,1);


SynthDef("pitchLFO", {|out = 0, devFreq = 0.5, midiCenter = 60, midiDev = 10|

Out.kr(out, LFDNoise3.kr(devFreq, midiDev, midiCenter))

}).perform(Main.versionAtMost(3,3).if { \memStore }{ \add });


// sound synth

// mix of sine and pulse, interval, basic frequency read from control bus


SynthDef("simpleMix", {|out = 0, in = 0, soundMix = 0.5, pulseWidth = 0.5, 

att = 0.005, rel = 0.1, amp = 0.1, midiInt = 3, midiAdd = 0|

var mixL, mixR, freq1, freq2;

freq1 = (In.kr(in, 1) + midiAdd).midicps;

freq2 = freq1 * midiInt.midicps / 0.midicps;

mixL = (SinOsc.ar(freq1, 0, amp) * (1 - soundMix)) + (Pulse.ar(freq1, pulseWidth, amp) * soundMix);

mixR = (SinOsc.ar(freq2, 0, amp) * (1 - soundMix)) + (Pulse.ar(freq2, pulseWidth, amp) * soundMix);

Out.ar(out, EnvGen.ar(Env.perc(att, rel), doneAction: 2) *  [mixL, mixR])

}).perform(Main.versionAtMost(3,3).if { \memStore }{ \add });

)



(

// define GUI variables 


~int = [10, 11.5]; // interval bounds

~rel = [0.04, 0.1]; // short and long release time

~amp = 0.1; // amplitude



// run LFO synth and pattern


~pitchCtr = Synth("pitchLFO", [\out, ~cb.index, \midiCenter, 70, \midiDev, 20, \devFreq, 1]);


// Pbind refers to environmental variables 


p = Pbind(\instrument, \simpleMix,

\dur, 0.1,

\in, ~cb.index,

\amp, Pfunc({ ~amp }),

\midiInt, Pfunc({ rrand(~int[0], ~int[1]) }),

\rel, Prand([0,1], inf).collect({|i| ~rel[i]  }),

\soundMix, Pwhite(0.1, 0.9, inf)).play;

)



(

// update gui values by pressing the update button and play around


VarGui([\int, [[0, 19, \linear, 0.1, 3], [0, 19, \linear, 0.1, 4]], 

    \rel, [[0.01, 0.4, \linear, 0.005, 0.01], [0.01, 0.4, \linear, 0.005, 0.15]], 

    \amp, [0.0, 0.3, \linear, 0.01, 0.15]],

[\pitchLFO, [\midiCenter, [45, 80, \linear, 0.1, 70],

\midiDev, [0, 20, \linear, 0.1, 20], 

\devFreq, [0.1, 15, \linear, 0.1, 1]]],

[~pitchCtr], players: [p]

).gui(20)

)



//////////////////////////////////////////////////// 



// running a sound synth controlled by a LFO pitch synth and a Pbind, which sets synth args (like Pmono)



// SynthDefs from above


(

~cb = Bus.control(s,1);

~pitchCtr = Synth("pitchLFO", [\out, ~cb.index, \midiCenter, 65, \midiDev, 2.2, \devFreq, 7]);

~s = Synth("simpleMix",[\in, ~cb.index, \rel, 10000 /* neglecting perc enevelope */] );


~dur = [0.15, 0.35]; // 2 durations

~durNum = [4, 2]; // number of durations within a period


~amp = [0.05, 0.2]; // 2 amplitudes

~ampNum = [5, 1]; // number of amplitudes within a period


~int = [1, 5]; // interval bounds

~midiAdd = [0, 0]; // midi add bounds - added to pitch center given by LFO

)



// Pbind refers to environmental variables 


(

p = Pbind(\type, \set,

// periods of durations and amplitudes

\dur, Pn( Plazy({ Pshuf((0!~durNum[0]) ++ (1!~durNum[1])) }) ).collect({|i| ~dur[i] }),

\amp, Pn( Plazy({ Pshuf((0!~ampNum[0]) ++ (1!~ampNum[1])) }) ).collect({|i| ~amp[i] }),

\soundMix, Pfunc({ rrand(0.0, 1.0) }),

// interval and add chosen between bounds

\midiInt, Pfunc({ rrand(~int[0], ~int[1]) }),

\midiAdd, Pfunc({ rrand(~midiAdd[0], ~midiAdd[1]) }),

\args, [\amp, \soundMix, \midiInt, \midiAdd]

).play;

)



// update gui values by pressing the update button and play around


(

VarGui([\dur, [[0.1, 0.4, \linear, 0.05, 0.15], [0.1, 0.4, \linear, 0.05, 0.35]], 

\durNum, [[1, 5, \linear, 1, 4], [1, 5, \linear, 1, 2]],

\amp, [[0.0, 0.3, \linear, 0.01, 0.05], [0.0, 0.3, \linear, 0.01, 0.15]], 

\ampNum, [[1, 5, \linear, 1, 5], [1, 5, \linear, 1, 1]],

\int, [[-20, 20.0, \linear, 0.1, 3], [-20, 20.0, \linear, 0.1, 18]],

\midiAdd, [[-15, 15.0, \linear, 0.1, -9], [-15, 15.0, \linear, 0.1, 9]] ],

    [\pitchLFO, [\midiCenter, [45, 80, \linear, 0.1, 65],

\midiDev, [0, 20, \linear, 0.1, 2.2], 

\devFreq, [0.1, 15, \linear, 0.1, 7]],

\mainSynth, [\out, [0, 1, \linear, 1, 0]] ],

    [~pitchCtr, ~s], players: [p]

).gui(18)

)


// stop all after saving a parameter snapshot by button / dialog to the file "XY" 

// you can then play synths and pattern again and reload the GUI with the appropriate pathname



VarGui.load("PathToXY", "XY",  [~pitchCtr, ~s], server: s).gui(18);




//////////////////////////////////////////////////// 



// Synth definition with arrayed control

(

SynthDef(\vibes, { |amp = 0.005, devFactor = 0.1, devFreq = 10, devDisturb = 0.1| 

var freq = \midi.kr(20!30).midicps;

Out.ar(0, Mix.fill(30, {|i| [i.even.if {0}{1}, i.even.if {1}{0}] * 

SinOsc.ar(SinOsc.ar(devFreq * LFDNoise3.ar(0.5, devDisturb), 

0, devFactor * freq[i], freq[i]), mul: amp) } )) 

}).perform(Main.versionAtMost(3,3).if { \memStore }{ \add });

)


// open gui with random midi values first 


(

~s = Synth.newPaused(\vibes); // so gui can refer to Synth 


v = VarGui([], 

[\aSynth, [\midi, 30.collect({ [45.0, 95, \linear, 0.01, rrand(45,95)] }),

\devFactor, [0, 0.01, \linear, 0.001, 0.005],

\devFreq, [0, 1, \linear, 0.01, 0.5], 

\devDisturb, [0, 0.99, \linear, 0.01, 0.1], 

\amp, [0, 0.05, \linear, 0.001, 0.005]]], 

[~s], [], (0..33).clumps([30,3,1]) /* color scheme */

).gui(16).update;

)



// start sound


~s.run;



// update whole control array with one random value


v.updateSynthSliders(\aSynth, \midi, rrand(50, 80.0)!30);



// update random group, synth may be given by index


v.updateSynthSliders(0, \midi, rrand(50, 80.0)!5, rrand(0,25));


// phase shift gui


(

p = Pbind(

\dur, 0.07,

\type, \rest,

\i, Pseries(),

\do, Pfunc { |e| v.updateSynthSliders(0, \midi, (e.i / 5).sin * 25 + 70, e.i % 30) }

).play(AppClock)

)



// stop updating


p.stop;

 

 

// free synth


~s.free;