On Fri, 8 Apr 2022 03:34:45 +0200 Ichthyostega <p...@ichthyostega.de> wrote:
>Am 08.04.22 um 00:38 schrieb Will Godfrey: >> In almost all cases we want to send any changes to everyone who could have >> created them or would like to know. Currently that's the CLI and the GUI. If >> these don't want to know, then they can just ignore the message, and the CLI >> will want to know that a change to padsynth has occurred. If we ever support >> either a network and/or OSC these too would want to know. This is where >> resolveReplies comes in. > >This is not what the code base tells me. InterChange::resolveReplies() does not >produce the feedback. It only handles some midiLearn() stuff and produces and >feeds response text for CLI commands into the global Log. >And it sets the finishedCLI flag by side-effect. > >As far as I can see, resolveReplies() is not responsible to pass on the >reactions to the GUI. It does not even have an outlet towards the GUI. >Rather, the outlet towards the GUI is within the processing of the mediate() >function (InterChange.cpp line 1866), somewhere right in the middle of the >processing sequence, because mediate() uses a do-while-loop. > >> This is of course a bit special as the result has been split in >> two 'action started' and 'action completed'. Everywhere else just has >> 'action completed'. >> >> It's actually more than that as we don't want to send multiple 'action >> started' if one hasn't yet had time to complete so before sending the >> response we need to check if one is already in progress. >> >> Or is there something I've missed? > > >It's actually much more shades of Gray. >We have several Parts and each has a Kit with multiple PADSynth items. >Each of those produces its own Status updates. All of this runs concurrently. >For example you can load several instruments into several parts and while >one instrument loads you can trigger the next one or manipulate the UI, while >further commands can be fed from a CLI script (or even from MIDI), and all of >this can happen in any order. Moreover, it is not just start/stop, we have >re-started builds, and we have wavetables which are computed but not yet >picked up by the Synth, and we have extended cross fade times. > >Interchange::mediate() is the command dispatcher of Yoshimi -- but honestly, >I am not able to get a clear grasp of the logic of the "mediate()" function. >This function is way to complicated for my poor brain. It has conditional >compiled code, it has hidden side effects in every clause (the "returns" >function actually dispatches to the low priority thread, under certain >condidtions). It uses an uninitialised Command block, which is passed as >pointer to several functions in sequence, which gradually fill out fields >in various usage patterns, but also only under some conditions. The mediate() >function has Read and Write functionality lumped into each other. And it uses >three object fields of the Interchange object (comeFrom, syncWrite and >setUndo). >Note these would be messed up by concurrent calls. And each SynthEngine >instance >has its own Interchange object, and we can have several Synths, which are >passed >on by pointer almost everywhere into the code base. I have now looked almost >two >hours at that mediate() and at sendDirect() and sendNormal() functions and I am >just not "genius" enough to come to a reliable conclusion what happens when >under which condition, even more so when events are coming in concurrently. > >Please note that the status updates will come from several threads; >Some commands will be dispatched directly, some via the background thread. >The status updates from finished Wavetable builds will come from a pool of >background threads. And the signal for the crossfade start and stop and >the "clean" state will come from the Synth thread. >And all of that any time in any order. >That's poisonous. > >On the other hand, when we drop those concurrent invocations directly >into the "toGUI()" queue, I can understand what will happen. This code >is a non-blocking ringbuffer, it uses state-of-the-art memory synchronisation >with Atomics, and thus will ensure that concurrent calls will be serialised >without data corruption and the single consumer will get them cleanly, albeit >with no predictable order (but that is fine here). On the receiver side, there >is a single consumer in the UI thread, and we can order the states to be >displayed and use the maximum, and this way the UI will always reflect >the "hottest" state. > >Will, please understand me right: I want to deliver good work and >don't mess up something in subtle ways, which are hard to detect and fix. >Since this "padthread" stuff works concurrently, we're operating on a >higher "danger level", and various assumptions from the good old single >threaded world do not hold any more. > >-- Hermann Apologies :( I really shouldn't work late... and tired. I didn't even check against my own descriptions! This whole structure was built up over a very long time with me learning as I was going, so I've forgotten most of the details. The ControlModel.pdf in dev_notes is *supposed* to describe the overall structure, and mostly shows what you are saying - although it doesn't include the loopback behaviour, nor the most recent undo stuff of course. I'll go with what you suggest. Also if there's general agreement, after this work is completed I think we should pause further new development and see if we can clean up what we already have. -- Will J Godfrey https://willgodfrey.bandcamp.com/ http://yoshimi.github.io Say you have a poem and I have a tune. Exchange them and we can both have a poem, a tune, and a song. _______________________________________________ Yoshimi-devel mailing list Yoshimi-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/yoshimi-devel