package com.mojang.blaze3d.audio; import com.google.common.collect.Sets; import java.util.Set; import org.apache.logging.log4j.LogManager; import javax.annotation.Nullable; import java.nio.ByteBuffer; import org.lwjgl.system.MemoryStack; import org.lwjgl.openal.ALCapabilities; import org.lwjgl.openal.ALCCapabilities; import org.lwjgl.openal.AL10; import org.lwjgl.openal.AL; import net.minecraft.util.Mth; import org.lwjgl.openal.ALC10; import java.nio.IntBuffer; import org.lwjgl.openal.ALC; import org.apache.logging.log4j.Logger; public class Library { private static final Logger LOGGER; private long device; private long context; private static final ChannelPool EMPTY; private ChannelPool staticChannels; private ChannelPool streamingChannels; private final Listener listener; public Library() { this.staticChannels = Library.EMPTY; this.streamingChannels = Library.EMPTY; this.listener = new Listener(); } public void init() { this.device = tryOpenDevice(); final ALCCapabilities aLCCapabilities2 = ALC.createCapabilities(this.device); if (OpenAlUtil.checkALCError(this.device, "Get capabilities")) { throw new IllegalStateException("Failed to get OpenAL capabilities"); } if (!aLCCapabilities2.OpenALC11) { throw new IllegalStateException("OpenAL 1.1 not supported"); } ALC10.alcMakeContextCurrent(this.context = ALC10.alcCreateContext(this.device, (IntBuffer)null)); final int integer3 = this.getChannelCount(); final int integer4 = Mth.clamp((int)Mth.sqrt((float)integer3), 2, 8); final int integer5 = Mth.clamp(integer3 - integer4, 8, 255); this.staticChannels = new CountingChannelPool(integer5); this.streamingChannels = new CountingChannelPool(integer4); final ALCapabilities aLCapabilities6 = AL.createCapabilities(aLCCapabilities2); OpenAlUtil.checkALError("Initialization"); if (!aLCapabilities6.AL_EXT_source_distance_model) { throw new IllegalStateException("AL_EXT_source_distance_model is not supported"); } AL10.alEnable(512); if (!aLCapabilities6.AL_EXT_LINEAR_DISTANCE) { throw new IllegalStateException("AL_EXT_LINEAR_DISTANCE is not supported"); } OpenAlUtil.checkALError("Enable per-source distance models"); Library.LOGGER.info("OpenAL initialized."); } private int getChannelCount() { try (final MemoryStack memoryStack2 = MemoryStack.stackPush()) { final int integer4 = ALC10.alcGetInteger(this.device, 4098); if (OpenAlUtil.checkALCError(this.device, "Get attributes size")) { throw new IllegalStateException("Failed to get OpenAL attributes"); } final IntBuffer intBuffer5 = memoryStack2.mallocInt(integer4); ALC10.alcGetIntegerv(this.device, 4099, intBuffer5); if (OpenAlUtil.checkALCError(this.device, "Get attributes")) { throw new IllegalStateException("Failed to get OpenAL attributes"); } int integer5 = 0; while (integer5 < integer4) { final int integer6 = intBuffer5.get(integer5++); if (integer6 == 0) { break; } final int integer7 = intBuffer5.get(integer5++); if (integer6 == 4112) { return integer7; } } } return 30; } private static long tryOpenDevice() { for (int integer1 = 0; integer1 < 3; ++integer1) { final long long2 = ALC10.alcOpenDevice((ByteBuffer)null); if (long2 != 0L && !OpenAlUtil.checkALCError(long2, "Open device")) { return long2; } } throw new IllegalStateException("Failed to open OpenAL device"); } public void cleanup() { this.staticChannels.cleanup(); this.streamingChannels.cleanup(); ALC10.alcDestroyContext(this.context); if (this.device != 0L) { ALC10.alcCloseDevice(this.device); } } public Listener getListener() { return this.listener; } @Nullable public Channel acquireChannel(final Pool c) { return ((c == Pool.STREAMING) ? this.streamingChannels : this.staticChannels).acquire(); } public void releaseChannel(final Channel cws) { if (!this.staticChannels.release(cws) && !this.streamingChannels.release(cws)) { throw new IllegalStateException("Tried to release unknown channel"); } } public String getDebugString() { return String.format("Sounds: %d/%d + %d/%d", this.staticChannels.getUsedCount(), this.staticChannels.getMaxCount(), this.streamingChannels.getUsedCount(), this.streamingChannels.getMaxCount()); } static { LOGGER = LogManager.getLogger(); EMPTY = new ChannelPool() { @Nullable @Override public Channel acquire() { return null; } @Override public boolean release(final Channel cws) { return false; } @Override public void cleanup() { } @Override public int getMaxCount() { return 0; } @Override public int getUsedCount() { return 0; } }; } public enum Pool { STATIC, STREAMING; } static class CountingChannelPool implements ChannelPool { private final int limit; private final Set activeChannels; public CountingChannelPool(final int integer) { this.activeChannels = Sets.newIdentityHashSet(); this.limit = integer; } @Nullable @Override public Channel acquire() { if (this.activeChannels.size() >= this.limit) { return null; } final Channel cws2 = Channel.create(); if (cws2 != null) { this.activeChannels.add(cws2); } return cws2; } @Override public boolean release(final Channel cws) { if (!this.activeChannels.remove(cws)) { return false; } cws.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(); } } interface ChannelPool { @Nullable Channel acquire(); boolean release(final Channel cws); void cleanup(); int getMaxCount(); int getUsedCount(); } }