2 "Unpack FFT" UGens (c) 2007 Dan Stowell.
3 Magical UGens for treating FFT data as demand-rate streams.
6 // Actually this just wraps up a bundle of Unpack1FFT UGens
7 UnpackFFT : MultiOutUGen {
8 *new { | chain, bufsize, frombin=0, tobin |
9 var upperlimit = bufsize/2;
10 tobin = if(tobin.isNil, upperlimit, {tobin.min(upperlimit)});
11 ^[Unpack1FFT(chain, bufsize, (frombin..tobin), 0),
12 Unpack1FFT(chain, bufsize, (frombin..tobin), 1)].flop.flatten;
16 Unpack1FFT : PV_ChainUGen {
17 *new { | chain, bufsize, binindex, whichmeasure=0 |
18 //("bufsize:"+bufsize).postln;
19 ^this.multiNew('demand', chain, bufsize, binindex, whichmeasure);
23 // This does the demanding, to push the data back into an FFT buffer.
24 PackFFT : PV_ChainUGen {
26 *new { | chain, bufsize, magsphases, frombin=0, tobin, zeroothers=0 |
27 tobin = tobin ?? {bufsize/2};
28 ^this.multiNewList(['control', chain, bufsize, frombin, tobin, zeroothers, magsphases.size] ++ magsphases.asArray)
33 // Conveniences to apply calculations to an FFT chain
34 PV_ChainUGen : WidthFirstUGen {
36 // Give it a func to apply to whole set of vals: func(mags, phases)
37 pvcalc { |numframes, func, frombin=0, tobin, zeroothers=0|
38 var origmagsphases, magsphases, ret;
39 origmagsphases = UnpackFFT(this, numframes, frombin, tobin).clump(2).flop;
40 magsphases = func.value(origmagsphases[0], origmagsphases[1]);
41 // Add phases back if they've been ignored
42 magsphases = magsphases.size.switch(
43 1, {magsphases ++ origmagsphases[1]},
45 // any larger than 2 and we assume it's a list of magnitudes
46 {[magsphases, origmagsphases[1]]}
48 magsphases = magsphases.flop.flatten;
49 ^PackFFT(this, numframes, magsphases, frombin, tobin, zeroothers);
51 // The same but for two chains together
52 pvcalc2 { |chain2, numframes, func, frombin=0, tobin, zeroothers=0|
53 var origmagsphases, origmagsphases2, magsphases, ret;
54 origmagsphases = UnpackFFT(this, numframes, frombin, tobin).clump(2).flop;
55 origmagsphases2 = UnpackFFT(chain2, numframes, frombin, tobin).clump(2).flop;
56 magsphases = func.value(origmagsphases[0], origmagsphases[1], origmagsphases2[0], origmagsphases2[1]);
57 // Add phases back if they've been ignored
58 magsphases = magsphases.size.switch(
59 1, {magsphases ++ origmagsphases[1]},
61 // any larger than 2 and we assume it's a list of magnitudes
62 {[magsphases, origmagsphases[1]]}
64 magsphases = magsphases.flop.flatten;
65 ^PackFFT(this, numframes, magsphases, frombin, tobin, zeroothers);
68 // Give it a func to apply to each bin in turn: func(mag, phase, index)
69 pvcollect { |numframes, func, frombin=0, tobin, zeroothers=0|
71 magsphases = UnpackFFT(this, numframes, frombin, tobin).clump(2);
72 magsphases = magsphases.collect({ |mp, index|
73 ret = func.value(mp[0], mp[1], index + frombin, index).asArray;
74 ret = if(ret.size==1, {ret ++ mp[1]}, ret); // Add phase if it's been ignored
76 ^PackFFT(this, numframes, magsphases, frombin, tobin, zeroothers);