now you can use the RampIntegrator
and the WindowFunctions in the following example to do FM and PM for the grainPhases:
(
var multiChannelDwhite = { |triggers|
var demand = Dwhite(-1.0, 1.0);
triggers.collect{ |localTrig|
Demand.ar(localTrig, DC.ar(0), demand)
};
};
{
var numChannels = 8;
var reset, tFreqMD, tFreq;
var overlapMD, overlap;
var events, voices, windowPhases, triggers;
var grainFreqMod, grainFreqs, grainPhases, grainWindows;
var grainOscs, grains, sig;
var fmods, modPhases, pmods;
reset = Trig1.ar(\reset.tr(0), SampleDur.ir);
tFreqMD = \tFreqMD.kr(1);
tFreq = \tFreq.kr(10) * (2 ** (SinOsc.ar(0.3) * tFreqMD));
overlapMD = \overlapMD.kr(1);
overlap = \overlap.kr(2) * (2 ** (LFDNoise3.ar(0.1) * overlapMD));
events = EventScheduler.ar(triggerRate: tFreq, reset: reset);
voices = VoiceAllocator.ar(
numChannels: numChannels,
trig: events[0],
rate: events[1] / overlap,
subSampleOffset: events[2],
);
windowPhases = voices[0..numChannels - 1];
triggers = voices[numChannels..numChannels * 2 - 1];
grainWindows = HanningWindow.ar(windowPhases, \skew.kr(0.05));
grainFreqMod = multiChannelDwhite.(triggers);
grainFreqs = \freq.kr(440) * (2 ** (grainFreqMod * \freqMD.kr(1)));
fmods = ExponentialWindow.ar(windowPhases, \pitchSkew.kr(0.03), \pitchShape.kr(0));
grainPhases = RampIntegrator.ar(
trig: triggers,
rate: grainFreqs * (1 + (fmods * \pitchMD.kr(2))),
subSampleOffset: events[2]
);
modPhases = RampIntegrator.ar(
trig: triggers,
rate: grainFreqs * \pmRatio.kr(1.5),
subSampleOffset: events[2]
);
pmods = SinOsc.ar(DC.ar(0), modPhases * 2pi);
grainPhases = (grainPhases + (pmods * \pmIndex.kr(1))).wrap(0, 1);
grainOscs = SinOsc.ar(DC.ar(0), grainPhases * 2pi);
grains = grainOscs * grainWindows;
grains = PanAz.ar(2, grains, \pan.kr(0));
sig = grains.sum;
sig = LeakDC.ar(sig);
sig * 0.1;
}.play;
)
here via the plot:
(
var multiChannelDwhite = { |triggers|
var demand = Dwhite(-1.0, 1.0);
triggers.collect{ |localTrig|
Demand.ar(localTrig, DC.ar(0), demand)
};
};
{
var numChannels = 5;
var reset, tFreqMD, tFreq;
var overlapMD, overlap;
var events, voices, phases, triggers;
var grainFreqMod, grainFreqs, grainPhases, grainWindows;
var grainOscs, grains;
reset = Trig1.ar(\reset.tr(0), SampleDur.ir);
tFreqMD = \tFreqMD.kr(0);
tFreq = \tFreq.kr(400) * (2 ** (SinOsc.ar(50) * tFreqMD));
overlapMD = \overlapMD.kr(0);
overlap = \overlap.kr(5) * (2 ** (SinOsc.ar(50) * overlapMD));
events = EventScheduler.ar(tFreq, reset);
voices = VoiceAllocator.ar(
numChannels: numChannels,
trig: events[0],
rate: events[1] / overlap,
subSampleOffset: events[2],
);
phases = voices[0..numChannels - 1];
triggers = voices[numChannels..numChannels * 2];
grainWindows = HanningWindow.ar(phases, \skew.kr(0.5));
grainFreqMod = multiChannelDwhite.(triggers);
grainFreqs = \freq.kr(800) * (2 ** (grainFreqMod * \freqMD.kr(2)));
grainPhases = RampIntegrator.ar(
trig: triggers,
rate: grainFreqs,
subSampleOffset: events[2]
);
grainOscs = SinOsc.ar(DC.ar(0), grainPhases * 2pi);
grains = grainOscs * grainWindows;
}.plot(0.041);
)