Continuous phase from asynchronous triggers

hey, i would like to get a continuous phase from asynchronous triggers which i can distribute and overlap round robin across multiple channels. In the best case to be used with the timingInformation function below:

this works for synchronous triggers:

(
var timingInformation = { |numChannels, trig, grainRate|
	var rate = if(trig.rate == \audio, \ar, \kr);
	var arrayOfTrigsAndPhases = numChannels.collect{ |i|
		var localTrig = PulseDivider.perform(rate, trig, numChannels, i);
		var hasTriggered = PulseCount.perform(rate, localTrig) > 0;
		var localPhase = Sweep.perform(rate, localTrig, grainRate * hasTriggered);
		[localTrig, localPhase];
	};
	var trigsAndPhasesArray = arrayOfTrigsAndPhases.flop;
	(\trigger : trigsAndPhasesArray[0], \phase: trigsAndPhasesArray[1]);
};

var hanningWindow = { |phase|
	(1 - (phase * 2pi).cos) / 2 * (phase < 1);
};

{
	var numChannels = 4;
	
	var tFreq = \tFreq.kr(100);
	var trig = Impulse.ar(tFreq);
	var overlap = \overlap.kr(2);  
	var grainRate = tFreq / min(overlap, numChannels);

	var timings = timingInformation.(numChannels, trig, grainRate);

	hanningWindow.(timings.phase);

}.plot(0.1);
)

this works for getting a continuous phase from asynchronous triggers:

(
var asyncTrigPhase = { |triggerRate, asynchronicity|
	var minDuration = (2 ** asynchronicity) / triggerRate;
	var maxDuration = (2 ** (-1 * asynchronicity)) / triggerRate;
	var demand = Dunique(minDuration * ((maxDuration / minDuration) ** Dwhite(0, 1)));
	var duration = Duty.ar(demand, DC.ar(0), demand);
	var phase = LFSaw.ar(1 / duration, 1).linlin(-1, 1, 0, 1);
	(phase: phase, trigger: HPZ1.ar(phase) < 0);
};

var hanningWindow = { |phase|
	(1 - (phase * 2pi).cos) / 2 * (phase < 1);
};

{
	var tFreq = \tFreq.kr(100);
	var phase = asyncTrigPhase.(tFreq, \asynchronicity.kr(1)).phase;

	hanningWindow.(phase);

}.plot(0.1);
)

but not if you would like to distribute them round robin across multiple channels and use the overlap to adjust the window length.

any help is very much appreciated. thanks.

you can see the discontinuities in this plot, when you increase the overlap up to numChannels:

(
var timingInformation = { |numChannels, triggerRate, asynchronicity, overlap|
	var arrayOfTrigsAndPhases = numChannels.collect{ |i|

		var localFreq = triggerRate / numChannels;
		var localRatio = numChannels / overlap;

		var minDuration = (2 ** asynchronicity) / localFreq;
		var maxDuration = (2 ** (-1 * asynchronicity)) / localFreq;
		var demand = Dunique(minDuration * ((maxDuration / minDuration) ** Dwhite(0, 1)));
		var duration = Duty.ar(demand, DC.ar(0), demand);

		var localPhase = LFSaw.ar(1 / duration, (1 - (i / numChannels + 0.5)) * 2).linlin(-1, 1, 0, 1);
		var localTrig = HPZ1.ar(localPhase) < 0;
		var hasTriggered = PulseCount.ar(localTrig) >= 1;

		localPhase = localPhase * hasTriggered * localRatio;
		[localTrig, localPhase];
	}.reverse;
	var trigsAndPhasesArray = arrayOfTrigsAndPhases.flop;
	(\trigger : trigsAndPhasesArray[0], \phase: trigsAndPhasesArray[1]);
};

var hanningWindow = { |phase|
	(1 - (phase * 2pi).cos) / 2 * (phase < 1);
};

{
	var numChannels = 5;
	var tFreq = \tFreq.kr(100);
	var overlap = min(\overlap.kr(4), numChannels);

	var timings = timingInformation.(numChannels, tFreq, \asynchronicity.kr(1), overlap);

	hanningWindow.(timings.phase);

}.plot(0.2);
)

i would also like to use a triggered Sweep instead of LFSaw.