diff --git a/src/main/java/org/blockartistry/mod/DynSurround/client/PlayerSoundEffectHandler.java b/src/main/java/org/blockartistry/mod/DynSurround/client/PlayerSoundEffectHandler.java index 0754714..fa0c494 100644 --- a/src/main/java/org/blockartistry/mod/DynSurround/client/PlayerSoundEffectHandler.java +++ b/src/main/java/org/blockartistry/mod/DynSurround/client/PlayerSoundEffectHandler.java @@ -24,9 +24,7 @@ package org.blockartistry.mod.DynSurround.client; -import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Random; @@ -39,25 +37,23 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.audio.ISound; -import net.minecraft.client.audio.MovingSound; import net.minecraft.client.audio.PositionedSound; import net.minecraft.client.audio.SoundHandler; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.BlockPos; +import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraft.world.biome.BiomeGenBase; import net.minecraftforge.fml.relauncher.SideOnly; -import paulscode.sound.SoundSystemConfig; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.relauncher.Side; +import paulscode.sound.SoundSystemConfig; @SideOnly(Side.CLIENT) public class PlayerSoundEffectHandler implements IClientEffectHandler { private static final Random RANDOM = new XorShiftRandom(); - private static final float VOLUME_INCREMENT = 0.02F; - private static final float VOLUME_DECREMENT = 0.015F; private static final float MASTER_SCALE_FACTOR = ModOptions.getMasterSoundScaleFactor(); private static final int SPOT_SOUND_RANGE = 6; private static final int SOUND_QUEUE_SLACK = 12; @@ -87,9 +83,12 @@ public SpotSound(final EntityPlayer player, final SoundEffect sound) { this.repeat = false; this.repeatDelay = 0; - this.xPosF = (float) player.posX + RANDOM.nextInt(SPOT_SOUND_RANGE) - RANDOM.nextInt(SPOT_SOUND_RANGE); - this.yPosF = (float) player.posY + 1 + RANDOM.nextInt(SPOT_SOUND_RANGE) - RANDOM.nextInt(SPOT_SOUND_RANGE); - this.zPosF = (float) player.posZ + RANDOM.nextInt(SPOT_SOUND_RANGE) - RANDOM.nextInt(SPOT_SOUND_RANGE); + this.xPosF = MathHelper + .floor_double(player.posX + RANDOM.nextInt(SPOT_SOUND_RANGE) - RANDOM.nextInt(SPOT_SOUND_RANGE)); + this.yPosF = MathHelper.floor_double( + player.posY + 1 + RANDOM.nextInt(SPOT_SOUND_RANGE) - RANDOM.nextInt(SPOT_SOUND_RANGE)); + this.zPosF = MathHelper + .floor_double(player.posZ + RANDOM.nextInt(SPOT_SOUND_RANGE) - RANDOM.nextInt(SPOT_SOUND_RANGE)); } @Override @@ -99,116 +98,6 @@ public float getVolume() { } - private static class PlayerSound extends MovingSound { - private boolean fadeAway; - private final WeakReference player; - private final SoundEffect sound; - - public PlayerSound(final EntityPlayer player, final SoundEffect sound) { - this(player, sound, true); - } - - public PlayerSound(final EntityPlayer player, final SoundEffect sound, final boolean repeat) { - super(new ResourceLocation(sound.sound)); - // Don't set volume to 0; MC will optimize out - this.sound = sound; - this.volume = (repeat && !sound.skipFade) ? 0.01F : sound.volume; - this.pitch = sound.pitch; - this.player = new WeakReference(player); - this.repeat = repeat; - this.fadeAway = false; - - // Repeat delay - this.repeatDelay = sound.repeatDelay; - - // Initial position - this.xPosF = (float) (player.posX); - this.yPosF = (float) (player.posY + 1); - this.zPosF = (float) (player.posZ); - } - - public void fadeAway() { - this.fadeAway = true; - if (this.sound.skipFade) { - this.volume = 0.0F; - this.donePlaying = true; - } - } - - public boolean sameSound(final SoundEffect snd) { - return this.sound.equals(snd); - } - - @Override - public void update() { - if (this.fadeAway) { - this.volume -= VOLUME_DECREMENT; - if (this.volume < 0.0F) { - this.volume = 0.0F; - } - } else if (this.volume < this.sound.volume) { - this.volume += VOLUME_INCREMENT; - if (this.volume > this.sound.volume) - this.volume = this.sound.volume; - } - - if (this.volume == 0.0F) - this.donePlaying = true; - } - - @Override - public float getVolume() { - return super.getVolume() * MASTER_SCALE_FACTOR; - } - - @Override - public float getXPosF() { - final EntityPlayer player = this.player.get(); - if (player == null) { - this.donePlaying = true; - return 0.0F; - } - return (float) player.posX; - } - - @Override - public float getYPosF() { - final EntityPlayer player = this.player.get(); - if (player == null) { - this.donePlaying = true; - return 0.0F; - } - return (float) player.posY + 1; - } - - @Override - public float getZPosF() { - final EntityPlayer player = this.player.get(); - if (player == null) { - this.donePlaying = true; - return 0.0F; - } - return (float) player.posZ; - } - - @Override - public String toString() { - return this.sound.toString(); - } - - @Override - public boolean equals(final Object anObj) { - if (this == anObj) - return true; - if (anObj instanceof PlayerSound) - return this.sameSound(((PlayerSound) anObj).sound); - if (anObj instanceof SoundEffect) - return this.sameSound((SoundEffect) anObj); - return false; - } - - } - private static boolean didReloadOccur() { final int count = BiomeRegistry.getReloadCount(); if (count != reloadTracker) { @@ -230,37 +119,6 @@ private boolean canFitSound() { return currentSoundCount() < (maxSoundCount() - SOUND_QUEUE_SLACK); } - // Active loop sounds - private static final List activeSounds = new ArrayList(); - - private static void clearSounds() { - for (final PlayerSound sound : activeSounds) - sound.fadeAway(); - activeSounds.clear(); - } - - private static boolean isActive(final SoundEffect sound) { - for (final PlayerSound s : activeSounds) - if (s.equals(sound)) - return true; - return false; - } - - private static boolean isSoundQueued(final PlayerSound sound) { - final SoundHandler handler = Minecraft.getMinecraft().getSoundHandler(); - return handler.isSoundPlaying(sound) || handler.sndManager.delayedSounds.containsKey(sound); - } - - private static void playSound(final EntityPlayer player, final SoundEffect sound) { - try { - final PlayerSound s = new PlayerSound(player, sound); - Minecraft.getMinecraft().getSoundHandler().playSound(s); - activeSounds.add(s); - } catch (final Throwable t) { - ; - } - } - public static void playSoundAtPlayer(EntityPlayer player, final SoundEffect sound, final int tickDelay) { if (player == null) player = EnvironState.getPlayer(); @@ -284,31 +142,10 @@ public static void playSoundAt(final BlockPos pos, final SoundEffect sound, fina handler.playDelayedSound(s, tickDelay); } - private static void processSounds(final EntityPlayer player, final List sounds) { - // Need to remove sounds that are active but not - // in the incoming list - final Iterator itr = activeSounds.iterator(); - while (itr.hasNext()) { - final PlayerSound sound = itr.next(); - if (!sounds.contains(sound.sound)) { - sound.fadeAway(); - itr.remove(); - } else if (!isSoundQueued(sound)) { - itr.remove(); - } - } - - // Add sounds from the incoming list that are not - // active. - for (final SoundEffect sound : sounds) - if (!isActive(sound)) - playSound(player, sound); - } - @Override public void process(final World world, final EntityPlayer player) { if (didReloadOccur() || player.isDead) - clearSounds(); + SoundManager.clearSounds(); // Dead players hear no sounds if (player.isDead) @@ -320,7 +157,9 @@ public void process(final World world, final EntityPlayer player) { final List sounds = new ArrayList(); sounds.addAll(BiomeRegistry.getSounds(playerBiome, conditions)); sounds.addAll(BiomeRegistry.getSounds(BiomeRegistry.PLAYER, conditions)); - processSounds(player, sounds); + + SoundManager.update(); + SoundManager.queueSounds(sounds); SoundEffect sound = null; if (canFitSound()) { @@ -346,10 +185,10 @@ public void diagnostics(final DiagnosticEvent.Gather event) { final StringBuilder builder = new StringBuilder(); builder.append("SoundSystem: ").append(currentSoundCount()).append('/').append(maxSoundCount()); event.output.add(builder.toString()); - for (final PlayerSound sound : activeSounds) { + for (final SoundEffect sound : SoundManager.activeSounds()) { builder.setLength(0); builder.append("Active Sound: ").append(sound.toString()); - builder.append(" (effective volume:").append(sound.getVolume()).append(')'); + builder.append(" (effective volume:").append(sound.getVolume() * MASTER_SCALE_FACTOR).append(')'); event.output.add(builder.toString()); } } diff --git a/src/main/java/org/blockartistry/mod/DynSurround/client/SoundManager.java b/src/main/java/org/blockartistry/mod/DynSurround/client/SoundManager.java new file mode 100644 index 0000000..a13e352 --- /dev/null +++ b/src/main/java/org/blockartistry/mod/DynSurround/client/SoundManager.java @@ -0,0 +1,224 @@ +/* + * This file is part of Dynamic Surroundings, licensed under the MIT License (MIT). + * + * Copyright (c) OreCruncher + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.blockartistry.mod.DynSurround.client; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; + +import org.blockartistry.mod.DynSurround.ModOptions; +import org.blockartistry.mod.DynSurround.client.EnvironStateHandler.EnvironState; +import org.blockartistry.mod.DynSurround.client.fx.SoundEffect; +import org.blockartistry.mod.DynSurround.util.XorShiftRandom; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.MovingSound; +import net.minecraft.client.audio.SoundHandler; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.fml.relauncher.Side; + +@SideOnly(Side.CLIENT) +public class SoundManager { + + private static final Map emitters = new HashMap(); + + private static final Random RANDOM = new XorShiftRandom(); + private static final float VOLUME_INCREMENT = 0.02F; + private static final float VOLUME_DECREMENT = 0.015F; + private static final float MASTER_SCALE_FACTOR = ModOptions.getMasterSoundScaleFactor(); + + private static class PlayerSound extends MovingSound { + private boolean fadeAway; + private final SoundEffect sound; + + public PlayerSound(final SoundEffect sound) { + super(new ResourceLocation(sound.sound)); + + // Don't set volume to 0; MC will optimize out + this.sound = sound; + this.volume = !sound.skipFade ? 0.01F : sound.volume; + this.pitch = sound.getPitch(RANDOM); + this.repeat = sound.repeatDelay == 0; + this.fadeAway = false; + + // Repeat delay + this.repeatDelay = 0; + + final EntityPlayer player = EnvironState.getPlayer(); + // Initial position + this.xPosF = (float) (player.posX); + this.yPosF = (float) (player.posY + 1); + this.zPosF = (float) (player.posZ); + } + + public void fadeAway() { + this.fadeAway = true; + if (this.sound.skipFade) { + this.volume = 0.0F; + this.donePlaying = true; + } + } + + public boolean sameSound(final SoundEffect snd) { + return this.sound.equals(snd); + } + + @Override + public void update() { + if (this.fadeAway) { + this.volume -= VOLUME_DECREMENT; + if (this.volume < 0.0F) { + this.volume = 0.0F; + } + } else if (this.volume < this.sound.volume) { + this.volume += VOLUME_INCREMENT; + if (this.volume > this.sound.volume) + this.volume = this.sound.volume; + } + + if (this.volume == 0.0F) + this.donePlaying = true; + } + + @Override + public float getVolume() { + return super.getVolume() * MASTER_SCALE_FACTOR; + } + + @Override + public float getXPosF() { + return MathHelper.floor_double(EnvironState.getPlayer().posX); + } + + @Override + public float getYPosF() { + return MathHelper.floor_double(EnvironState.getPlayer().posY + 1); + } + + @Override + public float getZPosF() { + return MathHelper.floor_double(EnvironState.getPlayer().posZ); + } + + @Override + public String toString() { + return this.sound.toString(); + } + + @Override + public boolean equals(final Object anObj) { + if (this == anObj) + return true; + if (anObj instanceof PlayerSound) + return this.sameSound(((PlayerSound) anObj).sound); + if (anObj instanceof SoundEffect) + return this.sameSound((SoundEffect) anObj); + return false; + } + } + + private static class Emitter { + + protected final SoundEffect effect; + protected PlayerSound activeSound; + protected final SoundHandler handler; + + protected int repeatDelay = 0; + + public Emitter(final SoundEffect sound) { + this.effect = sound; + this.handler = Minecraft.getMinecraft().getSoundHandler(); + } + + public void update() { + if (activeSound != null) { + if (handler.isSoundPlaying(activeSound)) + return; + activeSound.fadeAway(); + activeSound = null; + if (this.effect.repeatDelay > 0) { + this.repeatDelay = this.effect.repeatDelay; + return; + } + } else if (this.repeatDelay > 0) { + if (--this.repeatDelay > 0) + return; + } + + this.activeSound = new PlayerSound(effect); + try { + handler.playSound(this.activeSound); + } catch (final Throwable t) { + ; + } + } + + public void fade() { + if (this.activeSound != null) + this.activeSound.fadeAway(); + } + } + + public static void clearSounds() { + for (final Emitter emit : emitters.values()) + emit.fade(); + emitters.clear(); + } + + public static void queueSounds(final List sounds) { + // Need to remove sounds that are active but not + // in the incoming list + final List active = new ArrayList(emitters.keySet()); + for (final SoundEffect effect : active) { + if (!sounds.remove(effect)) + emitters.remove(effect).fade(); + } + + // Add sounds from the incoming list that are not + // active. + for (final SoundEffect sound : sounds) + emitters.put(sound, new Emitter(sound)); + } + + public static void update() { + final Iterator> itr = emitters.entrySet().iterator(); + while (itr.hasNext()) { + final Entry e = itr.next(); + e.getValue().update(); + } + } + + public static List activeSounds() { + return new ArrayList(emitters.keySet()); + } + +}