All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.lwjgl.openal.AL Maven / Gradle / Ivy

Go to download

A cross-platform 3D audio API appropriate for use with gaming applications and many other types of audio applications.

There is a newer version: 3.3.4
Show newest version
/*
 * Copyright LWJGL. All rights reserved.
 * License terms: https://www.lwjgl.org/license
 */
package org.lwjgl.openal;

import org.lwjgl.system.*;

import javax.annotation.*;
import java.nio.*;
import java.util.*;

import static org.lwjgl.openal.AL10.*;
import static org.lwjgl.openal.EXTThreadLocalContext.*;
import static org.lwjgl.system.APIUtil.*;
import static org.lwjgl.system.JNI.*;
import static org.lwjgl.system.MemoryStack.*;
import static org.lwjgl.system.MemoryUtil.*;

/**
 * This class must be used before any OpenAL function is called. It has the following responsibilities:
 * 
    *
  • Creates instances of {@link ALCapabilities} classes. An {@code ALCapabilities} instance contains flags for functionality that is available in an OpenAL * context. Internally, it also contains function pointers that are only valid in that specific OpenAL context.
  • *
  • Maintains thread-local and global state for {@code ALCapabilities} instances, corresponding to OpenAL contexts that are current in those threads and the * entire process, respectively.
  • *
* *

ALCapabilities creation

*

Instances of {@code ALCapabilities} can be created with the {@link #createCapabilities} method. An OpenAL context must be current in the current thread * or process before it is called. Calling this method is expensive, so {@code ALCapabilities} instances should be cached in user code.

* *

Thread-local state

*

Before a function for a given OpenAL context can be called, the corresponding {@code ALCapabilities} instance must be made current in the current * thread or process. The user is also responsible for clearing the current {@code ALCapabilities} instance when the context is destroyed or made current in * another thread.

* *

Note that OpenAL contexts are made current process-wide by default. Current thread-local contexts are only available if the * {@link EXTThreadLocalContext ALC_EXT_thread_local_context} extension is supported by the OpenAL implementation. OpenAL Soft, the implementation * that LWJGL ships with, supports this extension and performs better when it is used.

* * @see ALC */ public final class AL { @Nullable private static FunctionProvider functionProvider; @Nullable private static ALCapabilities processCaps; private static final ThreadLocal capabilitiesTLS = new ThreadLocal<>(); private static ICD icd = new ICDStatic(); private AL() {} static void init() { functionProvider = new FunctionProvider() { // We'll use alGetProcAddress for both core and extension entry points. // To do that, we need to first grab the alGetProcAddress function from // the OpenAL native library. private final long alGetProcAddress = ALC.getFunctionProvider().getFunctionAddress("alGetProcAddress"); @Override public long getFunctionAddress(ByteBuffer functionName) { long address = invokePP(alGetProcAddress, memAddress(functionName)); if (address == NULL && Checks.DEBUG_FUNCTIONS) { apiLog("Failed to locate address for AL function " + memASCII(functionName)); } return address; } }; } static void destroy() { if (functionProvider == null) { return; } setCurrentProcess(null); functionProvider = null; } /** * Sets the specified {@link ALCapabilities} for the current process-wide OpenAL context. * *

If the current thread had a context current (see {@link #setCurrentThread}), those {@code ALCapabilities} are cleared. Any OpenAL functions called in * the current thread, or any threads that have no context current, will use the specified {@code ALCapabilities}.

* * @param caps the {@link ALCapabilities} to make current, or null */ public static void setCurrentProcess(@Nullable ALCapabilities caps) { processCaps = caps; capabilitiesTLS.set(null); // See EXT_thread_local_context, second Q. icd.set(caps); } /** * Sets the specified {@link ALCapabilities} for the current OpenAL context in the current thread. * *

Any OpenAL functions called in the current thread will use the specified {@code ALCapabilities}.

* * @param caps the {@link ALCapabilities} to make current, or null */ public static void setCurrentThread(@Nullable ALCapabilities caps) { capabilitiesTLS.set(caps); icd.set(caps); } /** * Returns the {@link ALCapabilities} for the OpenAL context that is current in the current thread or process. * * @throws IllegalStateException if no OpenAL context is current in the current thread or process */ public static ALCapabilities getCapabilities() { ALCapabilities caps = capabilitiesTLS.get(); if (caps == null) { caps = processCaps; } return checkCapabilities(caps); } private static ALCapabilities checkCapabilities(@Nullable ALCapabilities caps) { if (caps == null) { throw new IllegalStateException( "No ALCapabilities instance set for the current thread or process. Possible solutions:\n" + "\ta) Call AL.createCapabilities() after making a context current.\n" + "\tb) Call AL.setCurrentProcess() or AL.setCurrentThread() if an ALCapabilities instance already exists." ); } return caps; } /** * Creates a new {@link ALCapabilities} instance for the OpenAL context that is current in the current thread or process. * * @param alcCaps the {@link ALCCapabilities} of the device associated with the current context * * @return the ALCapabilities instance */ public static ALCapabilities createCapabilities(ALCCapabilities alcCaps) { FunctionProvider functionProvider = ALC.check(AL.functionProvider); ALCapabilities caps = null; try { long GetString = functionProvider.getFunctionAddress("alGetString"); long GetError = functionProvider.getFunctionAddress("alGetError"); long IsExtensionPresent = functionProvider.getFunctionAddress("alIsExtensionPresent"); if (GetString == NULL || GetError == NULL || IsExtensionPresent == NULL) { throw new IllegalStateException("Core OpenAL functions could not be found. Make sure that the OpenAL library has been loaded correctly."); } String versionString = memASCIISafe(invokeP(GetString, AL_VERSION)); if (versionString == null || invokeI(GetError) != AL_NO_ERROR) { throw new IllegalStateException("There is no OpenAL context current in the current thread or process."); } APIVersion apiVersion = apiParseVersion(versionString); int majorVersion = apiVersion.major; int minorVersion = apiVersion.minor; int[][] AL_VERSIONS = { {0, 1} // OpenAL 1 }; Set supportedExtensions = new HashSet<>(32); for (int major = 1; major <= AL_VERSIONS.length; major++) { int[] minors = AL_VERSIONS[major - 1]; for (int minor : minors) { if (major < majorVersion || (major == majorVersion && minor <= minorVersion)) { supportedExtensions.add("OpenAL" + Integer.toString(major) + Integer.toString(minor)); } } } // Parse EXTENSIONS string String extensionsString = memASCIISafe(invokeP(GetString, AL_EXTENSIONS)); if (extensionsString != null) { MemoryStack stack = stackGet(); StringTokenizer tokenizer = new StringTokenizer(extensionsString); while (tokenizer.hasMoreTokens()) { String extName = tokenizer.nextToken(); try (MemoryStack frame = stack.push()) { if (invokePZ(IsExtensionPresent, memAddress(frame.ASCII(extName, true)))) { supportedExtensions.add(extName); } } } } if (alcCaps.ALC_EXT_EFX) { supportedExtensions.add("ALC_EXT_EFX"); } return caps = new ALCapabilities(functionProvider, supportedExtensions); } finally { if (alcCaps.ALC_EXT_thread_local_context && alcGetThreadContext() != NULL) { setCurrentThread(caps); } else { setCurrentProcess(caps); } } } static ALCapabilities getICD() { return ALC.check(icd.get()); } /** Function pointer provider. */ private interface ICD { default void set(@Nullable ALCapabilities caps) {} @Nullable ALCapabilities get(); } /** * Write-once {@link ICD}. * *

This is the default implementation that skips the thread/process lookup. When a new ALCapabilities is set, we compare it to the write-once * capabilities. If different function pointers are found, we fall back to the expensive lookup. This will never happen with the OpenAL-Soft * implementation.

*/ private static class ICDStatic implements ICD { @Nullable private static ALCapabilities tempCaps; @Override public void set(@Nullable ALCapabilities caps) { if (tempCaps == null) { tempCaps = caps; } else if (caps != null && caps != tempCaps && ThreadLocalUtil.areCapabilitiesDifferent(tempCaps.addresses, caps.addresses)) { apiLog("[WARNING] Incompatible context detected. Falling back to thread/process lookup for AL contexts."); icd = AL::getCapabilities; // fall back to thread/process lookup } } @Override @Nullable public ALCapabilities get() { return WriteOnce.caps; } private static final class WriteOnce { // This will be initialized the first time get() above is called @Nullable private static final ALCapabilities caps = ICDStatic.tempCaps; static { if (caps == null) { throw new IllegalStateException("No ALCapabilities instance has been set"); } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy