A few obvious things:
- You should fill your ring buffer before calling StartStream()
- You want your main loop to keep the buffer full by writing data to it whenever it isn't full. You could do this the wrong way by polling and sleeping. If the queue is big enough you can sleep for a second at a time and the overhead won't be so big.
- The "right" way to do it is to use an Event object and signal it from the callback every time the queue is becomes "not full". The main() loop blocks on that Event with WFSO and wakes whenever it can write data to the queue. (hint: use an auto-reset Event).
- If all you want to do is play a soundfile you could use PA's WriteStream() API which does all this internally.
Other notes:
- It is non-trivial to write a correct atomic FIFO queue. You haven't shown your code for this.
- Your callback doesn't deal with the case where the queue is empty. In that case it should probably output silence.
- You might not want to be newing up a new buffer for every block. Consider returning used blocks to the main thread via a second queue and reusing them.
- You probably want to bound the size of the queue (3-5 seconds of audio is more than enough for most scenarios) -- this is what I mean by "not full" above. Another way to think about this is in terms of a high watermark: PA callback drains the buffer while it is non-empty, main() fills the buffer while it is less full than the (e.g. 5 second duration) watermark. Callback wakes main whenever the buffer is lower than the watermark.