roc-toolkit
roc-toolkit copied to clipboard
ALSA sink and source
Intro
Currently we use SoX sink and source to interact with all audio systems except PulseAudio.
SoX backend is good at portability and the variety of drivers it supports, but it also has several limitations and problems that affect us:
- it does not allow to configure I/O latency;
- there are ALSA underruns from time to time;
- there are unnecessary sample format conversions;
- there is no way to pause and resume the device (we will need it in future);
- there is no way to perform rewinds (we probably will need it in future).
So the plan is to keep SoX as the fallback backend for exotic audio systems, and implement native backends for most used audio systems: PulseAudio, ALSA, CoreAudio.
This task is for implementing native ALSA backed.
Task
We should add three new classes:
- sndio::AlsaBackend
- sndio::AlsaSink
- sndio::AlsaSource
Then we should register AlsaBackend in sndio::BackendDispatcher. We can use PulseaudioBackend and PulseaudioSink as an example. BackendDispatcher should prefer AlsaBackend over SoxBackend for ALSA devices (if ALSA support is enabled at compile time).
These classes should be added to roc_sndio/target_alsa directory.
AlsaSink and AlsaSource should allow to configure I/O latency (just like PulseaudioSink does). More advanced features, like pause/resume and rewinds, are not needed for now.
We can start with implementing AlsaBackend and AlsaSink. We can add AlsaSource later, it's less critical.
Implementation
The code for ALSA setup and I/O may be found here: https://github.com/gavv/signal-estimator/tree/master/src
See AlsaUtils, AlsaWriter, and AlsaReader classes there. We can just adopt these classes to Roc interfaces.
There is also another snippet available, along with an explanatory blog post.
Additional info
- #251 provides an overview of roc_sndio module concepts
- #246 provides detailed instructions on adding a new backend
This feature is important since we need it to achieve a low IO latency on major audio drivers.
I'll take a stab at this. Also in case anyone was looking for more information/ explanation on some of the examples I found this helpful link, https://stackoverflow.com/questions/24040672/the-meaning-of-period-in-alsa, the embedded links are especially useful.
Great!
Hi Thomas, how is it going? Do you still have plans to work on this?
Thomas mailed me that he has no time for this now, so this issue if free again.
Hello, looking into this at the moment.
@davex25 Hi, do you still have plans on this?
Unassigning this again so that someone could pick it up. @davex25 feel free to ping me if you'll decide to continue working on this.
Hello gavv, Can I work on this?
In case it's of any use to you, here was my basic implementation of it, which I never made pull-request worthy. I had also created a JACK source and sink, not sure if I ever pushed that.
https://github.com/davex25/roc/commit/06e2ab6aa432a8c66696a7cec4f787618c5fde2c
@enigmaro sure, you're welcome!
@gavv To set hardware parameters for the AlsaSink can I adopt default parameter values from the config here? In particular just the format, access and periods.
Format and access should correspond to our internal representation, to avoid unnecessary conversions (sample_t and Frame). We use PCM with native-endian interleaved floats.
Latency should be configured by --io-latency option.
As for the number of periods, we can start from hardcoding it to 2 and see whether it's a good universal value. If we find cases where a different number of periods can provide better performance (smaller minimum latency w/o glitches), we can try different default value, or calculate it, or as the last resort make it configurable.