PV_ChainUGen
is an abstract class, not used directly, but only its subclasses are. It represents phase-vocoder UGens - i.e. UGens which apply some kind of transformation to the frequency-domain signal produced by FFT.
It encompasses all unit generators whose output is an FFT chain. This is why FFT is in this group but IFFT is not - the IFFT ugen outputs ordinary time-domain audio.
For more information on using these unit generators, see FFT Overview.
Returns the FFT chain buffer's size.
(
{
var chain = FFT(LocalBuf(1024));
chain.fftSize.poll;
0.0
}.play;
)
pvcalc applies a function to the frequency-domain data of an FFT chain. See -pvcollect below for discussion of efficiency considerations. See also -pvcalc2 below, and UnpackFFT.
xxxxxxxxxx
chain = chain.pvcalc(numframes, func, frombin, tobin, zeroothers)
numframes |
Number of FFT frames to process |
func |
The function that takes two arrays as inputs ( xxxxxxxxxx // example function { | magnitudes, phases | [mags.reverse, phases.reverse] // e.g. upside-down spectrum } |
frombin |
Range start (optional) |
tobin |
Range end (optional) |
zeroothers |
If set to 1 then bins outside of the range being processed are silenced. |
The method pvcalc2 is just like -pvcalc but can combine two FFT chains.
xxxxxxxxxx
chain = chain.pvcalc2(chain2, numframes, func, frombin, tobin, zeroothers)
chain2 |
The scond FFT chain. |
numframes |
Number of FFT frames to process |
func |
The function that takes four arrays as inputs (magnitudes1, phases1, magnitudes2, phases2) and returns a resulting pair of arrays xxxxxxxxxx // example function { | magnitudes1, phases1, magnitudes2, phases2 | [magnitudes1, phases2] // e.g. use the magnitudes of one, ane the phases of the other } |
frombin |
Range start (optional) |
tobin |
Range end (optional) |
zeroothers |
If set to 1 then bins outside of the range being processed are silenced. |
Process each bin of an FFT chain, separately, by applying a function to each bin of an FFT chain.
xxxxxxxxxx
chain = chain.pvcollect(numframes, func, frombin, tobin, zeroothers)
numframes |
Number of FFT frames to process |
func |
The function that processes each bin. It should be a function that takes xxxxxxxxxx // example function { | magnitude, phase, bin, index | // randomize magnitudes somewhat (noisier signal) [magnitude * (5.0.rand2.dbamp), phase] } The bin is the integer bin number, starting at 0 for DC, while index is the iteration number, always starting with 0. You can optionally ignore the phase and only return a single (magnitude) value, in which case the phase is assumed to be left unchanged. |
frombin |
Range start (optional) |
tobin |
Range end (optional) |
zeroothers |
If set to 1 then bins outside of the range being processed are silenced. |
Note that this procedure can be relatively CPU-heavy, depending on how you use it. Using pvcollect (or its components, UnpackFFT & PackFFT) is usually less efficient than using a single "PV_" unit generator to process an FFT chain, because it involves the creation of quite a large graph of demand-rate unit generators.
If you wish to reduce the CPU impact of using this approach, try the following:
// a sound file
c.free; c = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
(
{
var in, chain, v;
in = PlayBuf.ar(1, c, BufRateScale.kr(c), loop: 1);
chain = FFT(LocalBuf(1024), in);
chain = chain.pvcalc(1024, {|mags, phases|
//////// Try uncommenting each of these lines in turn and re-running the synth:
[mags * {1.5.rand}.dup(mags.size), phases + {pi.rand}.dup(phases.size)]; // Arbitrary filter, arbitrary phase shift
//[mags.reverse, phases.reverse]; // Upside-down!
//[mags.differentiate, phases.differentiate]; // Differentiate along frequency axis
//[mags[30..] ++ mags[..30], phases[30..] ++ phases[..30]]; // ".rotate" doesn't work directly, but this is equivalent
}, frombin: 0, tobin: 250, zeroothers: 0);
0.5 * IFFT(chain).dup
}.play
)
xxxxxxxxxx
c.free; c = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
(
x = {
var fftsize = 1024;
var in, chain, in2, chain2, out;
in = PlayBuf.ar(1, c, BufRateScale.kr(c), loop: 1);
chain = FFT(LocalBuf(fftsize), in);
// JMcC babbling brook
in2 = ({
RHPF.ar(OnePole.ar(BrownNoise.ar, 0.99), LPF.ar(BrownNoise.ar, 14)
* 400 + 500, 0.03, 0.003) }!2)
+ ({ RHPF.ar(OnePole.ar(BrownNoise.ar, 0.99), LPF.ar(BrownNoise.ar, 20)
* 800 + 1000, 0.03, 0.005) }!2
) * 4;
chain2 = FFT(LocalBuf(fftsize), in2);
chain = chain.pvcalc2(chain2, fftsize, { |mags, phases, mags2, phases2|
[
mags * mags2 / 10,
phases2 + phases
]
}, frombin: 0, tobin: 125, zeroothers: 0);
out = IFFT(chain);
0.5 * out.dup
}.play
)
xxxxxxxxxx
c.free; c = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
(
{
var in, chain, v;
in = PlayBuf.ar(1, c, BufRateScale.kr(c), loop: 1);
chain = FFT(LocalBuf(1024), in);
v = LFPar.kr(0.5).range(0.1, 1);
chain = chain.pvcollect(1024, {|mag, phase, index|
//////// Try uncommenting each of these lines in turn and re-running the synth:
//mag;
//[mag, phase];
//[mag, phase] / 3;
//[mag, phase].sqrt;
//[mag, 3.14.rand];
//[mag, LFNoise0.kr.range(0, 3.14)];
//[mag * Dseq([1, 0, 0, 1, 1, 0, 1, 0].dupEach(8), 999999999999)]; // Can even use Demand ugens! One val demanded each frame
//[mag.sqrt, 3.14.rand];
//if(index % 7 == 0, mag, 0); // Comb filter
//if(LFNoise0.kr(10) > 0.5, mag, 0);
//mag + DelayN.kr(mag, 1, v); // Spectral delay
if((index - LFPar.kr(0.1).range(2, 1024/20)).abs < 10, mag, 0); // Swept bandpass
}, frombin: 0, tobin: 250, zeroothers: 0);
0.5 * IFFT(chain).dup
}.play
)