yeah, i agree. making playable and good-sounding softsynths is just not easy, even in a high-level DSL. (and cabinetry isn’t easy even with power tools), details matter. (e.g., aliasing is fairly ubiquitous; one scsynth feature that would help a lot would be an option to oversample a UGen graph.)
anyways, i’d be happy to try and contribute a couple of voice definitions.
@andrew the functions listed do seem like good candidates for unified command -> voice interface. we can get more specific by examining the engines and considering the other points raised in the GH issue:
@cfd90 i agree that many parameterization decisions can and should be left to the scripting layer. for example i strongly favor the practice of the noteOn/start command accepting Hz and unit velocity, rather than MIDI numbers. what is needed is unifying e.g. polysub and molly arguments: the former takes (id, hz) and the latter (id, hz, vel): to complete the example, i’d add vel to polysub but make both take “velocity” as raw [0, 1]. (the control layer can take care of velocity curve/scale, because different controllers will require different tunings of these. the engine could accept curve-tuning parameters but eh… that seems less efficient.)
[as an aside/example, here’s a point of friction: in a voice architecture with many unique parameters, i think it makes sense to expose a raw per-voice amplitude rather than a generalized velocity, because it gives more freedom to the script to maybe correlate amplitude with other parameters in a velocity map. but with a small and generic parameter set you would probably want to make a larger number of more opinionated synth types which specify their own velocity mapping.)
the best way to enforce these conventions would be to provide a wrapper class. the wrapper would take care of control bus and voice instance management (providing the one voice / all voices mapping,) and common attributes like an ADSR volume envelope, maybe Hz and param smoothing, etc. this would also of course reduce boilerplate code considerably.
(BTW: yes, i believe it is totally possible to switch engines from a parameter action and it should “just work.” with the caveat that lua should keep track of the “loading” state. here’s an example of that. i’d be curious to know what happens if anyone wants to try adapting this to use an engine parameter… and maybe someone already did.)
another under-documented feature of the engine module is that the available commands can be dynamically queried (in the static field engine.commands.)
i would love to promise to implement this, it is straightforward… but i’m underwater on norns development promises already. so i won’t for now.