223 lines
7.2 KiB
Java
223 lines
7.2 KiB
Java
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 dft) {
|
|
if (!this.staticChannels.release(dft) && !this.streamingChannels.release(dft)) {
|
|
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 dft) {
|
|
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<Channel> activeChannels;
|
|
|
|
public CountingChannelPool(final int integer) {
|
|
this.activeChannels = Sets.<Channel>newIdentityHashSet();
|
|
this.limit = integer;
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public Channel acquire() {
|
|
if (this.activeChannels.size() >= this.limit) {
|
|
Library.LOGGER.warn("Maximum sound pool size {} reached", this.limit);
|
|
return null;
|
|
}
|
|
final Channel dft2 = Channel.create();
|
|
if (dft2 != null) {
|
|
this.activeChannels.add(dft2);
|
|
}
|
|
return dft2;
|
|
}
|
|
|
|
@Override
|
|
public boolean release(final Channel dft) {
|
|
if (!this.activeChannels.remove(dft)) {
|
|
return false;
|
|
}
|
|
dft.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 dft);
|
|
|
|
void cleanup();
|
|
|
|
int getMaxCount();
|
|
|
|
int getUsedCount();
|
|
}
|
|
}
|