Here’s a little experiment with voice stealing I needed for something I’m working on. This is pretty rough, so I can’t guarantee that it’s doing everything correctly, but I think it’s ROUGHLY the best way I can think of to implement this kind of thing.
What is it doing?
Creates a subclass of PmonoStream that’s very similar to PmonoArticStreamexcept that, rather than deciding whether to re-use the existing event or re-articulate new one based on the \sustain value for that note, it simply always assumes you re-use the existing note. If the in-progress note ends BEFORE the next one is played, then it switches back to re-articulate mode (e.g. spawns a new event the next time around).
For a pool of 4 voices (Pdef(\roundRobin)), simply create a pool of 4 PmonoReuse streams and distribute new events through each of these stepwise. Beacuse PmonoReuse gives us a hard guarantee that we have a maximum of one instance of a synth at a given time, our pool of 4 gives us a guarantee of 4. All of the lifetime handling stuff is managed by PmonoReuse, so Pdef(\roundRobin) is exceedingly simple - and, in fact, shouldn’t need to be changed to much even for very different voice handling workflows.
Yeah, I see this too - I think this is a timing issue, probably the note is ending on the server at the exact same time that sclang is waking up and deciding to free the note. By the time the sclang message reaches the server, the Synth is already gone. Normally this isn’t a problem, because with common Pattern use-cases, the sclang pattern is the ONLY thing that can tell a Synth to free itself - but I made this voice stealing thing is specifically for cases where the Synth itself controls how long it plays - sclang doesn’t know.
There’s a way to wrap up messages to not show e.g. Note not found errors, probably I need to do that.
But in general: do you see the correct behavior, where basically you have NO MORE than 4 synths running at any given time, and probably fewer if you are playing them with legato < 1.0?