Problems initializing PV_PlayBuf and PV_RecordBuf

I was checking this nice short video by @elifieldsteel about PV_PlayBuf and PV_RecordBuf. In the example the source signal is first analyzed and when the analysis is done, the fun begins.

I am trying to do a real time version and it is almost working but I got some issues with initialization. Any ideas about what is going on? These classes are new to me, so there might be something obvious I overlooked. I also realize that they were not necessarily intended for real time use, but but but…

(
s.waitForBoot{
	SynthDef(\pv, {
		var in = In.ar(\in.kr(0), 1);
		var recBuf = \recBuf.kr(0);
		var pvBuf = \pvBuf.kr(0);
		var rec = RecordBuf.ar(in, recBuf, loop: 1);
		var sig = PlayBuf.ar(1, recBuf, BufRateScale.kr(b), loop: 1);
		var localbuf = LocalBuf.new(8192);
		var chain = FFT(localbuf, sig, hop: 0.25);
		chain = PV_RecordBuf(chain, pvBuf, run: 1, hop: 0.25, loop: 1);
		chain = PV_PlayBuf(localbuf, pvBuf, \rate.kr(1), loop: 1);
		sig = IFFT(chain);
		Out.ar(\out.kr(0), sig!2)
	}).add;
	
	~buf = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");
	s.sync;
	~recBuf = Buffer.alloc(s, ~buf.duration * s.sampleRate);
	s.sync;
	~pvBuf = Buffer.alloc(s, ~buf.duration.calcPVRecSize(8192, 0.25));
	s.sync;
	~bus = Bus.audio(s, 1);
	~sig = { Out.ar(~bus, PlayBuf.ar(1, ~buf, loop: 1)) }.play; // silent output at first
}
)

// now, first time I try to instantiate the synth, I get this error
//....//
// WARNING: There is a mismatch between the PV databuffer you are using and this instance of PV_PlayBuf
// FFT size of databuf: 0.000000
// FFT size of current process: 8192.000000
// sometimes I also get 'Bad values' messages
// ..... ////

// WATCHED THE VOLUME, the playback is wrong and choppy
~syn = Synth(\pv, [recBuf: ~recBuf, pvBuf: ~pvBuf, in: ~bus], addAction: 'addToTail')

( // BUT, after freeing the synth and trying again, it works as suspected without error messages
~syn.free;
~syn = Synth(\pv, [recBuf: ~recBuf, pvBuf: ~pvBuf, in: ~bus], addAction: 'addToTail')
)

//...and we can set the rate
~syn.set(\rate, 0.1) // very slow
~syn.set(\rate, 0) // spectral freeze
~syn.set(\rate, -0.3) // reversed

I think you’ll need a second LocalBuf for this.

hjh

I just tried that now, same behavior: error first time, no error when freeing and trying again.

Ah, then it’s probably the recBuf.

The first three values of the recBuf are (if I recall – not sure that it’s clearly documented) frame size, window type and hop ratio (not necessarily in that order! – better .getn(0, 3, _.postln) to verify). PV_RecordBuf sets these and I believe PV_PlayBuf assumes that this will be done in advance. If both PV_RecordBuf and PV_PlayBuf are being initialized simultaneously, then those 3 data values may not be available on time.

Since you find that it works the second time, you could probably force the three values in by .setn. It’s a hack, but so is running them simultaneously (which I’m not sure will work in the end anyway, because of the frame-size delay that’s inherent in FFT processes).

hjh

After initialization, before instantiating the pv-synth, the first 3 values of ~recBuf are [0.0, 0.0, 0.0]. I tried setting them to non-zero values before playing the synth, but same result as before.

This is a very ugly way to do it which still produces the error message but at least it sounds right. With wait times shorter than 0.05 it doesn’t really work. For wait = 0.04 it sometimes do, sometimes don’t. I wish I could find a smoother, less ugly way:)

(
{
	~syn = Synth(\pv, [recBuf: ~recBuf, pvBuf: ~pvBuf, in: ~bus], addAction: 'addToTail');
	0.05.wait;
	~syn.free;
	~syn = Synth(\pv, [recBuf: ~recBuf, pvBuf: ~pvBuf, in: ~bus], addAction: 'addToTail');
}.fork
)

If you haven’t run PV_RecordBuf yet, then PV_RecordBuf won’t have had the chance to initialize those values!

So run the synth once, stop it, and then check the values.

Then, next time you’re initting a new buffer, alloc first and then setn the 3 values (use completionMessage for this).

hjh

I tried that a couple of times, does not solve the problem unfortunately. The need for a relatively long wait time of 50ms in my last hack seems to indicate that it is not just the first samle(s) but something else.

It does solve it.

I can reproduce the initial problem, but the problem goes away if I set up the first three data values as noted above.

~fftSize = 8192;
~hop = 0.25;

b = Buffer.alloc(s, 1.calcPVRecSize(~fftSize, ~hop, s.sampleRate),
	completionMessage: { |buf| buf.setnMsg(0, [~fftSize, ~hop, 0]) }
);

// then, no error + expected sound with this block
(
a = {
	var sig = SinOsc.ar(880);
	var fft = FFT(LocalBuf(~fftSize, 1), sig, ~hop);
	var fft2 = LocalBuf(~fftSize, 1);
	PV_RecordBuf(fft, b, run: 1, loop: 0, hop: ~hop);
	fft2 = PV_PlayBuf(fft2, b);
	fft2 = IFFT(fft2);
	Line.kr(0, 1, 1, doneAction: 2);
	(fft2 * 0.05).dup
}.play;
)

hjh

Great, thanks. I was trying to create the buffer first, then set the values which did not work for me, but this does, thanks a lot.

That’s quite odd. If you create the buffer and give a little time for it to be allocated, then .setn(...), there’s no reason why that shouldn’t work.

If you .alloc and .setn in one block without any syncing, then it wouldn’t work. The completion message is just an alternative to sync.

hjh