Skip to content
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

Add PulseAudio backend to SuperCollider #11

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

llloret
Copy link
Member

@llloret llloret commented Jul 3, 2020

Summary:
On some Linux systems Jack is either not available or difficult to configure.
Users and distribution maintainers should have the option to use a PulseAudio backend instead.
This RFC is about adding a new PulseAudio backend to SuperCollider.
Jack will remain the default choice since it has technical advantages.

@llloret llloret changed the title Create RFC for pulseaudio (PulseAudio backend for SuperCollider) Add PulseAudio backend for SuperCollider Jul 3, 2020
@llloret llloret changed the title Add PulseAudio backend for SuperCollider Add PulseAudio backend to SuperCollider Jul 3, 2020
@dyfer
Copy link
Member

dyfer commented Jul 3, 2020

At some point I was interested in the option to use SC on Linux without JACK. I haven't tried but I was wondering whether building with PortAudio would allow to achieve that? (I'm not a primarily a Linux user and I haven't followed any possible PortAudio - PulseAudio incompatibilities).

@jamshark70
Copy link

jamshark70 commented Jul 3, 2020

I haven't tried but I was wondering whether building with PortAudio would allow to achieve that?

If I understand correctly, the currently experimental PortAudio backend would connect directly to ALSA. For the casual user (e.g. somebody who is interested in live coding but who is not interested in esoteric Linux configuration, who installed TidalCycles or FoxDot, and then found out that they have to do extra stuff, potentially complex stuff, to get any sound at all), ALSA configuration is not simpler than JACK configuration. So PA does (potentially) have the benefit of a shorter sequence between installation and making noise.

I think a PA backend would also require the server devices primitive in sclang. Currently devices is not implemented in Linux because it's irrelevant for JACK. But PA does maintain a list of input and output devices, so a PA-enabled SC build should maintain cross-platform compatibility in this area.

The factors as I see them are the maintenance burden (which I think will be higher than the estimate in this RFC but probably not terribly high) vs the support cost of the bug reports filed by Tidal/FoxDot/Sonic Pi users who can't get sound right away. The latter would be very very nice to resolve and good public relations for the SC community, so I'd lean in favor of having the option of a SC/PA build that might be easier to bundle with various live coding environments.

@dyfer
Copy link
Member

dyfer commented Jul 3, 2020

If I understand correctly, the currently experimental PortAudio backend would connect directly to ALSA.

As I've dug a little into this, it seems PortAudio -> PulseAudio is possible through the ALSA compatibility layer (albeit not an ideal case), based on this report.

I think a PA backend would also require the server devices primitive in sclang. Currently devices is not implemented in Linux because it's irrelevant for JACK.

I think this should already be implemented - I've added PortAudio device listing to be used on Windows (since 3.11). This code is enabled based on backend choice, and not on the platform itself, so if PortAudio backend is chosen, sclang should also use PA for listing devices.

Looking up "portaudio pulseadio" in a search engine brings out https://github.com/bkgood/portaudio-pulseaudio
This repo seems outdated, I wonder if there are more recent attempts to include PulseAudio host API in PortAudio?

@grammoboy2
Copy link

The analyses of the problem isn't very good I think. Tell me on which Linux distribution JACK is not available please.

Configuration isn't that hard as well, with the good resources.

Ardour uses ALSA as default now, but it has it's focus on plugins, not external programs. Pulseaudio is a option now too maybe, for cheap (bluetooth?) devices? But afaik that's not for serious work as what you would expect with SuperCollider. But at least you can ask the Ardour team on advise.

Pipewire seems to want to bridge the different audio sytems on Linux: https://pipewire.org/

I'm a happy JACK user though.

@sonoro1234
Copy link

Having portaudio on Linux or Jack on windows is mostly done, only a few tweaks are needed for that.

Another path to consider is to add an RtAudio backend which works on Linux, Window and Macintosh OS X.
https://github.com/thestk/rtaudio
Description:
A set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound, ASIO, and WASAPI) operating systems.

@llloret
Copy link
Member Author

llloret commented Jul 4, 2020

Thank you for the feedback. Just some comments below.

Another path to consider is to add an RtAudio backend which works on Linux, Window and Macintosh OS X.
https://github.com/thestk/rtaudio

I had considered going the rtaudio route, but I thought a direct native approach would have more perfrmance. For example, the rtaudio pulseaudio is using the "simple" API on pulseaudio, not the full async one.

Having portaudio on Linux or Jack on windows is mostly done, only a few tweaks are needed for that.
Can you elaborate on this? I mean, I have a basic proof of concept of PulseAudio working now, and it was not rocket science, but it was not trivial either, in the sense that I had to research the PulseAudio API. Was there a simpler way that I missed?

@llloret
Copy link
Member Author

llloret commented Jul 4, 2020

@jamshark70 , thank you for your feedback. Yes, I take it there might be some more work to do on other fronts as well, but in general this seems quite well contained. I am happy to make the necessary changes in sclang or other places, with a bit of guidance from someone that knows those areas.

