Make microphone polling interval configurable#877
Make microphone polling interval configurable#877watsaig wants to merge 5 commits intoRustAudio:masterfrom
Conversation
|
Thanks. If memory serves me well, then in #799 we even moved to a |
|
Regarding this PR: as far as I know there are two big latency sources for a VoIP like application:
Switching to the condvar should remove the polling latency entirely. I would also be open to other implementations. The reason we have the polling interval is to convert from OS callbacks pushing samples to OS callbacks pulling samples: Microphone
----- Analog cable -----> DAC
----- Kernel polling/interrupts -----> OS audio buffer
----- Kernel calling on "non empty" buffer ----> Rodio microphone callback (via CPAL)
Rodio callback <----- kernel calling on "non full" buffer ----
OS audio buffer <----- kernel pollling/interrupst ------------
ADC <--------------------analog cable-------------------------
SpeakerSo we have a thread to "reverse the flow" at the microphone side, to go from the I did the initial microphone design mostly to get the feature landed. It might For example maybe we could do without the thread. If you want to feel free to |
|
Thanks for the feedback! I'll take a deeper dive and see if the condvar approach works best or if we can do without the thread as you mentioned |
|
Hi again, In my opinion this the perfect situation to use a channel, as we have data produced in a callback and the consumer implementing In the new branch, I added feature-gated support for Here are some results, with a buffer size of 16 (to minimize latencey) vs the default 512, ran on my macbook pro M3 with the internal microphone: Current implementation (rtrb)Buffer size: 16
Buffer size: 512
std::mpscBuffer size: 16
Buffer size: 512
kanalBuffer size: 16
Buffer size: 512
I also added a benchmark to look at channel throughput. I'm not sure it's very representative of real use but it shows Overall
Hope that helps, let me know if you have any suggestions for other things to look into! |
Amazing! I've been planning to add a tool like this forever. You went above and beyond here :)
Completely agreed!
OS <-> Rodio callback latency should be constant.
I'm surprised anyways, given the data you so clearly presented I'd be happy to merge a PR switching to the I do wonder (this is an idea, follow up on this only if you'd like to!) if we could lower CPU utilization by sending groups of 16 samples. Maybe even eight (OS audio output & input uses double buffering with a period of half the buffer size as far as I know, so theoretically eight could give a lower total latency). |
|
Thanks, sounds good! I'll cherry-pick the mpsc parts here.
Agreed, I was unclear here: I meant that there could be different delays all the way to the consumer e.g. if different channel implementations have a different way of signaling to the receiver that new data is available. But that might not make a big difference anyway in the grand scheme of things.
I don't quite follow, sorry. Do you mean batching in the callback, so that the channel type becomes |
|
Very nice! Did you also get a chance to measure the |
Sorry I meant send chunks as in
Would be interesting to see! Give that a shot if you got time Craig! |
|
I didn't try the condvar approach. Correct me if I'm misunderstanding the idea but a ring buffer + condvar sounds like a channel which is what I was referring to when I mentioned reinventing the channel. Actually that is more or less the first example that both of these intros to channels go with: Jon Gjenset - Crust of Rust: Channels; Mara Bos - Rust Atomics and Locks, which is then heavily optimized in As far as I understand, the sender side in #799 is less efficient (notifies the receiver on every push even if the receiver is already awake), and there is a potential race in case the sender notifies the condvar while the receiver is locking the mutex here where the receiver would then have to wait until the next sample is sent to wake up. Again please correct me if I'm wrong but I believe this is exactly the kind of thing that channels abstract away to provide a more efficient (and simpler) way to send data between threads.
Yes, I agree that batching data should be a little more efficient. Would you advise an array (with microphone being generic over N; or the size being hardcoded)? Or a vec with the length set to the value (or half the value) of the buffer size? And should On another note I forgot to ask this earlier but my current mpsc implementation uses |
Sure, that's what channels do under the hood. I'm not saying you should copy the #799 approach directly (we closed it because we don't need the non-
@yara-blue will be able to provide more guidance for compatibility with the future direction of Rodio. I just wanted to add that working with Rodio's architecture is all about iterating over single samples (although "producers" should guarantee that they are always frame-aligned; i.e. zero-pad when a mid-stream interruption occurs). Keeping internal buffers with full chunks as kind of a double-buffer scheme could work, but likely extends well beyond the scope of this PR. It would be really nice to get support on such a topic though.
I propose that it silently drops. This will facilitate the use case where the sink is stalled e.g. due to heavy CPU load, and when it recovers, it immediately gets "live" data again. |
Pretty much, though what we ideally have is an spsc. Rust's mpsc channel has to It is more complex so if we can get 90% of the way there with a simple channel
I'm gonna answer these out of order. For the forseeable future Rodio will keep extending I think an array of 16 seems like a reasonable minimum. At 44khz that's 0.3 ms
Good question! As usual agree with Roderick, we should definitely silently drop.
Mhm.. not sure, ALSA (the linux audio API) force prints on under-run (an |






The microphone functionality added recently is very useful but for some use cases, the hard-coded 5ms poll interval is a limitation.
This makes the interval configurable, as suggested by @roderickvd in the original PR.