How to have a single UI apply to several subpatches/instances? (e.g. drumkit)
Greetings all,
I am trying to build an FMsynth-based drumkit (after spending days building my own from scratch, I ended up just reverse-engineering & modifying X.FM, heheh :S). I'd like 16 different drums available at a time, each with the same modifiability. So imagine X.FM scaled way down, times 16.
Obviously I don't want to see ALL of those at once; a single UI will suffice. But I want to be able to switch to any one of the 16 instances and control them with the same UI. Initially I just used [poly~ FMsynth 16] and brought up the desired instance window, but this is rather inelegant; a separate window for a drum might be OK if I could choose instances within that one window, but ideally I'd like it to be embedded in a patch (i.e. that has other controls global to all instances).
What is the best solution?? Here's a couple I'm looking at:
1) Main patch contains a [bpatcher @name FMsynthMaster] that contains 16 [bpatcher @name FMsynth]s plus a [thispatcher] to which I send offset messages to. Easiest way, but seems wasteful?
2) A single [bpatcher @name FMsynth] containing the set of UI objects going into a [poly~ FMsynth 16]. Probably more efficient, since unused voices don't use CPU. But how to deal with windows for each instance, can't they be "embedded" in some way??
Perhaps there are other solutions that I can't think of at the moment. But there's another, hairier problem: pattrstorage.
I'd like each instance/drum to have a preset object - for 16 variations of that one drum. So, a separate .json for each drum. In the main patch I'd like to save entire 'kits' - a master json - but I'd like to include any changes I've stored in the individual drums' presets. Is there a "best" way of doing this? (and, can the master preset contain info on which .json a particular drum is using, and potentially recall an entirely new "set of presets" (via loading a new json), or is this getting way too nuts?)
These two issues seem really complicated to me while I'm planning the layout for this project. Any bits of advice would be greatly appreciated.
Cheers :)
I don't really see how you would need 16 different patches to do basically the same thing. Post the patch and I'll try to explain my meaning. Instead, your audio processing should be flexible enough to perform 16 different operations with the same logic. You can do what you want with sends and receives, but as it stands it sounds like you're doing something wrong.
I would simply make the ui, route output to a send, and abstract the audioprocessing into a subpatcher. In other words, there is no call for a complex interaction between ui and audio processing. Instead
1) Make the UI before even bothering with the audio processing. When designing it, you will get a clear idea of the functionality of the patch and what data specifically you will need to save.
2) Build the data storage system. Make sure that you choose amongst all available options (pattr, coll, and dict) for the one that best fits the purpose. Presets are generally defined by pattr, but it is possible (though I would argue improbable) that your patch is big enough to make coll or dict a better fit.
3)THEN proceed to build the audio synthesis subpatch, using multiple inlets and list processing to route the input where it needs to go. This will enable you to avoid many design pitfalls.
Thanks for the reply. You seem to be suggesting (2): the use of poly~ ("16 operations with the same logic", "abstract the audioprocessing into a subpatcher") with a single UI. This is an option, but then each poly~ instance will have a different set of parameters which will have to update the UI every time I want to edit a different instance. That seems like it could be a hassle to set up?
My patch is similar to X.FM~ (from ExamplesOverview). Now imagine you want to have 16 of those in use simultaneously (i.e. each 'drum' is one instance of X.FM~, though only needs a single voice). It would be nice to be able to select a drum (using a [tab @tabs 1 2 3 ... 16] for example) and have the UI reflect and modify the parameters of that drum.
I hope that clarifies the first issue a bit. Once I figure that out I might have an easier time tackling the storage part.
Again, why don't you post the patch?
re poly, no, that's not what I meant. There has to be a way to use one patcher to synthesize all drumhits. You should not need to load multiple copies of the same thing, instead you should load one thing and cause it to do multiple drum hits.
You can find the patch in your Max application directory under /examples/synths/FMSynth/X.FM~.maxpat
(in [p DSP~] there's a [poly~ X.FM-voice~ 16], but of course here all voices are the same. I want 16 *different* voices (i.e. each instance corresponding with a different drum)).
Since I want polyphony, there MUST be multiple copies of the core DSP, in one form or another - each drum needs it's own signal generator(s). There are basically 2 ways to do this: contain all voices in a poly~, or have separate patches for each voice. But this isn't the issue.
The issue is: how to manage the control of several distinct voices using (what appears to the user as) a single set of UI objects.
in that case send and receive. If you use one set of sends and multiple receives named after that send you can send one set of data throughout the patcher hierarchy. Use prepend and route to put it where it needs to go.
What you say re dsp sounds... odd, but I admit my dsp isn't where it needs to be. I will investigate later.
The problem then is when I select a different drum the UI has to reflect its parameters. I could use pattrstorage to maintain the 16 different "states", but I find that very cumbersome and limiting. I plan on having some of the parameters (selectable for each drum) controlled by LFOs and other events. This control data needs to be visible and configurable via the UI as well. So it wont work to have these external controls routed *through* the UI into a voice - it would have to be passed to the voice directly. But then how do I make these parameter changes visible *in* the UI?
I think I'm gonna go with solution (1) for now, and hope it isn't as taxing on my CPU as I fear it might be. I'll post here again and let you know how it goes.
there are many different ways how you could this, and i´d suggest that you
find your way by using a 3-voice dummy synth with only 2 parameters.
in all cases you need to request the current parameters of the currently chosen
voice in order to update the GUI - and that means that somewhere between
the GUI element and cycle~ (for example) there must be a parameter present.
the other direction is easy, simply have a [gate 16] after every GUI element.
in your case i dont think that using poly~, target, and s/r makes sense, it only
makes things like your flexible GUI more complicated.
i would have 16 parallel synths instead. and if it about saving CPU, then maybe
have 16 poly~ patchers of each one voice.
As a pattr zealot, I've knocked up a simple example to show 'one way' of doing the 'single UI to reflect/control individual poly voices'. The crux of the patch is pattr objects in the UI layer dynamically bind to equivalent pattrs in the specific voice of a poly~ object, that contains the N instances of your dsp.
Depending on your needs, a (purely) pattr solution may or may not be most appropriate - its usually my go to solution, due to the systems abilty to do 'wireless communication' throughout the patcher heirarchy without resorting to send/receive pairs (coupled with the fact that adding presets to a 'pattr-based system is a doddle).
Referring to the original question, I'd definitely be in favour of using poly~ to load N instances of your (X)FM synth. This will allow you take advantage of poly~s voice muting (to save on CPU), as well as acheive polyphony. Additioanlly, you *should* be able to avoid hard-coding how many drum voices your patch supports (should you find that 16 is too few, or perhaps too CPU intensive)
Finally, to add 'presets per voice', in line with the example patch I've posted, I'd probably go with adding a pattrstorage object into the poly subpatch, rather than using a single pattrstorage in the 'top-level' of the patch. The difficulties I'd envisage in doing this are that you'd have to manage N uniquely named pattrstorage objects, and manage an xml/json preset file for each. You'd also need to interact with the preset managing remotely in the UI, which could be complicated, depending on your requirements...
Put the individual drum params into nums or flonums (within the subpatch) Connect the nums to a send. Put a recieve on the ui.
forgot to say, bang them all when you click on one drum. A mite messy but it'll work.