I think you hit the nail in the head with how much this would help other communities that are using SuperCollider as the backend. These people are normally not SC experts, nor audio experts, nor linux experts, so configuring things that do not work out of the box is a big barrier of entry for them. That's why I believe such a PulseAudio backend will help spread SC in those places. And according to the basic proof of concept I have, supporting a basic configuration would not be that difficult.

@llloret
Copy link
Member Author

llloret commented Jul 4, 2020

@grammoboy2 , thanks for your comment.

The thing is that there are Linux distributions where using Jack involves making changes to the system, it does not work out of the box. Now, for someone tech-savvy, those things probably amount to nothing, but for the majority of people using those systems, it can be something next to impossible to do.

The idea is not to remove Jack and use PulseAudio, this is about giving the chance to use PulseAudio on the systems where Jack is hard to configure and use. So for people like you, which are happy with Jack, there will be no change at all. To reiterate, this just to lower the barrier of entry to SuperCollider (and SW that uses SC as a backend), on systems where Jack is hard.

@llloret
Copy link
Member Author

llloret commented Jul 4, 2020

Just an example: On Ubuntu, the default audio backend is PulseAudio, and while you can use Jack, from what I know, configuring it is not straightforward for someone that is not tech-oriented. Keep in mind that a lot of people using Ubuntu and related systems are not experts, they are just everyday users that want to get some work (or music ;)) done. If we could give them a user experience where they could just start using SuperCollider, Tidal, Sonic Pi, whatever, without having to go hunting around forums for how to use Jack in there, I believe the SuperCollider adoption would be quite higher.

@sonoro1234
Copy link

sonoro1234 commented Jul 4, 2020

I had considered going the rtaudio route, but I thought a direct native approach would have more perfrmance. For example, the rtaudio pulseaudio is using the "simple" API on pulseaudio, not the full async one.

The best thing with RtAudio is that it is very alive and responsive repo: thestk/rtaudio#213 and other issues and additions. And given that portaudio is not an alive project it could break in the near future so that RtAudio would be a good replacement that unifies the three major OSes.

Perhaps a longer but very productive path (for all the opensource community) would be: apply your async PulseAudio knowledge to extend RtAudio and then make an RtAudio backend to SC?

Can you elaborate on this? I mean, I have a basic proof of concept of PulseAudio working now, and it was not rocket science, but it was not trivial either, in the sense that I had to research the PulseAudio API. Was there a simpler way that I missed?

I can only elaborate for Jack on windows : supercollider/supercollider#3692
For portaudio on linux I only meant portaudio on Linux, may be not PulseAudio via portaudio.

@llloret
Copy link
Member Author

llloret commented Jul 5, 2020

@sonoro1234, I think what I'll do is finish my simple proof of concept using pure pulseaudio and then try using it through rtaudio, and then see which one looks better

@llloret
Copy link
Member Author

llloret commented Jul 9, 2020

Hi, so I think I have got this in a mature enough state in my branch, and the code is ready for a Pull Request. I have asked around in the dev channel, but mention it here too.
Is it ok to do the proper Pull request in the SuperCollider repository to start having a proper look at it?
@sonoro1234 , you might be interested to know that in the end I went the rtaudio route, because I liked the API so much more, and it is much more maintainable than the raw pulseaudio C based API.

@cianoc
Copy link

cianoc commented Jul 9, 2020

I think making SuperCollider more widely available is a good thing, but I worry that PulseAudio users could mistake the poor performance of PulseAudio for SuperCollider problems. Once you get beyond the simple kinds of livecoding use cases then users will likely run into timing, latency and dropout issues. And while you could do a live gig using Pulse Audio, it's probably not the greatest idea.

Maybe some documentation could be added both in the downloads/install section of the website, and in the SuperCollider documentation that makes it clear that if the user is experiencing performance issues, and they're using the JACK version, then they should switch to the JACK version.

@sonoro1234
Copy link

you might be interested to know that in the end I went the rtaudio route, because I liked the API so much more, and it is much more maintainable than the raw pulseaudio C based API.

Thats great!!. So if I correctly understand SC has now a RtAudio interface (not only for PulseAudio).
Did you implemented the async PulseAudio API for RtAudio or just used the already existent one?

@llloret
Copy link
Member Author

llloret commented Jul 10, 2020 via email

@sonoro1234
Copy link

But yes, in the future it should be very simple to use this same RtAudio implementation for Windows and Mac

Or may be also in Linux with OSS, ALSA or Jack.

@llloret
Copy link
Member Author

llloret commented Jul 10, 2020

Yes, perhaps. But for now, I prefer not to touch the jack backend at all, since that will require much more thought and consensus within the group.

I want to keep this specific RFC very focused on providing an alternative pulseaudio backend on systems where jack is hard to set up. Not more, not less.

That it might perhaps be used in the future to consolidate work (and ease maintenance), is a bonus, but I don't want to center the discussion around that.

llloret added a commit to llloret/supercollider that referenced this pull request Jul 10, 2020
For a discussion of why we should have a PulseAudio backend for
SuperCollider on Linux, see supercollider/rfcs#11.

It is important to understand that this is not meant to be a replacement
for jack at all. This is just to give an option on systems where jack is
difficult to set up. jack will continue to be the default.

This PR adds support for PulseAudio, both inputs and outputs. The device
selection is done outside SC, as per PulseAudio philosophy, where
applications output to a default sink, and get input from a default
source, and the actual connections happen on the system's setup /
PulseAudio control applications.

The changes are pretty self contained, with just one C++ file added, and
then some changes to CMakeLists and READMEs as required.

The selection of frontend is done at build time, and by default the
backend will continue to be jack. This will only try to use the
PulseAudio backend if instructed explicitly to do so, using the AUDIOAPI
cmake variable. So, for users that are currently happy with their setup,
using jack, this change will not affect them at all and will be
completely transparent.

On systems where it is difficult to use jack, the user / maintainer can
set the cmake variable to pulseaudio, and then, and only then, it will
use the PulseAudio backend.

As you can see the code is not too complex, and should be easy to
maintain. Note that the expectation is that the PulseAudio backend will
be used in simple-ish systems, so we may not want to support complex /
exotic audio configurations, and encourage people to use jack for that.
@sonoro1234
Copy link

That it might perhaps be used in the future to consolidate work (and ease maintenance), is a bonus, but I don't want to center the discussion around that.

Yes, of course, in the future

@patrickdupuis
Copy link

I support making PulseAudio usable for people who can't install and configure JACK on their system. Assuming of course that performance is reasonably acceptable, that we clearly communicate PulseAudio as an experimental feature, and that we do our utmost to help users make use of JACK by way of documentation and community support.

@mossheim
Copy link
Contributor

Hey @llloret , just read the RFC and this discussion, i'm definitely in support of this. I have a few questions and concerns for you and for some other people in this thread.


First, i think this should be documented in the RFC text: on which systems can Jack not be obtained, and on which systems is it difficult to configure (and why?). you've answered that here but it's a big question mark for me when i first read the RFC.


I have concerns about using rtaudio for this, after seeing the comment in this issue:

The Pulse support in RtAudio has been fairly minimal. It was contributed by a user and has never been extensively investigated by myself or others to bring it up to what I might consider "full" support. The ALSA support, on the other hand, is (or at least was) robust and includes "realtime" functionality that was tested and verified.

The PulseAudio backend uses mutexes in the audio generation callback, which I've heard is unacceptable in real-time audio. It would probably take days for me to understand RtAudio, understand why the mutexes are needed, and remove them. So I'll focus on the latency for now. OpenMPT's PulseAudio support doesn't use mutexes, but tends to lock up the entire program.

even if rtaudio is responsive, i'm not sure i trust their implementation at the moment. i think i'd recommend to use PulseAudio's API directly. to quote from my comment on the PR:

libsoundio appears to be abandoned (latest commit 14 August 2018), so i would not prefer it over rtaudio (latest commit 7 June 2020) just based on that fact alone.

i wasn't really able to find any other library that [wraps pulseaudio].

is the main issue with using the PulseAudio API directly the asynchronicity in some callbacks? or were there more issues @llloret ? i'd consider whether we're just causing trouble for ourselves later on here by relying on another project's experimental work, when we might be able to make patches to our backend more easily and reliably ourselves.

@llloret
Copy link
Member Author

llloret commented Jul 13, 2020

Hey @llloret , just read the RFC and this discussion, i'm definitely in support of this. I have a few questions and concerns for you and for some other people in this thread.

First, i think this should be documented in the RFC text: on which systems can Jack not be obtained, and on which systems is it difficult to configure (and why?). you've answered that here but it's a big question mark for me when i first read the RFC.

I have concerns about using rtaudio for this, after seeing the comment in this issue:

The Pulse support in RtAudio has been fairly minimal. It was contributed by a user and has never been extensively investigated by myself or others to bring it up to what I might consider "full" support. The ALSA support, on the other hand, is (or at least was) robust and includes "realtime" functionality that was tested and verified.

The PulseAudio backend uses mutexes in the audio generation callback, which I've heard is unacceptable in real-time audio. It would probably take days for me to understand RtAudio, understand why the mutexes are needed, and remove them. So I'll focus on the latency for now. OpenMPT's PulseAudio support doesn't use mutexes, but tends to lock up the entire program.

even if rtaudio is responsive, i'm not sure i trust their implementation at the moment. i think i'd recommend to use PulseAudio's API directly. to quote from my comment on the PR:

libsoundio appears to be abandoned (latest commit 14 August 2018), so i would not prefer it over rtaudio (latest commit 7 June 2020) just based on that fact alone.
i wasn't really able to find any other library that [wraps pulseaudio].
is the main issue with using the PulseAudio API directly the asynchronicity in some callbacks? or were there more issues @llloret ? i'd consider whether we're just causing trouble for ourselves later on here by relying on another project's experimental work, when we might be able to make patches to our backend more easily and reliably ourselves.

