iFFT Freeze/Scrub

I should clarify: when I say it’s been unreliable, I mean if I repeatedly attempt to analyse the same file, most of the time it fails, as I mentioned, and occasionally it appears to work.

It seems to work more reliably when I comment-out all the lines related to the pitch analysis.

I’ve also found that often (but not all the time) when I trigger a note to instantiate a synth, the server immediately quits, with status 0 (which isn’t very helpful).

Here’s the synth (note I’m not doing anything with the pitch analysis data yet):

(

b = Buffer.read(s, ~fftPath);
p = Buffer.read(s, ~pitchPath);

//////////////////
// Define Synth //
//////////////////

SynthDef(\pvscrub, {
	arg out = 0, fBuf = 1,
	pOffset = 0, pBend = 0,
	amp = 0.1, gate = 1,
	ampEnvAttack = 0.01, ampEnvDecay = 0, ampEnvSustain = 1.0, ampEnvRelease = 2,
	grainSize = 0.1, grainPitchDispersion = 0.01, grainTimeDispersion = 0.004,
	//filterCutoff, filterResonance, filterShape,
	scrubPos = 0.1, scrubPosInertia = 0.01,
	pvParam1 = 0, pvParam2 = 0;

	var fftSize = ~fftSize, chain, bufNum, ampEnv, centerFreq, result;

	// Amplitude envelope Attack Sustain Release type
	// Free synth after envelope finishes
	ampEnv = Env.adsr(ampEnvAttack, ampEnvDecay, ampEnvSustain, ampEnvRelease, amp).kr(2, gate);

	// Read FFT buffer
 	bufNum = LocalBuf.new(fftSize);
	chain = PV_BufRd(bufNum, fBuf, min(scrubPos.lag(scrubPosInertia), 1));

	// FFT FX
	chain = PV_RectComb(chain, pvParam1.lag(1), 0);
	chain = PV_MagSmooth(chain, pvParam2.lag(1));

	// TODO

	// Resynthesise FFT
	result = IFFT(chain).dup;

	// Time-domain pitch-shift
	result = PitchShift.ar(
        result,
        grainSize,
		pOffset,   // Pitch-shift ratio
		grainPitchDispersion.lag(2),
        grainTimeDispersion.lag(2)
    );

	// Filter

	// TODO

	// Resonant low-pass

	// Non-resonant hi-Pass/shelf

	// Final output
	Out.ar(out, result * ampEnv);
}).add;

)

And here’s the MIDI setup

(

//////////////////
// Setup Busses //
//////////////////

~setupControls = {
	// Setup control buses
	~scrubControl = Bus.control(s, 1);
	~scrubInertiaControl = Bus.control(s, 1);
	~bendControl = Bus.control(s, 1);
	~grainPitchDispersionControl = Bus.control(s, 1);
	~pvParam1Control = Bus.control(s, 1);
	~pvParam2Control = Bus.control(s, 1);
	//~filterCutoffControl = Bus.control(s, 1);
	//~filterResonanceControl = Bus.control(s, 1);
	//~filterShapeControl = Bus.control(s, 1);
}.value;

////////////////
// Setup MIDI //
////////////////

~setupMIDI = {
	var notes, on, off, cc, centreNote = 60;

	MIDIClient.init;
	MIDIIn.connectAll;


	notes = Array.newClear(128);    // array has one slot per possible MIDI note

	// Handle note-on events
	on = MIDIFunc.noteOn({
		arg vel, note, chan, src;

		var noteScaled, pOffsetRatio;

		// Note clamped to 48..72 range
		// 2 octaves either side of C4, -24..+24 semitone range
		noteScaled = max(min(note, centreNote + 24), centreNote - 24) - centreNote;

		// Pitch-offset as ratio
		pOffsetRatio = noteScaled.midiratio;

		// Push synth to notes array
		notes[note] = Synth(\pvscrub, [\out: 0,\fBuf: b,\pOffset: pOffsetRatio,\amp: vel.linlin(0, 127, 0, 1)]);
		// Setup control busses
		notes[note].set(\scrubPos, ~scrubControl.asMap);
		notes[note].set(\scrubPosInertia, ~scrubInertiaControl.asMap);
		notes[note].set(\pBend, ~bendControl.asMap);
		notes[note].set(\grainPitchDispersion, ~grainPitchDispersionControl.asMap);
		notes[note].set(\pvParam1, ~pvParam1Control.asMap);
		notes[note].set(\pvParam2, ~pvParam2Control.asMap);
		//notes[note].set(\filterCutoff, ~filterCutoffControl.asMap);
		//notes[note].set(\filterResonance, ~filterResonanceControl.asMap);
		//notes[note].set(\filterShape, ~filterShapeControl.asMap);

	});

	// Handle note-off events
	off = MIDIFunc.noteOff({
		arg vel, note, chan, src;
		notes[note].release;
	});

	// Handle CC events
	cc = MIDIFunc.cc({
		arg val, num, chan, src;

		switch (num,
			1,  { ~scrubControl.set(linlin(val, 0, 127, 0.0, 1.0)); postln("scrub: " + val); },
			76, { ~scrubInertiaControl.set(linlin(val, 0, 127, 0.01, 5.0)); postln("scrub inertia: " + val ); },
			74, { ~grainPitchDispersionControl.set(linlin(val, 0, 127, 0.0, 0.5)); postln("grain pitch dispersion: " + val); },
			71, { ~pvParam1Control.set(linlin(val, 0, 127, 0.0, 32)); postln("pv param 1: " + val); },
			77, { ~pvParam2Control.set(linlin(val, 0, 127, 0.0, 0.99)); postln("pv param 2: " + val); }
		);
		//num.postln;
	});

	// Handle pitch-bend events

	// TODO

}.value;

)

If the code is correct, perhaps it’s a SuperCollider bug. I’m using v3.11.2 on macOS 10.15.6.

It feels a bit like there may be an issue with the RecordBuf and PV_RecordBug uGens. Maybe files are being opened for writing, the write fails, for some reason, and the files are not properly closed again.

It could be that the RT server crashes with the synth are related to attempting to read from corrupt analysis files (though loading the files into the “b” and “p” buffers doesn’t report errors).