Org.lwjgl.LWJGLException: Could not locate OpenAL library. (Ubuntu64)

SO is Ubuntu 64.



I get the exception in the subject exception if i try to run jme without setting the library path.



Apparently Natives (com.jme3.system.Natives.java (160+) ) doesn't extract the openal64 library.



The exception disappears if i add the check for the needOAL condition in the Linux64 case



if(needOAL) extract…



The "needOAL" case is missing for both Linux64 and Linux32 so it is intentional.



I remember a topic about openal libraries under Linux where someone said that every linux shipped its own openal… well, that's not my case :D.




Looks like we have a paradox of sorts. Some OSes have OpenAL and some don't, and depending on that jME3 may or may not work. We can't have a solution for both cases since we can't know beforehand if OpenAL is installed or not.

Options?

I'm not an expert of native libraries loading but what if we check the java.library.path? Like this:


String librarypath = System.getProperty("java.library.path");
String[] env = librarypath.split(System.getProperty("path.separator"));
String openallibname = System.mapLibraryName("openal64");
        System.out.println(openallibname);
for (String string : env) {
    File dir = new File(string);
    File lib = new File(dir, openallibname);
    if(lib.exists()) {
        System.out.println("OpenAl Library found");
    }
}



Option B: a setting option:



Or leaving it as it is: it isn't a big problem after all.

Naah, no extract natives button for the user… :slight_smile: Maybe some option that you can set in the Natives.java in code, like setExtractOpenAL(true).

Maybe we can search a special folder like /lib/system to find libopenal, and if its not there then we extract it. Of course this is Linux specific but we have no choice, right?

In theory looking inside the directories pointed by java.library.path should work because those directories are the sources for System.loadlibrary.

I did a test parsing the java.library.path.



Adding this class:


package com.jme3.system;

import java.io.File;

public class LibraryFinder {

   private final File[] JAVA_LIB_PATH_DIRS;

   public LibraryFinder() {
      String javaLibraryPath = System.getProperty("java.library.path");
      String pathSeparator = System.getProperty("path.separator");
      String[] pathElements = javaLibraryPath.split(pathSeparator);
      File[] dirs = new File[pathElements.length];
      for(int i = 0; i< pathElements.length; i++) {
         dirs[i] = new File(pathElements[i]);
      }
      JAVA_LIB_PATH_DIRS = dirs;
   }

   public boolean libExists(String libraryName) {
      String libFile = System.mapLibraryName(libraryName);
      for(int i = 0; i < JAVA_LIB_PATH_DIRS.length; i++) {
         File parent = JAVA_LIB_PATH_DIRS[i];
         File file = new File(parent, libFile);
         if(file.exists() && file.canRead()) {
            return true;
         }
      }
      return false;
   }

   @Override
   public String toString() {
      String text = "LibFindern";
      text += "java.library.path elements (" + JAVA_LIB_PATH_DIRS.length + "):n";
      for(int i = 0; i < JAVA_LIB_PATH_DIRS.length; i++) {
         text += JAVA_LIB_PATH_DIRS[i] + "n";
      }
      return text;
   }
}



And applying minor changes to Natives.java (one if in extractNativeLib, restored openal checks for linux in extractNativeLibs).

package com.jme3.system;

import com.jme3.system.JmeSystem.Platform;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Helper class for extracting the natives (dll, so) from the jars.
 * This class should only be used internally.
 */
public class Natives {
    private static final LibraryFinder LIBRARY_FINDER = new LibraryFinder();
   
    private static final Logger logger = Logger.getLogger(Natives.class.getName());
    private static final byte[] buf = new byte[1024];
    private static File workingDir = new File("").getAbsoluteFile();

    public static void setExtractionDir(String name){
        workingDir = new File(name).getAbsoluteFile();
    }

    protected static void extractNativeLib(String sysName, String name, boolean load) throws IOException{
        boolean libOnPath = LIBRARY_FINDER.libExists(name);
        if(libOnPath) {
            logger.log(Level.INFO, "Library " + name + " extraction not required.");
        } else {

            String fullname = System.mapLibraryName(name);

            String path = "native/"+sysName+"/" + fullname;
            InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
            //InputStream in = Natives.class.getResourceAsStream();
            if (in == null) {
                logger.log(Level.WARNING, "Cannot locate native library: {0}/{1}",
                        new String[]{ sysName, fullname} );
                return;
            }
            File targetFile = new File(workingDir, fullname);
            OutputStream out = new FileOutputStream(targetFile);
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            in.close();
            out.close();
            logger.log(Level.FINE, "Copied {0} to {1}", new Object[]{fullname, targetFile});
        }
    }

    private static String getExtractionDir(){
        URL temp = Natives.class.getResource("");
        if (temp != null) {
            StringBuilder sb = new StringBuilder(temp.toString());
            if (sb.indexOf("jar:") == 0) {
                sb.delete(0, 4);
                sb.delete(sb.indexOf("!"), sb.length());
                sb.delete(sb.lastIndexOf("/") + 1, sb.length());
            }
            try {
                return new URL(sb.toString()).toString();
            } catch (MalformedURLException ex) {
                return null;
            }
        }
        return null;
    }

    protected static void extractNativeLibs(Platform platform, AppSettings settings) throws IOException{
        String renderer = settings.getRenderer();
        String audioRenderer = settings.getAudioRenderer();
        boolean needLWJGL = false;
        boolean needGG = false;
        boolean needJOGL = false;
        boolean needJOAL = false;
        boolean needOAL = false;
        boolean needJInput = false;
        if (renderer != null){
            if (renderer.startsWith("LWJGL")){
                needLWJGL = true;
            }else if (renderer.startsWith("JOGL")){
                needGG = true;
                needJOGL = true;
            }
        }
        if (audioRenderer != null){
            if (audioRenderer.equals("LWJGL")){
                needLWJGL = true;
                needOAL = true;
            }else if (audioRenderer.equals("JOAL")){
                needJOAL = true;
                needGG = true;
                needOAL = true;
            }
        }
        needJInput = settings.useJoysticks();

        if (needLWJGL){
            logger.log(Level.INFO, "Extraction Directory #1: {0}", getExtractionDir());
            logger.log(Level.INFO, "Extraction Directory #2: {0}", workingDir.toString());
            logger.log(Level.INFO, "Extraction Directory #3: {0}", System.getProperty("user.dir"));
            // LWJGL supports this feature where
            // it can load libraries from this path.
            // This is a fallback method in case the OS doesn't load
            // native libraries from the working directory (e.g Linux).
            System.setProperty("org.lwjgl.librarypath", workingDir.toString());
        }

        switch (platform){
            case Windows64:
                if (needLWJGL){
                    extractNativeLib("windows", "lwjgl64", true);
                }
                if (needJOGL){
                    extractNativeLib("win64", "jogl_awt", false);
                    extractNativeLib("win64", "jogl", true);
                }

                if (needJOAL)
                    extractNativeLib("win64", "joal_native", true);

                if (needGG)
                    extractNativeLib("win64", "gluegen-rt", false);

                if (needOAL)
                    extractNativeLib("windows", "OpenAL64", true);
               
                if (needJInput){
                    extractNativeLib("windows", "jinput-dx8_64", true);
                    extractNativeLib("windows", "jinput-raw_64", true);
                }
                break;
            case Windows32:
                if (needLWJGL){
                    extractNativeLib("windows", "lwjgl", true);
                }
                if (needJOGL){
                    extractNativeLib("win32", "jogl_awt", false);
                    extractNativeLib("win32", "jogl", true);
                }

                if (needJOAL)
                    extractNativeLib("win32", "joal_native", true);

                if (needGG)
                    extractNativeLib("win32", "gluegen-rt", false);

                if (needOAL)
                    extractNativeLib("windows", "OpenAL32", true);

                if (needJInput){
                    extractNativeLib("windows", "jinput-dx8", true);
                    extractNativeLib("windows", "jinput-raw", true);
                }
                break;
            case Linux64:
                if (needLWJGL){
                    extractNativeLib("linux", "lwjgl64", true);
                }
                if (needJOGL){
                    extractNativeLib("linux64", "jogl_awt", false);
                    extractNativeLib("linux64", "jogl", true);
                }

                if (needJOAL)
                    extractNativeLib("linux64", "joal_native", true);

                if(needOAL) {
                    extractNativeLib("linux", "openal64", true);
                }

                if (needGG)
                    extractNativeLib("linux64", "gluegen-rt", false);

                if (needJInput)
                    extractNativeLib("linux", "jinput-linux64", true);

                break;
            case Linux32:
                if (needLWJGL){
                    extractNativeLib("linux", "lwjgl", true);
                }
                if (needJOGL){
                    extractNativeLib("linux32", "jogl_awt", false);
                    extractNativeLib("linux32", "jogl", true);
                }

                if(needOAL) {
                    extractNativeLib("linux", "openal", true);
                }

                if (needJOAL)
                    extractNativeLib("linux32", "joal_native", true);

                if (needGG)
                    extractNativeLib("linux32", "gluegen-rt", false);

                if (needJInput)
                    extractNativeLib("linux", "jinput-linux", true);

                break;
            case MacOSX_PPC32:
                if (needLWJGL){
                    extractNativeLib("macosx", "lwjgl", true);
                }
                if (needJOGL){
                    extractNativeLib("macosx_ppc", "jogl_awt", false);
                    extractNativeLib("macosx_ppc", "jogl", true);
                }

                if (needJOAL)
                    throw new UnsupportedOperationException("JOAL not available on Mac OS PPC");

                if (needGG)
                    extractNativeLib("macosx_ppc", "gluegen-rt", false);

                if (needOAL)
                    extractNativeLib("macosx", "openal", true);

                if (needJInput)
                    extractNativeLib("macosx", "jinput-osx", true);

                break;
            case MacOSX32:
                if (needLWJGL){
                    extractNativeLib("macosx", "lwjgl", true);
                }
                if (needJOGL){
                    extractNativeLib("macosx_universal", "jogl_awt", false);
                    extractNativeLib("macosx_universal", "jogl", true);
                }

                if (needJOAL)
                    extractNativeLib("macosx_universal", "joal_native", true);

                if (needGG)
                    extractNativeLib("macosx_universal", "gluegen-rt", false);

                if (needOAL)
                    extractNativeLib("macosx", "openal", true);

                if (needJInput)
                    extractNativeLib("macosx", "jinput-osx", true);

                break;
            case MacOSX_PPC64:
                if (needLWJGL){
                    extractNativeLib("macosx", "lwjgl", true);
                }
                if (needJOGL){
                    extractNativeLib("macosx_ppc", "jogl_awt", false);
                    extractNativeLib("macosx_ppc", "jogl", true);
                }

                if (needJOAL)
                    throw new UnsupportedOperationException("JOAL not available on Mac OS 64 bit");

                if (needGG)
                    extractNativeLib("macosx_ppc", "gluegen-rt", false);

                if (needOAL)
                    extractNativeLib("macosx", "openal", true);

                if (needJInput)
                    extractNativeLib("macosx", "jinput-osx", true);

                break;
            case MacOSX64:
                if (needLWJGL){
                    extractNativeLib("macosx", "lwjgl", true);
                }
                if (needJOGL){
                    extractNativeLib("macosx_universal", "jogl_awt", false);
                    extractNativeLib("macosx_universal", "jogl", true);
                }

                if (needJOAL)
                    throw new UnsupportedOperationException("JOAL not available on Mac OS 64 bit");

                if (needGG)
                    extractNativeLib("macosx_universal", "gluegen-rt", false);

                if (needOAL)
                    extractNativeLib("macosx", "openal", true);

                if (needJInput)
                    extractNativeLib("macosx", "jinput-osx", true);

                break;
           
               
        }
    }

}



It works on Ubuntu64 and Windows XP Pro 32.

The rationale behind that is:

System.loadLibrary works if the loaded library is in the java.library.path. The java.library.path value is a set of directories. LibraryFinder checks if a given library name matches a library file contained in one of the directories pointed by java.library.path. If that file exists then System.loadLibrary cannot fail due to a library file miss (because it scans the same directories of LibraryFinder searching for the same file).

