NRT passing Buffers / b_alloc

Dear list,
a modified and clearer version of an old problem: in the following example there is a some strange mix of buffers happening. the rendered file plays the recorded buffer instead of the sample from synth \one which is what is present at the output!
theres something im getting wrong with the buffer allocation in NRT… any ideas on how to pass the right buffers for this setup? im unsure whether p.add is the probloem, or the way its being read in the pattern…
it would be so helpful if Pproto could do b_alloc instead of only reading Buffers…
Thanks,
Jan

code here:

(q=Bus.audio(s,8);

(
(
SynthDef(\one, { |out,dur,sbuf,amp,rate2,f1,f2,rate,amp2,start|
	var in=PlayBuf.ar(1,sbuf,rate,loop:1);
	Out.ar(out,in*EnvGen.ar(Env.sine,1,4,timeScale:dur,doneAction:2).tanh)}).store
);
);
(
(
SynthDef(\two, { |out,cbuf,dur,sbuf,amp,rate2,f1,rate,amp2,start,ini|
	var in=In.ar(ini);
	var buf=  RecordBuf.ar(SinOsc.ar(f1), cbuf);
	var snd= PlayBuf.ar(1,cbuf);
	var testbuffersize= BufFrames.kr(cbuf).poll(1);
	Out.ar(out,Pan2.ar((amp*in)*EnvGen.ar(Env.sine,1,4,timeScale:dur,doneAction:2).tanh))}).store //supposed to play sample from other synth
);
);

(p=(1..1).collect{|i| (Pspawner({|sp|
	sp.par( Pproto(
		{
			~sbuf= ( type: \allocRead, path:Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff").yield;
		},
		Pbind(
			\instrument,\one,
			\dur,20,
			\rate,(1..1),
			\sbuf, Pkey(\sbuf),
			\out,Pfunc{q.index+(0..7)},
			\addAction, \addToHead,
		);
));
		loop{sp.seq( 
		Pbind(
			\instrument,\two,
			\dur,20,
			\amp,0.05,
			\ini,Pfunc{q.index+(0..7)},
			\f1,Pwhite(200,600).clump(8),
			\cbuf,[(0..7)],  //what to pass here?
			\addAction, \addToTail,
		);
);}}).asScore(60) 
)});

(8.do({
	|i| p.add([0.0, [\b_alloc, i,2**18,1]]);  //right way to allocate NRT buffers/cbuf?
}));

c = ServerOptions.new.memSize_(2**20).blockSize_(512).maxNodes_(8192);

p.do{|buf,index| buf.render("~/desktop/test.wav".format(index).standardizePath,sampleRate:96000,sampleFormat:"int24",options:c)})

BufFrames shows that the Buffer size doesnt correspond to the passed Buffer (cbuf), but the one being loaded in the first synth (sbuf)!

any ideas, anyone? :frowning:
nrt can really be a tough one…

sbuf’s buffer number comes from the server’s buffer allocator… any guesses what is the first buffer number it will give you?

It will be… 0.

Then you do 8.do and allocate buffers numbered i.

How does 8.do count?

0 to 7.

See the problem? You’re using buffer number 0 for two different things.

If you want to read sbuf using a buffer number from the server’s buffer allocator, then you should get all the buffer numbers from the server’s allocator. Or, hardcode all of the buffer numbers. A mix-and-match approach is very likely to produce conflicts.

hjh

hi james,
thanks for your reply, but in that case allocating 8.do starting from 8+i should solve it, right? but it wont…sbuf is being read through pprotos buffer allocation. this mix of having to provide buffers with audio and empty buffers is what makes it tricky, as pproto cannot just allocate empty ones…

im not quite sure i understand the alternatives suggested: how to either get all the bufnums from the server / or hardcoding them? would it imply to refrain from pprotos own buffer allocation (which i was trying to avoid) and .add all the buffers instead, both cbuf and sbuf?
j

adapted some existing examples: in this case the sbuf sample plays right. poll shows that buffer numbers in cbuf also are indexed according to allocation, but the buffers are empty eventhough they should contain 2**18 samples. numFrames remains zero.

(
var server = Server(\nrt, NetAddr.localAddr, s.options.copy);
var buf =  Buffer.allocConsecutive(8,server, 2**18,bufnum:8);  //bufnum reflects itself in synth but numFrames wont...
	
q=Bus.audio(s,8);

(
(
SynthDef(\one, { |out,dur,sbuf,amp,rate2,f1,f2,rate,amp2,start|
	var in=PlayBuf.ar(1,sbuf,rate*BufRateScale.kr(sbuf),startPos:start*BufFrames.kr(sbuf),loop:1);
	Out.ar(out,in*EnvGen.ar(Env.sine,1,8,timeScale:dur,doneAction:2).tanh)}).store
);
);

(
SynthDef(\two, { |out,cbuf,dur,sbuf,amp,rate2,f1,rate,amp2,start,ini|
	var inn=In.ar(ini);
	var in2=SinOsc.ar((LFDNoise3.ar(0.1).range(1,1))*f1);
	var buf=  RecordBuf.ar(in2, cbuf);
	var testsnd= PlayBuf.ar(1,cbuf,loop:1);
	var testbuffer=BufFrames.kr(cbuf.poll(1)).poll(1);
	Out.ar(out,Pan2.ar((amp*inn)*EnvGen.ar(Env.perc,1,4,timeScale:dur,doneAction:2).tanh))}).store //now it plays/renders sample
);

(p=(1..1).collect{|i| (Pspawner({|sp|
	sp.par( Pproto(
		{	 (~sbuf= ( type: \allocRead, path:Platform.resourceDir +/+ "sounds/a11wlk01.wav").yield);
		},
		Pbind(
			\instrument,\one,
			\dur,20,
			\rate,(1..1),
			\start, Pwhite(0,1.0).clump(1),
			\f1,Pwhite(200,2000).clump(8),
			\sbuf, Pfunc { |ev| ev[\sbuf]},
			\out,Pfunc{q.index+(0..0)},
			\addAction, \addToHead,
		);
));
		loop{sp.seq(
		Pbind(
			\instrument,\two,
			\dur,10,
			\amp,0.01,
			\rate, 1,
			\ini,Pfunc{q.index+(0..7)},
			\f1,100*(1..8),
			\amp2,0.1,
			\cbuf,buf,  //?
			\out, 0,
			\addAction, \addToTail,
		);
);}}).asScore(60,timeOffset:0.001)
)});

c = ServerOptions.new.memSize_(2**20).blockSize_(1024).maxNodes_(8192);

p.do{|buf,index| buf.render("~/desktop/test.wav".format(index).standardizePath,sampleRate:96000,sampleFormat:"int24",options:c)}
)

One of the principles you might get from the NRT guide (if you haven’t read the NRT guide help file, please do) is that you can’t simply create a server object for the NRT rendering and just alloc or read buffers. You can create Buffer objects but the alloc messages must be added to the score.

var buf =  Buffer.allocConsecutive(8,server, 2**18,bufnum:8);

Here, you’re reserving buffer numbers. But I don’t see anywhere in your code that you are putting any allocation messages into the score. If they aren’t in the score, then the NRT server won’t reserve memory for them and those buffers will report 0 frames. (allocConsecutive sends the messages immediately to a server which, in this case, isn’t running – and can’t be running yet, because the NRT server will start only after the score is completely generated. So you can’t rely on allocConsecutive here. You can use it to reserve buffer numbers, as long as you write the buffer allocation messages into the score by yourself.)

I’m afraid I’m not able to post a working code example for you – I’m traveling now, writing this on a tablet. I don’t have my computer. So you’re on your own for the next couple of days for the code example, unless somebody else steps in. I hope it’s helpful at least to point out where the problem is.

hjh

many thanks. yes im working through the helpfile and adapting the examples as far as possible. im trying to make it work for a setup with pproto etc which doesnt make things necessarily easier unfortunately… but your comments are definitely helpful! ill keep on digging to get to it.

and btw i thought this line would allocate it in the score, with bufnum indexed accordingly in the pattern:

(8.do({
|i| p.add([0.0, [\b_alloc, i,2**18,1]]);
}));

but it doesnt;)

