File: PV_ChainUGen.schelp

package info (click to toggle)
supercollider 1%3A3.13.0%2Brepack-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 80,292 kB
  • sloc: cpp: 476,363; lisp: 84,680; ansic: 77,685; sh: 25,509; python: 7,909; makefile: 3,440; perl: 1,964; javascript: 974; xml: 826; java: 677; yacc: 314; lex: 175; objc: 152; ruby: 136
file content (248 lines) | stat: -rw-r--r-- 7,947 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
class:: PV_ChainUGen
summary:: Base class for UGens that operate on FFT chains
categories:: UGens>FFT

description::
code::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 link::Classes/FFT::.

It encompasses all unit generators whose output is an FFT chain. This is why link::Classes/FFT:: is in this group but link::Classes/IFFT:: is not - the IFFT ugen outputs ordinary time-domain audio.

For more information on using these unit generators, see link::Guides/FFT-Overview::.

classmethods::
private:: categories

instancemethods::

private::addCopiesIfNeeded

method::fftSize
Returns the FFT chain buffer's size.

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


method:: pvcalc
pvcalc applies a function to the frequency-domain data of an FFT chain. See link::#-pvcollect:: below for discussion of efficiency considerations. See also link::#-pvcalc2:: below, and link::Classes/UnpackFFT::.

code::
chain = chain.pvcalc(numframes, func, frombin, tobin, zeroothers)
::


argument::numframes
Number of FFT frames to process

argument::func
The function that takes two arrays as inputs  (code::magnitude::, and code::phase::) and returns a resulting pair of arrays code::[magnitude, phase]::.
code::
// example function
{ | magnitudes, phases |
	[mags.reverse, phases.reverse] // e.g. upside-down spectrum
}
::

argument::frombin

Range start (optional)

argument::tobin

Range end (optional)

argument::zeroothers

If set to 1 then bins outside of the range being processed are silenced.



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

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

argument::chain2
The scond FFT chain.

argument::numframes
Number of FFT frames to process

argument::func
The function that takes four arrays as inputs (magnitudes1, phases1, magnitudes2, phases2) and returns a resulting pair of arrays code::[magnitude, phase]::.
code::
// example function
{ | magnitudes1, phases1, magnitudes2, phases2 |
	[magnitudes1, phases2] // e.g. use the magnitudes of one, ane the phases of the other
}
::

argument::frombin

Range start (optional)

argument::tobin

Range end (optional)

argument::zeroothers

If set to 1 then bins outside of the range being processed are silenced.



method:: pvcollect
Process each bin of an FFT chain, separately, by applying a function to each bin of an FFT chain.

code::
chain = chain.pvcollect(numframes, func, frombin, tobin, zeroothers)
::

argument::numframes
Number of FFT frames to process

argument::func
The function that processes each bin. It should be a function that takes code:: magnitude, phase, bin, index :: as inputs and returns a resulting array code::[magnitude, phase]::.
code::
// example function
{ | magnitude, phase, bin, index |
	// randomize magnitudes somewhat (noisier signal)
	[magnitude * (5.0.rand2.dbamp), phase]
}
::

The strong::bin:: is the integer bin number, starting at 0 for DC, while strong::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.

argument::frombin

Range start (optional)

argument::tobin

Range end (optional)

argument::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:
list::
## Use the frombin and tobin arguments to limit the number of FFT bins that will be included in the calculation. Often the lower FFT bins contain the loudest and/or most relevant information, so perhaps your effect sounds very similar if you ignore the higher-up bins (either leave them unprocessed, or discard them by setting the zeroothers argument to 1, which has the effect of a band-pass frequency-domain filter).
## Use a smaller FFT buffer size.
## Avoid creating ugens inside your calculation function if at all possible. For example, a deterministic ugen such as LFPar.kr(0.5, 0, 1) will be replicated once for each bin if specified inside the function, despite the fact that the output is always the same. Define it outside the calculation function and then reference it by variable name.
## Avoid unused calculations! For example, uncommenting all the different lines in the above will waste effort because many values will be calculated but not used. This cannot be optimised away during compilation. It is particularly important because all calculations are duplicated (once for each bin) so can have a significant impact on efficiency.
## If you find yourself calling pvcollect on an FFT chain more than once in series, you should definitely try to combine your processing into a single pvcollect function, to avoid unnecessary unpacking-then-packing-then-unpacking-then-packing.
::

Examples::

subsection:: pvcalc
code::
// 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
)
::

subsection:: pvcalc2
code::
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
)
::

subsection:: pvcollect
code::
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
)

::