VoicerProcessGroup

VoicerToggleGroup

Allows user-defined processes to be triggered from a button (VoicerToggleGroup) or drop down menu (VoicerProcessGroup).

Note: In general, you should not use these classes directly. There instances should be created and destroyed using aVoicer.addProcess(states, type) and aVoicer.removeProcess(aProcessGroup).

Syntax for states array

The syntax is very similar to that for SCButton: an array of arrays, each inner array containing the information for one state. Here, however, each state also needs an action function and a task:

[ [ "Label", { action }, { arg v; { task } } or nil, foreground color, background color ],

[ "Label", { action }, ..... ] ..... ]

The action function is passed two arguments: the voicer process to which it belongs, and this VoicerProcessGroup. Playing, stopping, etc. can then be directed to the first argument. In addition, you can use group.stopAll or group.stopOthers to replace a currently playing task with another:

[ [ "Seq 1", { arg p, g; g.stopOthers(p); p.play }, VoicerSequencer(...) ],

[ "Stop seq 1", { arg p, g; p.stop } ],

[ "Seq 2", { arg p, g; g.stopOthers(p); p.play }, VoicerSequencer(...) ],

[ "Chg dur", { arg p, g; p.task.durs_([....]) } ],

[ "Stop seq 2", { arg p, g; p.stop } ] ]

In the task slot, you should supply a QuantTask (or subclass), or a function whose value will be wrapped in a QuantTask. Note that carefully: if you use a function, the function should return another function that will be used in the task. The outer function will be passed the voicer to use as an argument, so you should write it this way:

{ arg v; // outer function

{ // inner function

loop({

[60, 62, 64, 65, 67, 69, 71, 72].midicps.do({ arg f;

v.gate1(f, 0.5)

})

})

}

}

That's a lot of braces to keep track of--but just remember that the function that follows the argument should be written exactly the same as if you were saying QuantTask.new(func). Why? Because you can't give an argument to a Task at play time. If you use VoicerSequencer, the whole mess is avoided:

// the same task just shown:

{ arg v; VoicerSequencer(v, Pseq([60, 62, 64, 65, 67, 69, 71, 72], inf), 0.5) }

The "arg v" here may be omitted because you know which voicer you're using to add the process to:

myVoicer.addProcess([

[ "Play Cmaj", { arg p, g; g.play },

VoicerSequencer(myVoicer, Pseq([60, 62, 64, 65, 67, 69, 71, 72], inf), 0.5) ],

[ "Stop CMaj", { arg p, g; p.stop } ]

], \toggle);

Note that the task is omitted in several of the example states above. This is because often you want several states in a row to act on the same task. By omitting the task, this state will use the last task that was provided.

VoicerProcessGroup and VoicerToggleGroup are defined using the same syntax. The difference is that VoicerProcessGroup uses a pop up menu and VoicerToggleGroup uses a button. VoicerProcessGroup is more flexible because you can choose the actions out of order, but VoicerToggleGroup is more immediate because you just click and it does it. In VoicerToggleGroup, however, you always go through the actions in order (which is perfect for simple stop-start actions with only one task). To make a toggle group, use aVoicer.addProcess(states, \toggle).

Example:

(

s.waitForBoot({

SynthDef("voicerprocess", { arg freq, gate, ffreq, rq, outbus;

Out.ar(outbus, RLPF.ar(Pulse.ar([freq, freq*0.998], 0.25), ffreq, rq, 0.5)

* EnvGen.kr(Env.adsr(0, 0.1, 0.75, 1), gate, doneAction:2))

}).send(s);

v = Voicer(15, "voicerprocess").clock_(TempoClock.default).latency_(0.2);

// so you can play with filter

v.mapGlobal(\rq, value: 0.15, spec: [0.05, 1]);

v.mapGlobal(\ffreq, value: 1300, spec: \freq);

v.gui;

// TempoClock.default.gui; // optional, if you have TempoClockGui installed

// note that play and stop are crisscrossed here with their tags

// this is b/c when you click play, it does the action for state #1 (not #0)

// and vice versa

v.addProcess([

[ "Play CMaj", { arg p, g; p.stop },

VoicerSequencer(v, Pseq([60, 62, 64, 65, 67, 69, 71, 72].midicps, inf), 0.25) ],

[ "Stop CMaj", { arg p, g; p.reset; p.play } ]

], \toggle);

v.addProcess([ // this one will be a menu; note that actions correspond to tags here

[ "Stop CMaj", { arg p, g; p.stop },

VoicerSequencer(v, Pseq([60, 62, 64, 65, 67, 69, 71, 72].midicps, inf), 0.25) ],

[ "Play CMaj", { arg p, g; p.play } ],

[ "Reset CMaj", { arg p; p.reset } ]

]);

});

)

// click the button, see what happens

// try starting one, setting the offset to 0.5, then starting the other :)

v.free;