@brianlheim, thanks for the feedback. Let me try to address your concerns. In the testing I have done so far, I have not found any issues regarding stability, dropouts, etc. Yes, there is a mutex in the audio callback, but the only contention points are in calls related to starting, stopping or aborting the stream, which is something that we are not doing while streaming, so I am not too worried about that. The audio callback will be able to fetch the mutext instantly every time it needs to. If we went the direct pulseaudio way, it is very possible we would have to implement the same mutexes to make sure that it does not break while you are starting or stopping the stream.

On things about performance, I tend to believe that the proof is trying and testing. I don't think that mutex there will hurt at all.

Also, my initial concern about RtAudio using the "simple" pulseaudio API turned out to be unfounded. That API is enough to deal with one stream (multi-channel in and out) of audio per application, which is all that we need. I was confused by the meaning of one stream, thinking it was just in OR out, but it turns out it can do both.

What I would suggest is people try the current implementation and see how well it is working for them.

@llloret
Copy link
Member Author

llloret commented Jul 13, 2020

I support making PulseAudio usable for people who can't install and configure JACK on their system. Assuming of course that performance is reasonably acceptable, that we clearly communicate PulseAudio as an experimental feature, and that we do our utmost to help users make use of JACK by way of documentation and community support.

That's brilliant. Really glad to hear that.

@mossheim
Copy link
Contributor

yes, yaml-cpp is an example of this.

@sonoro1234
Copy link

I cant understand the motivation of package maintainers for wanting us to take care of devendored dependency when we are not touching installation but just use vendored dependency for statically linking. What I am missing?

@jamshark70
Copy link

jamshark70 commented Jul 15, 2020

is this something that could be solved by providing clearer documentation and/or runnable post-installation scripts? if not, why?

Going from memory, the steps include:

  • Adding a group with raised RT privileges.
  • Adding the current user to that group.
  • Setting a memory limit in a configuration file (AFAIK this can be done only by editing the file by hand; I forget which file).
  • Installing JACK modules for PulseAudio.
  • Configuring PulseAudio and/or JACK to hook up automatically.

Much of this could be scripted, but if something goes wrong, AFAICS the fallback is manual. I'm not intimate enough with shell scripting to know how robust a script would be. (Even installing JACK itself -- jack1 and jack2 are mutually incompatible; if our script mandates one and not the other, it can cause serious dependency hell in some environments.)

If we could be reasonably sure a script would be reliable in a large percentage of environments, I'd be ok with that too.

@mossheim
Copy link
Contributor

@sonoro1234 , @dvzrv is usually the one to request devendoring so i will let him answer in more detail. regardless, i do not want this dependency in our codebase at all, at least when there are viable alternatives. i am saying this as the person who is typically coordinating and managing our vendored dependencies.

@llloret
Copy link
Member Author

llloret commented Jul 15, 2020

is this something that could be solved by providing clearer documentation and/or runnable post-installation scripts? if not, why?

Adding some documentation, while certainly helping a number of users, will not help much the non tech oriented people that we would to like to help with this new backend. I am thinking about people that are not tech savvy at all, so that anything that is not working out of the box might be too much for them. I agree that they could perhaps run a simple script (if you can get them to find that script in the first place), but if things do not work as expected, then they are completely blocked and unable to use the SW. Apart from having to maintain those scripts on different platforms, it seems the kind of thing where we could potentially be getting lots of question about specific steps in such scripts not working as expected.
To reiterate, there seems to be already enough information on the web, to help tech-savvy users to make jack work in most Linux systems, this is about helping more users (that do not need all the power and flexibility of jack) to enjoy SuperCollider.
And keep in mind, a non-tech savvy user today, might be the expert implementing fabulous new SuperCollider features tomorrow... this is about lowering the barrier of entry to SuperCollider.

just from scanning the RFC and this conversation there seems to be some general agreement that jack is difficult to work with, but it's not clear to me why the next step is to switch to a different tool.

I think that the PulseAudio backend is the way to go in this regard, because it can offer a decent SuperCollider experience out of the box, that integrates nicely with what a lot of Linux distributions are working with regarding audio. Whether we like it or not, most Linux distributions are using PulseAudio as the audio backend, so offering something that works with that is a worthwhile goal, in my view.

Of course, we want users that have the skills to be able to continue using the jack backend, but adding the PulseAudio backend does not detract from that at all.

@mossheim
Copy link
Contributor

just wanted to leave a note that i haven't given up on this RFC, just kinda busy right now with non-SC related personal issues. this is at the top of my priority list when i can make time again!

@mossheim
Copy link
Contributor

mossheim commented Aug 7, 2020

coming back to this now, i'm of the opinion that (1) this should be runtime selected, and (2) should be achieved without any new vendored dependencies.

i'm going to use the term 'server-backend' to refer to what is called a "backend" in scsynth and supernova to disambiguate it from the other meanings of 'backend'.


