NRT and Supernova

Hi there -

I am curious about using Supernova to render multiple audio files at the same time.

I’m not entirely sure this is possible, based on what I know about Supernova - but I was hoping the more-knowledgeable here would have some ideas.

My understanding is that if you start Supernova and dispatch your synths via a ParGroup, the server will distribute your synths across several cores. Presumably, this is an scsynth routing solution - and the NRT rendering would be handled via sclang.

I’d like to start somewhere simple - would it be difficult to simply render 5 sine tones on 5 cores at the same time?

I appreciate the help, cheers!

NRT starts its own server instance - so using scsynth here should be fine- you can have multiple nrt scsynth instance run at once.
Sclang can help kick off nrt or format scores, but once the nrt process is running, sclang has nothing to do with it.
Hope that helps
Josh

/*
Josh Parmenter
www.realizedsound.net/josh
*/

No, it isn’t – supernova is a full replacement for scsynth. It isn’t a wrapper around scsynth.

Supernova NRT would produce only one file, just like scsynth. The commandline interface supports only one output path.

If the 5 files don’t have any interdependencies, then you could run them in separate scsynth processes. If they do have dependencies (like, file 2 is a filtered version of file 1’s signal), then they’d have to be in series, not parallel (which could be done in the same process, or separately with the file 2 process running after file 1 is complete and reading from file 1).

hjh

Thank you - but just to clarify here:

You mean that they are “supernova processes”, right? I think I’m still confused here.

If I understand correctly:

sclang parses some messages to “kick off” the NRT in the form of a score, those messages usually activate synths stored on sclang - but supernova could be used in place of sclang without too many major differences.

The big difference is that the processes must be “parallel” - so all signals that modulate or control each other must be kept within a single ParGroup, where it will operate on a single core.

So, if I wanted to create 5 simultaneous renderings of sine wave tones, outputting 5 .wav files - I could write something to execute 5 NRT scores at the same time, but give each instance its own ParGroup. supernova would manage distributing these instances over the available cores and I would not have much say in the matter…

Is this all correct?

So, something like the following code should be using the benefits of parallel processing…although, I’m getting an error when running it:

*** ERROR: in main(): boost::interprocess::bad_alloc
SynthDef("sine",
	{|freq, out, gate=1|
		Out.ar(out, SinOsc.ar(freq, mul: 0.1)*Linen.kr(gate, doneAction:2));
}).store;

Server.supernova;
Score.program = Server.program;

Task({
4.do{|i|
var server, chans = 2, duration = 100, pattern1, pattern2, nrtScore;
Date.getDate.postln;


pattern1 = Pbind(
	\instrument, \sine,
	\freq, Pwhite(0, 10000, inf),
	\stretch, 1,
	\dur, Prand([0.1, 0.05, 1], inf));

nrtScore = PparGroup(pattern1, inf).asScore(duration: duration, timeOffset:0.11);

	nrtScore.recordNRT(outputFilePath: "/Users/bd/Desktop/"++i++".flac",
	headerFormat: 'flac',   //this allows longer files..?
	sampleFormat: 'int16',
	oscFilePath: "/Users/bd/Desktop/"++i++".txt",
	duration:duration,
    action: {
		(Date.getDate).postln;
		"NRT done".postln;

});

	};
}).play;

NRT workflow is that sclang creates SynthDefs and also creates a Score (the messages), and then a NRT server process (not language process) is launched to read the SynthDefs and score from disk. (SynthDefs may be embedded in the Score too.) Then the server process runs all the DSP and writes the output file.

Supernova is a server process, not a language process. In theory it may be used in place of scsynth (I suppose just a typo here).

It’s very easy to get confused about what ParGroup does. Evidence of that confusion is that these paragraphs contradict each other – in the first paragraph, the immediate children of a ParGroup run in series (one core); in the second, they are distributed among cores. (The right answer is the second.)

Also – “I could write something to execute 5 NRT scores at the same time” – the only way to do this is to run 5 NRT processes (which your example does). But be aware in this case that ParGroup then becomes irrelevant! If you are running multiple server processes at the same time, the OS will put them on different cores to whatever extent is possible – including scsynth. (If you were thinking “scsynth has only one DSP thread” – there is no way to share one DSP thread across multiple separate processes. If you run 5 scsynths, then you have 5 DSP threads, and the OS will parallelize them.)

So if the 5-processes approach is what you want, then you can just use scsynth, no problem. Multiple scsynths on the same machine have always been parallel (this has been suggested before as a way to parallelize – you lose the unified signal flow of supernova, but avoid supernova’s little quirks).

Btw, NRT in supernova is not at all well tested, and may simply not work. In that case, “5 scsynths” may be the only way to go.

hjh

1 Like

This is very interesting - thank you for taking the time to explain all this. It is not something I’ve seen discussed elsewhere.

It sounds like it is a very clear-cut case to use multiple scsynths to render multiple processes separately - but this does makes me a little bit uncertain about what, if any, benefits supernova would have in this context. You mention “unified signal flow” - but I’m not sure what that means with regards to parallel processes and threads.