This line uses buffer numbers 8-15.

This line operates on buffers 0 to 7.

So, naturally, this won’t allocate memory for the buffer numbers in the buf array.

Also you removed that last bit from the revised example, so it wouldn’t take effect anyway.

hjh

sorry, its not apparent in the example but i had tried all combinations. even when buffers number get aligned and the buffer effectively gets passed to the score it wont be accessed for some reason, stil unknown to me…
here the two options are tested:

(
var server = Server(\nrt, NetAddr.localAddr, s.options.copy);
var buf =  Buffer.allocConsecutive(8, server, 2**18, bufnum: 8);//bufnum reflects itself in synth but numFrames wont...
// var buf=   Buffer.new(server, 2**18,1,8); //alternatively, like in the NRT example

q=Bus.audio(s,8);

(
(
SynthDef(\one, { |out,dur,sbuf,amp,rate2,f1,f2,rate,amp2,start|
	var in=PlayBuf.ar(1,sbuf,rate*BufRateScale.kr(sbuf),startPos:start*BufFrames.kr(sbuf),loop:1);
	Out.ar(out,in*EnvGen.ar(Env.sine,1,8,timeScale:dur,doneAction:2).tanh)}).store
);
);

(
SynthDef(\two, { |out,cbuf,dur,sbuf,amp,rate2,f1,rate,amp2,start,ini|
	var inn=In.ar(ini);
	var in2=SinOsc.ar((LFDNoise3.ar(0.1).range(1,1))*f1);
	var buf=  RecordBuf.ar(in2, cbuf);
	var testsnd= PlayBuf.ar(1,cbuf,loop:1);
	var testbuffer=BufFrames.kr(cbuf.poll(1)).poll;
	Out.ar(out,Pan2.ar((amp*inn)*EnvGen.ar(Env.perc,1,4,timeScale:dur,doneAction:2).tanh))}).store //now it plays/renders sample
);

(p=(1..1).collect{|i| (Pspawner({|sp|
	sp.par( Pproto(
		{	 (~sbuf= ( type: \allocRead, path:Platform.resourceDir +/+ "sounds/a11wlk01.wav").yield);
		},
		Pbind(
			\instrument,\one,
			\dur,20,
			\rate,(1..1),
			\start, Pwhite(0,1.0).clump(1),
			\f1,Pwhite(200,2000).clump(8),
			\sbuf, Pfunc { |ev| ev[\sbuf]},
			\out,Pfunc{q.index+(0..0)},
			\addAction, \addToHead,
		);
));
		loop{sp.seq(
		Pbind(
			\instrument,\two,
			\dur,10,
			\amp,0.01,
			\rate, 1,
			\ini,Pfunc{q.index+(0..7)},
			\f1,100*(1..8),
			\amp2,0.1,
			\cbuf,buf,  //or (8..15) ?
			\out, 0,
			\addAction, \addToTail,
		);
);}}).asScore(10,timeOffset:0.001)
)});

(8.do({
	|i| p.add([0.0, [\b_alloc, i+8,2**18,1]]);
}););

//p.add([0.0, buf.allocMsg]); //to work with Buffer.new

c = ServerOptions.new.memSize_(2**20).blockSize_(1024).maxNodes_(8192);

p.do{|buf,index| buf.render("~/desktop/test.wav".format(index).standardizePath,sampleRate:96000,sampleFormat:"int24",options:c)}
)

