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.
chain = chain.pvcalc(numframes, func, frombin, tobin, zeroothers)
numframes |
Number of FFT frames to process |
func |
The function that takes two arrays as inputs ( // 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.
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 // 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.
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 // example function { | magnitude, phase, bin, index | [mags.reverse, phases.reverse] // e.g. upside-down spectrum } 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 )
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 )
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].stutter(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 )