Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to end the stream from the callback? #907

Open
vincent-sparks opened this issue Aug 29, 2024 · 5 comments
Open

Is it possible to end the stream from the callback? #907

vincent-sparks opened this issue Aug 29, 2024 · 5 comments

Comments

@vincent-sparks
Copy link

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>> 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?

@Ralith
Copy link
Contributor

Ralith commented Aug 30, 2024

the stream is deliberately made !Send for future compatibility with AAudio

What does this have to do with giving your callbacks ownership of an Arc?

@vincent-sparks
Copy link
Author

vincent-sparks commented Aug 31, 2024

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.

@Ralith
Copy link
Contributor

Ralith commented Aug 31, 2024

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.

@vincent-sparks
Copy link
Author

vincent-sparks commented Sep 2, 2024

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.

@Ralith
Copy link
Contributor

Ralith commented Sep 2, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants