Merging multiple scores (made from patterns) in NRT mode and then rendering them in one file

Hello!

I would like to use different patterns to create some scores and then be able to combine them into one so I can render them into one file. I want to be able to program when each score starts and ends.

for example score1 (made from patterns) will start at x time and end x+10, then score2 will start at x+5 and end at x+20 etc…
How can i render the different scores as one with .recordNRT

I upload some code (that doesn’t work as wanted) to illustrate the issue.
Any help would be really appreciated.

thank you!!

(
var server = Server(\nrt,
    options: ServerOptions.new
.numOutputBusChannels_(2)
.numInputBusChannels_(2)
),
def = SynthDef(\buf1, { |out, bufnum, rate = 1, time = 0.1, start = 0, amp = 0.1,dur|
	var release= Line.kr(0,1,(rate.reciprocal),doneAction: 0);
	var sig = PlayBuf.ar(1, bufnum, rate*(2048 / Server.default.sampleRate),Impulse.kr(rate), startPos: start);
    Out.ar(out, (sig*0.5 ).dup);
});

def.add;  // the pattern needs the def in the SynthDescLib

x = PmonoArtic(
	\buf1,
    \bufnum,  Pseq([1],inf),
	\rate, Pseq([5,8,3],inf)*1,
    \start, 0,
    \time, 0.1,
	\dur, Pkey(\rate).reciprocal,
    \legato, 1,
).asScore(duration: 10, timeOffset: 0.001);


q = PmonoArtic(
	\buf1,
    \bufnum,  Pseq([1],inf),
	\rate, Pseq([5,8,3],inf)*1,
    \start, 0,
    \time, 0.1,
	\dur, Pkey(\rate).reciprocal,
    \legato, 1,
).asScore(duration: 10, timeOffset: 10);





// the score also needs the def and buffer
x.add([0.0, [\d_recv, def.asBytes]]);
x.add([0.0, Buffer.new(server,2048,1,bufnum:1).allocReadMsg(Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff")]);
x.sort;

q.add([10.0, [\d_recv, def.asBytes]]);
q.add([10.0, Buffer.new(server,2048,1,bufnum:1).allocReadMsg(Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff")]);
q.sort;


//how can i combine x and q so then render them as one with recordNRT??


~outFile =(("~/Desktop/test.aiff").standardizePath);
x.recordNRT(
    outputFilePath: ~outFile.standardizePath,
    sampleRate: 44100,
    headerFormat: "AIFF",
    sampleFormat: "int16",
    options: server.options,
    duration: 20
);

server.remove;
)

I can’t test this right now but something like:

(
var offset = 10;
q.score.do { |item|
  x.add(item.copy.put(0, item[0] + offset));
};
x.sort;
)

might do what you want?

I almost didn’t notice it in the other current thread about NRT, but there is a problem with this usage.

Every ScoreStreamPlayer is its own (pseudo-)server – meaning that each call to .asScore starts with new node ID, bus and buffer allocators – meaning that node/bus/buffer ID collisions are basically guaranteed.

(
x = Pbind(
	\midinote, Pn(60, 1),
	\dur, 2
).asScore(2);

y = Pbind(
	\midinote, Pn(67, 1),
	\dur, 2
).asScore(2);
)

"\nx:".postln; x.score.do(_.postln);
"\ny:".postln; y.score.do(_.postln);

x:
[0.0, [/g_new, 1, 0, 0]]
[0.0, [9, default, 1000, 0, 1, out, 0, freq, 261.6255653006, amp, 0.1, pan, 0.0]]
[1.6, [15, 1000, gate, 0]]
[2.0, [c_set, 0, 0]]

y:
[0.0, [/g_new, 1, 0, 0]]
[0.0, [9, default, 1000, 0, 1, out, 0, freq, 391.99543598175, amp, 0.1, pan, 0.0]]
[1.6, [15, 1000, gate, 0]]
[2.0, [c_set, 0, 0]]



y.score.do { |array|
	x.add(array.copy.put(0, array[0] + 1))
};

x.sort;

x.score.do(_.postln);

[0.0, [/g_new, 1, 0, 0]]
[0.0, [9, default, 1000, 0, 1, out, 0, freq, 261.6255653006, amp, 0.1, pan, 0.0]]

// duplicate node ID (server error will print at render time)
[1.0, [/g_new, 1, 0, 0]]
// duplicate node ID (will print, and I think also this note will fail)
[1.0, [9, default, 1000, 0, 1, out, 0, freq, 391.99543598175, amp, 0.1, pan, 0.0]]

[1.6, [15, 1000, gate, 0]]
[2.0, [c_set, 0, 0]]
[2.6, [15, 1000, gate, 0]]
[3.0, [c_set, 0, 0]]

And, looking at this output, each asScore initializes the default group 1, so you will get redundant group messages. This probably isn’t damaging to the end result, but it will produce errors during rendering.

The duplicate node ID for the synth (I think) will cause notes not to sound, which is damaging to the end result.

I don’t see a way to address this in ScoreStreamPlayer. It might help to add a ScoreStreamPlayer *newFrom { |server| ... } method, so that a Server object would create allocators, and all of the asScore calls could share those. But that doesn’t currently exist.

AFAICS with the current implementation, the only way is to merge the patterns into one (probably using Ptpar).

(
x = Pbind(
	\midinote, Pn(60, 1),
	\dur, 2
);

y = Pbind(
	\midinote, Pn(67, 1),
	\dur, 2
);

z = Ptpar([
	0, x,
	1, y
], 1).asScore(3);

z.score.do(_.postln);
)

[0.0, [/g_new, 1, 0, 0]]  -- only one, OK!

// distinct node IDs, OK!
[0.0, [9, default, 1000, 0, 1, out, 0, freq, 261.6255653006, amp, 0.1, pan, 0.0]]
[1.0, [9, default, 1001, 0, 1, out, 0, freq, 391.99543598175, amp, 0.1, pan, 0.0]]

[1.6, [15, 1000, gate, 0]]
[2.6, [15, 1001, gate, 0]]
[3.0, [c_set, 0, 0]]

hjh

1 Like

Thank you, for all these insights about nrt!

Things start to make sense for me now, since I cannot find much about nrt online, the discussions here are be very enlightening!

Ptpar does what I wanted! I hadn’t noticed that in Ptpar you can add time of execution, so that is really great!