Library.java

com.mojang.blaze3d.audio.Library

信息

  • 全限定名:com.mojang.blaze3d.audio.Library
  • 类型:public class
  • 包:com.mojang.blaze3d.audio
  • 源码路径:src/main/java/com/mojang/blaze3d/audio/Library.java
  • 起始行号:L26
  • 职责:

    TODO

字段/常量

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L27
    • 说明:

      TODO

  • NO_DEVICE

    • 类型: int
    • 修饰符: public static final
    • 源码定位: L28
    • 说明:

      TODO

  • NO_DEVICE_NAME

    • 类型: String
    • 修饰符: private static final
    • 源码定位: L29
    • 说明:

      TODO

  • DEFAULT_CHANNEL_COUNT

    • 类型: int
    • 修饰符: private static final
    • 源码定位: L30
    • 说明:

      TODO

  • currentDevice

    • 类型: long
    • 修饰符: private
    • 源码定位: L31
    • 说明:

      TODO

  • currentDeviceName

    • 类型: String
    • 修饰符: private
    • 源码定位: L32
    • 说明:

      TODO

  • context

    • 类型: long
    • 修饰符: private
    • 源码定位: L33
    • 说明:

      TODO

  • supportsDisconnections

    • 类型: boolean
    • 修饰符: private
    • 源码定位: L34
    • 说明:

      TODO

  • EMPTY

    • 类型: Library.ChannelPool
    • 修饰符: private static final public public public public public
    • 源码定位: L35
    • 说明:

      TODO

  • staticChannels

    • 类型: Library.ChannelPool
    • 修饰符: private
    • 源码定位: L60
    • 说明:

      TODO

  • streamingChannels

    • 类型: Library.ChannelPool
    • 修饰符: private
    • 源码定位: L61
    • 说明:

      TODO

  • listener

    • 类型: Listener
    • 修饰符: private final
    • 源码定位: L62
    • 说明:

      TODO

内部类/嵌套类型

  • com.mojang.blaze3d.audio.Library.ChannelPool

    • 类型: interface
    • 修饰符: private
    • 源码定位: L244
    • 说明:

      TODO

  • com.mojang.blaze3d.audio.Library.CountingChannelPool

    • 类型: class
    • 修饰符: private static
    • 源码定位: L257
    • 说明:

      TODO

  • com.mojang.blaze3d.audio.Library.Pool

    • 类型: enum
    • 修饰符: public static
    • 源码定位: L311
    • 说明:

      TODO

构造器

方法

下面的方法块按源码顺序生成。

public void init(String preferredDevice, DeviceList currentDevices, boolean useHrtf) @ L64

  • 方法名:init
  • 源码定位:L64
  • 返回类型:void
  • 修饰符:public

参数:

  • preferredDevice: String
  • currentDevices: DeviceList
  • useHrtf: boolean

说明:

TODO

private IntBuffer createAttributes(MemoryStack stack, boolean enableHrtf) @ L107

  • 方法名:createAttributes
  • 源码定位:L107
  • 返回类型:IntBuffer
  • 修饰符:private

参数:

  • stack: MemoryStack
  • enableHrtf: boolean

说明:

TODO

private int getChannelCount() @ L120

  • 方法名:getChannelCount
  • 源码定位:L120
  • 返回类型:int
  • 修饰符:private

参数:

说明:

TODO

public String currentDeviceName() @ L151

  • 方法名:currentDeviceName
  • 源码定位:L151
  • 返回类型:String
  • 修饰符:public

参数:

说明:

TODO

private static String queryDeviceName(long deviceId) @ L155

  • 方法名:queryDeviceName
  • 源码定位:L155
  • 返回类型:String
  • 修饰符:private static

参数:

  • deviceId: long

说明:

TODO

private static long openDeviceOrFallback(String preferredDevice, String systemDefaultDevice) @ L168

  • 方法名:openDeviceOrFallback
  • 源码定位:L168
  • 返回类型:long
  • 修饰符:private static

参数:

  • preferredDevice: String
  • systemDefaultDevice: String

说明:

TODO

private static OptionalLong tryOpenDevice(String name) @ L189

  • 方法名:tryOpenDevice
  • 源码定位:L189
  • 返回类型:OptionalLong
  • 修饰符:private static

参数:

  • name: String

说明:

TODO

public void cleanup() @ L194

  • 方法名:cleanup
  • 源码定位:L194
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public Listener getListener() @ L203

  • 方法名:getListener
  • 源码定位:L203
  • 返回类型:Listener
  • 修饰符:public

参数:

说明:

TODO

public Channel acquireChannel(Library.Pool pool) @ L207

  • 方法名:acquireChannel
  • 源码定位:L207
  • 返回类型:Channel
  • 修饰符:public

参数:

  • pool: Library.Pool

说明:

TODO

public void releaseChannel(Channel channel) @ L211

  • 方法名:releaseChannel
  • 源码定位:L211
  • 返回类型:void
  • 修饰符:public

参数:

  • channel: Channel

说明:

TODO

public String getChannelDebugString() @ L217

  • 方法名:getChannelDebugString
  • 源码定位:L217
  • 返回类型:String
  • 修饰符:public

参数:

说明:

TODO

public boolean isCurrentDeviceDisconnected() @ L228

  • 方法名:isCurrentDeviceDisconnected
  • 源码定位:L228
  • 返回类型:boolean
  • 修饰符:public

参数:

说明:

TODO

public static DeviceTracker createDeviceTracker() @ L232

  • 方法名:createDeviceTracker
  • 源码定位:L232
  • 返回类型:DeviceTracker
  • 修饰符:public static

参数:

说明:

TODO

代码

@OnlyIn(Dist.CLIENT)
public class Library {
    private static final Logger LOGGER = LogUtils.getLogger();
    public static final int NO_DEVICE = 0;
    private static final String NO_DEVICE_NAME = "(None)";
    private static final int DEFAULT_CHANNEL_COUNT = 30;
    private long currentDevice;
    private String currentDeviceName = "(None)";
    private long context;
    private boolean supportsDisconnections;
    private static final Library.ChannelPool EMPTY = new Library.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 Library.ChannelPool staticChannels = EMPTY;
    private Library.ChannelPool streamingChannels = EMPTY;
    private final Listener listener = new Listener();
 
