package net.minecraft.client.audio; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import io.netty.util.internal.ThreadLocalRandom; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import net.minecraft.client.Minecraft; import net.minecraft.client.settings.GameSettings; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; import paulscode.sound.SoundSystem; import paulscode.sound.SoundSystemConfig; import paulscode.sound.SoundSystemException; import paulscode.sound.SoundSystemLogger; import paulscode.sound.Source; import paulscode.sound.codecs.CodecJOrbis; import paulscode.sound.libraries.LibraryLWJGLOpenAL; public class SoundManager { /** The marker used for logging */ private static final Marker LOG_MARKER = MarkerManager.getMarker("SOUNDS"); private static final Logger logger = LogManager.getLogger(); /** A reference to the sound handler. */ private final SoundHandler sndHandler; /** Reference to the GameSettings object. */ private final GameSettings options; /** A reference to the sound system. */ private SoundManager.SoundSystemStarterThread sndSystem; /** Set to true when the SoundManager has been initialised. */ private boolean loaded; /** A counter for how long the sound manager has been running */ private int playTime = 0; /** * Identifiers of all currently playing sounds. Type: HashBiMap<String, ISound> */ private final Map playingSounds = HashBiMap.create(); /** * Inverse map of currently playing sounds, automatically mirroring changes in original map */ private final Map invPlayingSounds; /** A HashMap<String, SoundPoolEntry> of the playing sounds. */ private Map playingSoundPoolEntries; /** * Contains sounds mapped by category. Type: Multimap<SoundCategory, String> */ private final Multimap categorySounds; /** A subset of playingSounds, this contains only ITickableSounds */ private final List tickableSounds; /** Contains sounds to play in n ticks. Type: HashMap<ISound, Integer> */ private final Map delayedSounds; /** * The future time in which to stop this sound. Type: HashMap<String, Integer> */ private final Map playingSoundsStopTime; private static final String __OBFID = "CL_00001141"; public SoundManager(SoundHandler p_i45119_1_, GameSettings p_i45119_2_) { this.invPlayingSounds = ((BiMap)this.playingSounds).inverse(); this.playingSoundPoolEntries = Maps.newHashMap(); this.categorySounds = HashMultimap.create(); this.tickableSounds = Lists.newArrayList(); this.delayedSounds = Maps.newHashMap(); this.playingSoundsStopTime = Maps.newHashMap(); this.sndHandler = p_i45119_1_; this.options = p_i45119_2_; try { SoundSystemConfig.addLibrary(LibraryLWJGLOpenAL.class); SoundSystemConfig.setCodec("ogg", CodecJOrbis.class); } catch (SoundSystemException var4) { logger.error(LOG_MARKER, "Error linking with the LibraryJavaSound plug-in", var4); } } public void reloadSoundSystem() { this.unloadSoundSystem(); this.loadSoundSystem(); } /** * Tries to add the paulscode library and the relevant codecs. If it fails, the master volume will be set to zero. */ private synchronized void loadSoundSystem() { if (!this.loaded) { try { (new Thread(new Runnable() { private static final String __OBFID = "CL_00001142"; public void run() { SoundSystemConfig.setLogger(new SoundSystemLogger() { private static final String __OBFID = "CL_00002378"; public void message(String p_message_1_, int p_message_2_) { if (!p_message_1_.isEmpty()) { SoundManager.logger.info(p_message_1_); } } public void importantMessage(String p_importantMessage_1_, int p_importantMessage_2_) { if (!p_importantMessage_1_.isEmpty()) { SoundManager.logger.warn(p_importantMessage_1_); } } public void errorMessage(String p_errorMessage_1_, String p_errorMessage_2_, int p_errorMessage_3_) { if (!p_errorMessage_2_.isEmpty()) { SoundManager.logger.error("Error in class \'" + p_errorMessage_1_ + "\'"); SoundManager.logger.error(p_errorMessage_2_); } } }); SoundManager.this.sndSystem = SoundManager.this.new SoundSystemStarterThread(null); SoundManager.this.loaded = true; SoundManager.this.sndSystem.setMasterVolume(SoundManager.this.options.getSoundLevel(SoundCategory.MASTER)); SoundManager.logger.info(SoundManager.LOG_MARKER, "Sound engine started"); } }, "Sound Library Loader")).start(); } catch (RuntimeException var2) { logger.error(LOG_MARKER, "Error starting SoundSystem. Turning off sounds & music", var2); this.options.setSoundLevel(SoundCategory.MASTER, 0.0F); this.options.saveOptions(); } } } /** * Returns the sound level (between 0.0 and 1.0) for a category, but 1.0 for the master sound category */ private float getSoundCategoryVolume(SoundCategory p_148595_1_) { return p_148595_1_ != null && p_148595_1_ != SoundCategory.MASTER ? this.options.getSoundLevel(p_148595_1_) : 1.0F; } /** * Adjusts volume for currently playing sounds in this category */ public void setSoundCategoryVolume(SoundCategory p_148601_1_, float p_148601_2_) { if (this.loaded) { if (p_148601_1_ == SoundCategory.MASTER) { this.sndSystem.setMasterVolume(p_148601_2_); } else { Iterator var3 = this.categorySounds.get(p_148601_1_).iterator(); while (var3.hasNext()) { String var4 = (String)var3.next(); ISound var5 = (ISound)this.playingSounds.get(var4); float var6 = this.getNormalizedVolume(var5, (SoundPoolEntry)this.playingSoundPoolEntries.get(var5), p_148601_1_); if (var6 <= 0.0F) { this.stopSound(var5); } else { this.sndSystem.setVolume(var4, var6); } } } } } /** * Cleans up the Sound System */ public void unloadSoundSystem() { if (this.loaded) { this.stopAllSounds(); this.sndSystem.cleanup(); this.loaded = false; } } /** * Stops all currently playing sounds */ public void stopAllSounds() { if (this.loaded) { Iterator var1 = this.playingSounds.keySet().iterator(); while (var1.hasNext()) { String var2 = (String)var1.next(); this.sndSystem.stop(var2); } this.playingSounds.clear(); this.delayedSounds.clear(); this.tickableSounds.clear(); this.categorySounds.clear(); this.playingSoundPoolEntries.clear(); this.playingSoundsStopTime.clear(); } } public void updateAllSounds() { ++this.playTime; Iterator var1 = this.tickableSounds.iterator(); String var3; while (var1.hasNext()) { ITickableSound var2 = (ITickableSound)var1.next(); var2.update(); if (var2.isDonePlaying()) { this.stopSound(var2); } else { var3 = (String)this.invPlayingSounds.get(var2); this.sndSystem.setVolume(var3, this.getNormalizedVolume(var2, (SoundPoolEntry)this.playingSoundPoolEntries.get(var2), this.sndHandler.getSound(var2.getSoundLocation()).getSoundCategory())); this.sndSystem.setPitch(var3, this.getNormalizedPitch(var2, (SoundPoolEntry)this.playingSoundPoolEntries.get(var2))); this.sndSystem.setPosition(var3, var2.getXPosF(), var2.getYPosF(), var2.getZPosF()); } } var1 = this.playingSounds.entrySet().iterator(); ISound var4; while (var1.hasNext()) { Entry var9 = (Entry)var1.next(); var3 = (String)var9.getKey(); var4 = (ISound)var9.getValue(); if (!this.sndSystem.playing(var3)) { int var5 = ((Integer)this.playingSoundsStopTime.get(var3)).intValue(); if (var5 <= this.playTime) { int var6 = var4.getRepeatDelay(); if (var4.canRepeat() && var6 > 0) { this.delayedSounds.put(var4, Integer.valueOf(this.playTime + var6)); } var1.remove(); logger.debug(LOG_MARKER, "Removed channel {} because it\'s not playing anymore", new Object[] {var3}); this.sndSystem.removeSource(var3); this.playingSoundsStopTime.remove(var3); this.playingSoundPoolEntries.remove(var4); try { this.categorySounds.remove(this.sndHandler.getSound(var4.getSoundLocation()).getSoundCategory(), var3); } catch (RuntimeException var8) { ; } if (var4 instanceof ITickableSound) { this.tickableSounds.remove(var4); } } } } Iterator var10 = this.delayedSounds.entrySet().iterator(); while (var10.hasNext()) { Entry var11 = (Entry)var10.next(); if (this.playTime >= ((Integer)var11.getValue()).intValue()) { var4 = (ISound)var11.getKey(); if (var4 instanceof ITickableSound) { ((ITickableSound)var4).update(); } this.playSound(var4); var10.remove(); } } } /** * Returns true if the sound is playing or still within time */ public boolean isSoundPlaying(ISound p_148597_1_) { if (!this.loaded) { return false; } else { String var2 = (String)this.invPlayingSounds.get(p_148597_1_); return var2 == null ? false : this.sndSystem.playing(var2) || this.playingSoundsStopTime.containsKey(var2) && ((Integer)this.playingSoundsStopTime.get(var2)).intValue() <= this.playTime; } } public void stopSound(ISound p_148602_1_) { if (this.loaded) { String var2 = (String)this.invPlayingSounds.get(p_148602_1_); if (var2 != null) { this.sndSystem.stop(var2); } } } public void playSound(ISound p_148611_1_) { if (this.loaded) { if (this.sndSystem.getMasterVolume() <= 0.0F) { logger.debug(LOG_MARKER, "Skipped playing soundEvent: {}, master volume was zero", new Object[] {p_148611_1_.getSoundLocation()}); } else { SoundEventAccessorComposite var2 = this.sndHandler.getSound(p_148611_1_.getSoundLocation()); if (var2 == null) { logger.warn(LOG_MARKER, "Unable to play unknown soundEvent: {}", new Object[] {p_148611_1_.getSoundLocation()}); } else { SoundPoolEntry var3 = var2.cloneEntry(); if (var3 == SoundHandler.missing_sound) { logger.warn(LOG_MARKER, "Unable to play empty soundEvent: {}", new Object[] {var2.getSoundEventLocation()}); } else { float var4 = p_148611_1_.getVolume(); float var5 = 16.0F; if (var4 > 1.0F) { var5 *= var4; } SoundCategory var6 = var2.getSoundCategory(); float var7 = this.getNormalizedVolume(p_148611_1_, var3, var6); double var8 = (double)this.getNormalizedPitch(p_148611_1_, var3); ResourceLocation var10 = var3.getSoundPoolEntryLocation(); if (var7 == 0.0F) { logger.debug(LOG_MARKER, "Skipped playing sound {}, volume was zero.", new Object[] {var10}); } else { boolean var11 = p_148611_1_.canRepeat() && p_148611_1_.getRepeatDelay() == 0; String var12 = MathHelper.func_180182_a(ThreadLocalRandom.current()).toString(); if (var3.isStreamingSound()) { this.sndSystem.newStreamingSource(false, var12, getURLForSoundResource(var10), var10.toString(), var11, p_148611_1_.getXPosF(), p_148611_1_.getYPosF(), p_148611_1_.getZPosF(), p_148611_1_.getAttenuationType().getTypeInt(), var5); } else { this.sndSystem.newSource(false, var12, getURLForSoundResource(var10), var10.toString(), var11, p_148611_1_.getXPosF(), p_148611_1_.getYPosF(), p_148611_1_.getZPosF(), p_148611_1_.getAttenuationType().getTypeInt(), var5); } logger.debug(LOG_MARKER, "Playing sound {} for event {} as channel {}", new Object[] {var3.getSoundPoolEntryLocation(), var2.getSoundEventLocation(), var12}); this.sndSystem.setPitch(var12, (float)var8); this.sndSystem.setVolume(var12, var7); this.sndSystem.play(var12); this.playingSoundsStopTime.put(var12, Integer.valueOf(this.playTime + 20)); this.playingSounds.put(var12, p_148611_1_); this.playingSoundPoolEntries.put(p_148611_1_, var3); if (var6 != SoundCategory.MASTER) { this.categorySounds.put(var6, var12); } if (p_148611_1_ instanceof ITickableSound) { this.tickableSounds.add((ITickableSound)p_148611_1_); } } } } } } } /** * Normalizes pitch from parameters and clamps to [0.5, 2.0] */ private float getNormalizedPitch(ISound p_148606_1_, SoundPoolEntry p_148606_2_) { return (float)MathHelper.clamp_double((double)p_148606_1_.getPitch() * p_148606_2_.getPitch(), 0.5D, 2.0D); } /** * Normalizes volume level from parameters. Range [0.0, 1.0] */ private float getNormalizedVolume(ISound p_148594_1_, SoundPoolEntry p_148594_2_, SoundCategory p_148594_3_) { return (float)MathHelper.clamp_double((double)p_148594_1_.getVolume() * p_148594_2_.getVolume(), 0.0D, 1.0D) * this.getSoundCategoryVolume(p_148594_3_); } /** * Pauses all currently playing sounds */ public void pauseAllSounds() { Iterator var1 = this.playingSounds.keySet().iterator(); while (var1.hasNext()) { String var2 = (String)var1.next(); logger.debug(LOG_MARKER, "Pausing channel {}", new Object[] {var2}); this.sndSystem.pause(var2); } } /** * Resumes playing all currently playing sounds (after pauseAllSounds) */ public void resumeAllSounds() { Iterator var1 = this.playingSounds.keySet().iterator(); while (var1.hasNext()) { String var2 = (String)var1.next(); logger.debug(LOG_MARKER, "Resuming channel {}", new Object[] {var2}); this.sndSystem.play(var2); } } /** * Adds a sound to play in n tick */ public void playDelayedSound(ISound p_148599_1_, int p_148599_2_) { this.delayedSounds.put(p_148599_1_, Integer.valueOf(this.playTime + p_148599_2_)); } private static URL getURLForSoundResource(final ResourceLocation p_148612_0_) { String var1 = String.format("%s:%s:%s", new Object[] {"mcsounddomain", p_148612_0_.getResourceDomain(), p_148612_0_.getResourcePath()}); URLStreamHandler var2 = new URLStreamHandler() { private static final String __OBFID = "CL_00001143"; protected URLConnection openConnection(final URL p_openConnection_1_) { return new URLConnection(p_openConnection_1_) { private static final String __OBFID = "CL_00001144"; public void connect() {} public InputStream getInputStream() throws IOException { return Minecraft.getMinecraft().getResourceManager().getResource(p_148612_0_).getInputStream(); } }; } }; try { return new URL((URL)null, var1, var2); } catch (MalformedURLException var4) { throw new Error("TODO: Sanely handle url exception! :D"); } } /** * Sets the listener of sounds */ public void setListener(EntityPlayer p_148615_1_, float p_148615_2_) { if (this.loaded && p_148615_1_ != null) { float var3 = p_148615_1_.prevRotationPitch + (p_148615_1_.rotationPitch - p_148615_1_.prevRotationPitch) * p_148615_2_; float var4 = p_148615_1_.prevRotationYaw + (p_148615_1_.rotationYaw - p_148615_1_.prevRotationYaw) * p_148615_2_; double var5 = p_148615_1_.prevPosX + (p_148615_1_.posX - p_148615_1_.prevPosX) * (double)p_148615_2_; double var7 = p_148615_1_.prevPosY + (p_148615_1_.posY - p_148615_1_.prevPosY) * (double)p_148615_2_ + (double)p_148615_1_.getEyeHeight(); double var9 = p_148615_1_.prevPosZ + (p_148615_1_.posZ - p_148615_1_.prevPosZ) * (double)p_148615_2_; float var11 = MathHelper.cos((var4 + 90.0F) * 0.017453292F); float var12 = MathHelper.sin((var4 + 90.0F) * 0.017453292F); float var13 = MathHelper.cos(-var3 * 0.017453292F); float var14 = MathHelper.sin(-var3 * 0.017453292F); float var15 = MathHelper.cos((-var3 + 90.0F) * 0.017453292F); float var16 = MathHelper.sin((-var3 + 90.0F) * 0.017453292F); float var17 = var11 * var13; float var19 = var12 * var13; float var20 = var11 * var15; float var22 = var12 * var15; this.sndSystem.setListenerPosition((float)var5, (float)var7, (float)var9); this.sndSystem.setListenerOrientation(var17, var14, var19, var20, var16, var22); } } class SoundSystemStarterThread extends SoundSystem { private static final String __OBFID = "CL_00001145"; private SoundSystemStarterThread() {} public boolean playing(String p_playing_1_) { Object var2 = SoundSystemConfig.THREAD_SYNC; synchronized (SoundSystemConfig.THREAD_SYNC) { if (this.soundLibrary == null) { return false; } else { Source var3 = (Source)this.soundLibrary.getSources().get(p_playing_1_); return var3 == null ? false : var3.playing() || var3.paused() || var3.preLoad; } } } SoundSystemStarterThread(Object p_i45118_2_) { this(); } } }