PV_ChainUGen:
Filter:
Classes | UGens > FFT

PV_ChainUGen : WidthFirstUGen : UGen : AbstractFunction : Object

Base class for UGens that operate on FFT chains

Description

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.

Class Methods

Inherited class methods

Instance Methods

.fftSize

Returns the FFT chain buffer's size.

(
{
    var chain = FFT(LocalBuf(1024));
    chain.fftSize.poll;
    0.0
}.play;
)

.pvcalc(numframes, func, frombin: 0, tobin, zeroothers: 0)

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)

Arguments:

numframes

Number of FFT frames to process

func

The function that takes two arrays as inputs (magnitude, and phase) and returns a resulting pair of arrays [magnitude, phase].

// 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.

.pvcalc2(chain2, numframes, func, frombin: 0, tobin, zeroothers: 0)

The method pvcalc2 is just like -pvcalc but can combine two FFT chains.

chain = chain.pvcalc2(chain2, numframes, func, frombin, tobin, zeroothers)

Arguments:

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 [magnitude, phase].

// 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.

.pvcollect(numframes, func, frombin: 0, tobin, zeroothers: 0)

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)

Arguments:

numframes

Number of FFT frames to process

func

The function that processes each bin. It should be a function that takes magnitude, phase, bin, index as inputs and returns a resulting array [magnitude, phase].

// 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.

Discussion:

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:

Inherited instance methods

Examples

pvcalc

// 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
)

pvcalc2

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
)

pvcollect

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
)