    public void init(@Nullable String preferredDevice, DeviceList currentDevices, boolean useHrtf) {
        this.currentDeviceName = "(None)";
        this.currentDevice = openDeviceOrFallback(preferredDevice, currentDevices.defaultDevice());
        this.currentDeviceName = queryDeviceName(this.currentDevice);
        this.supportsDisconnections = false;
        ALCCapabilities alcCapabilities = ALC.createCapabilities(this.currentDevice);
        if (OpenAlUtil.checkALCError(this.currentDevice, "Get capabilities")) {
            throw new IllegalStateException("Failed to get OpenAL capabilities");
        } else if (!alcCapabilities.OpenALC11) {
            throw new IllegalStateException("OpenAL 1.1 not supported");
        } else {
            try (MemoryStack stack = MemoryStack.stackPush()) {
                IntBuffer attr = this.createAttributes(stack, alcCapabilities.ALC_SOFT_HRTF && useHrtf);
                this.context = ALC10.alcCreateContext(this.currentDevice, attr);
            }
 
            if (OpenAlUtil.checkALCError(this.currentDevice, "Create context")) {
                throw new IllegalStateException("Unable to create OpenAL context");
            } else {
                ALC10.alcMakeContextCurrent(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 Library.CountingChannelPool(staticChannelCount);
                this.streamingChannels = new Library.CountingChannelPool(streamingChannelCount);
                ALCapabilities alCapabilities = AL.createCapabilities(alcCapabilities);
                OpenAlUtil.checkALError("Initialization");
                if (!alCapabilities.AL_EXT_source_distance_model) {
                    throw new IllegalStateException("AL_EXT_source_distance_model is not supported");
                } else {
                    AL10.alEnable(512);
                    if (!alCapabilities.AL_EXT_LINEAR_DISTANCE) {
                        throw new IllegalStateException("AL_EXT_LINEAR_DISTANCE is not supported");
                    } else {
                        OpenAlUtil.checkALError("Enable per-source distance models");
                        LOGGER.info("OpenAL initialized on device {}", this.currentDeviceName);
                        this.supportsDisconnections = ALC10.alcIsExtensionPresent(this.currentDevice, "ALC_EXT_disconnect");
                    }
                }
            }
        }
    }
 
    private IntBuffer createAttributes(MemoryStack stack, boolean enableHrtf) {
        int maxAttributes = 5;
        IntBuffer attr = stack.callocInt(11);
        int numHrtf = ALC10.alcGetInteger(this.currentDevice, 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(this.currentDevice, 4098);
            if (OpenAlUtil.checkALCError(this.currentDevice, "Get attributes size")) {
                throw new IllegalStateException("Failed to get OpenAL attributes");
            }
 
            IntBuffer attributes = stack.mallocInt(size);
            ALC10.alcGetIntegerv(this.currentDevice, 4099, attributes);
            if (OpenAlUtil.checkALCError(this.currentDevice, "Get attributes")) {
                throw new IllegalStateException("Failed to get OpenAL attributes");
            }
 
            int pos = 0;
 
            while (pos < size) {
                int attribute = attributes.get(pos++);
                if (attribute == 0) {
                    break;
                }
 
                int attributeValue = attributes.get(pos++);
                if (attribute == 4112) {
                    return attributeValue;
                }
            }
        }
 
        return 30;
    }
 
    public @Nullable String currentDeviceName() {
        return this.currentDeviceName;
    }
 
    private static String queryDeviceName(long deviceId) {
        String name = ALC10.alcGetString(deviceId, 4115);
        if (name == null) {
            name = ALC10.alcGetString(deviceId, 4101);
        }
 
        if (name == null) {
            name = "Unknown (0x" + HexFormat.of().toHexDigits(deviceId) + ")";
        }
 
        return name;
    }
 
    private static long openDeviceOrFallback(@Nullable String preferredDevice, @Nullable String systemDefaultDevice) {
        OptionalLong device = OptionalLong.empty();
        if (preferredDevice != null) {
            device = tryOpenDevice(preferredDevice);
        }
 
        if (device.isEmpty() && systemDefaultDevice != null) {
            device = tryOpenDevice(systemDefaultDevice);
        }
 
        if (device.isEmpty()) {
            device = tryOpenDevice(null);
        }
 
        if (device.isEmpty()) {
            throw new IllegalStateException("Failed to open OpenAL device");
        } else {
            return device.getAsLong();
        }
    }
 
    private static OptionalLong tryOpenDevice(@Nullable String name) {
        long device = ALC10.alcOpenDevice(name);
        return device != 0L && !OpenAlUtil.checkALCError(device, "Open device") ? OptionalLong.of(device) : OptionalLong.empty();
    }
 
    public void cleanup() {
        this.staticChannels.cleanup();
        this.streamingChannels.cleanup();
        ALC10.alcDestroyContext(this.context);
        if (this.currentDevice != 0L) {
            ALC10.alcCloseDevice(this.currentDevice);
        }
    }
 
    public Listener getListener() {
        return this.listener;
    }
 
    public @Nullable Channel acquireChannel(Library.Pool pool) {
        return (pool == Library.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 getChannelDebugString() {
        return String.format(
            Locale.ROOT,
            "Sounds: %d/%d + %d/%d",
            this.staticChannels.getUsedCount(),
            this.staticChannels.getMaxCount(),
            this.streamingChannels.getUsedCount(),
            this.streamingChannels.getMaxCount()
        );
    }
 
    public boolean isCurrentDeviceDisconnected() {
        return this.supportsDisconnections && ALC11.alcGetInteger(this.currentDevice, 787) == 0;
    }
 
    public static DeviceTracker createDeviceTracker() {
        DeviceList deviceList = DeviceList.query();
        if (CallbackDeviceTracker.isSupported()) {
            LOGGER.debug("Using SOFT_system_events callback for tracking audio device changes");
            return CallbackDeviceTracker.createAndInstall(deviceList);
        } else {
            LOGGER.debug("Using polling for tracking audio device changes");
            return new PollingDeviceTracker(deviceList);
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    private interface ChannelPool {
        @Nullable Channel acquire();
 
        boolean release(Channel channel);
 
        void cleanup();
 
        int getMaxCount();
 
        int getUsedCount();
    }
 
    @OnlyIn(Dist.CLIENT)
    private static class CountingChannelPool implements Library.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) {
                    Library.LOGGER.warn("Maximum sound pool size {} reached", this.limit);
                }
 
                return null;
            } else {
                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;
            } else {
                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();
        }
    }
 
    @OnlyIn(Dist.CLIENT)
    public static enum Pool {
        STATIC,
        STREAMING;
    }
}

引用的其他类

  • CallbackDeviceTracker

    • 引用位置: 方法调用
    • 关联成员: CallbackDeviceTracker.createAndInstall(), CallbackDeviceTracker.isSupported()
  • Channel

    • 引用位置: 参数/方法调用/返回值
    • 关联成员: Channel.create()
  • DeviceList

    • 引用位置: 参数/方法调用
    • 关联成员: DeviceList.query()
  • DeviceTracker

    • 引用位置: 返回值
  • Listener

    • 引用位置: 字段/构造调用/返回值
    • 关联成员: Listener()
  • OpenAlUtil

    • 引用位置: 方法调用
    • 关联成员: OpenAlUtil.checkALCError(), OpenAlUtil.checkALError()
  • PollingDeviceTracker

    • 引用位置: 构造调用
    • 关联成员: PollingDeviceTracker()
  • Mth

    • 引用位置: 方法调用
    • 关联成员: Mth.clamp(), Mth.sqrt()