Oh my! It was an arithmetic mistake leading to an overflow. Some desperate print debugging last night finally revealed where the overflow was happening. Memory errors are so so weird. The overflow was actually happening when copying out a segment from the shared ringbuffer, not in the places I was looking and which were showing memory corruption.
(A ringbuffer is just a fancy sounding software version of a tape loop that you keep writing over – one tape head to write, another tape head or several others to read.)
A realtime-safe ringbuffer collects live input in the audio thread, and a non-realtime background thread (where it’s OK to block and stall sometimes) copies that into a shared memory ringbuffer, which is what the instruments actually read from when they ask for live input during renders. It’d been scratching my head trying to find problems with copying from the realtime-safe to non-realtime ringbuffer because that’s all new, but the problem was when copying portions out from the non-realtime buffer into a new buffer for the instrument to use. (Yes, there are a lot of copies!) With certain offset values I’d made an off-by-one error which read one element past the end of the new buffer being given to the instruments.
C gets a bad rap for these kinds of problems, which is fair! They’re easy mistakes to make and they can be really difficult to track down – all signs were pointing to either a problem with the shared ringbuffer itself – or even more weirdly – totally unrelated memory from renders being freed in a cleanup thread after playback is complete. Still, I love these kinds of problems because once you find them, like a mysterious but small leak, they’re easy to fix and they stay fixed.
Astrid ran all night without crashing. A relief!