I would love to confirm if NRT in Supernova simply does not work also - if anyone else has tried to use it. My example above does throw an serious-looking error message, but was able to render a few files fairly easily (more trouble with longer renders, though)

Thanks again!

It should work in general. I have personally fixed some bugs in Supernova regarding to NRT processing. If you find that something does not work, please file a bug report on GitHub!

1 Like

If the signal chains are 100% independent of each other, then supernova has no benefits over scsynth in this case.

Parallel DSP also needs to be able to handle cases such as:

frequency LFO (sample-hold noise e.g., something with randomness)
| | | | |_ oscillator 1
| | | |_ oscillator 2
| | |_ oscillator 3
| |_ oscillator 4
|_ oscillator 5

The oscillators do not depend on each other, and could run in parallel. But they all depend on the LFO. So you have to guarantee that the LFO finishes before any one of the oscillators begins. In supernova, this would be modeled like:

SynthDef(\lfo, { |out| Out.kr(out, LFDNoise3.kr(12).exprange(200, 1000)) }).add;

g = Group.new;
p = ParGroup(g, \addToTail);  // head/tail doesn't matter yet lol

~lfoBus = Bus.control(s, 1);
~lfo = Synth(\lfo, [out: ~lfoBus], target: g, addAction: \addToHead);  // NOW it matters
~oscs = Array.fill(5, {
	Synth(\default, [freq: ~lfoBus.asMap], p)  // order doesn't matter in pargroup
});

sc-pargroup

The LFO is outside the ParGroup – before it – and the oscillators are marked for parallel processing by making them immediate children of the ParGroup. It can get more complex – a ParGroup can contain regular Groups, in which case, you would have parallel series of synths, where each series remains in order.

Supernova has benefits in this case (but that isn’t the case in your code example). It would be possible, using temporary audio files, to use multiple scsynths for these cases as well – but, considerably more difficult than ParGroups – so much more difficult that it wouldn’t even be worth trying.

BTW I had said before that it wasn’t possible to write multiple output files in NRT. That isn’t strictly true. There is only one “output file path,” but it should work to allocate recording Buffers, prepare them for recording, and use DiskOut. Users normally don’t do this “by hand” because you can just turn on recording (which does all of that for you) – but the components of recording are available, and could (should?) be possible to replicate in NRT.

Actually it does! I wasn’t at the computer and couldn’t try it last night.

No error for your code example in Linux. Which OS are you using?

hjh

2 Likes

Hi, I’m trying to use supernova for NRT and having some problems –
first off, if I run NRT from scide with the supernova server running it actually calls a new scsynth process and DOESN’T run supernova! (e.g. the code from the nrt help file)
ok, so I can just run supernova on the command line. and yes, it works fine on the osc scores generated by scide.
HOWEVER, i’m generating my own osc scores which always work with scsynth, and do NOT work with supernova! They consistently dump core. So there’s some vital difference in the osc required by supernova. I’m going to dig into the osc bytecode I’m generating and try to figure out how I’d need to modify it to have supernova accept it – but if there’s anyone here who has worked on this and might know what the differences it might be (and why there’s any difference at all, seems like there shouldn’t be??), or what scsynth/supernova source code to consult… Any help is appreciated!
I’ll update if/when I can figure out any differences.

1 Like

They consistently dump core.

Can you share the core dump?

So there’s some vital difference in the osc required by supernova. I’m going to dig into the osc bytecode

There shouldn’t be a difference in the required OSC messages. That’s very likely just a bug in Supernova.

Try to come up with a minimal reproducing example, then we can run it in a debug version of Supernova to see where the crash happens.

Bugs exist :slight_smile:

The core dump is not very informative:

supernova -N /tmp/test_supernova.osc _ /tmp/test_supernova.wav 44100 wav int16 -o 2 -i 0 -n 5600 -m 800000 -z 8192 -b 1024 -a 4096 -c 16384 -w 1024 -V 0
Supernova booting
Found 0 LADSPA plugins
Performing non-rt synthesis:
  Next OSC bundle: 0.000000000
terminate called without an active exception
Aborted (core dumped)

example minimal osc file attached. extension should be .osc or nothing, not .txt but this platform is picky about extensions apparently.
test_supernova.txt (454 Bytes)

this runs in scsynth but not supernova.

here’s the scide version; the generated osc works on both synths.
temp_oscscore1031.txt (432 Bytes)

a = Score([
    [0.0, ['/d_recv',
        SynthDef(\NRTsine0, { |out, freq = 440|
            Out.ar(out, SinOsc.ar(freq, 0, 0.2))
        }).asBytes
    ]],
    [0.0, (x = Synth.basicNew(\NRTsine0, s, 1000)).newMsg(args: [freq: 400])],
    [1.0, x.freeMsg]
]);

a.recordNRT(
    outputFilePath: "/tmp/nrt_test.wav".standardizePath,
    headerFormat: "wav",
    sampleFormat: "int16",
    options: s.options,
    duration: 1,
    action: { "done".postln }
);

My use of n_free and quit are different, and I should pare that down to make a truly minimal example (will be a couple days before I can circle back to that but I plan to), but I also kindof suspect it may be the way it’s padding/parsing the synthdef?
yeah bugs exist but i would have expected the osc parsing code to be shared, not replicated!

