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

how to handle write_callback API when creating language bindings #28

Closed
capr opened this Issue Oct 2, 2015 · 7 comments

Comments

Projects
None yet
2 participants
@capr
Contributor

capr commented Oct 2, 2015

I've noticed that the write callback is called from a different thread (using WASAPI on a Win7 x64 here). This makes it difficult to bind to dynamic languages (I was about to attempt a LuaJIT binding for instance). Is there anything that can be done about that, like, say a lock/write/unlock API like DirectSound?

How do you push data in the write callback if you can't even use locking primitives?

Thanks,
Cosmin.

@andrewrk

This comment has been minimized.

Owner

andrewrk commented Oct 2, 2015

The safest way to push data to the write callback is to use a ring buffer. libsoundio provides an efficient ring buffer implementation (docs) for your convenience.

luajit is garbage collected, which means that it's generally not real-time safe. This means that the binding code you write in C will probably want to implement the write callback without exposing it directly in the API and do some internal buffering (via a ring buffer probably). Then you can have your lock/write/unlock API on top of that. At this point I would probably implement it without a callback style, but just allow the API user to push sound to or clear the ring buffer, and as long as it has enough sound buffered up you will get no underruns.

Some backends (PulseAudio) have large buffers already. In this situation, your internal buffering can be smaller to compensate. So I suggest calculating the internal buffering size after opening the stream and the software_latency of the stream is known. You can, of course, request specific software_latency value from the backend, but in the case of bindings like this I suggest not exposing that in your API.

This goes for anyone implementing bindings in a garbage collected language (python, perl, go, ruby, javascript, java, etc).

@andrewrk andrewrk changed the title from Make libsoundio reentrant to tips on implementing bindings for garbage collected language Oct 2, 2015

@andrewrk andrewrk added the question label Oct 2, 2015

@capr

This comment has been minimized.

Contributor

capr commented Oct 2, 2015

Hi Andrew,

Thanks for answering. I have a few more questions.

In sio_record.c, I see that you use the ring buffer without locking. Does that mean that you can read from it in one thread and write to it in another thread safely without locking? That would solve the issue entirely.

[btw, I don't think garbage collection has anything to do with this, other than adding to the latency -- the problem I have is with the fact that the Lua interpreter state is not re-entrant so I can't enter into it from a callback called from another thread]

@andrewrk

This comment has been minimized.

Owner

andrewrk commented Oct 2, 2015

In sio_record.c, I see that you use the ring buffer without locking. Does that mean that you can read from it in one thread and write to it in another thread safely without locking? That would solve the issue entirely.

Yes :-) The libsoundio ring buffer implementation is a fixed size, one reader, one writer, lock-free queue. It uses atomic integers for the read index and the write index.

[btw, I don't think garbage collection has anything to do with this, other than adding to the latency -- the problem I have is with the fact that the Lua interpreter state is not re-entrant so I can't enter into it from a callback called from another thread]

I see - let me attempt to rename it again.

@andrewrk andrewrk changed the title from tips on implementing bindings for garbage collected language to how to handle write_callback API when creating language bindings Oct 2, 2015

@capr

This comment has been minimized.

Contributor

capr commented Oct 2, 2015

Cool, thanks Andrew. I'll try the ring buffer idea then. Awesome work btw!

PS: I assume you don't care about WinXP (i.e. DirectSound) anymore right? :)

@andrewrk

This comment has been minimized.

Owner

andrewrk commented Oct 2, 2015

PS: I assume you don't care about WinXP (i.e. DirectSound) anymore right? :)

Correct. Microsoft stopped supporting Windows XP on 2014-04-08, so I feel justified in not supporting it.

@andrewrk

This comment has been minimized.

Owner

andrewrk commented Oct 2, 2015

By the way, I am pleased to hear that you are writing luajit bindings to libsoundio. Feel free to ask me for a code review any time, and let me know when you release the first version and I will include a link to your project on http://libsound.io/.

@andrewrk

This comment has been minimized.

Owner

andrewrk commented Oct 5, 2015

I'm going to mark this as resolved - feel free to open another issue if you have further questions or concerns.

@andrewrk andrewrk closed this Oct 5, 2015

@andrewrk andrewrk referenced this issue Jan 26, 2016

Closed

Node bindings? #55

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment