From 4534304d1546e9e7b72a7c85ceb4c0095598bc5d Mon Sep 17 00:00:00 2001 From: Bixilon Date: Mon, 24 May 2021 19:40:00 +0200 Subject: [PATCH] sounds: improve sound api, play packet sounds, 3d surround sound --- .../data/mappings/sounds/SoundEvent.kt | 1 - .../gui/rendering/input/LeftClickHandler.kt | 3 +- .../gui/rendering/input/camera/Camera.kt | 5 ++- .../events/CameraPositionChangeEvent.kt | 26 ++++++++++++++ .../gui/rendering/sound/AudioPlayer.kt | 36 ++++++++++++++++--- .../gui/rendering/sound/SoundSource.kt | 29 ++++++++++----- .../modding/event/events/PlaySoundEvent.kt | 34 ++++++++++++++++++ .../packets/s2c/play/SoundEventS2CP.kt | 8 ++++- 8 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/modding/events/CameraPositionChangeEvent.kt create mode 100644 src/main/java/de/bixilon/minosoft/modding/event/events/PlaySoundEvent.kt diff --git a/src/main/java/de/bixilon/minosoft/data/mappings/sounds/SoundEvent.kt b/src/main/java/de/bixilon/minosoft/data/mappings/sounds/SoundEvent.kt index e44650af8..f6473503b 100644 --- a/src/main/java/de/bixilon/minosoft/data/mappings/sounds/SoundEvent.kt +++ b/src/main/java/de/bixilon/minosoft/data/mappings/sounds/SoundEvent.kt @@ -28,6 +28,5 @@ data class SoundEvent( resourceLocation = resourceLocation, ) } - } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/input/LeftClickHandler.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/input/LeftClickHandler.kt index 308ce407e..149b7596a 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/input/LeftClickHandler.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/input/LeftClickHandler.kt @@ -24,6 +24,7 @@ import de.bixilon.minosoft.data.mappings.items.tools.MiningToolItem import de.bixilon.minosoft.data.player.Hands import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.RenderWindow +import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3 import de.bixilon.minosoft.modding.event.CallbackEventInvoker import de.bixilon.minosoft.modding.event.events.BlockBreakAckEvent import de.bixilon.minosoft.protocol.packets.c2s.play.ArmSwingC2SP @@ -133,7 +134,7 @@ class LeftClickHandler( clearDigging() connection.world.setBlockState(raycastHit.blockPosition, null) - renderWindow.rendering.audioPlayer.playSoundEvent(connection.registries.soundEventRegistry[BLOCK_BREAK_SOUND]!!) // ToDO + renderWindow.rendering.audioPlayer.playSoundEvent(connection.registries.soundEventRegistry[BLOCK_BREAK_SOUND]!!, raycastHit.blockPosition.toVec3) // ToDo } val canStartBreaking = currentTime - breakSent >= ProtocolDefinition.TICK_TIME diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt index 5142136c3..7d12ebd01 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/input/camera/Camera.kt @@ -21,6 +21,7 @@ import de.bixilon.minosoft.data.mappings.biomes.Biome import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.RenderWindow import de.bixilon.minosoft.gui.rendering.modding.events.CameraMatrixChangeEvent +import de.bixilon.minosoft.gui.rendering.modding.events.CameraPositionChangeEvent import de.bixilon.minosoft.gui.rendering.modding.events.FrustumChangeEvent import de.bixilon.minosoft.gui.rendering.modding.events.ScreenResizeEvent import de.bixilon.minosoft.gui.rendering.sky.SkyRenderer @@ -173,6 +174,7 @@ class Camera( skyRenderer.setSkyColor(RenderConstants.BLACK_COLOR) } } ?: skyRenderer.setSkyColor(RenderConstants.DEFAULT_SKY_COLOR) + connection.fireEvent(CameraPositionChangeEvent(renderWindow, cameraPosition)) } private fun calculateProjectionMatrix(screenDimensions: Vec2): Mat4 { @@ -302,6 +304,7 @@ class Camera( fun setPosition(position: Vec3) { playerEntity.position = position cameraPosition = getAbsoluteCameraPosition() + positionChangeCallback() } fun getTargetBlock(): RaycastHit? { @@ -339,7 +342,7 @@ class Camera( } companion object { - private val CAMERA_UP_VEC3 = Vec3(0.0f, 1.0f, 0.0f) + val CAMERA_UP_VEC3 = Vec3(0.0f, 1.0f, 0.0f) private const val PLAYER_EYE_HEIGHT = 1.3 // player is 1.8 blocks high, the camera is normally at 0.5. 1.8 - 0.5 = 1.3 private const val PLAYER_SPRINT_SPEED_MODIFIER = 1.30000001192092896 diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/modding/events/CameraPositionChangeEvent.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/modding/events/CameraPositionChangeEvent.kt new file mode 100644 index 000000000..ea64529d5 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/modding/events/CameraPositionChangeEvent.kt @@ -0,0 +1,26 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.modding.events + +import de.bixilon.minosoft.gui.rendering.RenderWindow +import de.bixilon.minosoft.gui.rendering.Rendering +import glm_.vec3.Vec3 + +class CameraPositionChangeEvent( + renderWindow: RenderWindow = Rendering.currentContext!!, + newPosition: Vec3, +) : RenderEvent(renderWindow) { + val newPosition: Vec3 = newPosition + get() = Vec3(field) +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/sound/AudioPlayer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/sound/AudioPlayer.kt index 2209366fc..07a33b1c0 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/sound/AudioPlayer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/sound/AudioPlayer.kt @@ -19,8 +19,13 @@ import com.google.gson.JsonPrimitive import de.bixilon.minosoft.data.mappings.ResourceLocation import de.bixilon.minosoft.data.mappings.sounds.SoundEvent import de.bixilon.minosoft.gui.rendering.Rendering +import de.bixilon.minosoft.gui.rendering.input.camera.Camera +import de.bixilon.minosoft.gui.rendering.modding.events.CameraPositionChangeEvent import de.bixilon.minosoft.gui.rendering.sound.sounds.Sound import de.bixilon.minosoft.gui.rendering.sound.sounds.SoundList +import de.bixilon.minosoft.gui.rendering.util.VecUtil.toVec3 +import de.bixilon.minosoft.modding.event.CallbackEventInvoker +import de.bixilon.minosoft.modding.event.events.PlaySoundEvent import de.bixilon.minosoft.protocol.network.connection.PlayConnection import de.bixilon.minosoft.protocol.protocol.ConnectionStates import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition @@ -32,6 +37,7 @@ import de.bixilon.minosoft.util.Queue import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogMessageType +import glm_.vec3.Vec3 import org.lwjgl.openal.AL import org.lwjgl.openal.ALC import org.lwjgl.openal.ALC10.* @@ -99,6 +105,17 @@ class AudioPlayer( listener = SoundListener() + connection.registerEvent(CallbackEventInvoker.of { + queue += { + listener.position = it.newPosition + listener.setOrientation(it.renderWindow.inputHandler.camera.cameraFront, Camera.CAMERA_UP_VEC3) + } + }) + + connection.registerEvent(CallbackEventInvoker.of { + playSoundEvent(it.soundEvent, it.position.toVec3, it.volume, it.pitch) + }) + Log.log(LogMessageType.RENDERING_LOADING, LogLevels.INFO) { "OpenAL loaded!" } @@ -106,11 +123,10 @@ class AudioPlayer( latch.countDown() } - fun playSoundEvent(soundEvent: SoundEvent) { - playSound(sounds[soundEvent]!!.getRandom()) + fun playSoundEvent(soundEvent: SoundEvent, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) { + playSound(sounds[soundEvent]!!.getRandom(), position, volume, pitch) } - private fun getAvailableSource(): SoundSource? { for (source in sources.toSynchronizedList()) { if (source.available) { @@ -121,24 +137,34 @@ class AudioPlayer( if (sources.size > SoundConstants.MAX_SOURCES_AMOUNT) { return null } - val source = SoundSource(false) + val source = SoundSource() sources += source return source } - fun playSound(sound: Sound) { + fun playSound(sound: Sound, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) { queue += add@{ sound.load(connection.assetsManager) if (sound.loadFailed) { return@add } val source = getAvailableSource() ?: let { + // ToDo: Queue sound for later (and check a certain delay to not make the game feel laggy) Log.log(LogMessageType.RENDERING_GENERAL, LogLevels.WARN) { "Can not play sound: No source available!" } return@add } + position?.let { + source.relative = false + source.position = it + } ?: let { + source.position = Vec3(0, 0, 0) + source.relative = true + } source.sound = sound + source.pitch = pitch * sound.pitch + source.gain = volume * sound.volume source.play() } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/sound/SoundSource.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/sound/SoundSource.kt index f506de72b..2e80837d7 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/sound/SoundSource.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/sound/SoundSource.kt @@ -18,16 +18,20 @@ import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY import glm_.vec3.Vec3 import org.lwjgl.openal.AL10.* -class SoundSource(loop: Boolean = false) { +class SoundSource { private val source: Int = alGenSources() - init { - if (loop) { - alSourcei(source, AL_LOOPING, AL_TRUE) - } else { - alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE) + var loop: Boolean = false + set(value) { + alSourcei(source, AL_LOOPING, value.AL_VALUE) + field = value + } + + var relative: Boolean = false + set(value) { + alSourcei(source, AL_SOURCE_RELATIVE, value.AL_VALUE) + field = value } - } var position: Vec3 = Vec3.EMPTY set(value) { @@ -68,7 +72,7 @@ class SoundSource(loop: Boolean = false) { get() = alGetSourcei(source, AL_SOURCE_STATE) == AL_PLAYING val available: Boolean - get() = isPlaying + get() = !isPlaying fun play() { alSourcePlay(source) @@ -87,4 +91,13 @@ class SoundSource(loop: Boolean = false) { alDeleteSources(source) } + companion object { + val Boolean.AL_VALUE: Int + get() = if (this) { + AL_TRUE + } else { + AL_FALSE + } + } + } diff --git a/src/main/java/de/bixilon/minosoft/modding/event/events/PlaySoundEvent.kt b/src/main/java/de/bixilon/minosoft/modding/event/events/PlaySoundEvent.kt new file mode 100644 index 000000000..a2f9babdc --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/modding/event/events/PlaySoundEvent.kt @@ -0,0 +1,34 @@ +/* + * Minosoft + * Copyright (C) 2021 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.modding.event.events + +import de.bixilon.minosoft.data.SoundCategories +import de.bixilon.minosoft.data.mappings.sounds.SoundEvent +import de.bixilon.minosoft.protocol.network.connection.PlayConnection +import de.bixilon.minosoft.protocol.packets.s2c.play.SoundEventS2CP +import glm_.vec3.Vec3i + +class PlaySoundEvent( + connection: PlayConnection, + val category: SoundCategories?, + position: Vec3i, + val soundEvent: SoundEvent, + val volume: Float, + val pitch: Float, +) : CancelableEvent(connection) { + val position: Vec3i = position + get() = Vec3i(field) + + constructor(connection: PlayConnection, packet: SoundEventS2CP) : this(connection, packet.category, packet.position, packet.soundEvent, packet.volume, packet.pitch) +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/SoundEventS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/SoundEventS2CP.kt index f5d826b14..05c97b26b 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/SoundEventS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/SoundEventS2CP.kt @@ -14,6 +14,8 @@ package de.bixilon.minosoft.protocol.packets.s2c.play import de.bixilon.minosoft.data.SoundCategories import de.bixilon.minosoft.data.mappings.sounds.SoundEvent +import de.bixilon.minosoft.modding.event.events.PlaySoundEvent +import de.bixilon.minosoft.protocol.network.connection.PlayConnection import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition @@ -46,12 +48,16 @@ class SoundEventS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() { position = Vec3i(buffer.readFixedPointNumberInt() * 4, buffer.readFixedPointNumberInt() * 4, buffer.readFixedPointNumberInt() * 4) volume = buffer.readFloat() pitch = if (buffer.versionId < ProtocolVersions.V_16W20A) { - buffer.readByte() * ProtocolDefinition.PITCH_CALCULATION_CONSTANT / 100f + buffer.readByte() * ProtocolDefinition.PITCH_CALCULATION_CONSTANT / 100.0f } else { buffer.readFloat() } } + override fun handle(connection: PlayConnection) { + connection.fireEvent(PlaySoundEvent(connection, this)) + } + override fun log() { Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "SoundEvent effect (category=$category, position=$position, soundEvent=$soundEvent, volume=$volume, pitch=$pitch)" } }