How could communication with the PortAudio callback work? Maybe 1.7's atomics?

I would like to play around with PortAudio.jl. My goal is to create some simple functions like sine, saw, or triangle wave generators, maybe some basic filters or delays, and then adjust parameters using a simple GUI or even just once in a while through the REPL, all while the sounds are playing.

I think I have issues trying to understand the architectural logic of this. I have managed to get something basic going that relates to the PortAudio tutorial:

https://gist.github.com/jkrumbiegel/d309bde8635cfb32d7db598bf30c98ee

So I have a callback function working, but the difficult thing is now to remove all audio-related logic from the callback, because I can’t change that on the fly. I don’t want to hardcode wave generators into the callback. I want to be able to add arbitrary sound modifiers later, and the current type of function where I compute one specific thing without talking to the outside world isn’t that.

So I assume what I have to do is use some kind of buffer, maybe a ring buffer, where I can put audio data I calculate with my pipeline, then the callback just writes that data into the output buffer so it’s played.

So how can I write data into a buffer that some callback is constantly reading from? I know that PortAudio has a ringbuffer implementation for this purpose, but I’m not sure how to use it in Julia. How do I know where the callback is currently reading from, and how can I update the buffer without overwriting something that is currently being read? I’ve read that the PortAudio ringbuffer is safe for single-reader, single-writer, but what does that mean exactly if they can’t coordinate?

Of course I could let the callback set some Int that tells the outside loop where it currently is reading from, but that sounds like a race condition. Maybe this communication could work with the new atomics I thought, that the Int that tells me where the callback is, is written and read atomically. But I’m not sure if I can put something like that into a @cfunction like in the gist above.

If someone has a better understanding of these things, I’d be very interested to hear about how this could work!