The problem
Currently we have a per-track sound queue that works great. If a track is busy and a new sound is played, we can queue it until the track is clear. The problem is when we play shows that include sound along with other elements that are supposed to be synced with that sound. (e.g. Extra ball show which is display animation, light show, and sounds.)
In the current design of MPF, when that show plays, if the sound track is busy, the display and light shows will play, but the sound would be queued, so the sound would play by itself later and/or out of sync with the other effects.
This is VERY common. I spent the past few days playing pinball, and it happens a lot. Not just extra balls, but specials, jackpots.. Even things like important shots made. It happens a lot.
Also, some have suggested that show priorities mean this is not a problem. That is not true. If a show at priority 200 includes sound and the current sound track is busy with a sound playing at priority 100, then the show can cut off that sound and play in sync. But if the reverse is true, where a new show at priority 100 is played but the current playing sound is priority 200, then the show’s lights and display will play immediately at 100 but the sound will not play since it’s lower than the current playing sound.
The proposed solution
In MPF, we need a way to know that a show can be played, e.g. that all of the elements it has in it are available. This means that either:
- MPF has to know what’s in a show and whether the resources are available before he show is played, or
- MPF has to query the resources to find out if they’re available before a show is played.
Option 2 is more complex since PluginPlayers are running remotely across BCP, so MPF would have to make a BCP call and wait for a response before playing. Even though this is essentially “instant”, the way BCP is handled means it would have to wait a few ticks of the game loop for the BCP send/receive.
My goal is to build a universal queue, with intelligence handled by individual config players. (Not every config player would use this, since some config players don’t have to deal with queues.)
Here’s what I propose
We create a dictionary, on the MPF side, of “semaphores” (or “locks” or “mutexes” or whatever we want to call them) that correspond to resources that must be available in order for a show (or anything, really) to play. The names of these semaphores are arbitrary, and they’re created by the config players. Each config player can create as many semaphores as it needs (or none) based on the resources it manages.
So the sound player might create one semaphore for each sound track, the slide player might create one for each display target, etc.
When shows are processed (at boot), in addition to the individual config players being called to validate and process the show config (which they do now), they will also be responsible for building a list of which semaphores need to be clear in order for that show to play. The list of semaphores a show needs will be stored as an attribute of the show itself.
The individual resources that have semaphores associated with them will be responsible for continuously updating the status of their semaphores, so MPF’s semaphore registry always knows the current state of any semaphore. Plugin config players will send their updates to MPF via BCP.
Then when MPF needs to play a show, it will just check the show’s list of which semaphores that show needs and compare it to the states of those semaphores in its registry. If they’re all free, great, the show plays. If not, the show is added to a queue.
Every time a semaphore is cleared, MPF will check to see if any shows that are queued can be played now. If so, it plays the show.
The show queue can use other existing features similar to the current audio queue, including max queue time, the ability to use shorter shows or alternate shows if the show is queued too long, etc.
When modes stop, the queue will be checked for any shows from that mode, and those shows will be removed from the queue (similar to how it currently works to stop running shows from a mode when that mode stops.)
A bonus feature would be for semaphores to be locked with an estimated end time, so if a show is requested but it needs a semaphore that’s busy, perhaps an alternate show that doesn’t need that semaphore could be played immediately rather than delaying the show while waiting for the semaphore to clear.
Implementation
I don’t think this will be too complex to write, and I think it can be pretty universal.
Also, all of this is sort of “under the hood” stuff. I don’t think any of this needs to be exposed to users. They just know that if they want to play a show, it will be queued until everything in it can play. (And the queue settings and behaviors here are no different than the way the sound queue works today, so not too hard to understand.)
Limitations
Once the semaphores are cleared and the show plays, there’s no further checking to see if the semaphores are still clear when a specific step comes up in a show. For example, if a show needs audio, but not until 5 secs in, something else could lock that audio track in the meantime.
There’s no ability to track which semaphores are needed where in a show. So if a semaphore is locked, that could delay an entire show even if the step from the show that needs that semaphore doesn’t come until later in the show.
These limitations seem ok to me.