I can see how the VCV design for expander modules does not work for MM - VCV expects the expander to be immediately to the left or right, and MM doesn’t give control over the module layout.
But the expander mechanism is an important feature in many VCV plugins, including my Venom plugin. I would love to have that feature in MM.
I have a potential solution that I think would not be difficult to implement - add two custom port types - one for left expansion port, and one for right. A module that supports expanders would need to have one or both ports added, probably at the extreme top or bottom near or at where screws would go.
Only allow 0 or 1 cable per port, and only allow left patched to right, never left to left or right to right.
So to “install” an expander would require adding a cable from left of one to right of the other, or vice versa. The API can be extended to detect when an expander cable is added or deleted, and update the existing VCV API expander objects for both modules appropriately. Once that is done, all other stock expander code should simply work. This seems clean, and can support daisy chaining just fine.
Perhaps add a preference as to whether expander cables are displayed or not.
For patches created in VCV, it would be difficult to prevent a user from stacking cables at an expander port. The code that rights the yml could simply honor the first cable in the list, and ignore all others. I have written code to trace cables between modules for my Venom Bypass module, so I know it can be done with the existing VCV API.
Oh, interesting, I like that. Yeah, since positioning modules isn’t as useful on a small screen, using a similar concept like cables is a good idea. I think that’s the way to go.
When saving a patch from VCV that has expanders, I wonder if the Hub (which is what scans the patch in VCV and writes the yml) can figure out if a module in VCV has an expander without needing special expander cables. Something like checking module->getLeft/RightExpander().moduleId for all the modules in the patch. Or maybe get it from Module::toJson()? The Hub already scans all the modules in the patch to create the yml file, so it’s not much extra to check for expanders. However it’s done, it would be cool if the Hub could get the module IDs of all modules’ expanders and then use that info to create the “expander cables”, and then save that in the patch yml. That way modules in VCV won’t need any changes at all, nor any changes to the expander workflow in VCV.
Then when loading a patch, the MM could draw the expander cables (or hide them, like you say), or display the connection some other way (highlight connected expanders when you hover a module?). Also we could show expander left and right “ports” in the list of knobs and jacks on the ModuleView page, so even if expander cables are hidden, you could still add, view and remove connections just like you do with regular cables.
As far as the MM engine passing data between expanders, there’s an onExpanderChange() event the engine needs to call, and the left/RightExpander module pointers will need to be set… isn’t there also some type of double-buffer for passing data between modules that the VCV engine manages? I’ll have to look that up… But it should be possible!
Yes - each expander object includes a producer message pointer and a consumer message pointer that can be used for communication. I think most plugins opt for this method of communication. But it is not the only way. My Venom plugin uses the Module ID in the expander object to get a pointer to the expander module, and then the parent module directly reads from and writes to the expander(s) within the process method. In my implementation the expanders don’t actually do any of the work - everything is handled by the parent. (well, my expanders do maintain info as to whether they are in an active chain, but nothing else)
Both architectures are officially supported by VCV. The communication buffer has the same one sample delay that cables have. The direct read/write method of course has no sample delay.
Almost. The left and right expander objects are automatically maintained for every module in the patch, regardless whether they are used or not. Most modules ignore them.
If the plugin uses the double buffer for communication, then I think the hub can simply test whether the buffer pointers are NULL or not, and only setup the cables if they are not NULL.
But if the buffers are not used, then some other method is needed to indicate whether the expander objects are active. This would require at least some change to the module code to support MM. Perhaps the simplest way would be to write a Left(Right)Expander=true attribute to the data JSON whenever the expander is active. It would be a trivial code change, and transparent to normal VCV operation.
So your hub would have to test both the communication buffers and the data JSON to properly setup the cables in all cases.