Fixes for pegasus flying wind sound effect

This fixes several aspects of the wind sound effect:
-Interpolation of FadeOutSoundInstance breaking the effect's volume controls
-Attenuation and panning (3D audio) should now work correctly for other clients
-New attenuation curve for velocity based attenuation
-New pitch controls same as above
-Made effect a continuous loop
-Added rate limiter so that sudden movements won't cause volume and pitch jolts (storms?)
This commit is contained in:
Slipstream 2022-10-03 20:30:43 +02:00
parent 69c8ceac45
commit e117fe44ea
6 changed files with 55 additions and 14 deletions

View file

@ -10,6 +10,7 @@ public interface USounds {
SoundEvent ENTITY_PLAYER_BATPONY_SCREECH = register("entity.player.batpony.screech");
SoundEvent ENTITY_PLAYER_REBOUND = register("entity.player.rebound");
SoundEvent ENTITY_PLAYER_PEGASUS_WINGSFLAP = register("entity.player.pegasus.wingsflap");
SoundEvent ENTITY_PLAYER_PEGASUS_FLYING = register("entity.player.pegasus.flying");
SoundEvent ENTITY_PLAYER_PEGASUS_DASH = register("entity.player.pegasus.dash");
SoundEvent ENTITY_PLAYER_PEGASUS_MOLT = register("entity.player.pegasus.molt");
SoundEvent ENTITY_PLAYER_CHANGELING_BUZZ = register("entity.player.changeling.buzz");

View file

@ -75,8 +75,10 @@ public class ClientInteractionManager extends InteractionManager {
PlayerPhysics physics = Pony.of(e).getPhysics();
return physics.isFlying() && physics.getFlightType() == FlightType.INSECTOID;
}, USounds.ENTITY_PLAYER_CHANGELING_BUZZ, 1F, 1F, Random.create(seed)));
} else if (type == SOUND_GLIDING && source instanceof PlayerEntity) {
} else if (type == SOUND_GLIDING && source instanceof PlayerEntity && isClientPlayer((PlayerEntity) source)) {
soundManager.play(new MotionBasedSoundInstance(SoundEvents.ITEM_ELYTRA_FLYING, (PlayerEntity)source, Random.create(seed)));
} else if (type == SOUND_GLIDING && source instanceof PlayerEntity) {
soundManager.play(new MotionBasedSoundInstance(USounds.ENTITY_PLAYER_PEGASUS_FLYING, (PlayerEntity)source, Random.create(seed)));
} else if (type == SOUND_MAGIC_BEAM) {
soundManager.play(new LoopedEntityTrackingSoundInstance(USounds.SPELL_CAST_SHOOT, 0.3F, 1F, source, seed));
}

View file

@ -81,6 +81,12 @@ public abstract class FadeOutSoundInstance extends MovingSoundInstance {
progress = 0;
}
protected void setVolume(float vol){
sourceVolume = vol;
targetVolume = vol;
progress = 0;
}
private float getLerpedVolume() {
float delta = MinecraftClient.getInstance().getTickDelta();
float interpolate = MathHelper.clamp(MathHelper.lerp(delta, prevProgress, progress) / transitionTicks, 0, 1);

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.client.sound;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.math.MathHelper;
@ -11,18 +12,30 @@ public class MotionBasedSoundInstance extends FadeOutSoundInstance {
private final PlayerEntity player;
// Tune these if you want to change the way this sounds!
// Currently just hardcoded for flying because nothing else uses this class
private static final float MAX_VELOCITY = 1.5f; // Max velocity before we clamp volume (units/tick?)
private static final float VOLUME_AT_MAX = 1.0f;
private static final float ATTEN_EXPO = 2.0f; // Exponent for velocity based attenuation
private static final int FADEIN_TICKS = 20; // Ticks for fade-in
private static final float MIN_PITCH = 0.7f; // Pitch at 0-speed
private static final float MAX_PITCH = 2.6f; // Pitch at reference speed MAX_VELOCITY
private static final float MAX_RATE_TICKS = 20.0f; // How many ticks it takes to go from 0 to max volume (filter!)
private int tickCount;
private float currentVal; // Cache last tick's curve value
public MotionBasedSoundInstance(SoundEvent sound, PlayerEntity player, Random random) {
super(sound, player.getSoundCategory(), 0.1F, random);
this.player = player;
currentVal = 0.0f;
}
@Override
protected boolean shouldKeepPlaying() {
++tickCount;
if (player.isRemoved() || tickCount > 200) {
if (player.isRemoved()) {
return false;
}
@ -32,30 +45,41 @@ public class MotionBasedSoundInstance extends FadeOutSoundInstance {
return false;
}
// Update effect position
x = ((float)player.getX());
y = ((float)player.getY());
z = ((float)this.player.getZ());
z = ((float)player.getZ());
// Get velocity
float f = (float)player.getVelocity().horizontalLength();
float lastVal = currentVal;
// First we normalise the volume to the maximum velocity we're targeting, then we make it a curve.
// Drag is not linear, and neither is the woosh it produces, so a curve makes it sound more natural.
currentVal = (float) Math.pow(MathHelper.clamp(f / MAX_VELOCITY, 0, 1),ATTEN_EXPO);
// Primitive lowpass filter/rate limiter thingy to rule out sudden jolts
currentVal = lastVal + MathHelper.clamp(currentVal - lastVal, -(1/MAX_RATE_TICKS), 1/MAX_RATE_TICKS);
if (f >= 1.0E-7D) {
volume = MathHelper.clamp(f / 4F, 0, 1);
// Multiply output volume by reference volume for overall gain control.
volume = currentVal * VOLUME_AT_MAX;
} else {
volume = 0.0F;
}
if (tickCount < 20) {
volume = 0;
} else if (tickCount < 40) {
volume = (float)(volume * ((tickCount - 20) / 20D));
// If we only just started playing, fade in!
if (tickCount < FADEIN_TICKS) {
volume *= ((tickCount) / (float)FADEIN_TICKS);
}
if (volume > 0.8F) {
pitch = 1 + (volume - 0.8F);
} else {
pitch = 1;
}
// Control pitch with velocity
pitch = MathHelper.lerp(currentVal,MIN_PITCH,MAX_PITCH);
setTargetVolume(volume);
// Setting target volume every frame breaks interpolation. We set volume directly,
// so that FadeOutSoundInstance only handles stopping the sound with an actual fade out!
setVolume(volume);
return true;
}

View file

@ -21,6 +21,14 @@
"unicopia:woosh/woosh5"
]
},
"entity.player.pegasus.flying": {
"sounds": [
{
"name": "unicopia:wind/flying_loop",
"volume": 0.6
}
]
},
"item.magic.aura": {
"category": "player",
"subtitle": "unicopia.subtitle.magic_aura",