Will it work in win64, mac or linuxes that have openal installed? I don't know but in theory why not...

I checked the java.library.path and it indeed has the paths in it, including C:WindowsSystem32, which usually contains the OpenAL dlls.

However, I don't want to use them, as usually the Creative implementation has various issues with it, especially the older versions. So if possible, I would only want to do this check for Linux machines, and maybe Macs, if they have the same issue.

Testing just the linux case is easier.


            ...
            ...
            case Linux64:
                if(needOAL && !new LibraryFinder().libExists("openal64")) {
                    extractNativeLib("linux", "openal64", true);
                }
            ...
            ...
            case Linux32:
                if(needOAL && !new LibraryFinder().libExists("openal")) {
                    extractNativeLib("linux", "openal", true);
                }
            ...
            ...



The rest of Natives.java remains as it is in the repository.

While we're at it. jMP still has problems creating the jme.log when its run from an application package because it tries ti create it in the root folder. Maybe the log should simply use the natives path as well or also get a setPath() method.

hmm, why not just extract it every time?

faust said:

hmm, why not just extract it every time?


In a previous topic the developer of a cross platform jme game pointed out that many linux distributions have their own openal implementation out of the box. The "built-in" openal seems to work better so it has been suggested to use that instead of the one packed with jme3.
pgi said:

faust said:

hmm, why not just extract it every time?


In a previous topic the developer of a cross platform jme game pointed out that many linux distributions have their own openal implementation out of the box. The "built-in" openal seems to work better so it has been suggested to use that instead of the one packed with jme3.


I'd disagree that the version bundled with the disto works better (it use to be the case) but LWJGL has a few releases ago switched to using openal-soft which runs alot better IMO. The normal openal is hardly updated and in some cases has sound popping issues(on linux). Openal-Soft has the advantage of falling back to software mode if no hardware acceleration is found (as opposed to completely failing). Also just look up the OpenAL-Soft project and have a look at their online code source (recent change list), its heavily maintained with fixes and tweaks going into it almost daily.

As for the LWJGL project they are in the process of integrating nightly builds of openal-soft with the nightly builds of lwjgl (so the version used by lwjgl should be much more up to date). Also some will be happy to know (yes i'm looking at you Momoko_Fan :)) recently support for EFX was added to LWJGL (should be added to svn once an updated version of openal-soft is added) again I think this feature is not available on some standard versions of openal.

I think JackNeil was using LWJGL1 with his Grappling Hook game, so that could explain the issues he had by bundling the OpenAL library with it. However I vaguely remember there was another case where someone else complained about this…

If the distro is using an old version of OpenAL it's their fault, I think it's best if we extract it if we can't find it, then its guaranteed that jME won't crash and that the most appropriate OpenAL library will be used.



EFX support is great, I added an issue in GoogleCode about it so we don't forget. :slight_smile:

Momoko_Fan said:

I checked the java.library.path and it indeed has the paths in it, including C:WindowsSystem32, which usually contains the OpenAL dlls.
However, I don't want to use them, as usually the Creative implementation has various issues with it, especially the older versions. So if possible, I would only want to do this check for Linux machines, and maybe Macs, if they have the same issue.


Mac's don't have the same issue, as OpenAL is installed by default (I think since 10.4). Java works out of the box with that OpenAL install, and I'm sure other apps aswell.

For now I made it extract OpenAL by default on Linux, I'll try to keep the OpenAL natives updated from the latest in OpenAL Soft.

Momoko_Fan said:

For now I made it extract OpenAL by default on Linux, I'll try to keep the OpenAL natives updated from the latest in OpenAL Soft.


good move.

Hey, Pal!
If u are using this tutorial:

and getting error:

java.lang.UnsatisfiedLinkError: The required native library 'OpenAL64' was not found in the classpath via 'native/windows/OpenAL64.dll'

U can use this classpath declaration while compiling (different from official tutorial):


:: We build the sample application into the build directory… ::
cd HelloJME3
javac -d build -cp "lib/*;" src/mygame/HelloJME3.java

:: … and run it. ::
cd build
java -cp "../lib/*;" mygame.HelloJME3

It uses all jars from lib, not only specified