i’d like to implement a specific form of phase randomization that had originally been implemented in python. I tried asking GPT for suggestions, but to no avail, as the resulting SC code is unusable.
The description of the code im trying to translate is the following:
“The random phase are complex numbers whose real and imaginary parts squared sum to 1. Then after computing the FFT magnitudes, the first half of FFT (corresponding to [negative frequencies] is multiplied with this random phase, and the second half (corresponding to positive frequencies) with the conjugate (flip sign of the imaginary part and keep the real part) of these same random phase.”
Would anyone know how this would like in SClang? Unfortunately it’s mathematics are well beyond my capacities…I’d assume .pvcalc would be the right way to go?
Not too sure, but I think the negative frequencies are just mirrored versions of the positive when using real audio input. So supercollider doesn’t give you access to them because they are useless for audio applications?
I’m not sure either, these were also described as first and second half of FFT and linked to this as explanation https://dsp.stackexchange.com/questions/431/what-is-the-physical-significance-of-negative-frequencies
They apparently are of use for certain fft processing.
But all in all it doesn’t help me in understanding how to go about the implementation. I guess PV_Mul and PV_Conj would be of use but the two questions still elude me…
nothing wrong with it, i’m just looking to recreate this specific implementation because of the resulting sound, which is not arbitrary randomization like with PV_Diffuser.
Ah, I see, so you think it’s essentially an arbitrary randomization of the phase, just expressed a bit more complicatedly?
I got the explanation from a programmer I was working with who is assisting in the development of the scattering transform https://www.kymat.io/
this specific phase implementation was from one of the main developers of the scattering transform.
I could try to find the code that actually reproduces the process in python, maybe that’s more clarifying than the verbal explanation (to someone who understands the maths at least)
To clarify one thing, the FFT Ugen in SC is a real FFT, not a full FFT, so there are no negative frequencies/mirror bins. This is super confusing in SC and in Max, since the terms are used interchangeably. FYI, in Max the fft~ object uses an fft, but the pfft~ object uses a real fft. In pd it looks like the object is correctly labeled as an rfft object. Clear as mud, I know.
In SC you can do a full fft on a signal in the language using the Signal.fft method. So you could implement your algorithm in the language if necessary. In that case, the code would non-realtime and could look very similar to the python code.
Thanks for clarifying this! It’s very good to know to not amount to even more confusion, given the complexity of fft. I was hoping to get around it with PV_Ugens, but good to now its not the approach for this case!
So to do what i’m after with Signal means that to basically adapt the python syntax to sclang. That sounds like a too challenging task given the maths of it!
var fftSize = ..... you decide this.....;
var halfSize = fftSize div: 2;
var randPhases = Array.fill(fftSize div: 2, {
var theta = 2pi.rand;
Complex(theta.cos, theta.sin)
});
randPhases[0] = Complex(1, 0); // phase shift of DC is pointless
randPhases = randPhases.add(Complex(1, 0)); // phase shift of Nyquist is pointless
(halfSize - 1, halfSize - 2 .. 1).do { |i|
randPhases = randPhases.add(randPhases[i].conjugate);
};
// edit: match fft format
randPhases = Complex(
randPhases.collect(_.real).as(Signal),
randPhases.collect(_.imag).as(Signal)
);
That should be your window of random phases. Then, given a same-size chunk of audio, as a Signal:
sig = sig * Signal.hanningWindow(sig.size);
fft = sig.fft(Signal.newClear(sig.size), Signal.fftCosTable(sig.size));
fft = fft * randPhases;
ifft = fft.real.ifft(fft.imag, Signal.fftCosTable(sig.size));
sig = ifft.real;
You could save some load on the garbage collector by caching the Hann window and the cos table in their own variables, instead of repeatedly creating them fresh.
Actually how to do the windowing is a whole other conversation, but this at least is the basic format.
Although… (last one for now, promise!)… having done all that, I think it’s probably not necessary. I think you should be able to get the same result with PV_Mul, and probably the same result with PV_Diffuser (but I don’t have time to check the source code to be sure).
The important characteristic of a real FFT is that what the quote calls negative and positive frequency bins are mirror images about the X axis (reflected vertically).
The “complex numbers whose real and imaginary parts squared sum to 1” are polar vectors with magnitude 1 and angle theta. A “negative frequency bin” would be rotated counterclockwise by theta radians. The corresponding positive frequency, being multiplied by the complex conjugate, would be rotated clockwise by the same amount – so the rotated point will still be a mirror image about the X axis.
So if you start with a real FFT, as SC PV units do, and perform this operation, you will end up with a different real FFT frame – but still real.
So I believe PV units can handle it natively. I could be wrong but I doubt it.
This actually comes pretty close to what I’m after, thank you for the extensive piece of code!
In the case of trying to recreate it with PV Ugens, you mean that the combo PV_Mul and PV_Diffuser would be the equivalent to the Signal process you’ve proposed?
a follow up question: inversely, is it possible to do the transformations that PV Ugens provide with the Signal class? for example to multiply magnitudes of two different sound files?
If you load the random phases into a buffer and access it in the SynthDef with FFTTrigger, then PV_Mul might get the same result as the complex multiplication that the quote describes.
thank you! actually it is very convenient to have this code as an quasi NRT process that can render the files. would this code also allow for further fft processing like convolving one signal with another, or use phases and mags of two different files? i found ways to tweak the magnitudes on the costable but how to insert another files fft data is not clear to me.