Modifying synthdefs whilst there are commands in queue for them

hiya, first post

am just gonna dump the readme for a ugen i made in here cos that’s basically my question

is there a better solution than this? it is fucking gnarly tho
I’ve been using it for 4+ years now, works alright. i occaisonally have to offset for supernova when i rebuild sc

(sorry about the formatting)

begin readme

Scenario:
a bunch of synths that have been made by a sequencer - ie. with latency - are sitting in the server command queue waiting to happen
some change happens to the synthdef that means the arglists in the s_new commands for the synths in the queue are now completely invalid,
and/or buffers those synths were using have been freed,
so we really should NOT be creating those synths.

Proposal:
‘cancel’ all pending synths, that are in the command queue ie. dispatched with latency
so that the synth will start, but NO UGENS in it will be ran - not even the constructors.
To do this we do something pretty hairy.

    also stop all live synths within one block (same as usual free)                                                                                                       
                                                                                                                                                                          
    use a scheme of comparing value on bus, to value passed to Synth Control
            this way we don't need to keep track of synths, in order to be able be able to immediately hard-stop all of them

Approach:

scsynth
unit->mParent ( a Graph)
mCalcUnits
mNumCalcUnits

supernova needs different approach
the pointer to units list in mParent is not used - supernova attaches the stuff that it uses on top of these structs, and leaves them uninitialised
use fixed offsets into this extra attached stuff

we replace the unitdef for all units in the synth, with our own unitdef
so that all other ctors will run our ctor function
we test to see if all the unitdefs have already been replaced, to know when we are running in the context of another UGen

assumptions:
for supernova: location of units and calc_units_count

    that the unitdef struct for each unit is NOT THEN USED for anything other than running the dtor
            because each unit will be constructed with one unitdef (the usual one), 
                    then destructed with another (the replacment one, from our UGen)

I think: it should be OK to have this as not the very first UGen - as long as none of the ones before require dtor functions
Should only be a good reason for it.
With intended usage, In.kr will be the only UGen before SynthStopper.

What would be alternative, more standard approach?
To cancel pending synths, the only thing I can think of is deleting the synthdef from the server
(which is synchronous, so should work)
(but will produce ‘synthdef not found’ messages, and also require a version number appended to the synthdef name,
AND also - most cumbersomely - require resending the synthdef with a new version number -
so either rebuilding it, or keeping it hanging around.)
then also employing what we do here (compare bus value with value in synth args, and free self if they don’t match)
in order to stop already-running synths.

Further:

    Possibly try to work out why scsynth ignores NodeEnd() done in ctor - it differs from supernova, here.

    LATER if at all: run for certain number of blocks, to allow pending parameter sets to be done, before ending.

end readme

yeah, dunno what is up with that formatting, i didn’t insert any tags or anything it was all supposed to be one thing

this bit is mostly referring to when swapping buffers out, IIRC
i guess you could say ‘but they produce 0 DC’ but who knows what that will do in the context of the patch

To be honest, if a new SynthDef version will have different arguments and invalidate s_new messages, then I would absolutely avoid name collisions here!

That is, from one point of view, replacing SynthDefs with new ones where the interface (control name list) is totally different and keeping the same name could be considered a logic error.

I think you’re better off ensuring unique SynthDef names.

You can use scsynth’s /error command to suppress error posting for specific commands.

hjh

I think you’re better off ensuring unique SynthDef names.

IIRC you can’t just rename an already-built synthdef, so you have to rebuild it again with a new name, or do some (debatably) equally mad stuff (inside the bytes of the built one)… & obviously rebuilding a synthdef can be very expensive

the buffer thing is what really got me too, i guess ppl just swap out soundfiles and hope the bufnum doesn’t get reused before their synth is created and even then … ?

thank you for taking the time with my terribly formatted op :slight_smile:

my ugen allows:
mysynthmakingthing.stopAllSynths()
and that will stopp all currently sounding synths within one block AND all the ones in the queue won’t make any sound either (but it’s hairy …?)

i guess it’s cos having a UI for stuff makes it more difficult to reason about stuff cos the UI things need to happen ‘immediately’ and you have the queue of sequencing stuff going at the same time

I do have some thoughts on these issues (resource management, interaction between GUIs and timed processes) but not in a position to write up detailed examples right now.

Briefly: I’m a big believer in helper objects for resource management. For “hope the bufnum doesn’t get reused before their synth is created,” a reference-counter would probably work. When you put the synth into queue, you’d increase the reference count; when the synth goes away (language receives /n_end with that node ID), decrease the counter. If the counter is greater than 0, then it’s not ok to manipulate that buffer. I could write up an example, but not immediately at the moment.

For GUIs, a rule that I try to follow strictly is that everything you want the system to be able to do, you should be able to do using code statements (method or function calls). If there is a parameter referenced in a pattern, then that parameter needs to be modeled in some type of storage – a variable, or Pdefn, or something of your own design. Then, the code statement to change the parameter is just ~myParm = newValue or Pdefn(\myParm, newValue). Now: the GUI can simply run this instruction when needed.

GUI → storage ← pattern

A lot of UI problems disappear when there’s intermediate storage sitting in between the “right now” and “timed” components.

hjh