the server-backend should be runtime selectable because package maintainers shouldn't have extra work to do, and users shouldn't have to build an alternate configuration themselves if the point is to provide options to technologically incapable users.


the PulseAudio server-backend should be implemented without any new vendored dependencies because it seems that would be an easy thing to do, and because maintainers don't want to manage more vendored dependencies. if rtaudio is not reliably obtainable from package managers, then i would recommend using something else or writing this server-backend directly against the PulseAudio API.

@jamshark70
Copy link

coming back to this now, i'm of the opinion that (1) this should be runtime selected, and (2) should be achieved without any new vendored dependencies.

FWIW I agree with both of these. Practically speaking, it won't be released otherwise.

@sonoro1234
Copy link

sonoro1234 commented Aug 8, 2020

if runtime selection is to be done, it will be a major change: SC_NewAudioDriver has to be redone returning a different audio_driver depending on command-line option, it would be a change letting choose portaudio-driver or jack-driver or native-driver in mac osx also (for example)

As for the vendoring issue I have not any new feedback from @dvzrv #11 (comment)

@jamshark70
Copy link

if runtime selection is to be done, it will be a major change

I suppose an alternative might be to ship two scsynth binaries in Linux.

The main points are: We can't ask novice users to build scsynth for PulseAudio, and we can't ask release managers to issue two separate releases for Linux. That means either one scsynth binary that can choose the backend at launch time, or two binaries (which could be just adding another build target).

I'm not sure it does have to be extended to Mac. The specific purpose is to allow Linux users to skip JACK installation and configuration. Adding a command line option for that purpose doesn't absolutely require implementing it in all OSes. (macOS users don't have to deal with RT privileges, thread priority and interfacing with non-JACK apps.)

@mossheim
Copy link
Contributor

mossheim commented Aug 8, 2020

adding a new target in the build system and getting all the logic and documentation around that right would seem to be comparable difficulty to making the scsynth-backend runtime selectable. in order to do the latter, as @sonoro1234 and @scztt have pointed out there's one function that has to change a bit and possibly take a new parameter, then update some uses of the preprocessor and make some changes in the build system. i'm happy to give people pointers on implementation and to review the PRs. the virtual class hierarchy to support this is already there, and it will be adding a feature several people already want. this is the solution we'd want long term, and all the alternative solutions proposed would require a similar amount of effort.

is it easier to do this only on Linux or on all platforms at once? not sure, that would probably be up to the implementer. i agree with @jamshark70 that it doesn't have to happen everywhere.

As for the vendoring issue I have not any new feedback from @dvzrv #11 (comment)

this thread has some context around the problem. we don't need to wait for him to reply here, because i'm saying it's not going to happen.

@sonoro1234
Copy link

we don't need to wait for him to reply here, because i'm saying it's not going to happen.

I understand that.
It is only that given that my question was not answered I still cant understand what I was asking in #11 (comment) which is the main reason for not using devendoring.

@dvzrv
Copy link
Member

dvzrv commented Aug 10, 2020

@sonoro1234 , @dvzrv is usually the one to request devendoring so i will let him answer in more detail. regardless, i do not want this dependency in our codebase at all, at least when there are viable alternatives. i am saying this as the person who is typically coordinating and managing our vendored dependencies.

Sorry for the late reply. I'm on too many bug trackers and tickets...

In regards to the vendored (aka. bundled) libraries I'll just refer to Fedora's excellent writeup on that topic: https://fedoraproject.org/wiki/Bundled_Libraries?rd=Packaging:Bundled_Libraries
Apart from that I have also seen upstreams starting to implement their own features for toolkits and libraries, basically turning their vendored dependency into a non-integrated fork (e.g. audacity or giada), sometimes making it impossible to use system versions of the libraries in question.
That being said: From an upstream maintenance point of view a clear downside of developing against the packaged versions of something like rtaudio is of course downstream version fragmentation. Looking at libpulse it seems that the margin is smaller though.
However, a clear window of supported versions can be documented and maintained using preprocessor macros (if even required).

In regards to the general notion of this pull request: I'm happy to see, that time is invested in an alternative backend for Linux, as portaudio as a project is pretty much dead.
Pulseaudio is surely the more "integrated" solution in comparison to jack (although that really depends on the setup).
I think that having a runtime selection of the backend should be the way to go though, because otherwise this will lead to packaging overhead to provide one scsynth for jack and one for pulseaudio. It's both confusing for packagers and users.

@llloret as you have spent some time with this, what made you choose rtaudio over directly implementing against libpulse? I'm curious (from an implementational point of view) as the direct dependants of libpulse outweigh the direct dependants of rtaudio by far (on Arch at least).

Also, pipewire lurks on the horizon. :)

@llloret
Copy link
Member Author

llloret commented Aug 11, 2020

coming back to this now, i'm of the opinion that (1) this should be runtime selected, and (2) should be achieved without any new vendored dependencies.

