HS with VarGui using HS / HSpar with VarGui 


Part of: miSCellaneous


See also: Working with HS and HSpar, VarGui, VarGui shortcut builds, Event patterns and LFOs, Event patterns and Functions



It is recommended to get familiar with the concepts of the HS / PHS and HSpar / PHSpar classes before combining them with VarGui, see Working with HS and HSpar for an overview. 

Alternative control setups with event patterns and VarGui examples are discussed in Event patterns and LFOs.



Differences to other control setups with event patterns and VarGui 


Instances of HS and PHS are hybrid objects in the sense that their functionality is not interchangeable with that of some (one might think at a first glance) related objects (Synths and Pbinds). A HS contains a SynthDef but will also hold Synths derived thereof. A PHS holds Pbind pairs but, when played, instantiates two EventStreamPlayers to be synchronized, also taking control over the used HS. A played PHSpar even controls a third player switching between running control synths. 


On the other hand VarGui is already prepared to accept synths / synth definitions and pbinds / eventstream players / tasks functions / tasks for generating synth and stream players. For plugging these concepts together it seems practical to use objects (resp. introduce adapting ones) that fit into what's already there. This can be achieved by two methods:


1.) .newPaused, applicable to PHS / PHSpar makes the used HS / HSpar generate a new paused Synth (or possibly several in case of PHSpar). Synths are returned by the method and can be passed to VarGui, which automatically detects their derivation from HS / HSpar and adapts gui functionality.


2.) .asTask, applicable to PHS / PHSpar and PHSuse / PHSparUse returns a wrapper Task for compound players of the PHS family. Playing and stopping the wrapper Task invokes playing and stopping behaviour of the underlying compound players, per default also taking control over playing and stopping the help synth(s).


An alternative approach to link HS and HSpar with VarGui is shown in Ex. 3.




Example 1a:   HS / PHS



(

s = Server.local;

Server.default = s;

s.boot;

)


(

h = HS(s, { |midiCenter = 70, dev = 20, devFreq = 1| LFDNoise3.kr(devFreq, dev, midiCenter) });


p = PHS(h, [], 0.15, [ \midinote, Pkey(\val) ]);


// Instantiation and return of a new paused Synth from HS's synth definition


x = p.newPaused;


// second intermediate object:

// PHS's asTask method generates a wrapping task

// which will invoke expected player actions by notification


// Note asTask's flag newEnvir, it determines if 

// the player should run in a newly generated environment, defaults to true.

// This allows to take one PHS definition with varibles to be set in

// different environments (as with Pbind). 


t = p.asTask;   


// also compare play / stop behaviour with these options (default was hsStop: false, hsPlay: true):

// hsPlay = false will run Synth together with stream only for the first time


// t = p.asTask(hsStop: true, hsPlay: true);

// t = p.asTask(hsStop: true, hsPlay: false);

// t = p.asTask(hsStop: false, hsPlay: false);

)


(

// VarGui detects that a Synth derived from a HS has been passed:

// synth player stop / reset button and mode buttons are greyed out,

// pause button background color is blue as Synth is new and paused.

// Playing and stopping the stream player will also cause the synth's running 

// (and stopping with asTask's option hsStop = true),

// however playing and stopping the help synth separately is still possible.


// For Synths derived from HS / HSpar pushing the general stop button will 

// only cause pausing - other synths will be freed - whereas Cmd. will free all synths,

// but ends HS gui control. Then a new VarGui would have to polled from reevaluated x and t. 


v = VarGui([], 

[[\midiCenter, [50, 65, \lin, 0.01, 60],

\dev, [0, 10, \lin, 0.01, 10],

\devFreq, [0, 3, \lin, 0.01, 0.5] ]],

t, x 

).gui;

)


// free resources, PHSplayer is wrapped, so use Cmd-. or this


h.free; 



Example 1b:   HS / PHS and PHSuse



(

h = HS(s, { |midiCenter = 70, dev = 20, devFreq = 1| LFDNoise3.kr(devFreq, dev, midiCenter) });


p = PHS(h, [], 0.15, [ \midinote, Pkey(\val) ]);

q = PHSuse(h, 0.3, [ \midinote, Pkey(\val) + 5 ]);

r = PHSuse(h, 0.4, [ \midinote, Pkey(\val) + 10 ]);


x = p.newPaused;


// quant arg for syncing


t = [p,q,r].collect(_.asTask(quant: 0.3));

)


(

// The PHSplayer has control over the help synth,

// PHSusePlayers can't be played before,

// though it's possible to start all players together with shift-click.


// For the same reason hsStop and hsPlay options are only available for the PHS-wrapping Task.


// After PHSplayer has been started (and maybe paused) PHSusePlayers can be

// played and stopped separately. 



v = VarGui([],

[[ \midiCenter, [50, 65, \lin, 0.01, 60],

\dev, [0, 10, \lin, 0.01, 10],

\devFreq, [0, 3, \lin, 0.01, 0.5] ]],

t, x 

).gui;

)


h.free; 



Example 2a:   HSpar / PHSpar



(

h = HSpar(s, [

{ |midiCenter = 65, dev = 10, devFreq = 1| LFDNoise3.kr(devFreq, dev, midiCenter) }, 

{ |midiCenter = 90, dev = 10, devFreq = 1| LFSaw.kr(devFreq, 0, dev, midiCenter) }

]);


// switching behaviour defined by hsIndices

p = PHSpar(h, 

pbindArgs: [ 0.15, [ \midinote, Pkey(\thisVal) + [-4, 5] ]],

hsIndices: [ Pstutter(Prand([2, 3, 5, 11], inf), Pseq([0,1], inf))  

]);

// for PHSpar this returns a collection of new paused help synths


x = p.newPaused;


t = p.asTask(quant: 0.3, hsStop: true);


// You can specify start and stop options for selected Synths of HSpar 

// with indices or collections of indices, e.g.:

// t = p.asTask(quant: 0.3, hsStop: 0);

)


(

// Playing and stopping the PHSparPlayer's wrapper Task affects both help synths in parallel.


v = VarGui([],

[[54.1, 3.85, 1.5], [60, 7.8, 2.7]].collect { |y| [ 

\midiCenter, [50, 65, \lin, 0.01, y[0]],

\dev, [0, 10, \lin, 0.01, y[1]],

\devFreq, [0, 3, \lin, 0.01, y[2]]]

}, t, x 

).gui;

)


h.free; 



Example 2b:   HSpar / PHSpar and PHSparUse



(

h = HSpar(s, [

{ |midiCenter = 65, dev = 10, devFreq = 1| LFDNoise3.kr(devFreq, dev, midiCenter) }, 

{ |midiCenter = 90, dev = 10, devFreq = 1| LFSaw.kr(devFreq, 0, dev, midiCenter) }

]);

p = PHSpar(h, 

pbindArgs: [ 0.15, [ \midinote, Pkey(\thisVal) + [-4, 5] ]],

hsIndices: [ Pstutter(Prand([2, 3, 5, 11], inf), Pseq([0,1], inf))  

]);


x = p.newPaused;


q = PHSparUse(h,  

pbindArgs: [ 0.15, [ \midinote, Pkey(\thisVal) + [0, 9] ]],

hsIndices: [ Pstutter(Prand([2, 3, 5, 11], inf), Pseq([0,1], inf)) ] 

);


r = PHSparUse(h,  

pbindArgs: [ 0.15, [ \midinote, Pkey(\thisVal) + [2.5, 7] ]],

hsIndices: [ Pstutter(Prand([2, 3, 5, 11], inf), Pseq([0,1], inf)) ] 

);


t = [p,q,r].collect(_.asTask(quant: 0.3));

)


(

v = VarGui([],

[60, 75].collect { |y| [  

\midiCenter, [55, 85, \lin, 0.01, y],

\dev, [0, 10, \lin, 0.01, 10],

\devFreq, [0, 3, \lin, 0.01, 0.5]]

},

t, x 

).gui;

)


h.free; 



Example 2c:   HSpar / PHSpar and PHSparUse with switch pattern



(

h = HSpar(s, [

{ |midiCenter = 65, dev = 10, devFreq = 1| LFDNoise3.kr(devFreq, dev, midiCenter) }, 

{ |midiCenter = 90, dev = 10, devFreq = 1| LFSaw.kr(devFreq, 0, dev, midiCenter) }

]);

// define event pattern to switch between the two help synths,

// switch times (average and random deviation) to be controlled from gui as well as

// chord structure determined by a single interval parameter 


p = PHSpar(h, 

switchDur: Pfunc { ~swTmMid * (1 + rand2(~swTmDev)) },

switchIndex: Pseq([0,1], inf),

pbindArgs: [ 0.15, [ \midinote, Pkey(\thisVal) + Pfunc { [~int.neg, 0, ~int] } ]],

switchOn: true, // help synths to be resumed when active and ...

switchOff: true // ... to be paused when left - will be reflected by VarGui's button background colors

);

x = p.newPaused;


// chord structure of other pbinds will be taken from same variable ~int, so ...


q = PHSparUse(h, [ 0.15, [ \midinote, Pkey(\val) + Pfunc { ~int.neg / 3 + [~int.neg, 0, ~int] } ]]);

r = PHSparUse(h, [ 0.15, [ \midinote, Pkey(\val) + Pfunc { ~int * 3 / 5 + [~int.neg, 0, ~int] } ]]);


// ... players must be in the same environment, this is ensured by setting .asTask's flag newEnvir to false,

// all will be played and set in currentEnvironment.


t = [p,q,r].collect(_.asTask(quant: 0.3, newEnvir: false));

)


(

v = VarGui([

\swTmMid, [0.3, 1.5, \lin, 0.01, 1],

\swTmDev, [0, 0.5, \lin, 0.01, 0.5],

\int, [0, 12, \lin, 0.01, 8]

],

[60, 75].collect { |y| [  

\midiCenter, [55, 85, \lin, 0.01, y],

\dev, [0, 10, \lin, 0.01, 10],

\devFreq, [0, 3, \lin, 0.01, 0.5]]

},

t, x 

).gui;

)


// As can be seen in gui stopping with red stop button won't stop switching.

// Player had been started with asTask's default option switchStop = false

// and, as wrapped in a task, is not directly accessible.

// Anyway you can, as always, stop and free resources with Cmd-. or


h.free; 




Example 3:   Help Synths not passed explicitely



This is an alternative approach if syncing control synths and streams is not important - whereas resetting control synths becomes an option.


(

// Actual help synths can be defined separately and fed into

// formal help synths via control busses


// control synths hard-wired to allocated busses


c = Bus.control(s, 2).index;


SynthDef(\control_1, { |midiStart = 65, midiDiff = 15, duration = 10| 

Out.kr(c, XLine.kr(midiStart, midiStart + midiDiff, duration) ) }).add;

SynthDef(\control_2, { |midiStart = 90, midiDiff = 15, duration = 10| 

Out.kr(c+1, XLine.kr(midiStart, midiStart - midiDiff, duration) ) }).add;


h = HSpar(s, [{ In.kr(c) }, { In.kr(c+1) }] );


// PHSpar and PHSparUse defined in same way as above but ...

// ... PHSparPlayer wrapper Task defaults to hsStop = false,

// so reading values from bus c is not stopped with pausing and

// PHSparUsePlayers can still get new values from "external" control synths


p = PHSpar(h, 

pbindArgs: [ 0.15, [ \midinote, Pkey(\thisVal) + [-4, 5] ]],

hsIndices: [ Pstutter(Prand([2, 3, 5, 11], inf), Pseq([0,1], inf))  

]).asTask(quant: 0.3);



q = PHSparUse(h,  

pbindArgs: [ 0.15, [ \midinote, Pkey(\thisVal) + [0, 9] ]],

hsIndices: [ Pstutter(Prand([2, 3, 5, 11], inf), Pseq([0,1], inf)) ] 

).asTask(quant: 0.3);


r = PHSparUse(h,  

pbindArgs: [ 0.15, [ \midinote, Pkey(\thisVal) + [2.5, 7] ]],

hsIndices: [ Pstutter(Prand([2,3,5, 11], inf), Pseq([0,1], inf)) ] 

).asTask(quant: 0.3);

)



(

// Now control synths have to be started before streams !

// Caps + shift click for parallel play action will work on cocoa, 

// you can choose custom bundle latency (c) and set it slightly below server latency


v = VarGui([],

[60, 80].collect { |x| [ 

\midiStart, [50, 95, \lin, 0.01, x],

\midiDiff, [0, 30, \lin, 0.01, 15],

\duration, [1, 20, \lin, 0.01, 15]]

}, [p,q,r], [\control_1, \control_2]

).gui(playerPriority: \synth, sliderPriority: \synth);


// modes of "external" control synths can be set

// resetting (starting new synths of same definition) is also possible

)


// additional synths engaged here, so cleanup with Cmd-. or stopping in gui plus ...


h.free;