325 lines
11 KiB
Java
325 lines
11 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.Sets
|
|
* com.mojang.logging.LogUtils
|
|
* org.jspecify.annotations.Nullable
|
|
* org.lwjgl.openal.AL
|
|
* org.lwjgl.openal.AL10
|
|
* org.lwjgl.openal.ALC
|
|
* org.lwjgl.openal.ALC10
|
|
* org.lwjgl.openal.ALC11
|
|
* org.lwjgl.openal.ALCCapabilities
|
|
* org.lwjgl.openal.ALCapabilities
|
|
* org.lwjgl.openal.ALUtil
|
|
* org.lwjgl.system.MemoryStack
|
|
* org.slf4j.Logger
|
|
*/
|
|
package com.mojang.blaze3d.audio;
|
|
|
|
import com.google.common.collect.Sets;
|
|
import com.mojang.blaze3d.audio.Channel;
|
|
import com.mojang.blaze3d.audio.Listener;
|
|
import com.mojang.blaze3d.audio.OpenAlUtil;
|
|
import com.mojang.logging.LogUtils;
|
|
import java.nio.IntBuffer;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Objects;
|
|
import java.util.OptionalLong;
|
|
import java.util.Set;
|
|
import net.minecraft.SharedConstants;
|
|
import net.minecraft.util.Mth;
|
|
import org.jspecify.annotations.Nullable;
|
|
import org.lwjgl.openal.AL;
|
|
import org.lwjgl.openal.AL10;
|
|
import org.lwjgl.openal.ALC;
|
|
import org.lwjgl.openal.ALC10;
|
|
import org.lwjgl.openal.ALC11;
|
|
import org.lwjgl.openal.ALCCapabilities;
|
|
import org.lwjgl.openal.ALCapabilities;
|
|
import org.lwjgl.openal.ALUtil;
|
|
import org.lwjgl.system.MemoryStack;
|
|
import org.slf4j.Logger;
|
|
|
|
public class Library {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private static final int NO_DEVICE = 0;
|
|
private static final int DEFAULT_CHANNEL_COUNT = 30;
|
|
private long currentDevice;
|
|
private long context;
|
|
private boolean supportsDisconnections;
|
|
private @Nullable String defaultDeviceName;
|
|
private static final ChannelPool EMPTY = new ChannelPool(){
|
|
|
|
@Override
|
|
public @Nullable Channel acquire() {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean release(Channel channel) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void cleanup() {
|
|
}
|
|
|
|
@Override
|
|
public int getMaxCount() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public int getUsedCount() {
|
|
return 0;
|
|
}
|
|
};
|
|
private ChannelPool staticChannels = EMPTY;
|
|
private ChannelPool streamingChannels = EMPTY;
|
|
private final Listener listener = new Listener();
|
|
|
|
public Library() {
|
|
this.defaultDeviceName = Library.getDefaultDeviceName();
|
|
}
|
|
|
|
public void init(@Nullable String preferredDevice, boolean useHrtf) {
|
|
this.currentDevice = Library.openDeviceOrFallback(preferredDevice);
|
|
this.supportsDisconnections = false;
|
|
ALCCapabilities alcCapabilities = ALC.createCapabilities((long)this.currentDevice);
|
|
if (OpenAlUtil.checkALCError(this.currentDevice, "Get capabilities")) {
|
|
throw new IllegalStateException("Failed to get OpenAL capabilities");
|
|
}
|
|
if (!alcCapabilities.OpenALC11) {
|
|
throw new IllegalStateException("OpenAL 1.1 not supported");
|
|
}
|
|
try (MemoryStack stack = MemoryStack.stackPush();){
|
|
IntBuffer attr = this.createAttributes(stack, alcCapabilities.ALC_SOFT_HRTF && useHrtf);
|
|
this.context = ALC10.alcCreateContext((long)this.currentDevice, (IntBuffer)attr);
|
|
}
|
|
if (OpenAlUtil.checkALCError(this.currentDevice, "Create context")) {
|
|
throw new IllegalStateException("Unable to create OpenAL context");
|
|
}
|
|
ALC10.alcMakeContextCurrent((long)this.context);
|
|
int totalChannelCount = this.getChannelCount();
|
|
int streamingChannelCount = Mth.clamp((int)Mth.sqrt(totalChannelCount), 2, 8);
|
|
int staticChannelCount = Mth.clamp(totalChannelCount - streamingChannelCount, 8, 255);
|
|
this.staticChannels = new CountingChannelPool(staticChannelCount);
|
|
this.streamingChannels = new CountingChannelPool(streamingChannelCount);
|
|
ALCapabilities alCapabilities = AL.createCapabilities((ALCCapabilities)alcCapabilities);
|
|
OpenAlUtil.checkALError("Initialization");
|
|
if (!alCapabilities.AL_EXT_source_distance_model) {
|
|
throw new IllegalStateException("AL_EXT_source_distance_model is not supported");
|
|
}
|
|
AL10.alEnable((int)512);
|
|
if (!alCapabilities.AL_EXT_LINEAR_DISTANCE) {
|
|
throw new IllegalStateException("AL_EXT_LINEAR_DISTANCE is not supported");
|
|
}
|
|
OpenAlUtil.checkALError("Enable per-source distance models");
|
|
LOGGER.info("OpenAL initialized on device {}", (Object)this.getCurrentDeviceName());
|
|
this.supportsDisconnections = ALC10.alcIsExtensionPresent((long)this.currentDevice, (CharSequence)"ALC_EXT_disconnect");
|
|
}
|
|
|
|
private IntBuffer createAttributes(MemoryStack stack, boolean enableHrtf) {
|
|
int maxAttributes = 5;
|
|
IntBuffer attr = stack.callocInt(11);
|
|
int numHrtf = ALC10.alcGetInteger((long)this.currentDevice, (int)6548);
|
|
if (numHrtf > 0) {
|
|
attr.put(6546).put(enableHrtf ? 1 : 0);
|
|
attr.put(6550).put(0);
|
|
}
|
|
attr.put(6554).put(1);
|
|
return attr.put(0).flip();
|
|
}
|
|
|
|
private int getChannelCount() {
|
|
try (MemoryStack stack = MemoryStack.stackPush();){
|
|
int size = ALC10.alcGetInteger((long)this.currentDevice, (int)4098);
|
|
if (OpenAlUtil.checkALCError(this.currentDevice, "Get attributes size")) {
|
|
throw new IllegalStateException("Failed to get OpenAL attributes");
|
|
}
|
|
IntBuffer attributes = stack.mallocInt(size);
|
|
ALC10.alcGetIntegerv((long)this.currentDevice, (int)4099, (IntBuffer)attributes);
|
|
if (OpenAlUtil.checkALCError(this.currentDevice, "Get attributes")) {
|
|
throw new IllegalStateException("Failed to get OpenAL attributes");
|
|
}
|
|
int pos = 0;
|
|
while (pos < size) {
|
|
int attribute;
|
|
if ((attribute = attributes.get(pos++)) == 0) {
|
|
break;
|
|
}
|
|
int attributeValue = attributes.get(pos++);
|
|
if (attribute != 4112) continue;
|
|
int n = attributeValue;
|
|
return n;
|
|
}
|
|
}
|
|
return 30;
|
|
}
|
|
|
|
public static @Nullable String getDefaultDeviceName() {
|
|
if (!ALC10.alcIsExtensionPresent((long)0L, (CharSequence)"ALC_ENUMERATE_ALL_EXT")) {
|
|
return null;
|
|
}
|
|
ALUtil.getStringList((long)0L, (int)4115);
|
|
return ALC10.alcGetString((long)0L, (int)4114);
|
|
}
|
|
|
|
public String getCurrentDeviceName() {
|
|
String name = ALC10.alcGetString((long)this.currentDevice, (int)4115);
|
|
if (name == null) {
|
|
name = ALC10.alcGetString((long)this.currentDevice, (int)4101);
|
|
}
|
|
if (name == null) {
|
|
name = "Unknown";
|
|
}
|
|
return name;
|
|
}
|
|
|
|
public synchronized boolean hasDefaultDeviceChanged() {
|
|
String name = Library.getDefaultDeviceName();
|
|
if (Objects.equals(this.defaultDeviceName, name)) {
|
|
return false;
|
|
}
|
|
this.defaultDeviceName = name;
|
|
return true;
|
|
}
|
|
|
|
private static long openDeviceOrFallback(@Nullable String preferredDevice) {
|
|
OptionalLong device = OptionalLong.empty();
|
|
if (preferredDevice != null) {
|
|
device = Library.tryOpenDevice(preferredDevice);
|
|
}
|
|
if (device.isEmpty()) {
|
|
device = Library.tryOpenDevice(Library.getDefaultDeviceName());
|
|
}
|
|
if (device.isEmpty()) {
|
|
device = Library.tryOpenDevice(null);
|
|
}
|
|
if (device.isEmpty()) {
|
|
throw new IllegalStateException("Failed to open OpenAL device");
|
|
}
|
|
return device.getAsLong();
|
|
}
|
|
|
|
private static OptionalLong tryOpenDevice(@Nullable String name) {
|
|
long device = ALC10.alcOpenDevice((CharSequence)name);
|
|
if (device != 0L && !OpenAlUtil.checkALCError(device, "Open device")) {
|
|
return OptionalLong.of(device);
|
|
}
|
|
return OptionalLong.empty();
|
|
}
|
|
|
|
public void cleanup() {
|
|
this.staticChannels.cleanup();
|
|
this.streamingChannels.cleanup();
|
|
ALC10.alcDestroyContext((long)this.context);
|
|
if (this.currentDevice != 0L) {
|
|
ALC10.alcCloseDevice((long)this.currentDevice);
|
|
}
|
|
}
|
|
|
|
public Listener getListener() {
|
|
return this.listener;
|
|
}
|
|
|
|
public @Nullable Channel acquireChannel(Pool pool) {
|
|
return (pool == Pool.STREAMING ? this.streamingChannels : this.staticChannels).acquire();
|
|
}
|
|
|
|
public void releaseChannel(Channel channel) {
|
|
if (!this.staticChannels.release(channel) && !this.streamingChannels.release(channel)) {
|
|
throw new IllegalStateException("Tried to release unknown channel");
|
|
}
|
|
}
|
|
|
|
public String getDebugString() {
|
|
return String.format(Locale.ROOT, "Sounds: %d/%d + %d/%d", this.staticChannels.getUsedCount(), this.staticChannels.getMaxCount(), this.streamingChannels.getUsedCount(), this.streamingChannels.getMaxCount());
|
|
}
|
|
|
|
public List<String> getAvailableSoundDevices() {
|
|
List result = ALUtil.getStringList((long)0L, (int)4115);
|
|
if (result == null) {
|
|
return Collections.emptyList();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public boolean isCurrentDeviceDisconnected() {
|
|
return this.supportsDisconnections && ALC11.alcGetInteger((long)this.currentDevice, (int)787) == 0;
|
|
}
|
|
|
|
private static interface ChannelPool {
|
|
public @Nullable Channel acquire();
|
|
|
|
public boolean release(Channel var1);
|
|
|
|
public void cleanup();
|
|
|
|
public int getMaxCount();
|
|
|
|
public int getUsedCount();
|
|
}
|
|
|
|
private static class CountingChannelPool
|
|
implements ChannelPool {
|
|
private final int limit;
|
|
private final Set<Channel> activeChannels = Sets.newIdentityHashSet();
|
|
|
|
public CountingChannelPool(int limit) {
|
|
this.limit = limit;
|
|
}
|
|
|
|
@Override
|
|
public @Nullable Channel acquire() {
|
|
if (this.activeChannels.size() >= this.limit) {
|
|
if (SharedConstants.IS_RUNNING_IN_IDE) {
|
|
LOGGER.warn("Maximum sound pool size {} reached", (Object)this.limit);
|
|
}
|
|
return null;
|
|
}
|
|
Channel channel = Channel.create();
|
|
if (channel != null) {
|
|
this.activeChannels.add(channel);
|
|
}
|
|
return channel;
|
|
}
|
|
|
|
@Override
|
|
public boolean release(Channel channel) {
|
|
if (!this.activeChannels.remove(channel)) {
|
|
return false;
|
|
}
|
|
channel.destroy();
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void cleanup() {
|
|
this.activeChannels.forEach(Channel::destroy);
|
|
this.activeChannels.clear();
|
|
}
|
|
|
|
@Override
|
|
public int getMaxCount() {
|
|
return this.limit;
|
|
}
|
|
|
|
@Override
|
|
public int getUsedCount() {
|
|
return this.activeChannels.size();
|
|
}
|
|
}
|
|
|
|
public static enum Pool {
|
|
STATIC,
|
|
STREAMING;
|
|
|
|
}
|
|
}
|
|
|