Well, generally there is the option of using a so-called “blocking” stream (by not providing a callback method), but I don’t think this has any advantages over using a callback function. One of the disadvantages is that it is not available for all host API (which IMHO is already a deal-breaker).
When using a callback function, there are still two ways to stop some sound from being played:
- set some state in the callback so that it (the callback) knows it should stop playing
- stop the whole stream
I think the first option (as @jon has implemented it) makes more sense.
I would use a single stream for everything, both playing and recording, and keep that same stream active all the time.
BTW, I mentioned in Low sound latency with sounddevice module & wdm-ks driver - #7 by matthias.geier that it would be interesting to try to implement the audio callback in C for more stable, predictable and responsive performance.
In the meantime, I’ve started to do exactly that: GitHub - mgeier/python-rtmixer: 🎤 Reliable low-latency audio playback and recording with Python 🐍. This is still very much work in progress, but it should already support most of the use cases needed for PsychoPy. I would love to hear what you think about it!