I haven't gotten the motivation yet (damn weather!) for soldering the new light cables, but I have spent some time with the code the last days. Figured I could do something better than what was in place.
Said and done -
Previously the code was run sequentially and always in the same order. This has now been changed to be done via interrupts instead. I am drawing line by line, layer by layer, and it works great!
I've also sacrificed a little RAM for speed/comfort by adding a backbuffer. While the display is blazing away as fast as it can from the drawbuffer (i.e frontbuffer), all new drawings occur on the backbuffer. When the buffer is finished it simply redirects the current line-pointer to the other buffer and the display never knew what happened. I've tried this in the past, but I never got the flickering to go away. I realized I was going about it the wrong way - there's no need to restart the drawing from the top whenever the content changes. I'm already drawing the screen line by line times eight so nobody would notice really. I was also previously stalling the rendering for buffer creation due to the sequential handling and that caused flickering too. But by avoiding the above and rendering with double buffers, interrupts has given me a rock solid 60 fps screen that is flicker free and brighter than it's ever been. Best of all - it's fixed fps, so it will never dip below this. Ever.
I've also moved solenoid and switch handling into the very same line-drawing-routine, except that I only process information from these once 32 lines has been drawn (i.e at vertical blank, like the good old days). This has almost given me a 100-200x frequency on I/O without causing any flickering!
This is extremely good news as this means less chance of a missed switch hit or a solenoid firing too long. Previously, if the rendering and SD-card loading took 30ms, that was 30ms that couldn't be spent on anything - i.e a switch hit would not be registered during that time. That is remedied now thou and just to be extra sure I rewrote the switch code to be 4-5x faster as well. Turned out that piece of code still used the old and slow digitalWrite-code, but I took care of that.
Furthermore - with the changes above the logic loop didn't have to be running as fast as possible. So I've locked the game logic to 40Hz, including SD-card reading, buffer creation, transitions etc. I did a little counting and found that it never dipped below 42 Hz (this is all done on the machine's "spare time" now, mind you), so I lowered it a little extra for safety. A fixed rate means no surprises to game logic and the frame is guaranteed to be finished in time. Should it not be, however, the double buffering will prevent any flickering - which is always nice!
For the record.
I've tried using interruptTransfers in the DSPI-library, but they refuse to work for me due to trouble with the Max32 board definition and conflicting hardware vectors. Ironically, I'm the one who reported the bug in 2013 and it hasn't (to my understanding) been fixed yet...
Anyhow - had that been fixed I could have load animation data and sending data to the display "in the background" and go about my business the usual way. My way works just the same, but different.
Finally, I wrote a script to remove the initial two bytes of junk from wav-files. However, as I didn't read up on the wav-format, the files ended up being the correct size but not valid wav-files. Instead of working around it I simply powered through and converted the 636 sound files (will they all be used anyway?!) manually. That was joyful, for sure, but now the audio seems to be working fine. Still need to convert the old audio routines into new ones and decide on a sound priority/scheduling scheme.