Announcing three crates: rci, rosc and rsoundio


rci is a wrapper over environment variables of some common continuous integration services (atm only travis and circle-ci). It can also be used to check if your tests are running in a CI environment (I use this to skip some tests in rsoundio).


rosc is an implementation of the Open Sound Control Specification in pure Rust (the link to the spec. is dead). There are already some other implementations, but rosc supports almost all OSC features, compiles with modern rust (1.0+) and is properly tested.


rsoundio is a binding for libsoundio, a cross-platform audio input/output C library. We already have bindings for PortAudio and SDL but libsoundio offers some benefits over them. If you're interested what those benefits are take a look in the wiki.
There is still a lot to do and room for improvement, e.g. the binding for audio input is not implemented.
This was also the first time that I've written an FFI binding, so I would be really happy if someone with more experience in this domain could check this crate out and give me some comments/recommendations. Or even better, if someone would like to improve it and send me some pull requests.

Have a nice saturday, everyone!


Looks like rsoundio repo link in the crate metadata is broken.

1 Like

The project is still private in my own git server. I will fix the link and redirect it to the Github repo.

pub fn backend_count(&self) -> i32
pub fn input_device(&self, idx: i32) -> Option<Device>

It doesn't look like a good idea to use a signed integer for counts and indexes.

pub fn dec_ref(&self) {
    unsafe { ffi::soundio_device_unref(self.device) }

Beside this function being unsafe the bindings can and should manage refcounts transparently.

fn ne(&self, other: &Self) -> bool {

This is redundant, the trait already provides it.

impl<'a> Drop for OutStreamCallbacks<'a> {
fn drop(&mut self) {}

Any reason to have this impl?

extern "C" fn write_wrapper(raw_out: *mut ffi::SoundIoOutStream, min: c_int, max: c_int)
where W: FnMut(OutStream, i32, i32)

`W` seems unused here. Also you need to prevent panics from propagating into C.

>`pub fn register_write_callback<W>(&mut self, callback: Box<W>)`

I think the closure could just be taken by value here and boxed inside the function.

Are the callbacks called in a different thread. This at a minimum would require adding the `Send` bound.

> `NOTE: This can break if a callback is still active.`

Safe APIs can't permit anything breaking.

Thank you for the feedback! I've fixed anything you've mentioned (aside the checking destroy calls).

Some functions returned or expected i32s because that's how c_int is defined and I was to lazy to append the type casts. An unsigned integer is clearly the more appropriate type. Some days ago I stumbled about a similar problem in go, because the len(*Type) language builtin returns a signed and not an unsigned integer, how one would expect (here is a discussion why len(*Type) in go returns a signed int.

From now on, the reference counter for a device is decreased when drop is called. Because my library does not introduce pointers to device structs, beside those who are returned by libsoundio function calls, I don't have to call inc_ref anywhere (I could remove it).

I didn't knew that ne is provided by the PartialEq trait, but it makes totally sense.

The empty drop implementation for OutStreamCallback was a leftover, as well as the type parameter that was used for the x_wrapper functions. I've also changed the function signatures so that the callback is now moved and boxed inside.

Maybe I have to introduce some type of unregister_callback functions to check against calling destroy when there are still callbacks active. Any ideas?

If the callbacks are run on different threads it might be worthwile to document the assumptions and contract first. Right now reviewing this part probably requires deep understanding of libsoundio.

I don't have to call inc_ref anywhere

You could use it to implement Clone if that makes sense to do.

libsoundio author here. I'm happy to answer any questions about how it works.

In this particular case I believe the answer is in the docs, check the docs for the function pointer fields of the callbacks you're talking about.


1 Like

[quote="andrewrk, post:7, topic:4792"]
In this particular case I believe the answer is in the docs, check the docs for the function pointer fields of the callbacks you're talking about.
[/quote]I did check the docs but neither write_callback field nor soundio_outstream_start docs mention threads. Several other pieces of the docs refer to write_callback's thread but that doesn't paint a very clear picture. So I moved on.

Right so you don't have a guarantee about whether it is or isn't in a different thread, but you have to treat it like could be, and you also have to treat it as a realtime context, which means you can't do anything that takes a nondeterministic amount of time.