Is it possible to end the stream from the callback?
I'm writing an audio/video player that does decode in the main thread, then sends the decoded samples via a ring buffer to the callback. I have a way for the ring buffer to signal EOF, and when the callback sees that I would like to tell cpal to stop playing once it has finished the current batch of samples. I could of course hold a reference to the Stream from the main thread and simply destroy it once the decoder hits EOF, but then I would end up not playing whatever audio data was still left in the ring buffer when that happened.
I initially tried to do this by having the stream be owned by the callback, by sending it in via an Arc<Mutex<Option<Stream>>> which the callback sets to None to signify it is done playing; however, the stream is deliberately made !Send for future compatibility with AAudio. I note that AAudio allows the callback to return a sentinel value to signal the stream should end. Would it be feasible to do this with other backends?
the stream is deliberately made
!Sendfor future compatibility with AAudio
What does this have to do with giving your callbacks ownership of an Arc?
My original intent, since the callback object must be created before the stream, and any variables moved into it must stay that way, was to create an Arc<Mutex<Option<Stream>>> which I then clone() and pass to the callback. Immediately after creating the stream I *stream_arc.lock().unwrap() = Some(stream). When the callback wants to end the stream, it does *stream_arc.lock().unwrap() = None to destroy the stream object. This, however, does not compile, since the Stream object, by design, cannot be sent between threads and thus cannot be sent into the audio output thread, regardless of mechanism.
There does not seem to be any other way for the callback to decide when the stream should end, as opposed to the thread that created the stream. This issue is a question regarding the cross-platform feasibility of adding one.
There are no platform-specific concerns. You communicate information from the stream callback the same way you communicate information to it: by passing it through shared memory. Stick an AtomicBool in your Arc.
So when I hit EOF in the main thread, do I just spinloop until that flag gets set and then destroy the stream? That seems an awfully inefficient solution.
Would it be feasible cross-platform to add a sentinel value that the callback could return to close the stream from the stream thread? I'd be willing to contribute code to make this happen.
You can use whatever inter-thread notification mechanism you like, so long as you don't block your stream callback for too long. That's what CPAL would have to do to implement the behavior for you anyway. Coordinating threads is a deep subject, but you could start at park.