Imitate Michael Norris' Spectral Freeze

Hello everyone,

I am trying to recreate Michael Norris’ Spectral Freeze (Soundmagic Spectral - Michael Norris, Composer). My thought process was that I could use:

PV_Freeze to freeze the spectrum
PV_RandPhase (third party and had to build it) to randomize the phases
PV_Cutoff to filter out the high frequencies.

This kind of works, but not really. Norris’ spectral freeze continues to gather new audio as the freeze is in place. The duration of the freeze can be controlled with a “freeze amount” parameter. The PV_Freeze done by supercollider holds the freeze of that analysis window until re-triggered. So, unless you re-trigger at some rate, you can’t accumulate the past into the future. To solve this problem I tried to feedback the IFFT and add it back in to the FFT along with the new input while re-triggering the spectral freeze. This is better, but somehow there is some rhythmic pulsing where I just want a frozen spectrum.

SynthDef("spectralFreeze", { arg inputBus=10, freezeAmt, binCutoff = -0.9, bufnum, wet=1.0;
	var local, input, fft, freeze, diffuse, brickWall, ifft;

	local =;
	input =;
	fft = FFT(bufnum, input + (local*freezeAmt), 0.5);
	freeze = PV_Freeze(fft,; // try to trigger per window size
	diffuse = PV_RandPhase(freeze);
	brickWall = PV_Cutoff(diffuse, binCutoff);
	ifft =;;, ifft.dup*wet + ((1.0 - wet) * input));

c = Synth("soundIn");
d = Synth("spectralFreeze", [\freezeAmt, 1.0, \binCutoff, -0.9, \bufnum, ~fftBuffer], c, \addAfter);

Here is what I want things to sound like:

Here is what they actually sound like:

Any ideas?

One hint might be this bit of writing from Michael Norris:
In this effect, we take a number of analysis ‘windows’ from the start of the selected sound, and ‘freeze’ those sounds in time. The analyses are simply kept in memory, and then replayed in a random walk fashion (ie from one window to a neighbouring window), throughout the duration of the remainder of the selection. You can also specify the ‘freeze amount’, which is the percentage of the original spectrum which can be heard through the frozen sound-file.

I’m not sure if this is still true with the modern version of the plugin, since the number of analysis windows is not longer a parameter you can set. I’m not sure how one could keep analysis windows and perform a random walk through them in the PV ugen world.

1 Like

How about recording a whole buffer of sound with PV_BufWr and PV_BufRd? PV_BufRd, with a static pointer, is exactly like PV_Freeze

Josh Parmenter


Thank you! This is a very promising idea. I will give it a go and report back.

Sorry for the long delay. I was having trouble with PV stuff working in 3.14.0-dev (see below).

  1. I need to randomize the phases and ran into lots of issues trying to get a 3rd party PV Ugen to work (hence the 3.14.0-dev version)
  2. Is there a way to feedback the previous frame to blend into the PV_RecordBuf?

What I am trying to achieve is the ability to accumulate the spectrum while letting the oldest bits fade away. PV_RecordBuf allows me to keep a buffer of the frames, but then I don’t have a way to “overdub” that buffer.

SynthDef("pvrec", { arg recBuf, buf, hopSize=0.25;
    var in, chain, bufnum, ifft, player;
    bufnum =, 1);
	in =;
    // note the window type and overlaps... this is important for resynth parameters
	chain = FFT(bufnum, in, hopSize, 1);
    chain = PV_RecordBuf(
		buffer: chain, 
		recbuf: recBuf, 
		offset: 0, 
		run: 1, 
		loop: 0, 
		hop: hopSize, 
		wintype: 1

SynthDef("pvplay", { arg out=0, recBuf=1;
    var in, chain, bufnum;
    bufnum =;
	// Read through the buffer randomly (at what rate?)
	// chain = PV_BufRd(bufnum, recBuf,, 1, 0, 1).poll);
	chain = PV_BufRd(bufnum, recBuf, 0.1);
	// chain = PV_BufRd(bufnum, recBuf, 0);, IFFT(chain, 1).dup);
b = Buffer.alloc(s, s.sampleRate * 1);
y = Buffer.alloc(s, 1.calcPVRecSize(2048, 0.25));

Synth("pvrec", [\buf, b, \recBuf, y]);
{Synth("pvplay", [\out, 0, \recBuf, y])}.defer(2048/s.sampleRate);

I think you’d want to have numerous PV_BufRds playing with different pointers and amp scales.
PV_RecordBuf won’t do this on its own z I think focusing on controlling playback is the way to go.

Josh Parmenter

If you still need something (simple) to randomize phases (or otherwise modify them), the PV_ChainUGen ugen has a function .pvcalc() that should work great for this. It has a nice write up in the help file too! Also related are .pvcalc2() and .pvcollect().

PV_Diffuser randomizes phase on all bins.