Setting the phase of a complex signal

I have a complex signal and would like to zero out the phases. However, I seem to be unable to do this simply by setting .theta to 0 for all indexes. What is the proper way to do this (slightly modified example from the Signal help file)?


(
var size = 512;
var real, imag, cosTable, frqAmpPhs, complex;

// Create a signal of sine partials:
// partial freqs, amps, and phases
frqAmpPhs = [
    // 0 Hz (DC), amp: 1, phase pi/2 (cosine)
    [0, 1, 0.5pi],
    // other partials, various amp and phase
    [8], [13, 0.25], [21, 0.25], [55, 0.5, 0.5pi],
    // nyquist, amp: 1, phase pi/2 (cosine)
    [size/2, 1, 0.5pi]
];
real = Signal.newClear(size);
real.sineFill2(frqAmpPhs);

imag = Signal.newClear(size); // zeros
cosTable = Signal.fftCosTable(size);

// Perform fft
~complex = fft(real, imag, cosTable));

The following causes SuperCollider to freeze

~complex.theta.size.do{|i|
	~complex.theta[0] = 0};
1 Like

Try making a new Polar and converting it back to Complex:

~polar = Polar(~complex.rho, (0!~complex.theta.size));

~polar.theta;

~complex2 = ~polar.asComplex

Sam

I don’t see it freezing – but it will leave ~complex unchanged.

~complex is made of .real and .imag only.

.theta is calculated from real and imag. So every .theta makes a new array (this is why it’s slow-running for you – you’re doing 512x atan(imag, real) 512 times). You can modify that array, but it has zero effect on the original Complex data.

Sam’s approach is right: convert to an object that contains rho and theta arrays, then modify the arrays, then convert back to Cartesian Complex. EDIT: But the quick way for general cases is ~polar = ~complex.asPolar – then, after manipulation, ~complex = ~polar.asComplex.

hjh

Thanks a lot for the help @Sam_Pluta and @jamshark70, this makes sense to me now. To convert back to a complex Signal, I made this function, heavily inspired by some code @Gerhard_Eckel sent me:

~zeroPhase = {|item|
	var polar, newComplex, size;
	size = item.real.size;
	polar = item.asPolar;

	size.do{|i|
		polar.theta[i] = 0.0};

	newComplex = polar.real.as(Signal).complex(polar.imag.as(Signal));

	s.sync; // Not sure this is needed

	newComplex;
};

Best, Adam

~myPolarObject.asComplex

… and done.

hjh

Side note: your function operates entirely in the language, so there is no need for any kind of Server synchronization. s.sync is only ever needed if you want to synchronize with asynchronous Server commands. Here’s a typical example:

fork {
    SynthDef({ ... }, \foo).add;
    s.sync; // make sure SynthDef has been sent
    Synth(\foo);
}

Ah yes, of course, thank you!

@Adam_Pultz, you may want to try playing with the SignalBox quark.

SignalBox includes a number of extension methods for Signal along with the FreqSpectrum class, which provides a number of useful frequency domain methods.

Zeroing out the phase is as easy as calling -phase, and setting to zeros.

See also:

Help for FreqSpectrum can be found here.

… and simple example:

~size = 128;
~cosTable = Signal.fftCosTable(~size);

// real signal
~realSignal = Array.fill(~size, { 1.0.bilinrand }).as(Signal);
~imagSignal = Signal.zeroFill(~size);

~realSignal.plot("time domain - rando!");

// complex spectrum - cartesian
~complex = ~realSignal.fft(~imagSignal, ~cosTable);

// spectrum
~spectrum = FreqSpectrum.newComplex(~complex);

~spectrum.magnitude.plot("magnitude - rando!");
~spectrum.phase.plot("phase - rando!");

// reset the phase to zeros!
~newZeroSpectrum = ~spectrum.deepCopy;

~newZeroSpectrum.phase;  // check... all rando!
~newZeroSpectrum.phase = Signal.zeroFill(~size);
~newZeroSpectrum.phase;  // check... all zeros!

// now, resynthesize zeroed phase FreqSpectrum as Signal
~newRealZeroSignal = ~newZeroSpectrum.real.ifft(~newZeroSpectrum.imag, ~cosTable).real;  // we just want the real part

~newRealZeroSignal.plot("time domain - zero-ed!");

/*
NOTE:

Zero-ing the phase "just" returns a sum of cosines, hence the spike @ index = 0

We can also make it linear phase using -linearPhase OR minimum phase using -minimumPhase

Linear phase will look like zero-ed phase, but the spike will be centered.
*/

~newLinearSpectrum = ~spectrum.deepCopy.linearPhase;
~newMinimumSpectrum = ~spectrum.deepCopy.minimumPhase;

// and, resynthesize & plot
~newRealLinearSignal = ~newLinearSpectrum.real.ifft(~newLinearSpectrum.imag, ~cosTable).real;  // we just want the real part
~newRealMinimumSignal = ~newMinimumSpectrum.real.ifft(~newMinimumSpectrum.imag, ~cosTable).real;  // we just want the real part

~newRealLinearSignal.plot("time domain - linear!");
~newRealMinimumSignal.plot("time domain - minimum!");
1 Like

@joslloand,Thanks so much, I should definitely give this as look!