alsa-project / alsa-lib

The Advanced Linux Sound Architecture (ALSA) - library
GNU Lesser General Public License v2.1
366 stars 177 forks source link

Ability to set ALSA PLUGIN DIR, etc. at runtime via environment variables #82

Closed peanutbutterandcrackers closed 4 years ago

peanutbutterandcrackers commented 4 years ago

Hello there,

I would like to request a feature to set things like ALSA_PLUGIN_DIR etc during runtime using environment variables. Currently, these seem to be set-able only at build-time, or by using a .asoundrc file.

Or, if something like that is already possible, please do point me to the relevant documentation. Thank you very much.

perexg commented 4 years ago

The latest alsa-lib looks to the relative directory where the libasound.so is located: /alsa-lib/ .

You didn't write the purpose/demand to evaluate your request.

peanutbutterandcrackers commented 4 years ago

@perexg - Hello there,

Please forgive me for not clarifying things up. I will now try to explain my situation. However, I must admit that I am no expert and this is therefore just a request. Please do let me know if I have been unable to articulate myself well enough, or if I have left anything unclear, and I will try to explain that further.

I use GNU Guix a functional package manager. It can sit on top of any other distro. And, I recently ran into an issue where a program that was packaged using guix didn't produce sound on my machine. I looked at it, and it turned out that it was looking looking for libasound_module_conf_pulse.so in the directory that you said, <libasound.so.dir>/alsa-lib/<plugin.so>. Now, for guix, baked packages are the result of a function, the input of which are all the inputs necessary to prepare the package. Each package ends up in it's own 'store' in /gnu/store. So, the package alsa-lib happens to end up one directory in /gnu/store whereas alsa-plugins ends up in another. No problem there. Because guix has a way to mix two packages into one. The union-build. And I wrote a union build of these two packages: alsa-lib and alsa-plugins, today. The pkg-config .pm files were also edited to account for the new path. Everything worked. I present a tree listing of the build tree here:

.
├── bin
│   └── aserver
├── include
│   ├── alsa
│   │   ├── asoundef.h
│   │   ├── asoundlib.h
│   │   ├── conf.h
│   │   ├── control_external.h
│   │   ├── control.h
│   │   ├── error.h
│   │   ├── global.h
│   │   ├── hwdep.h
│   │   ├── input.h
│   │   ├── mixer_abst.h
│   │   ├── mixer.h
│   │   ├── output.h
│   │   ├── pcm_external.h
│   │   ├── pcm_extplug.h
│   │   ├── pcm.h
│   │   ├── pcm_ioplug.h
│   │   ├── pcm_old.h
│   │   ├── pcm_plugin.h
│   │   ├── pcm_rate.h
│   │   ├── rawmidi.h
│   │   ├── seq_event.h
│   │   ├── seq.h
│   │   ├── seqmid.h
│   │   ├── seq_midi_event.h
│   │   ├── sound
│   │   │   ├── asoc.h
│   │   │   ├── asound_fm.h
│   │   │   ├── emu10k1.h
│   │   │   ├── hdsp.h
│   │   │   ├── hdspm.h
│   │   │   ├── sb16_csp.h
│   │   │   ├── sscape_ioctl.h
│   │   │   ├── tlv.h
│   │   │   ├── type_compat.h
│   │   │   └── uapi
│   │   │       ├── asoc.h
│   │   │       ├── asound_fm.h
│   │   │       ├── emu10k1.h
│   │   │       ├── hdsp.h
│   │   │       ├── hdspm.h
│   │   │       ├── sb16_csp.h
│   │   │       ├── sscape_ioctl.h
│   │   │       └── tlv.h
│   │   ├── timer.h
│   │   ├── topology.h
│   │   ├── use-case.h
│   │   └── version.h
│   ├── asoundlib.h
│   └── sys
│       └── asoundlib.h
├── lib
│   ├── alsa-lib
│   │   ├── libasound_module_conf_pulse.la
│   │   ├── libasound_module_conf_pulse.so
│   │   ├── libasound_module_ctl_pulse.la
│   │   ├── libasound_module_ctl_pulse.so
│   │   ├── libasound_module_pcm_pulse.la
│   │   └── libasound_module_pcm_pulse.so
│   ├── libasound.la
│   ├── libasound.so
│   ├── libasound.so.2
│   ├── libasound.so.2.0.0
│   ├── libatopology.la
│   ├── libatopology.so
│   ├── libatopology.so.2
│   ├── libatopology.so.2.0.0
│   └── pkgconfig
│       ├── alsa.pc
│       └── alsa-topology.pc
└── share
    ├── aclocal
    │   └── alsa.m4
    ├── alsa
    │   ├── alsa.conf
    │   ├── cards
    │   │   ├── AACI.conf
    │   │   ├── aliases.conf
    │   │   ├── ATIIXP.conf
    │   │   ├── ATIIXP-MODEM.conf
    │   │   ├── ATIIXP-SPDMA.conf
    │   │   ├── AU8810.conf
    │   │   ├── AU8820.conf
    │   │   ├── AU8830.conf
    │   │   ├── Audigy2.conf
    │   │   ├── Audigy.conf
    │   │   ├── Aureon51.conf
    │   │   ├── Aureon71.conf
    │   │   ├── CA0106.conf
    │   │   ├── CMI8338.conf
    │   │   ├── CMI8338-SWIEC.conf
    │   │   ├── CMI8738-MC6.conf
    │   │   ├── CMI8738-MC8.conf
    │   │   ├── CMI8788.conf
    │   │   ├── CS46xx.conf
    │   │   ├── Echo_Echo3G.conf
    │   │   ├── EMU10K1.conf
    │   │   ├── EMU10K1X.conf
    │   │   ├── ENS1370.conf
    │   │   ├── ENS1371.conf
    │   │   ├── ES1968.conf
    │   │   ├── FireWave.conf
    │   │   ├── FM801.conf
    │   │   ├── FWSpeakers.conf
    │   │   ├── GUS.conf
    │   │   ├── HDA-Intel.conf
    │   │   ├── HdmiLpeAudio.conf
    │   │   ├── ICE1712.conf
    │   │   ├── ICE1724.conf
    │   │   ├── ICH4.conf
    │   │   ├── ICH.conf
    │   │   ├── ICH-MODEM.conf
    │   │   ├── Loopback.conf
    │   │   ├── Maestro3.conf
    │   │   ├── NFORCE.conf
    │   │   ├── PC-Speaker.conf
    │   │   ├── pistachio-card.conf
    │   │   ├── PMac.conf
    │   │   ├── PMacToonie.conf
    │   │   ├── PS3.conf
    │   │   ├── RME9636.conf
    │   │   ├── RME9652.conf
    │   │   ├── SB-XFi.conf
    │   │   ├── SI7018.conf
    │   │   ├── TRID4DWAVENX.conf
    │   │   ├── USB-Audio.conf
    │   │   ├── vc4-hdmi.conf
    │   │   ├── VIA686A.conf
    │   │   ├── VIA8233A.conf
    │   │   ├── VIA8233.conf
    │   │   ├── VIA8237.conf
    │   │   ├── VX222.conf
    │   │   ├── VXPocket440.conf
    │   │   ├── VXPocket.conf
    │   │   └── YMF744.conf
    │   └── pcm
    │       ├── center_lfe.conf
    │       ├── default.conf
    │       ├── dmix.conf
    │       ├── dpl.conf
    │       ├── dsnoop.conf
    │       ├── front.conf
    │       ├── hdmi.conf
    │       ├── iec958.conf
    │       ├── modem.conf
    │       ├── rear.conf
    │       ├── side.conf
    │       ├── surround21.conf
    │       ├── surround40.conf
    │       ├── surround41.conf
    │       ├── surround50.conf
    │       ├── surround51.conf
    │       └── surround71.conf
    └── doc
        ├── alsa-lib-1.2.2
        │   └── COPYING
        └── alsa-plugins-1.2.2
            ├── COPYING
            └── COPYING.GPL

17 directories, 145 files

As you can see in the listing above, this is a proper union of the two packages.

Now, before I go further, perhaps a summary is in order:

  1. In GNU Guix, each package is an output of a (build) function, the input of which are the dependencies, compilers, source-code, etc. Each program that is built ends up -- instead of ending up in the usual UNIX way of .so files in /usr/lib, execuatables in /usr/bin, etc -- in it's own separate store in /gnu/store as /gnu/store/unique_build_hash-package-name.
  2. alsa-lib is one package with it's own store in /gnu/store/build-hash-alsa-lib
  3. alsa-plugins:pulseaudio is another one with it's own store in /gnu/store/build-hash-alsa-plugins-pulseaudio.
  4. The program that I am trying to package aegisub requires alsa-plugins:pulseaudio to be available to it during runtime.
  5. In order to make this possible, I [created a union]() of alsa-lib and alsa-plugins:pulseaudio. And the union (hereafter known as alsa-union) works. There's no problem there.

However, when I built the package aegisub with alsa-union as an input instead of mere alsa-lib, during run time, the program was looking for the plugins in the the store of alsa-lib, and not the store of alsa-union. I was wondering why this might be the case, and it seems that it is because alsa-lib sets it's plugin-dir during build time to be the directory relative directory to where libasound.so is located. And this propagates over to alsa-union (as the packages alsa-lib and alsa-plugins:pulseaudio are the inputs to the function that creates the union package).

Now, if alsa-lib, along with the plugin-dir that it has set during build, also honoured a few environment variables (for instance ALSA_PLUGIN_PATH, etc.) during runtime, I could export the proper variables and it would work. But as things stand right now, the package aegisub is still looking for libasound_module_conf_pulse.so in the original location of alsa-lib (which was burned into it during build-time).

