-
Notifications
You must be signed in to change notification settings - Fork 365
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
Pulse Audio support #259
Comments
Mainloop not being thread safe and including an RC is quite a pain. Implementation in the case that there are several possible reader threads (I have this use case) will require some form of thread parking or possibly scoped threads to hold a mainloop for any length of time. Multiple contexts seems bad style. Basically to make non-blocking calls from an application, you have to poll or implement callbacks into your own event loop mechanism and still pseudo-serialize the calls to pulse or implement a state tracking API around the API 🐚 🐚 🐚 |
Technically, when That said, I definetly see the value of talking to Pulse directly without going through asla-lib, becuase it adds just another layer of bugs and latency. |
Is "it" Cpal or ALSA? I read the wiki but did not come away understanding an obviously related set of implications. It seems implications could exist both for extending CPAL or configuring PA & ALSA so that CPAL uses PA without libpulse. On my config, playback is exclusive to either ALSA & cpal or Pulse & all other programs can play at one time. I'm interested in exploring and documenting the topic of how the playback device, ALSA, and PA can interact particular to my use case, but would like to verify what end states I should be trying to reach. |
Here's the source of the plugin for more context: https://github.com/alsa-project/alsa-plugins/tree/master/pulse This is how it works in Ubuntu 18.04, and probably what you'd like to setup to test: Obviously, using pulse directly is less bug-prone: That that's not supported by Currently, we're suffering from a weird bug that occurs at Ubuntu 18.04 - see #215 - and it seems it's due to that long chain. For me it blocks shipping of the app for linux users, but I weren't able to find a solution or workaround so far. |
Wouldn't using ALSA instead of Pulse be preferable, though? Systems with PulseAudio are typically configured to use the alsa plugin, while systems without PulseAudio only have alsa, so software using alsa for output would have the benefit of working on both kinds of systems. |
It is reasonable to assume that - after all it's less maintenance that way.
That said, implementing pulse backend also has it's downsides:
In my view it's worth it though because:
|
The beep demo experiences erratic popping artifacts on my system unless I manually turn up pulse's latency. Other software on my system, generally using pulse natively, doesn't seem to have this problem, so native pulse support seems like it would have good chances of fixing it. e: Actually, setting the latency did not solve the problem; the artifacts are very erratic so it's hard to tell. |
@Ralith is there any chance that there were any applications streaming audio to/from Pulse Audio while you ran the beep example? I believe the default ALSA API (without any plugins) expects the user to have exclusive access to the device, so it's common to get glitches if there is some other stream open accessing the same device via Pulse Audio for example. It can be incredibly difficult to test this as Pulse Audio is really good at magically starting again any time some application wants to play a noise (e.g. even desktop or browser notifications). Pulse Audio support would be great and is pretty essential to get CPAL used in day-to-day Linux applications, but we must implement something along the lines of #204 to allow the user to select between multiple available APIs on a single system (e.g. ALSA/Pulse/Jack). The ASIO PR #221 makes a start at this in order to allow for selecting between WDM or ASIO on Windows. |
I don't think so, though it's hard to be certain. #272 indicates that the issue exists on other platforms, too, so perhaps it's not anything to do with pulse/alsa. |
I would be surprised if both those cases were the same problem as almost everything other than the API calls are implemented differently for each backend (e.g. I don't believe there is any looping or timing code shared by the backends).
Yeah, I'm using GNOME and in the short time I've had to test I haven't yet worked out a way to confidently and consistently disable pulseaudio without some random background application or notification turning it on again, I think the only way to test for sure is to log out of the DE entirely. I'm not sure if it is the same case for other DEs. |
I successfully reproduced the popping with pasystray reporting no clients other than the demo. |
Are you able to test ALSA without using pulseaudio at all - e.g. completely disabling it? And making sure that the CPAL application is the only application accessing the audio device? I'm still not convinced it isn't some conflict between pulseaudio access to the device and some (perhaps incorrect) access to the device from the CPAL implementation. |
Reproduced the popping with pulse disabled ( |
This is an implementation of the API described at RustAudio#204. Please see that issue for more details on the motivation. ----- A **Host** provides access to the available audio devices on the system. Some platforms have more than one host available, e.g. wasapi/asio/dsound on windows, alsa/pulse/jack on linux and so on. As a result, some audio devices are only available on certain hosts, while others are only available on other hosts. Every platform supported by CPAL has at least one **DefaultHost** that is guaranteed to be available (alsa, wasapi and coreaudio). Currently, the default hosts are the only hosts supported by CPAL, however this will change as of landing RustAudio#221 (cc @freesig). These changes should also accommodate support for other hosts such as jack RustAudio#250 (cc @derekdreery) and pulseaudio (cc @knappador) RustAudio#259. This introduces a suite of traits allowing for both compile time and runtime dispatch of different hosts and their uniquely associated device and event loop types. A new private **host** module has been added containing the individual host implementations, each in their own submodule gated to the platforms on which they are available. A new **platform** module has been added containing platform-specific items, including a dynamically dispatched host type that allows for easily switching between hosts at runtime. The **ALL_HOSTS** slice contains a **HostId** for each host supported on the current platform. The **available_hosts** function produces a **HostId** for each host that is currently *available* on the platform. The **host_from_id** function allows for initialising a host from its associated ID, failing with a **HostUnavailable** error. The **default_host** function returns the default host and should never fail. Please see the examples for a demonstration of the change in usage. For the most part, things look the same at the surface level, however the role of device enumeration and creating the event loop have been moved from global functions to host methods. The enumerate.rs example has been updated to enumerate all devices for each host, not just the default. **TODO** - [x] Add the new **Host** API - [x] Update examples for the new API. - [x] ALSA host - [ ] WASAPI host - [ ] CoreAudio host - [ ] Emscripten host **Follow-up PR** - [ ] ASIO host RustAudio#221 cc @ishitatsuyuki more to review for you if you're interested, but it might be easier after RustAudio#288 lands and this gets rebased.
This is an implementation of the API described at RustAudio#204. Please see that issue for more details on the motivation. ----- A **Host** provides access to the available audio devices on the system. Some platforms have more than one host available, e.g. wasapi/asio/dsound on windows, alsa/pulse/jack on linux and so on. As a result, some audio devices are only available on certain hosts, while others are only available on other hosts. Every platform supported by CPAL has at least one **DefaultHost** that is guaranteed to be available (alsa, wasapi and coreaudio). Currently, the default hosts are the only hosts supported by CPAL, however this will change as of landing RustAudio#221 (cc @freesig). These changes should also accommodate support for other hosts such as jack RustAudio#250 (cc @derekdreery) and pulseaudio (cc @knappador) RustAudio#259. This introduces a suite of traits allowing for both compile time and runtime dispatch of different hosts and their uniquely associated device and event loop types. A new private **host** module has been added containing the individual host implementations, each in their own submodule gated to the platforms on which they are available. A new **platform** module has been added containing platform-specific items, including a dynamically dispatched host type that allows for easily switching between hosts at runtime. The **ALL_HOSTS** slice contains a **HostId** for each host supported on the current platform. The **available_hosts** function produces a **HostId** for each host that is currently *available* on the platform. The **host_from_id** function allows for initialising a host from its associated ID, failing with a **HostUnavailable** error. The **default_host** function returns the default host and should never fail. Please see the examples for a demonstration of the change in usage. For the most part, things look the same at the surface level, however the role of device enumeration and creating the event loop have been moved from global functions to host methods. The enumerate.rs example has been updated to enumerate all devices for each host, not just the default. **TODO** - [x] Add the new **Host** API - [x] Update examples for the new API. - [x] ALSA host - [ ] WASAPI host - [ ] CoreAudio host - [ ] Emscripten host **Follow-up PR** - [ ] ASIO host RustAudio#221 cc @ishitatsuyuki more to review for you if you're interested, but it might be easier after RustAudio#288 lands and this gets rebased.
This is an implementation of the API described at RustAudio#204. Please see that issue for more details on the motivation. ----- A **Host** provides access to the available audio devices on the system. Some platforms have more than one host available, e.g. wasapi/asio/dsound on windows, alsa/pulse/jack on linux and so on. As a result, some audio devices are only available on certain hosts, while others are only available on other hosts. Every platform supported by CPAL has at least one **DefaultHost** that is guaranteed to be available (alsa, wasapi and coreaudio). Currently, the default hosts are the only hosts supported by CPAL, however this will change as of landing RustAudio#221 (cc @freesig). These changes should also accommodate support for other hosts such as jack RustAudio#250 (cc @derekdreery) and pulseaudio (cc @knappador) RustAudio#259. This introduces a suite of traits allowing for both compile time and runtime dispatch of different hosts and their uniquely associated device and event loop types. A new private **host** module has been added containing the individual host implementations, each in their own submodule gated to the platforms on which they are available. A new **platform** module has been added containing platform-specific items, including a dynamically dispatched host type that allows for easily switching between hosts at runtime. The **ALL_HOSTS** slice contains a **HostId** for each host supported on the current platform. The **available_hosts** function produces a **HostId** for each host that is currently *available* on the platform. The **host_from_id** function allows for initialising a host from its associated ID, failing with a **HostUnavailable** error. The **default_host** function returns the default host and should never fail. Please see the examples for a demonstration of the change in usage. For the most part, things look the same at the surface level, however the role of device enumeration and creating the event loop have been moved from global functions to host methods. The enumerate.rs example has been updated to enumerate all devices for each host, not just the default. **TODO** - [x] Add the new **Host** API - [x] Update examples for the new API. - [x] ALSA host - [ ] WASAPI host - [ ] CoreAudio host - [ ] Emscripten host **Follow-up PR** - [ ] ASIO host RustAudio#221 cc @ishitatsuyuki more to review for you if you're interested, but it might be easier after RustAudio#288 lands and this gets rebased.
This will be obsolete soon. I recommend to invest the effort into PipeWire instead (#554). |
Considering the ugly technical challenges with PulseAudio's design (which is why it is being deprecated) discussed above and that PulseAudio will be obsolete soon, I suggest to close this issue in favor of PipeWire #554. |
I am not sure if that's a good idea, since not everyone will jump on Pipewire from day one. The question is, what kind of software era this lib wants to support, and if it is anything before "tomorrow" and "cutting edge", then PulseAudio is a must-have in my book. |
If there were unlimited labor available, sure, support the legacy API. But there is not, so let's focus on PipeWire. By the time that is done and robust, PulseAudio will be on the way out. Also, the end result will be much better with PipeWire than PulseAudio and it will probably be easier to implement PipeWire with CPAL's architecture. |
Bear in mind that the existing generally-functional alsa backend works decently on top of pulse. The question is whether we need to go to the effort of developing and maintaining a specialized backend in addition to that. |
That depends on the application. A simple music player application that only needs to output a stereo signal to whatever is configured as the default output in PulseAudio can make do with that. But creative applications that require user control of multichannel routing to arbitrary destinations cannot choose the devices used with the |
This issue is about PulseAudio, not PipeWire. As a user of this library, I would want this to work with Pulse now AND PipeWire in the future. They are separate things. (As an aside, I have zero interest in using PipeWire in the next 10 years.) |
Hi, I've implemented the PulseAudio protocol in rust, continuing a previous abandoned effort: https://github.com/colinmarc/pulseaudio-rs/ Playback is working - here's an example: https://github.com/colinmarc/pulseaudio-rs/blob/main/examples/playback.rs If there's interest from the maintainers, I'd be willing to contribute an adapter to integrate it into cpal. As far as the pulse/pw question goes, I think the Pulse API is actually much simpler and easier to use, at least for simple playback, and PipeWire has an embedded pulse server (my library has integration tests that run against both the actual PulseAudio daemon and Please let me know your thoughts! |
I think pipewire considers pulse clients first-class, so that's a fine approach for the foreseeable future, and surely an improvement over the status quo regardless. Thanks for taking this up, and I hope we can see it integrated! |
Tagging #250 regarding the sibling Linux sound server. I'm about to release a program under LGPL3 to which I own the full copyright (for now), but I don't want to discourage anyone from harvesting any bits and pieces in the name of getting all sound server API's under one roof, so I released an MIT licensed gist.
The main value of this gist for consumption is having a bit more abstract example than the documentation libpulse. Likely I'm going to overhaul it in the owning repo within the month.
My goal was not to get down to the rust libpulse bindings sys crate or talk directly to the server, but must mention that I quickly developed strong antipathy for the mainloop threaded API, which is inherited from the C client. Possibly the client-server interaction scheme has a deep pathological will to make pseudo-synchronous operations rely on libpulse's behavior and polling loop. Attempting to circumvent libpulse may involve worse hazards than managing them.
Playing directly to ALSA is a bad idea on Linux as it takes exclusive access to devices, and it's a bit weird to create monitors to listen to what other programs play.
The text was updated successfully, but these errors were encountered: