VoicerSequencer : QuantTask


A powerful and flexible sequencer for Voicer. Frequency, gate and note length get separate streams; any other argument can be streamed optionally as well. Any of these streams can be changed on-the-fly.


*new(voicer, freqs, durs, lengths, absLengths = true ... args)


Makes a new sequencer.


voicer: The voicer to play on. It should have its clock and latency values already set. The clock should be a TempoClock. The voicer's latency will be used for better timing.

freqs: A Stream or Pattern returning frequency values you want to use. Chords can be played by including an array in the stream. Rests should be written as an empty array (i.e. a chord of zero notes).

durs: A Stream or Pattern returning the number of beats until the next note or chord should sound. This must be an integer or float; arrays are not allowed here.

lengths: A Stream or Pattern returning the length of each note (which may be longer or shorter than the duration of this note, since Voicer is polyphonic). Alternately, you may provide an Integer or Float, which will be multiplied by the duration to get the note length--or nil, to use the duration exactly.

absLengths: if true, the values returned by the length stream will be used as is. If false, the values will be multiplied by the note's duration, allowing you to treat the length stream as a legato/staccato/overlap parameter.

args: An array of pairs: [\argname, Stream/Pattern, \argname, Stream/Pattern, etc.]


play(quant, offset, clock, doReset = false)


Play the sequence. It will begin at the next quant boundary, plus offset beats.


reset


Reset all streams to the beginning.


stop


Stop playing.


freqs_(newStream, quant, offset)

durs_(newStream, quant, offset)

lengths_(newStream, quant, offset)

putArg_(name, newStream, quant, offset)


Change a stream on the fly. If you do putArg on an argument that already has a stream, the old stream will be thrown out first.


latency_(val)


The latency desired in beats. This should match the voicer's latency; if not, notes will not play when you expect.


Example:


(

Instr([\test, \seqpantest], {

arg freq = 440, gate = 0, env, amp = 1, ffreq = 1000, rq = 1, pan = 0;

var out;

out = EnvGen.kr(env, gate, doneAction:2) *

RLPF.ar(Pulse.ar(freq, 0.25, amp), ffreq, rq);

Pan2.ar(out, pan)

}, [\freq, \amp, nil, nil, \freq, [1, 0.05], [-1, 1]]);


v = Voicer(10, Instr.at([\test, \seqpantest]), [\rq, 0.783, \amp, 0.25, \ffreq, 15238, \env, Env.adsr(0, 0.1, 0.75, 0.1), \pan, 0]).latency_(0.2);


t = TempoClock.new(96/60);

//t.gui; // skip this if you don't have TempoClockGui

v.clock_(t);


f = \freq.asSpec;


q = VoicerSequencer(v, 

Pseq([57, 60, 62, 64, 67].midicps, inf),

Prand([ Pseq([0.20], 20)/*, Pseq([0.5], 1)*/ ], inf),

nil, // use durations as lengths

[\ffreq, Pfunc({ f.map(0.5.rand + 0.5)}),

\pan, Pfunc({ 1.0.rand2 })]

);

)


// make rq gui-adjustable

c = v.mapGlobal(\rq, nil, 0.783, [0.05, 1]);

v.gui;


q.insp;


q.play(quant: 4);

q.stop;

q.reset;

q.start;


// for a rest, specify [] as a frequency: voicer thinks you want a chord of 0 notes

q.freqs_(Prand([Pseq([59, 60, 62, 64, 67].midicps, 3), Pseq([[]], 1)], inf), 4);

q.freqs_(Pseq([57, 60, 62, 64, 67].midicps, inf), 4, 2);


// can play a chord by including an array in your freqs pattern

q.freqs_(Pseq([[57, 74, 66, 64], 60, 62, 64, 67].midicps, inf), 4);



// note durations don't change but they sustain longer

q.lengths_(Pfunc({ 1.0.rand + 0.1 }));

q.lengths_(3);

q.lengths_(nil, 4);



// change filter and panning patterns

q.putArg(\ffreq, Pseq([Pgeom(800, 1.2, 18)], inf));

q.putArg(\pan, Pseq([Pseries(-1, 0.05, 40)], inf));


q.stop; // you should stop the sequencer before stopping the clock;

// otherwise some notes may get stuck

v.free; t.stop;