is there a way to “reset” the start count of PulseDivider?
If i understand it right this parameter is non-modulatable, as it relates to the start of the trigger count.
Would there be a way to alter the count with it running? I Imagine something like the reset parameter of Demand Ugens…
I thought I had a solution with Demand, but I think I just found a bug instead: edit: that’s probably an issue with some local changes in Poll, maybe not reproducible in the main branches.
(
a = {
var trig = Impulse.kr(0);
var demand = Demand.kr(trig, 0, Dseq([100, 101], inf));
demand.poll(trig);
Silent.ar(1);
}.play;
)
UGen(OutputProxy): 0 // not 100!!??!?!?!?
a.release;
Oh, but I just thought of a workaround:
(
a = { |division = 4, start = 0, t_reset = 0|
var trig = Impulse.kr(2);
var count = Demand.kr(trig, t_reset, Dseries(0, 1, inf));
// 1. "lazy equality" (Object helpfile)
// 2. Trig1 is just to force a single (kr) sample
// instead of a gate until the next trigger
var div = Trig1.kr(((count + start + 1) % division) |==| 0, ControlDur.ir);
[count, div].poll(trig);
Silent.ar(1)
}.play;
)
a.set(\t_reset, 1);
a.release;
That’s based on analyzing PulseDivider’s behavior: with div == 4, it produces a trigger on the 4th, 8th etc. input triggers. (This is different, for instance, from modular clock dividers, which would output a trigger on the 1st, 5th, 9th etc.)
I was wanting to provide an array for the start parameter, but it seems that the lazy equality method doesnt let it expand?
(switched to .ar in the example)
(
a = { | t_reset = 0|
var trig = Impulse.ar(20);
var start = (1..8);
var division = 8;
var count = Demand.ar(trig, t_reset, Dseries(0, 1, inf));
// 1. "lazy equality" (Object helpfile)
// 2. Trig1 is just to force a single (kr) sample
// instead of a gate until the next trigger
var div = Trig1.ar(((count + start + 1) % division) |==| 0, SampleDur.ir).poll;
div.poll(trig);
Silent.ar(1)
}.play
)
the goal is to be able to switch between trigger streams with different starting arrays on each reset, which PulseDivider didn’t seem to allow!
It’s a very tricky problem actually… if we assume that arrays should “compose” |==| into an array of results, then if(arrayA |==| arrayB) will fail. But as you found, if we treat it as a regular object and produce a single result, then your case doesn’t work. There might not be a good way to solve it.
In the meantime, you can write the full form BinaryOpUGen('==', yourArrayOfUGens, 0) (edit: corrected typos in the code expression) and this will multichannel-expand.
Thank you for your suggestion with BinaryOpUgen, it seems to be working well, aside from one oddity: if the division is set to 1 there won’t be any trigger outputted. Shouldn’t it be expected to return the incoming rate?
(
a = { | t_reset = 0|
var trig = Impulse.ar(20);
var start = (1..8);
var division = MouseX.kr(1,8).round(1).poll;
var count = Demand.ar(trig, t_reset, Dseries(0, 1, inf));
var div = Trig1.ar(BinaryOpUGen('==', (count+start+1)%division, 0), SampleDur.ir);
div.sum*0.05;
}.play
If division == 1, and count and start are always integers, then this value is continuously 0. Then the == operation is continuously 1. A trigger is defined as a transition from non-positive to positive. “Continuously 1” is continuously positive, thus no transitions, thus only one initial trigger.
following up on this example i’m trying to dynamically reshuffle the start positions of triggers (for an envelope), which seems to cause discontinuities because of too fast retriggering. (with a linear and steady start position for the triggers there’s no problem with clicks)
would it be possible to keep envelopes intact and and still dynamically reshuffle the start positions of triggers?
({
var n=8;
var freq=80;
var btrig=Impulse.ar(freq);
var division = n;
var start = TIRand.ar(0,n-1,PulseDivider.ar(btrig,n)!8); //dynamically reshuffling start position of triggers causes clicks in envelope
// var start = (1..n); //linear sequence triger offset
var count = Demand.ar(btrig,0, Dseries(0, 1, inf));
var trig=Trig1.ar(BinaryOpUGen('==', ((count+start+1)%(division)), 0), SampleDur.ir);
var init= PulseCount.ar(trig)>0;
init*SinOsc.ar(0,Sweep.ar(trig,freq/n).clip(0,1)*1pi);
}.plot(1))
i think you need a continuous phase.
Ive once tried to convert this into a multi channel random trigger / phase with no luck. maybe we can work on this together.
i think in your example Sweep is receiving a trigger while going from 0 to 1 causing the discontinuity in the phase because the duration between one trigger and the next is unknown.
with this example you get at least a continiuous phase with random triggers to drive the hanning window (evaluate it several times). but its not multichannel with distributed phases / windows accross channels:
with my example the phase is calculated first and then you can get the triggers by HPZ1 which solves the unknown duration issue.
When using Pulsedivider and Sweep to distribute the triggers / phases round robin across the channels how i would like to (to disable the randomness and mask the triggers by demand sequences of 0s and 1s), its the other way around first the triggers are calculated and then plugged into Sweep to get the phases. I just have no idea how to go about that. but when solved this could be used for your use case with Pulsedivider as well i guess to get random multichannel triggers and continiuous phases.
ok, i see. i unfortunately don’t have an immediate suggestion for this also, but it sounds like it could pave a way for many usages surely!
alternatively for my example im pondering a compromise (too avoid too many calculations of phase e.g.) with a gate that blocks sweep from receiving new triggers until the envelope is closed, but this is still an unimplemented, abstract thought.
ideally of course, a smartly set up sweep just neatly triggers and closes envelopes, no matter what random sequence is fed;)
yes i think all the custom made granular stuff with better abilities for modulation per grain. especially FM/PM or AM per grain. i have been either using FM/PM with a stateless index window per grain or a frequency window per grain.
right now:
by using synchronous triggers by Impulse / Pulsedivider / Sweep you get continiuous grains.
by using synchronous triggers by Impulse / Pulsedivider / Sweep masked by demand sequences with 0s and 1s you get semi synchronous / asynchronous grains
by using asynchronous triggers and Pulsedivider you get a discontinuity in the phase.
i think Sweep just starts right away not caring of the first trigger so you have to make sure the first trigger has arrived with PulseCount to start Sweep. Additionally every Sweep gets interrupted by a new trigger not caring if it has reached its end destination or not which is causing the discontinuity.
Yes, i was wondering though if possibly with Gate.ar one could engineer a way in which retriggers only occur under the condition of Sweeps phase being 0, by actively ignoring retriggers while an envelope is open/ non-zero.
This was what i was envisioning “gating” the sweep input. While it avoids retriggering in the middle of an envelope, it effectively skips triggers and so basically creates a much more discontinuous trigger stream. The randomization does work, but overall not a satisfying solution to the problem unfortunately!
({
var n=8;
var freq=80;
var btrig=Impulse.ar(freq);
var division = n;
var start = TIRand.ar(0,n-1,PulseDivider.ar(btrig,n)!8); //dynamically reshuffling start position of triggers causes clicks in envelope
var linstart = (1..n); //linear sequence triger offset
var count = Demand.ar(btrig,0, Dseries(0, 1, inf));
var trig=Trig1.ar(BinaryOpUGen('==', ((count+start+1)%(division)), 0), SampleDur.ir);
var lintrig=Trig1.ar(BinaryOpUGen('==', ((count+linstart+1)%(division)), 0), SampleDur.ir);
var init= PulseCount.ar(trig)>0;
init*SinOsc.ar(0,Sweep.ar(trig*lintrig,freq/n).clip(0,1)*1pi);
}.plot(1))