PartConv Effect Bus Solution?

So, the noob here again.

I’m trying to figure out how to turn a PartConv into a bus FX and I cannot figure it out for the life of me. I’ve tried using BufRd and LocalBuf, but I just don’t get it. Any help much appreciated!

(
   SynthDef(\partConvBus, { 
	    arg out;
        var irspectrum, irbuffer, bufsize, fftsize;
	    fftsize = 4096;
		irbuffer = Buffer.read(s, "C:/Users/blah/IR.wav");
		bufsize = PartConv.calcBufSize(fftsize, irbuffer);
        irspectrum = Buffer.alloc(s, bufsize, 1);
        irspectrum.preparePartConv(irbuffer, fftsize);
        Out.ar(out, irbuffer);
}).add;
)

I am going to change it to be able to have the file as an argument after I figure this part out.

Thank you!!!

-Melas

Do not try to read buffers within a SynthDef.

Read the buffer outside the SynthDef, and pass the buffer number as an argument.

hjh

@jamshark70 Thanks…Alas, this should have been obvious I guess… Here’s what I ended up with:

(
~fftsize = 4096;
{
	var irbuffer, bufsize;
	irbuffer = Buffer.read(s, "C:/blah/blah.wav");
	bufsize = PartConv.calcBufSize(~fftsize, irbuffer);
	~irspectrum = Buffer.alloc(s, bufsize, 1);
	~irspectrum.preparePartConv(irbuffer, ~fftsize);
	s.sync;
	irbuffer.free
}
)

(
   SynthDef(\partConvBus, {
	arg in=0, out=0, amp=0.5;
	var sig, irspectrum, partconv;
	sig = In.ar(in, 2);
	partconv = PartConv.ar(sig, ~fftsize, ~irspectrum.bufnum, amp);
	sig = sig * partconv;
	Out.ar(out, sig);
}).add;
)

That’s hardcoding the ir buffer number into the SynthDef. IMO it’s generally better practice to have an argument for the buffer number, and include this in the Synth() creation.

(
   SynthDef(\partConvBus, {
	arg in=0, out=0, amp=0.5, irbufnum;
	var sig, irspectrum, partconv;
	sig = In.ar(in, 2);
	partconv = PartConv.ar(sig, ~fftsize, irbufnum, amp);
	sig = sig * partconv;
	Out.ar(out, sig);
}).add;
)

x = Synth(\partConvBus, [irbufnum: ~irspectrum]);

hjh

Yes, thank you very much @jamshark70 , however, I am now getting an error: “Spectral Data Buffer not Allocated”:

(
~fftsize = 4096;
{
	var irbuffer, bufsize;
	irbuffer = Buffer.read(s, "C:/blah.wav");
	bufsize = PartConv.calcBufSize(~fftsize, irbuffer);
	~irspectrum = Buffer.alloc(s, bufsize, 1);
	~irspectrum.preparePartConv(irbuffer, ~fftsize);
	//s.sync;
	//irbuffer.free
}
)


(
   SynthDef(\partConvBus, {
	arg in=0, out=0, amp=0.5, irbufnum;
	var sig, irspectrum, partconv;
	sig = In.ar(in, 2);
	partconv = PartConv.ar(sig, ~fftsize, irbufnum, amp);
	sig = sig * partconv;
	Out.ar(out, sig);
}).add;
)
x = Synth(\partConvBus, [irbufnum: ~irspectrum]);

Not sure exactly what’s going on, but ~irspectrum was not getting the bufnum when kept inside the first function. I am doing this and it works, in this order:


~irbuffer = Buffer.read(s,"C:/blah.wav");

(
~fftsize = 4096;
{
	var irbuffer, bufsize;
	irbuffer = ~irbuffer;
	bufsize = PartConv.calcBufSize(~fftsize, irbuffer);
	~irspectrum = Buffer.alloc(s, bufsize, 1);
	~irspectrum.preparePartConv(irbuffer, ~fftsize);
	//s.sync;
	//irbuffer.free

}.fork;
)

(
   SynthDef(\partConvBus, {
	arg in=0, out=0, amp=0.5, irbufnum;
	var sig, irspectrum, partconv;
	sig = In.ar(in, 2);
	partconv = PartConv.ar(sig, ~fftsize, irbufnum, amp);
	sig = sig * partconv;
	Out.ar(out, sig);
}).add;
)

x = Synth(\partConvBus, [\in, 93, \out, 0, irbufnum: ~irspectrum.bufnum]);

Right, it’s a sync problem. Buffer.read only tells the server to load the file but it doesn’t wait for the contents to finish loading… There should be s.sync after buffer reading. (Maybe also after allocating the ir spectrum buffer.)

hjh

I’ve been playing with this all day and there are definitely 2 things going on:

  1. I don’t know what I’m doing,

  2. PartConv seems to only works sometimes, and is very picky.

I haven’t been able to get it to sound like it was yesterday…

AFAICS you need three sync calls.

In general terms, when do you need sync?

  • The operation(s) are asynchronous – such as buffer reading, where the language sends a command to the server to start reading, but the language doesn’t automatically halt this thread until the command finishes.
  • AND the next operation depends on information that is available only when the previous one finished.

So let’s say A = reading the IR signal, B = allocating the IR spectrum buffer, C = preparePartConv, D = freeing the IR signal buffer.

To do B, you need to know the size of the IR buffer. You don’t know the size until after the server finishes reading and sends the statistics back to the language. So, you must sync before B.

To do C, you need the IR spectrum buffer to be fully allocated. This is also asynchronous. So, (IMO) better to sync before C. (Note below about this.)

To do D, you have to be sure the IR signal buffer is no longer needed, which is only true after preparePartConv has fully completed. So… yep… sync before D.

(
~fftSize = 1024;

fork {
	// A
	~irbuffer = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav",
		startFrame: 140000, numFrames: -1);
	
	s.sync;
	
	// B
	~irspectrum = Buffer.alloc(s, PartConv.calcBufSize(~fftSize, ~irbuffer), 1);
	
	s.sync;
	
	// C
	~irspectrum.preparePartConv(~irbuffer, ~fftSize);
	
	s.sync;
	
	// D
	~irbuffer.free;

	// *now* you're ready
};
)

If you do it that way, then… everything in PartConv is deterministic, so the result should be consistent. If you’re taking shortcuts by omitting sync, then it becomes much harder to guarantee a correct result every time. E.g., this should always sound the same:

(
a = { |fftSize = 1024, irbufnum|
	var freq = MouseX.kr(2, 440, 1);
	var sig = Impulse.ar(freq);
	sig = PartConv.ar(sig, fftSize, irbufnum);
	(sig * 0.1).dup
}.play(args: [irbufnum: ~irspectrum]);
)

a.release;

I note that the help file leaves out sync between B and C – this might actually work because they’re both “sequenced commands” in the server, queued up and executed in order. My opinion is that it’s better not to leave it to chance – I would sync it anyway. I wouldn’t leave out the other ones.

(Wouldn’t all of this syncing slow it down a lot if you’re preparing multiple channels? The channels aren’t interdependent. So you could read as many IR time domain buffers as needed, sync once, then allocate the spectrum buffers all at once, sync, multiple preparePartConv calls, sync, and free. There is no need to complete the entire process for one channel before moving to the next channel.)

hjh

2 Likes

That makes sense. I am really appreciating all your detailed input. I’ve altered my code. I can get PartConv to work fine on its own, EXCEPT when as a BUS. Very weird…

Out.ar mixes with what is already on the bus. Usually effect synths should use ReplaceOut.

hjh