That's fine. I am happy with that. I will work with the system's rtaudio version, that seems to offer enough functionality at the moment, and will be improving in the future.

Regarding the build time vs launch time (I prefer to be specific about the selection being at launch time, not at runtime - we will not be supporting changing the backend while SC is already running, since that introduces what I think is unnecessary complexity - people will know what server backend they want to use at lauch time)... for what it's worth, I have done some prototyping about it, and it is doable witout too much new code. It would be easier if the selection is done with an environment variable, instead of with a program argument, because that way there is no need to pass that argument around the different SC layers, which I am not familiar with. Besides, having an environment variable might be an easier way for distributors to select the default backend. Thoughts?

@llloret
Copy link
Member Author

llloret commented Aug 11, 2020

@llloret as you have spent some time with this, what made you choose rtaudio over directly implementing against libpulse? I'm curious (from an implementational point of view) as the direct dependants of libpulse outweigh the direct dependants of rtaudio by far (on Arch at least).

I tried a pilot implementation with both libpulse and rtaudio and the rtaudio one felt more natural and maintainable. That's the main reason. Don't get me wrong, I think that the libpulse library is great, but using it directly introduces a number of complexities like having to convert from asynchronous operations to synchronous ones, which is quite tedious. rtaudio did this for me and covered all the functionality that we wanted to start with, so I went with it.

Another potential advantage (although I prefer to decouple that from this PR), is that in the future, it might allow us to use it for other OSs (for example Windows) is we feel it is good enough.

@mossheim
Copy link
Contributor

it should be a program argument. a couple reasons why:

  • environment variables have an action at a distance quality that i generally prefer to avoid because it causes confusion
  • environment variables are hard to explain to beginners and nontechnical people
  • the fact that it requires reorganizing code should be seen as a plus -- it keeps the architecture of the code clear and doesn't hide dependencies
  • almost all arguments to scsynth are taken in as program options

Besides, having an environment variable might be an easier way for distributors to select the default backend

i don't understand why that would be easier, but regardless the default scsynth-backend should remain jack, without qualification. that's what we've already agreed on, correct?

@dvzrv
Copy link
Member

dvzrv commented Aug 11, 2020

Besides, having an environment variable might be an easier way for distributors to select the default backend. Thoughts?

I would always prefer explicit implementation (i.e. a flag parameter to scsynth) over implicit implementation (i.e. an environment variable that may or may not be set). Also, environment variables really don't help too much (as a default), because there are many different desktop environments and/or window management systems in use. It's very hard to get that right and e.g. the desktop entry specification also only points at "the desktop environment" for setting environment variables. At this point this could be your shell, logind, or any of the various desktop environments.

However, environment variables as an additional way of setting the backend could be useful.

@llloret
Copy link
Member Author

llloret commented Aug 11, 2020

i don't understand why that would be easier, but regardless the default scsynth-backend should remain jack, without qualification. that's what we've already agreed on, correct?

Ok, so the default behaviour, which is when no environment variable is selected would of course be to run Jack, no changes there; this is one of the hard constraints of my design. But if a distribution (or a user), prefers to use pulseaudio, then it is a matter of setting that specific environment variable at a system level, and then users do not need to know that they have to launch with a specific flag on scsynth, which will make it easier to use out of the box for these users.

  • environment variables have an action at a distance quality that i generally prefer to avoid because it causes confusion

  • environment variables are hard to explain to beginners and nontechnical people

I generally agree, but keep in mind that there are environment variables already being used to control the behaviour on other parts of SuperCollider, so this would not go against the alrady existing ethos. People would not generally need to see this variables, the distributor could take care of that for them, right?

* the fact that it requires reorganizing code should be seen as a plus -- it keeps the architecture of the code clear and doesn't hide dependencies

The reorganizing needs to be done regardless of whether we end up controlling through a program argument or environment variables. What I do not like is that if it is a program argument, I believe there will be a need to carry that through a number of functions / methods, so that it reaches the instantiation of the specific backend, which will require a number of simple changes along the path. With an environment variable, we just need to check it in one place and there is no need to carry that information around.

* almost all arguments to scsynth are taken in as program options

As you said, almost all of them are taken as options; in this case I believe there is a compelling case to use an enviroment variable itself. It simplifies the use case that we are looking for (where a non technical user would not need to do anything special when launching), and it simplifies the implementation. This would also apply to 3rd party music applications that use SC in the background since the backend woul d be configured at the system level, without a need to check what flags need to be used to launch.

@llloret
Copy link
Member Author

llloret commented Aug 11, 2020

I would always prefer explicit implementation (i.e. a flag parameter to scsynth) over implicit implementation (i.e. an environment variable that may or may not be set). Also, environment variables really don't help too much (as a default), because there are many different desktop environments and/or window management systems in use. It's very hard to get that right and e.g. the desktop entry specification also only points at "the desktop environment" for setting environment variables. At this point this could be your shell, logind, or any of the various desktop environments.

