package speedytools.clientside.sound; import net.minecraft.client.audio.ITickableSound; import net.minecraft.client.audio.PositionedSound; import net.minecraft.util.ResourceLocation; import speedytools.common.utilities.ErrorLog; import speedytools.common.utilities.UsefulFunctions; /** * Created by TheGreyGhost on 8/10/14. * * Used to create sound effects for the complex tool - powerup, sustain, and failure * * There are several basic states created from a couple of overlaid sounds * 1) power up - including sustain while the user holds down the button * 1b) failure (eg busy) * 2) working * 3) finishing * There are two overlaid sounds: * a1) CHARGE powerringchargeup followed by CHARGELOOP powerringchargeloop while the ring is powering up * a2) FAIL powerringchargefailure when a failure message is received * b) PERFORM powerringwhooshloop which is faded in during powering up, then looped while working and faded out when finished * performTick() should be called every tick by the client */ public class SoundEffectComplexTool { public SoundEffectComplexTool(SoundController i_soundController, RingSoundUpdateLink i_ringSoundUpdateLink) { soundController = i_soundController; failResource = new ResourceLocation(SoundEffectNames.POWERUP_FAILURE.getJsonName()); chargeloopResource = new ResourceLocation(SoundEffectNames.POWERUP_HOLD.getJsonName()); chargeResource = new ResourceLocation(SoundEffectNames.POWERUP.getJsonName()); performingResource = new ResourceLocation(SoundEffectNames.PERFORMING_ACTION.getJsonName()); ringSoundUpdateLink = i_ringSoundUpdateLink; } private final float CHARGE_MIN_VOLUME = 0.02F; private final float CHARGE_MAX_VOLUME = 0.2F; private final float PERFORM_MIN_VOLUME = 0.02F; private final float PERFORM_MAX_VOLUME = 0.2F; private final float FAIL_MIN_VOLUME = 0.02F; private final float FAIL_MAX_VOLUME = 0.2F; public void startPlaying() { stopAllSounds(); // chargeSound = new RingSpinSound(chargeResource, CHARGE_MIN_VOLUME, false, ); currentRingState = RingSoundInfo.State.IDLE; performTick(); } public void startPlayingIfNotAlreadyPlaying() { if (performSound != null && !performSound.isDonePlaying()) return; startPlaying(); } public void stopPlaying() { stopAllSounds(); } private void stopAllSounds() { if (chargeSound != null) { soundController.stopSound(chargeSound); chargeSound = null; } if (chargeLoopSound != null) { soundController.stopSound(chargeLoopSound); chargeLoopSound = null; } if (performSound != null) { soundController.stopSound(performSound); performSound = null; } if (failSound != null) { soundController.stopSound(failSound); failSound = null; } } private void setAllStopFlags() { if (chargeSound != null) { chargeSound.donePlaying = true;} if (chargeLoopSound != null) { chargeLoopSound.donePlaying = true;} if (performSound != null) { performSound.donePlaying = true;} if (failSound != null) { failSound.donePlaying = true;} } public void performTick() { ++ticksElapsed; RingSoundInfo ringSoundInfo = new RingSoundInfo(); boolean keepPlaying = ringSoundUpdateLink.refreshRingSoundInfo(ringSoundInfo); if (!keepPlaying) { setAllStopFlags(); return; } if (ringSoundInfo.ringState != currentRingState) { switch (ringSoundInfo.ringState) { case IDLE: { break; } case SPIN_UP: { if (chargeSound != null) { // chargeSound.donePlaying = true; soundController.stopSound(chargeSound); } chargeSound = new RingSpinSound(chargeResource, CHARGE_MIN_VOLUME, RepeatType.NO_REPEAT, chargeSettings); if (performSound != null) { // performSound.donePlaying = true; soundController.stopSound(performSound); } performSound = new RingSpinSound(performingResource, PERFORM_MIN_VOLUME, RepeatType.REPEAT, performSettings); powerupStartTick = ticksElapsed; soundController.playSound(chargeSound); soundController.playSound(performSound); break; } case SPIN_UP_ABORT: { if (chargeSound != null) { // chargeSound.donePlaying = true; soundController.stopSound(chargeSound); } spinupAbortTick = ticksElapsed; break; } case PERFORMING_ACTION: { performStartTick = ticksElapsed; break; } case SPIN_DOWN: { performStopTick = ticksElapsed; break; } case FAILURE: { if (chargeSound != null) { // chargeSound.donePlaying = true; soundController.stopSound(chargeSound); } if (failSound != null) { // failSound.donePlaying = true; soundController.stopSound(failSound); } failSound = new RingSpinSound(failResource, FAIL_MAX_VOLUME, RepeatType.NO_REPEAT, failSettings); soundController.playSound(failSound); failureStartTick = ticksElapsed; break; } default: { ErrorLog.defaultLog().debug("Illegal ringSoundInfo.ringState:" + ringSoundInfo.ringState + " in " + this.getClass()); } } currentRingState = ringSoundInfo.ringState; } switch (currentRingState) { case SPIN_UP: case PERFORMING_ACTION: { final int POWERUP_SOUND_DURATION_TICKS = 40; final int POWERUP_VOLUME_RAMP_TICKS = 10; final int POWERUP_VOLUME_CROSSFADE_TICKS = 10; if (ticksElapsed - powerupStartTick == POWERUP_SOUND_DURATION_TICKS) { if (chargeLoopSound != null) { // chargeLoopSound.donePlaying = true; soundController.stopSound(chargeLoopSound); } chargeLoopSound = new RingSpinSound(chargeloopResource, CHARGE_MIN_VOLUME, RepeatType.REPEAT, chargeLoopSettings); soundController.playSound(chargeLoopSound); } if (ticksElapsed - powerupStartTick <= POWERUP_SOUND_DURATION_TICKS) { float newVolume = CHARGE_MIN_VOLUME + (CHARGE_MAX_VOLUME - CHARGE_MIN_VOLUME) * (ticksElapsed - powerupStartTick) / (float)POWERUP_VOLUME_RAMP_TICKS; chargeSettings.volume = UsefulFunctions.clipToRange(newVolume, CHARGE_MIN_VOLUME, CHARGE_MAX_VOLUME); newVolume = PERFORM_MIN_VOLUME + (PERFORM_MAX_VOLUME - PERFORM_MIN_VOLUME) * (ticksElapsed - powerupStartTick) / (float)POWERUP_VOLUME_RAMP_TICKS; performSettings.volume = UsefulFunctions.clipToRange(newVolume, PERFORM_MIN_VOLUME, PERFORM_MAX_VOLUME); chargeLoopSettings.volume = 0.0F; } else if (ticksElapsed - powerupStartTick <= POWERUP_SOUND_DURATION_TICKS + POWERUP_VOLUME_CROSSFADE_TICKS) { int crossfadeTicks = ticksElapsed - powerupStartTick - POWERUP_SOUND_DURATION_TICKS; float newVolume = CHARGE_MIN_VOLUME + (CHARGE_MAX_VOLUME - CHARGE_MIN_VOLUME) * crossfadeTicks / (float)POWERUP_VOLUME_CROSSFADE_TICKS; chargeLoopSettings.volume = UsefulFunctions.clipToRange(newVolume, CHARGE_MIN_VOLUME, CHARGE_MAX_VOLUME); chargeSettings.volume = CHARGE_MAX_VOLUME - chargeLoopSettings.volume; performSettings.volume = PERFORM_MAX_VOLUME; } else { chargeLoopSettings.volume = CHARGE_MAX_VOLUME; performSettings.volume = PERFORM_MAX_VOLUME; chargeSettings.volume = 0; } final int PERFORM_VOLUME_FADEDOWN_TICKS = 5; if (currentRingState == RingSoundInfo.State.PERFORMING_ACTION) { performSettings.volume = PERFORM_MAX_VOLUME; int crossfadeTime = ticksElapsed - performStartTick; if (crossfadeTime <= PERFORM_VOLUME_FADEDOWN_TICKS) { float newVolume = CHARGE_MAX_VOLUME / (float)PERFORM_VOLUME_FADEDOWN_TICKS; chargeSettings.volume = newVolume; chargeLoopSettings.volume = newVolume; } else { chargeLoopSettings.volume = 0; chargeSettings.volume = 0; } } break; } case SPIN_DOWN: case SPIN_UP_ABORT: { chargeSettings.volume = 0; chargeLoopSettings.volume = 0; final int ABORT_VOLUME_FADEDOWN_TICKS = 20; performSettings.volume -= PERFORM_MAX_VOLUME / (float)ABORT_VOLUME_FADEDOWN_TICKS; if (performSettings.volume < 0) { performSettings.volume = 0; if (performSound != null) { // performSound.donePlaying = true; soundController.stopSound(performSound); } } break; } case FAILURE: { chargeSettings.volume = 0; chargeLoopSettings.volume = 0; failSettings.volume = FAIL_MAX_VOLUME; final int FAILURE_VOLUME_FADEDOWN_TICKS = 20; performSettings.volume -= PERFORM_MAX_VOLUME / (float)FAILURE_VOLUME_FADEDOWN_TICKS; if (performSettings.volume < 0) { performSettings.volume = 0; if (performSound != null) { // performSound.donePlaying = true; soundController.stopSound(performSound); } } break; } case IDLE: { // performSound.donePlaying = true; if (performSound != null) { soundController.stopSound(performSound); performSound = null; } break; } } } // private class PowerUpSoundUpdate implements RingSpinSoundUpdateMethod // { // public boolean updateSoundSettings(ComponentSoundSettings componentSoundSettings) // { // return false; // } // } private int ticksElapsed; private int powerupStartTick; private int performStartTick; private int performStopTick; private int failureStartTick; private int spinupAbortTick; RingSoundInfo.State currentRingState = RingSoundInfo.State.IDLE; private ComponentSoundSettings chargeSettings = new ComponentSoundSettings(0.01F); private ComponentSoundSettings chargeLoopSettings = new ComponentSoundSettings(0.01F); private ComponentSoundSettings performSettings = new ComponentSoundSettings(0.01F); private ComponentSoundSettings failSettings = new ComponentSoundSettings(0.01F); private RingSpinSound chargeSound; private RingSpinSound chargeLoopSound; private RingSpinSound performSound; private RingSpinSound failSound; private SoundController soundController; private ResourceLocation chargeResource; private ResourceLocation chargeloopResource; private ResourceLocation failResource; private ResourceLocation performingResource; private RingSoundUpdateLink ringSoundUpdateLink; /** * Used as a callback to update the sound's position and */ public interface RingSoundUpdateLink { public boolean refreshRingSoundInfo(RingSoundInfo infoToUpdate); } public static class RingSoundInfo { public enum State {IDLE, SPIN_UP, SPIN_UP_ABORT, PERFORMING_ACTION, SPIN_DOWN, FAILURE} public State ringState = State.IDLE; } // private interface RingSpinSoundUpdateMethod // { // public boolean updateSoundSettings(ComponentSoundSettings componentSoundSettings); // } private static class ComponentSoundSettings { public ComponentSoundSettings(float i_volume) { volume = i_volume; } public float volume; } public enum RepeatType {REPEAT, NO_REPEAT} private class RingSpinSound extends PositionedSound implements ITickableSound { public RingSpinSound(ResourceLocation i_resourceLocation, float i_volume, RepeatType i_repeat, ComponentSoundSettings i_soundSettings) { super(i_resourceLocation); repeat = (i_repeat == RepeatType.REPEAT); volume = i_volume; attenuationType = AttenuationType.NONE; componentSoundSettings = i_soundSettings; } private boolean donePlaying; ComponentSoundSettings componentSoundSettings; @Override public boolean isDonePlaying() { return donePlaying; } @Override public void update() { this.volume = componentSoundSettings.volume; } } }