My use of n_free and quit are different

I see that test_supernova.txt contains a /quit message at the end. You shouldn’t send /quit in NRT mode!

Instead the score should end with a dummy command and that’s what temp_oscscore1031.txt is doing.

Quoting from Score.schelp:

For NRT synthesis the final event should a dummy event, after which synthesis will cease. It is thus important that this event be timed to allow previous events to complete.

Actually, Score.recordNRT automatically adds the dummy command for you.

ok, interesting since the quit rather than dummy works fine in scsynth! I wonder where I got that from, since I made up my format by reverse engineering scide generated osc several years ago… I’ll fix it and report back. thanks for your help!

I had a closer look at the Supernova NRT code and it does explicitly handle quit requests in the main loop. The server should certainly not abort. I would recommend to open a ticket on GitHub.

(BTW scsynth just ignores the /quit command in NRT synthesis.)

This isn’t exactly a core dump – or, at least, it’s not the details of the dump.

Since I see “LADSPA plugins,” I assume you’re on Linux? In that case (you might have to install gdb from your distro’s package manager):

$ gdb --args supernova -N /tmp/test_supernova.osc _ /tmp/test_supernova.wav 44100 wav int16 -o 2 -i 0 -n 5600 -m 800000 -z 8192 -b 1024 -a 4096 -c 16384 -w 1024 -V 0

(gdb prints stuff)

// now a gdb prompt
(gdb) run

(more stuff, leading to a crash)

(gdb) where
... gdb prints backtrace

Now it should show you which function was running when it crashed.

This is what we need to know. So, for instance, you said “i would have expected the osc parsing code to be shared” but there is no evidence that the crash was occurring in the OSC parsing code. It’s very unlikely to be there, actually.

A concrete backtrace from gdb will do more to push this issue forward than anything else.

first off, if I run NRT from scide with the supernova server running it actually calls a new scsynth process and DOESN’T run supernova! (e.g. the code from the nrt help file)

Try:

Score.program = "supernova";

This is not well documented (not at all documented), but it looks like Score’s executable is a separate setting from the Server’s.

hjh

thanks jamshark, gdb seems like a good tool to know! here’s the gdb backtrace:

...
Performing non-rt synthesis:
[New Thread 0x7fffafe00640 (LWP 782064)]
  Next OSC bundle: 0.000000000
terminate called without an active exception

Thread 1 "supernova" received signal SIGABRT, Aborted.
__pthread_kill_implementation (no_tid=0, signo=6, threadid=140737341159232) at ./nptl/pthread_kill.c:44
44	./nptl/pthread_kill.c: No such file or directory.
(gdb) where
#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=140737341159232)
    at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=140737341159232) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=140737341159232, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x00007ffff7442476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff74287f3 in __GI_abort () at ./stdlib/abort.c:79
#5  0x00007ffff78a2b9e in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x00007ffff78ae20c in ?? () from /lib/x86_64-linux-gnu/libstdc++.so.6
#7  0x00007ffff78ae277 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#8  0x000055555560e29c in nova::sndfile_backend<nova::non_rt_functor, float, true>::~sndfile_backend() ()
#9  0x000055555557d22a in nova::nova_server::run_nonrt_synthesis(nova::server_arguments const&) [clone .cold] ()
#10 0x000055555558d296 in supernova_main(int, char**) ()
#11 0x00007ffff7429d90 in __libc_start_call_main (main=main@entry=0x555555584660 <main>, 
    argc=argc@entry=28, argv=argv@entry=0x7fffffffdb98) at ../sysdeps/nptl/libc_start_call_main.h:58
#12 0x00007ffff7429e40 in __libc_start_main_impl (main=0x555555584660 <main>, argc=28, 
    argv=0x7fffffffdb98, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, 
    stack_end=0x7fffffffdb88) at ../csu/libc-start.c:392
#13 0x0000555555587af5 in _start ()

i’ll report back again when i get rid of the quit statement and see if i can pinpoint the bytecode differences.

What bytecode? The score is just a list of OSC messages. The problem is clearly the /quit message.

According to the stacktrace the problem lies in the destructor of sndfile_backend. Something is calling std::terminate which causes the program to abort. Since this is a release build, we don’t see the exact place, but my first guess would be the destructor of a std::thread that hasn’t been joined.

Anyway, please open a ticket on GitHub. The forum is not really the right place for crash reports.

bytecode – i meant the fact that those .osc files (uploaded as .txt files in my previous message) are encoded in bytes not in plaintext, so it’s a bit more involved for me to inspect and edit them!
I’ll open a ticket on github once I have a bit more info. thanks again christof :slight_smile:

But if they are built from the same score, then their contents should be the same. It would be wrong if supernova required different binary OSC to get the same result.

hjh

opened a ticket on github. osc bytes padding inconsistency scsynth vs supernova · Issue #6746 · supercollider/supercollider · GitHub

the issue was not /quit but the way .asRawOSC pads synthdefs – supernova is picky about this but scsynth is not.