sounds: improve sound api, play packet sounds, 3d surround sound

This commit is contained in:
Bixilon 2021-05-24 19:40:00 +02:00
parent 504302025f
commit 4534304d15
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
8 changed files with 125 additions and 17 deletions

View File

@ -28,6 +28,5 @@ data class SoundEvent(
resourceLocation = resourceLocation, resourceLocation = resourceLocation,
) )
} }
} }
} }

View File

@ -24,6 +24,7 @@ import de.bixilon.minosoft.data.mappings.items.tools.MiningToolItem
import de.bixilon.minosoft.data.player.Hands import de.bixilon.minosoft.data.player.Hands
import de.bixilon.minosoft.gui.rendering.RenderConstants import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.RenderWindow 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.CallbackEventInvoker
import de.bixilon.minosoft.modding.event.events.BlockBreakAckEvent import de.bixilon.minosoft.modding.event.events.BlockBreakAckEvent
import de.bixilon.minosoft.protocol.packets.c2s.play.ArmSwingC2SP import de.bixilon.minosoft.protocol.packets.c2s.play.ArmSwingC2SP
@ -133,7 +134,7 @@ class LeftClickHandler(
clearDigging() clearDigging()
connection.world.setBlockState(raycastHit.blockPosition, null) 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 val canStartBreaking = currentTime - breakSent >= ProtocolDefinition.TICK_TIME

View File

@ -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.RenderConstants
import de.bixilon.minosoft.gui.rendering.RenderWindow import de.bixilon.minosoft.gui.rendering.RenderWindow
import de.bixilon.minosoft.gui.rendering.modding.events.CameraMatrixChangeEvent 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.FrustumChangeEvent
import de.bixilon.minosoft.gui.rendering.modding.events.ScreenResizeEvent import de.bixilon.minosoft.gui.rendering.modding.events.ScreenResizeEvent
import de.bixilon.minosoft.gui.rendering.sky.SkyRenderer import de.bixilon.minosoft.gui.rendering.sky.SkyRenderer
@ -173,6 +174,7 @@ class Camera(
skyRenderer.setSkyColor(RenderConstants.BLACK_COLOR) skyRenderer.setSkyColor(RenderConstants.BLACK_COLOR)
} }
} ?: skyRenderer.setSkyColor(RenderConstants.DEFAULT_SKY_COLOR) } ?: skyRenderer.setSkyColor(RenderConstants.DEFAULT_SKY_COLOR)
connection.fireEvent(CameraPositionChangeEvent(renderWindow, cameraPosition))
} }
private fun calculateProjectionMatrix(screenDimensions: Vec2): Mat4 { private fun calculateProjectionMatrix(screenDimensions: Vec2): Mat4 {
@ -302,6 +304,7 @@ class Camera(
fun setPosition(position: Vec3) { fun setPosition(position: Vec3) {
playerEntity.position = position playerEntity.position = position
cameraPosition = getAbsoluteCameraPosition() cameraPosition = getAbsoluteCameraPosition()
positionChangeCallback()
} }
fun getTargetBlock(): RaycastHit? { fun getTargetBlock(): RaycastHit? {
@ -339,7 +342,7 @@ class Camera(
} }
companion object { 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_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 private const val PLAYER_SPRINT_SPEED_MODIFIER = 1.30000001192092896

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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)
}

View File

@ -19,8 +19,13 @@ import com.google.gson.JsonPrimitive
import de.bixilon.minosoft.data.mappings.ResourceLocation import de.bixilon.minosoft.data.mappings.ResourceLocation
import de.bixilon.minosoft.data.mappings.sounds.SoundEvent import de.bixilon.minosoft.data.mappings.sounds.SoundEvent
import de.bixilon.minosoft.gui.rendering.Rendering 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.Sound
import de.bixilon.minosoft.gui.rendering.sound.sounds.SoundList 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.network.connection.PlayConnection
import de.bixilon.minosoft.protocol.protocol.ConnectionStates import de.bixilon.minosoft.protocol.protocol.ConnectionStates
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition 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.Log
import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType import de.bixilon.minosoft.util.logging.LogMessageType
import glm_.vec3.Vec3
import org.lwjgl.openal.AL import org.lwjgl.openal.AL
import org.lwjgl.openal.ALC import org.lwjgl.openal.ALC
import org.lwjgl.openal.ALC10.* import org.lwjgl.openal.ALC10.*
@ -99,6 +105,17 @@ class AudioPlayer(
listener = SoundListener() listener = SoundListener()
connection.registerEvent(CallbackEventInvoker.of<CameraPositionChangeEvent> {
queue += {
listener.position = it.newPosition
listener.setOrientation(it.renderWindow.inputHandler.camera.cameraFront, Camera.CAMERA_UP_VEC3)
}
})
connection.registerEvent(CallbackEventInvoker.of<PlaySoundEvent> {
playSoundEvent(it.soundEvent, it.position.toVec3, it.volume, it.pitch)
})
Log.log(LogMessageType.RENDERING_LOADING, LogLevels.INFO) { "OpenAL loaded!" } Log.log(LogMessageType.RENDERING_LOADING, LogLevels.INFO) { "OpenAL loaded!" }
@ -106,11 +123,10 @@ class AudioPlayer(
latch.countDown() latch.countDown()
} }
fun playSoundEvent(soundEvent: SoundEvent) { fun playSoundEvent(soundEvent: SoundEvent, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
playSound(sounds[soundEvent]!!.getRandom()) playSound(sounds[soundEvent]!!.getRandom(), position, volume, pitch)
} }
private fun getAvailableSource(): SoundSource? { private fun getAvailableSource(): SoundSource? {
for (source in sources.toSynchronizedList()) { for (source in sources.toSynchronizedList()) {
if (source.available) { if (source.available) {
@ -121,24 +137,34 @@ class AudioPlayer(
if (sources.size > SoundConstants.MAX_SOURCES_AMOUNT) { if (sources.size > SoundConstants.MAX_SOURCES_AMOUNT) {
return null return null
} }
val source = SoundSource(false) val source = SoundSource()
sources += source sources += source
return source return source
} }
fun playSound(sound: Sound) { fun playSound(sound: Sound, position: Vec3? = null, volume: Float = 1.0f, pitch: Float = 1.0f) {
queue += add@{ queue += add@{
sound.load(connection.assetsManager) sound.load(connection.assetsManager)
if (sound.loadFailed) { if (sound.loadFailed) {
return@add return@add
} }
val source = getAvailableSource() ?: let { 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!" } Log.log(LogMessageType.RENDERING_GENERAL, LogLevels.WARN) { "Can not play sound: No source available!" }
return@add return@add
} }
position?.let {
source.relative = false
source.position = it
} ?: let {
source.position = Vec3(0, 0, 0)
source.relative = true
}
source.sound = sound source.sound = sound
source.pitch = pitch * sound.pitch
source.gain = volume * sound.volume
source.play() source.play()
} }
} }

View File

@ -18,16 +18,20 @@ import de.bixilon.minosoft.gui.rendering.util.VecUtil.EMPTY
import glm_.vec3.Vec3 import glm_.vec3.Vec3
import org.lwjgl.openal.AL10.* import org.lwjgl.openal.AL10.*
class SoundSource(loop: Boolean = false) { class SoundSource {
private val source: Int = alGenSources() private val source: Int = alGenSources()
init { var loop: Boolean = false
if (loop) { set(value) {
alSourcei(source, AL_LOOPING, AL_TRUE) alSourcei(source, AL_LOOPING, value.AL_VALUE)
} else { field = value
alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE) }
var relative: Boolean = false
set(value) {
alSourcei(source, AL_SOURCE_RELATIVE, value.AL_VALUE)
field = value
} }
}
var position: Vec3 = Vec3.EMPTY var position: Vec3 = Vec3.EMPTY
set(value) { set(value) {
@ -68,7 +72,7 @@ class SoundSource(loop: Boolean = false) {
get() = alGetSourcei(source, AL_SOURCE_STATE) == AL_PLAYING get() = alGetSourcei(source, AL_SOURCE_STATE) == AL_PLAYING
val available: Boolean val available: Boolean
get() = isPlaying get() = !isPlaying
fun play() { fun play() {
alSourcePlay(source) alSourcePlay(source)
@ -87,4 +91,13 @@ class SoundSource(loop: Boolean = false) {
alDeleteSources(source) alDeleteSources(source)
} }
companion object {
val Boolean.AL_VALUE: Int
get() = if (this) {
AL_TRUE
} else {
AL_FALSE
}
}
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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)
}

View File

@ -14,6 +14,8 @@ package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.data.SoundCategories import de.bixilon.minosoft.data.SoundCategories
import de.bixilon.minosoft.data.mappings.sounds.SoundEvent 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.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition 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) position = Vec3i(buffer.readFixedPointNumberInt() * 4, buffer.readFixedPointNumberInt() * 4, buffer.readFixedPointNumberInt() * 4)
volume = buffer.readFloat() volume = buffer.readFloat()
pitch = if (buffer.versionId < ProtocolVersions.V_16W20A) { pitch = if (buffer.versionId < ProtocolVersions.V_16W20A) {
buffer.readByte() * ProtocolDefinition.PITCH_CALCULATION_CONSTANT / 100f buffer.readByte() * ProtocolDefinition.PITCH_CALCULATION_CONSTANT / 100.0f
} else { } else {
buffer.readFloat() buffer.readFloat()
} }
} }
override fun handle(connection: PlayConnection) {
connection.fireEvent(PlaySoundEvent(connection, this))
}
override fun log() { override fun log() {
Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "SoundEvent effect (category=$category, position=$position, soundEvent=$soundEvent, volume=$volume, pitch=$pitch)" } Log.log(LogMessageType.NETWORK_PACKETS_IN, level = LogLevels.VERBOSE) { "SoundEvent effect (category=$category, position=$position, soundEvent=$soundEvent, volume=$volume, pitch=$pitch)" }
} }