So, would it not be possible to have something at the (for example) raspbian (or other) distribution that configured a given environment variable for terminals (including ssh) or desktop launches?

@sonoro1234
Copy link

My two cents:

We already have at least these environment variables:

SC_SYSAPPSUP_PATH
SC_JACK_DEFAULT_INPUTS
SC_JACK_DEFAULT_OUTPUTS
SC_SYNTHDEF_PATH

Other way setting the command line wouldnt be to much work: in scsynth_main we have

struct World* world = World_New(&options);
so a field should be added for this option in WorldOptions and World and World_New modified to set this option
Then we have SC_AudioDriver* SC_NewAudioDriver(struct World* inWorld) in several places (once for every existent driver). Most of them will remain unchanged but the one for RtAudio-PulseAudio and the one for Jack have to be unified returning one driver or the other regarding this option

@dvzrv
Copy link
Member

dvzrv commented Aug 11, 2020

So, would it not be possible to have something at the (for example) raspbian (or other) distribution that configured a given environment variable for terminals (including ssh) or desktop launches?

No, not every distribution applies extreme modification to everything they package (such as Debian and derivatives).
Is there a specific use-case that you have in mind? I can't see any compelling argument for using environment variables yet, especially when looking at a well documented entry point such as command-line parameters to scsynth (used to control everything scsynth related) in comparison to non-documented and hidden away environment variables (used for two obscure jack related features) so far.

We already have at least these environment variables:

SC_SYSAPPSUP_PATH
SC_JACK_DEFAULT_INPUTS
SC_JACK_DEFAULT_OUTPUTS
SC_SYNTHDEF_PATH

Especially in the case of SC_JACK_DEFAULT_INPUTS and SC_JACK_DEFAULT_OUTPUTS the documentation on the topic and the use-case of these variables is rather obscure. The variables are supposed to control auto-connection to ports on hardware interfaces. Unfortunately, this doesn't really work in certain use-cases (e.g. long identifier names due to UUIDs in the hardware name when using firewire interfaces - the string gets truncated and not all ports can be connected due to an implicit string length restriction).
In an ecosystem with tools such as new-session-manager for session management and various tools to remember and restore JACK connections (e.g. aj-snapshot, carla, njconnect, qjackctl) it is also disputable whether this is useful at all. The established connections are static and only set once during start of scsynth. Use-cases with dynamic connecting/disconnecting scenarios are completely left aside.

The *_PATH environment variables are hardly relevant in this discussion IMHO. PATH related environment variables can be found anywhere where further resources have to be located (e.g. LV2_PATH for lv2 plugins, etc.).

When looking at the scsynth help output we can see what are the current options in regards to audio hardware and scsynth configuration:

supercollider_synth  options:
   -v print the supercollider version and exit
   -u <udp-port-number>    a port number 0-65535
   -t <tcp-port-number>    a port number 0-65535
   -B <bind-to-address>
          Bind the UDP or TCP socket to this address.
          Default 127.0.0.1. Set to 0.0.0.0 to listen on all interfaces.
   -c <number-of-control-bus-channels> (default 16384)
   -a <number-of-audio-bus-channels>   (default 1024)
   -i <number-of-input-bus-channels>   (default 8)
   -o <number-of-output-bus-channels>  (default 8)
   -z <block-size>                     (default 64)
   -Z <hardware-buffer-size>           (default 0)
   -S <hardware-sample-rate>           (default 0)
   -b <number-of-sample-buffers>       (default 1024)
   -n <max-number-of-nodes>            (default 1024)
   -d <max-number-of-synth-defs>       (default 1024)
   -m <real-time-memory-size>          (default 8192)
   -w <number-of-wire-buffers>         (default 64)
   -r <number-of-random-seeds>         (default 64)
   -D <load synthdefs? 1 or 0>         (default 1)
   -R <publish to Rendezvous? 1 or 0>  (default 1)
   -l <max-logins>                     (default 64)
          maximum number of named return addresses stored
          also maximum number of tcp connections accepted
   -p <session-password>
          When using TCP, the session password must be the first command sent.
          The default is no password.
          UDP ports never require passwords, so for security use TCP.
   -N <cmd-filename> <input-filename> <output-filename> <sample-rate> <header-format> <sample-format>
   -H <hardware-device-name>
   -V <verbosity>
          0 is normal behaviour.
          -1 suppresses informational messages.
          -2 suppresses informational and many error messages, as well as
             messages from Poll.
          The default is 0.
   -U <ugen-plugins-path>
          A list of paths seperated by `:`.
          If specified, standard paths are NOT searched for plugins.
   -P <restricted-path>    
          if specified, prevents file-accessing OSC commands from
          accessing files outside <restricted-path>.

To quit, send a 'quit' command via UDP or TCP, or press ctrl-C.

I see no reason why not to add a flag for setting an audio backend there.

in this case I believe there is a compelling case to use an enviroment variable itself. It simplifies the use case that we are looking for (where a non technical user would not need to do anything special when launching), and it simplifies the implementation. This would also apply to 3rd party music applications that use SC in the background since the backend woul d be configured at the system level, without a need to check what flags need to be used to launch.