im trying to pass p.sort before rendering.
whicht might be the essential element missing.
and get the following:

ERROR: Message ‘<=’ not understood.
RECEIVER:
Instance of Score { (0x1263c1268, gc=38, fmt=00, flg=00, set=02)
instance variables [3]
score : instance of Array (0x123daebc8, size=6, set=3)
routine : nil
isPlaying : false
}
ARGS:
Instance of Array { (0x1263c0e08, gc=38, fmt=01, flg=00, set=02)
indexed slots [2]
0 : Float 0.000000 00000000 00000000
1 : instance of Array (0x123dabff8, size=5, set=3)
}
CALL STACK:
DoesNotUnderstandError:reportError
arg this =
Nil:handleError
arg this = nil
arg error =
Thread:handleError
arg this =
arg error =
Object:throw
arg this =
Object:doesNotUnderstand
arg this =
arg selector = ‘<=’
arg args = [*1]
< FunctionDef in Method SequenceableCollection:insertionSortRange > (no arguments or variables)
SequenceableCollection:insertionSortRange
arg this = [*2]
arg function =
arg left = 0
arg right = 1
var i = 1
var j = 1
var test = [*2]
SequenceableCollection:mergeSort
arg this = [*2]
arg function =
var tempArray = [*2]
< closed FunctionDef >
var server =
var buf =
Interpreter:interpretPrintCmdLine
arg this =
var res = nil
var func =
var code = “(
var server = Server(\nrt, …”
var doc = nil
var ideClass =
Process:interpretPrintCmdLine
arg this =
^^ The preceding error dump is for ERROR: Message ‘<=’ not understood.
RECEIVER: a Score

any ideas what is interfering with the sorting?

indeed, p.sort was the missing element. it was the p=(1…1).collect array with pspawner that was interfering with it!
i wonder how it could be possible to keep the possibility to render parallel/multichannel scores while being able to sort, which is crucial.

If you have an array of separate scores and you want to sort and render them separately, then you need to call sort not on the array of scores, but rather on each element of the array.

That is, there is a difference between [3, 2, 1].sort (ok) and [[3, 2, 1]].sort (not ok) and [[3, 2, 1]].collect { |subarray| subarray.sort } (ok again). When you understand that, then the sorting problem with your array-of-scores will be clear.

p = p.collect { |score| score.sort };

If you want to merge all the scores into one before sorting… offhand I’m not 100% sure (still not at my computer), but you would have to do the merging first. Probably: make a new score object, then step through your array of scores and, for each one, add all the OSC commands to the new score. Simply doing myArrayOfScoreObjects.sort includes no instructions to merge, and SC has no magic to infer your meaning.

hjh

many thanks James, will try these options!