I did try writing a package definition that would set the --plugiin-dir of alsa-lib during build-time to point to the path to the union's store at /gnu/store/hash-alsa-union. But since the union is only built after the actual libs, that task seems impossible.


Now that I've went ahead and written all of that, I am having second thoughts. I just realized that I haven't quite noticed any libraries respecting environment variables during run-time. Perhaps there might be good reasons not to do so. So, perhaps this entire request is an ill-informed one.

However, I'm still going to post this. Perhaps you could have some useful insight that you could give me regarding the matter. Or some advice. Anything.

Sorry about the long convoluted post. I would appreciate any advice/suggestions that you might have.

peanutbutterandcrackers commented 4 years ago

Hey there,

For the moment, I have resolved this by choosing a different route, entirely.

Instead of creating a union build, I ended up redefining alsa-lib to build with --with-plugindir set to the /gnu/store/...-alsa-plugins-pulseaudio/lib/alsa-plugins. And that seems to have resolved the issue just as well, and in quite a bit of fewer lines of package declaration.

I think this issue can now be closed. But I will wait on you to see if you have any suggestions.

Thank you for your time!

perexg commented 4 years ago

I was wondering why this might be the case, and it seems that it is because alsa-lib sets it's plugin-dir during build time to be the directory relative directory to where libasound.so is located.

The current alsa-lib code uses the RTLD_DI_ORIGIN glibc extension to determine the libasound.so path at runtime. The hardcoded path is used only if this mechanism does not work:

https://github.com/alsa-project/alsa-lib/blob/c1e72460de5ddcfdc8b93e73952c6fe9d6f60591/src/dlmisc.c#L78-L101

It seems that runtime linker (ld-linux.so) does not use libasound from the directory you expect or the alsa-lib is not compiled with the RTLD_DI_ORIGIN code.

peanutbutterandcrackers commented 4 years ago

@perexg - This does sound like a better path. Please excuse my lack of knowledge, but what other inputs should I add to the alsa-lib package? glibc?

The following is the build log of alsa-lib in guix as it stands currently: alsa-lib-1.2.2.drv.build.log

perexg commented 4 years ago

The problem is probably this rpath: -Wl,-rpath=/gnu/store/zcjdb23gbhl0pcnvvm8rnlprkfl43cv5-alsa-lib-1.2.2/lib . It should point to the union tree.

peanutbutterandcrackers commented 4 years ago

I have asked another guix contributor, who is far more qualified than I am, to look into the matter. If need be, I will ask some further questions. You have been quite helpful. Thank you very much for your help.

roptat commented 4 years ago

Hi! I am said contributor :)

So, in Guix, since libraries are in different directories that are not guessable, we set the rpath at build time, so the dynamic loader can actually find them. One issue is that, at build time, we only know the path of inputs (dependencies) of the package, but we can't guess the path of any other package. So we can build alsa-lib with an rpath that includes its output and dependencies, but since alsa-plugins depends on alsa-lib, at that point, we do not know which directory alsa-plugins will be installed to.

The union is simply built after having built alsa-lib and alsa-plugins, by copying (or symlinking) all their files into a single directory under /gnu/store. Doing so, we preserve the rpath of the libraries and binaries. IIUC, alsa-lib looks for the pulseaudio plugin in its rpath, fails to do so, and falls back to its hard-coded directory.

We can wrap binaries to set LD_LIBRARY_PATH or similar and fix that for binaries, but that wouldn't work for a library. Similarly, we could patchelf it, but that's less than satisfying :).

Other libraries, like qtbase (see https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/packages/qt.scm#n579) actually have a similar mechanism to what @peanutbutterandcrackers asked for here, where they can read the value of, for instance, QT_PLUGIN_DIR, to find libraries in other packages that depend on it. Guix has the ability to set these variables for the user, if they need them.

Currently, alsa-lib and alsa-plugins work well on the Guix System, because of configuration. Since we control the whole system configuration, we have a simple mechanism to set an /etc/asound.conf file that references the pulseaudio plugin. This works for the Guix System itself, but not for guix on a foreign distro, since we don't control the configuration of foreign distros.

lfam commented 4 years ago

Hello,

If I understand correctly, this issue as it pertains to Guix has also been discussed previously on our bug tracker a few months ago:

https://bugs.gnu.org/40832

It includes a patch that makes alsa-lib respect the environment variable GUIX_ALSA_PLUGIN_DIRS. It does work but I haven't deployed it for Guix users yet:

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=40832#31

perexg commented 4 years ago

I implemented the ALSA_PLUGIN_DIR support in commit 8580c081c25678d11278efcb61bd15cf44d0a225 . Note that only one directory lookup is supported for the moment which is the difference from GUIX_ALSA_PLUGIN_DIRS .