@llloret Still, someone has to configure this. I don't want to make the choice for all users of a distribution. Sorry, but environment variables (as the only option) for setting an audio backend makes no sense. Auto-detecting running backends or giving the user choice is.

To give a great example of a similar mess in regards to environment variables: By default jack2 is auto-started by whichever variant that is configured as default during compile-time (jackd or jack-dbus) when a jack client triggers it.
If a user starts an application that triggers auto-start, then it makes use of ~/.jackdrc, starting jack on some previously used interface (might not be the current and will definitely conflict with any user-defined setting).
If jack-dbus is used it gets even more confusing, as now an instance of jack-dbus is running and never exits (unless killed), blocking any use of using jackd (the executable). All this can be circumvented by setting the environment variable JACK_NO_START_SERVER to prevent clients from auto-starting JACK.
Now guess how many distributions or users for that matter make use of this environment variable and how many users report "jack doesn't work" until after hours of debugging you figure out the autostart "fun" was involved?

@mossheim
Copy link
Contributor

i agree 100% with what @dvzrv wrote, he said it much better than i could. perhaps we add an environment variable later if there's a demonstrated need, but for a first implementation a command-line option is a must-have.

@grammoboy2
Copy link

grammoboy2 commented Nov 26, 2020

FYI: Pipewire looks to become the near future on the Linux platform

Abstract:

PipeWire is a low-level multimedia library and daemon that facili-tates negotiation and low-latency transport of multimedia content be-tween applications, filters and devices. It is built using modern Linuxinfrastructure and has both performance and security as its core de-sign guidelines. The goal is to provide services such as JACK andPulseAudio on top of this common infrastructure. PipeWire is mediaagnostic and supports arbitrary compressed and uncompressed for-mats. A common audio infrastructure with backwards compatibilitythat can support Pro Audio and Desktop Audio use cases can poten-tially unify the currently fractured audio landscape on Linux desk-tops and workstations and give users and developers a much betteraudio experience.

https://lac2020.sciencesconf.org/307881/document

@salkin-mada
Copy link

FYI: Pipewire looks to become the near future on the Linux platform

Not to derail completely from the PulseAudio subject here. Or maybe I am :)
Just wanted to add this @grammoboy2.
I was recently presented with this

Latency
Latency is nowhere near the latency that jack2 can handle. The reason is that the the alsa driver will send smaller packets to the device (USB) when configured with a smaller period. Because PipeWire uses timer based scheduling, the period is configured as large as possible and latency suffers.
All latencies measured with jack_iodelay and a loopback cable on a U-Phoria UMC404HD usb card. Listed are total roundtrip latencies

PipeWire
  4096   186.8ms
  1024    47.6ms
   512    37.0ms
   128    24.5ms
    64    19.2ms
    32    15.7ms  <many xruns>

JACK
  4096   184.7ms
  1024    56.7ms
   512    35.2ms
   128    14.2ms
    64     8.0ms
    32     4.8ms

@salkin-mada
Copy link

i agree 100% with what @dvzrv wrote, he said it much better than i could. perhaps we add an environment variable later if there's a demonstrated need, but for a first implementation a command-line option is a must-have.

This would be so nice to have. A small pa flag so we can run scsynth on our termux sessions on android. Wuhu!

@brianlheim @dvzrv @llloret how is this coming along? Anything that can be done to help?

@jamshark70
Copy link

Latency is nowhere near the latency that jack2 can handle.

I think this a/ is expected and b/ doesn't matter for the proposed use case.

An alternate audio backend is for users who want to get sound out of scsynth quickly, with minimum configuration -- users testing out Tidal or FoxDot. These users don't have a commitment to attain low latencies -- and if their needs change ("how can I live code along with my guitar?"), then the advice should be "you really need JACK for this."

@grammoboy2
Copy link

As far as I understand it, Pipewire will solve this issue in about a year (if they can accomplish what they think they can). So people might not get the same latency as with JACK, but they don't have to launch JACK to work with SC anymore. PipeWire handles it for them. JACK and PulseAudio will coexist under Pipewire.

@smoge
Copy link

smoge commented Jan 19, 2024

I think that the proposal retains its relevance, although certain contextual elements have evolved:

  1. Pipewire has been the default audio framework in Linux distributions since some years, beginning with its integration as default audio back-end in Fedora in 2021.

  2. There has been improvement in latency performance with Pipewire. However, from a professional audio perspective, it does not yet match JACK's capabilities. Personally, I continue to use JACK2 for my audio needs.

See more recent latency benchmarks here : https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Performance#latency

  1. Those updates do not detract from the merits of the proposal, as Pipewire is designed to integrate seamlessly with PulseAudio, as well as JACK. It would be especially relevant in current systems that continue to utilize PulseAudio and not pipewire. However, it seems that in the foreseeable future, while being supported, PulseAudio may slowly transition to being considered a "legacy" option.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.