001 package net.minecraft.client.audio; 002 003 import net.minecraftforge.client.*; 004 import net.minecraftforge.client.event.sound.*; 005 import net.minecraftforge.common.MinecraftForge; 006 import static net.minecraftforge.client.event.sound.SoundEvent.*; 007 import cpw.mods.fml.common.Side; 008 import cpw.mods.fml.common.asm.SideOnly; 009 import java.io.File; 010 import java.util.HashSet; 011 import java.util.Iterator; 012 import java.util.Random; 013 import java.util.Set; 014 import net.minecraft.client.settings.GameSettings; 015 import net.minecraft.entity.Entity; 016 import net.minecraft.entity.EntityLiving; 017 import net.minecraft.util.MathHelper; 018 import paulscode.sound.SoundSystem; 019 import paulscode.sound.SoundSystemConfig; 020 import paulscode.sound.codecs.CodecJOrbis; 021 import paulscode.sound.codecs.CodecWav; 022 import paulscode.sound.libraries.LibraryLWJGLOpenAL; 023 024 @SideOnly(Side.CLIENT) 025 public class SoundManager 026 { 027 /** A reference to the sound system. */ 028 public static SoundSystem sndSystem; 029 030 /** Sound pool containing sounds. */ 031 public SoundPool soundPoolSounds = new SoundPool(); 032 033 /** Sound pool containing streaming audio. */ 034 public SoundPool soundPoolStreaming = new SoundPool(); 035 036 /** Sound pool containing music. */ 037 public SoundPool soundPoolMusic = new SoundPool(); 038 039 /** 040 * The last ID used when a sound is played, passed into SoundSystem to give active sounds a unique ID 041 */ 042 private int latestSoundID = 0; 043 044 /** A reference to the game settings. */ 045 private GameSettings options; 046 047 /** Identifiers of all currently playing sounds. Type: HashSet<String> */ 048 private Set playingSounds = new HashSet(); 049 050 /** Set to true when the SoundManager has been initialised. */ 051 private static boolean loaded = false; 052 053 /** RNG. */ 054 private Random rand = new Random(); 055 private int ticksBeforeMusic; 056 057 public static int MUSIC_INTERVAL = 12000; 058 059 public SoundManager() 060 { 061 this.ticksBeforeMusic = this.rand.nextInt(MUSIC_INTERVAL); 062 } 063 064 /** 065 * Used for loading sound settings from GameSettings 066 */ 067 public void loadSoundSettings(GameSettings par1GameSettings) 068 { 069 this.soundPoolStreaming.isGetRandomSound = false; 070 this.options = par1GameSettings; 071 072 if (!loaded && (par1GameSettings == null || par1GameSettings.soundVolume != 0.0F || par1GameSettings.musicVolume != 0.0F)) 073 { 074 this.tryToSetLibraryAndCodecs(); 075 } 076 ModCompatibilityClient.audioModLoad(this); 077 MinecraftForge.EVENT_BUS.post(new SoundLoadEvent(this)); 078 } 079 080 /** 081 * Tries to add the paulscode library and the relevant codecs. If it fails, the volumes (sound and music) will be 082 * set to zero in the options file. 083 */ 084 private void tryToSetLibraryAndCodecs() 085 { 086 try 087 { 088 float var1 = this.options.soundVolume; 089 float var2 = this.options.musicVolume; 090 this.options.soundVolume = 0.0F; 091 this.options.musicVolume = 0.0F; 092 this.options.saveOptions(); 093 SoundSystemConfig.addLibrary(LibraryLWJGLOpenAL.class); 094 SoundSystemConfig.setCodec("ogg", CodecJOrbis.class); 095 SoundSystemConfig.setCodec("mus", CodecMus.class); 096 SoundSystemConfig.setCodec("wav", CodecWav.class); 097 ModCompatibilityClient.audioModAddCodecs(); 098 MinecraftForge.EVENT_BUS.post(new SoundSetupEvent(this)); 099 sndSystem = new SoundSystem(); 100 this.options.soundVolume = var1; 101 this.options.musicVolume = var2; 102 this.options.saveOptions(); 103 } 104 catch (Throwable var3) 105 { 106 var3.printStackTrace(); 107 System.err.println("error linking with the LibraryJavaSound plug-in"); 108 } 109 110 loaded = true; 111 } 112 113 /** 114 * Called when one of the sound level options has changed. 115 */ 116 public void onSoundOptionsChanged() 117 { 118 if (!loaded && (this.options.soundVolume != 0.0F || this.options.musicVolume != 0.0F)) 119 { 120 this.tryToSetLibraryAndCodecs(); 121 } 122 123 if (loaded) 124 { 125 if (this.options.musicVolume == 0.0F) 126 { 127 sndSystem.stop("BgMusic"); 128 } 129 else 130 { 131 sndSystem.setVolume("BgMusic", this.options.musicVolume); 132 } 133 } 134 } 135 136 /** 137 * Called when Minecraft is closing down. 138 */ 139 public void closeMinecraft() 140 { 141 if (loaded) 142 { 143 sndSystem.cleanup(); 144 } 145 } 146 147 /** 148 * Adds a sounds with the name from the file. Args: name, file 149 */ 150 public void addSound(String par1Str, File par2File) 151 { 152 this.soundPoolSounds.addSound(par1Str, par2File); 153 } 154 155 /** 156 * Adds an audio file to the streaming SoundPool. 157 */ 158 public void addStreaming(String par1Str, File par2File) 159 { 160 this.soundPoolStreaming.addSound(par1Str, par2File); 161 } 162 163 /** 164 * Adds an audio file to the music SoundPool. 165 */ 166 public void addMusic(String par1Str, File par2File) 167 { 168 this.soundPoolMusic.addSound(par1Str, par2File); 169 } 170 171 /** 172 * If its time to play new music it starts it up. 173 */ 174 public void playRandomMusicIfReady() 175 { 176 if (loaded && this.options.musicVolume != 0.0F) 177 { 178 if (!sndSystem.playing("BgMusic") && !sndSystem.playing("streaming")) 179 { 180 if (this.ticksBeforeMusic > 0) 181 { 182 --this.ticksBeforeMusic; 183 return; 184 } 185 186 SoundPoolEntry var1 = this.soundPoolMusic.getRandomSound(); 187 var1 = ModCompatibilityClient.audioModPickBackgroundMusic(this, var1); 188 var1 = SoundEvent.getResult(new PlayBackgroundMusicEvent(this, var1)); 189 190 if (var1 != null) 191 { 192 this.ticksBeforeMusic = this.rand.nextInt(MUSIC_INTERVAL) + MUSIC_INTERVAL; 193 sndSystem.backgroundMusic("BgMusic", var1.soundUrl, var1.soundName, false); 194 sndSystem.setVolume("BgMusic", this.options.musicVolume); 195 sndSystem.play("BgMusic"); 196 } 197 } 198 } 199 } 200 201 /** 202 * Sets the listener of sounds 203 */ 204 public void setListener(EntityLiving par1EntityLiving, float par2) 205 { 206 if (loaded && this.options.soundVolume != 0.0F) 207 { 208 if (par1EntityLiving != null) 209 { 210 float var3 = par1EntityLiving.prevRotationPitch + (par1EntityLiving.rotationPitch - par1EntityLiving.prevRotationPitch) * par2; 211 float var4 = par1EntityLiving.prevRotationYaw + (par1EntityLiving.rotationYaw - par1EntityLiving.prevRotationYaw) * par2; 212 double var5 = par1EntityLiving.prevPosX + (par1EntityLiving.posX - par1EntityLiving.prevPosX) * (double)par2; 213 double var7 = par1EntityLiving.prevPosY + (par1EntityLiving.posY - par1EntityLiving.prevPosY) * (double)par2; 214 double var9 = par1EntityLiving.prevPosZ + (par1EntityLiving.posZ - par1EntityLiving.prevPosZ) * (double)par2; 215 float var11 = MathHelper.cos(-var4 * 0.017453292F - (float)Math.PI); 216 float var12 = MathHelper.sin(-var4 * 0.017453292F - (float)Math.PI); 217 float var13 = -var12; 218 float var14 = -MathHelper.sin(-var3 * 0.017453292F - (float)Math.PI); 219 float var15 = -var11; 220 float var16 = 0.0F; 221 float var17 = 1.0F; 222 float var18 = 0.0F; 223 sndSystem.setListenerPosition((float)var5, (float)var7, (float)var9); 224 sndSystem.setListenerOrientation(var13, var14, var15, var16, var17, var18); 225 } 226 } 227 } 228 229 /** 230 * Stops all currently playing sounds 231 */ 232 public void stopAllSounds() 233 { 234 Iterator var1 = this.playingSounds.iterator(); 235 236 while (var1.hasNext()) 237 { 238 String var2 = (String)var1.next(); 239 sndSystem.stop(var2); 240 } 241 242 this.playingSounds.clear(); 243 } 244 245 public void playStreaming(String par1Str, float par2, float par3, float par4) 246 { 247 if (loaded && (this.options.soundVolume != 0.0F || par1Str == null)) 248 { 249 String var5 = "streaming"; 250 251 if (sndSystem.playing(var5)) 252 { 253 sndSystem.stop(var5); 254 } 255 256 if (par1Str != null) 257 { 258 SoundPoolEntry var6 = this.soundPoolStreaming.getRandomSoundFromSoundPool(par1Str); 259 var6 = SoundEvent.getResult(new PlayStreamingEvent(this, var6, par1Str, par2, par3, par4)); 260 261 if (var6 != null) 262 { 263 if (sndSystem.playing("BgMusic")) 264 { 265 sndSystem.stop("BgMusic"); 266 } 267 268 float var7 = 16.0F; 269 sndSystem.newStreamingSource(true, var5, var6.soundUrl, var6.soundName, false, par2, par3, par4, 2, var7 * 4.0F); 270 sndSystem.setVolume(var5, 0.5F * this.options.soundVolume); 271 MinecraftForge.EVENT_BUS.post(new PlayStreamingSourceEvent(this, var5, par2, par3, par4)); 272 sndSystem.play(var5); 273 } 274 } 275 } 276 } 277 278 /** 279 * Updates the sound associated with the entity with that entity's position and velocity. Args: the entity 280 */ 281 public void updateSoundLocation(Entity par1Entity) 282 { 283 this.updateSoundLocation(par1Entity, par1Entity); 284 } 285 286 /** 287 * Updates the sound associated with soundEntity with the position and velocity of trackEntity. Args: soundEntity, 288 * trackEntity 289 */ 290 public void updateSoundLocation(Entity par1Entity, Entity par2Entity) 291 { 292 String var3 = "entity_" + par1Entity.entityId; 293 294 if (this.playingSounds.contains(var3)) 295 { 296 if (sndSystem.playing(var3)) 297 { 298 sndSystem.setPosition(var3, (float)par2Entity.posX, (float)par2Entity.posY, (float)par2Entity.posZ); 299 sndSystem.setVelocity(var3, (float)par2Entity.motionX, (float)par2Entity.motionY, (float)par2Entity.motionZ); 300 } 301 else 302 { 303 this.playingSounds.remove(var3); 304 } 305 } 306 } 307 308 /** 309 * Returns true if a sound is currently associated with the given entity, or false otherwise. 310 */ 311 public boolean isEntitySoundPlaying(Entity par1Entity) 312 { 313 if (par1Entity != null && loaded) 314 { 315 String var2 = "entity_" + par1Entity.entityId; 316 return sndSystem.playing(var2); 317 } 318 else 319 { 320 return false; 321 } 322 } 323 324 /** 325 * Stops playing the sound associated with the given entity 326 */ 327 public void stopEntitySound(Entity par1Entity) 328 { 329 if (par1Entity != null && loaded) 330 { 331 String var2 = "entity_" + par1Entity.entityId; 332 333 if (this.playingSounds.contains(var2)) 334 { 335 if (sndSystem.playing(var2)) 336 { 337 sndSystem.stop(var2); 338 } 339 340 this.playingSounds.remove(var2); 341 } 342 } 343 } 344 345 /** 346 * Sets the volume of the sound associated with the given entity, if one is playing. The volume is scaled by the 347 * global sound volume. Args: the entity, the volume (from 0 to 1) 348 */ 349 public void setEntitySoundVolume(Entity par1Entity, float par2) 350 { 351 if (par1Entity != null && loaded) 352 { 353 if (loaded && this.options.soundVolume != 0.0F) 354 { 355 String var3 = "entity_" + par1Entity.entityId; 356 357 if (sndSystem.playing(var3)) 358 { 359 sndSystem.setVolume(var3, par2 * this.options.soundVolume); 360 } 361 } 362 } 363 } 364 365 /** 366 * Sets the pitch of the sound associated with the given entity, if one is playing. Args: the entity, the pitch 367 */ 368 public void setEntitySoundPitch(Entity par1Entity, float par2) 369 { 370 if (par1Entity != null && loaded) 371 { 372 if (loaded && this.options.soundVolume != 0.0F) 373 { 374 String var3 = "entity_" + par1Entity.entityId; 375 376 if (sndSystem.playing(var3)) 377 { 378 sndSystem.setPitch(var3, par2); 379 } 380 } 381 } 382 } 383 384 /** 385 * If a sound is already playing from the given entity, update the position and velocity of that sound to match the 386 * entity. Otherwise, start playing a sound from that entity. Args: The sound name, the entity, the volume, the 387 * pitch, unknown flag 388 */ 389 public void playEntitySound(String par1Str, Entity par2Entity, float par3, float par4, boolean par5) 390 { 391 if (par2Entity != null) 392 { 393 if (loaded && (this.options.soundVolume != 0.0F || par1Str == null)) 394 { 395 String var6 = "entity_" + par2Entity.entityId; 396 397 if (this.playingSounds.contains(var6)) 398 { 399 this.updateSoundLocation(par2Entity); 400 } 401 else 402 { 403 if (sndSystem.playing(var6)) 404 { 405 sndSystem.stop(var6); 406 } 407 408 if (par1Str == null) 409 { 410 return; 411 } 412 413 SoundPoolEntry var7 = this.soundPoolSounds.getRandomSoundFromSoundPool(par1Str); 414 415 if (var7 != null && par3 > 0.0F) 416 { 417 float var8 = 16.0F; 418 419 if (par3 > 1.0F) 420 { 421 var8 *= par3; 422 } 423 424 sndSystem.newSource(par5, var6, var7.soundUrl, var7.soundName, false, (float)par2Entity.posX, (float)par2Entity.posY, (float)par2Entity.posZ, 2, var8); 425 sndSystem.setLooping(var6, true); 426 sndSystem.setPitch(var6, par4); 427 428 if (par3 > 1.0F) 429 { 430 par3 = 1.0F; 431 } 432 433 sndSystem.setVolume(var6, par3 * this.options.soundVolume); 434 sndSystem.setVelocity(var6, (float)par2Entity.motionX, (float)par2Entity.motionY, (float)par2Entity.motionZ); 435 sndSystem.play(var6); 436 this.playingSounds.add(var6); 437 } 438 } 439 } 440 } 441 } 442 443 /** 444 * Plays a sound. Args: soundName, x, y, z, volume, pitch 445 */ 446 public void playSound(String par1Str, float par2, float par3, float par4, float par5, float par6) 447 { 448 if (loaded && this.options.soundVolume != 0.0F) 449 { 450 SoundPoolEntry var7 = this.soundPoolSounds.getRandomSoundFromSoundPool(par1Str); 451 var7 = SoundEvent.getResult(new PlaySoundEvent(this, var7, par1Str, par2, par3, par4, par5, par6)); 452 453 if (var7 != null && par5 > 0.0F) 454 { 455 this.latestSoundID = (this.latestSoundID + 1) % 256; 456 String var8 = "sound_" + this.latestSoundID; 457 float var9 = 16.0F; 458 459 if (par5 > 1.0F) 460 { 461 var9 *= par5; 462 } 463 464 sndSystem.newSource(par5 > 1.0F, var8, var7.soundUrl, var7.soundName, false, par2, par3, par4, 2, var9); 465 sndSystem.setPitch(var8, par6); 466 467 if (par5 > 1.0F) 468 { 469 par5 = 1.0F; 470 } 471 472 sndSystem.setVolume(var8, par5 * this.options.soundVolume); 473 MinecraftForge.EVENT_BUS.post(new PlaySoundSourceEvent(this, var8, par2, par3, par4)); 474 sndSystem.play(var8); 475 } 476 } 477 } 478 479 /** 480 * Plays a sound effect with the volume and pitch of the parameters passed. The sound isn't affected by position of 481 * the player (full volume and center balanced) 482 */ 483 public void playSoundFX(String par1Str, float par2, float par3) 484 { 485 if (loaded && this.options.soundVolume != 0.0F) 486 { 487 SoundPoolEntry var4 = this.soundPoolSounds.getRandomSoundFromSoundPool(par1Str); 488 var4 = SoundEvent.getResult(new PlaySoundEffectEvent(this, var4, par1Str, par2, par3)); 489 490 if (var4 != null) 491 { 492 this.latestSoundID = (this.latestSoundID + 1) % 256; 493 String var5 = "sound_" + this.latestSoundID; 494 sndSystem.newSource(false, var5, var4.soundUrl, var4.soundName, false, 0.0F, 0.0F, 0.0F, 0, 0.0F); 495 496 if (par2 > 1.0F) 497 { 498 par2 = 1.0F; 499 } 500 501 par2 *= 0.25F; 502 sndSystem.setPitch(var5, par3); 503 sndSystem.setVolume(var5, par2 * this.options.soundVolume); 504 MinecraftForge.EVENT_BUS.post(new PlaySoundEffectSourceEvent(this, var5)); 505 sndSystem.play(var5); 506 } 507 } 508 } 509 510 /** 511 * Pauses all currently playing sounds 512 */ 513 public void pauseAllSounds() 514 { 515 Iterator var1 = this.playingSounds.iterator(); 516 517 while (var1.hasNext()) 518 { 519 String var2 = (String)var1.next(); 520 sndSystem.pause(var2); 521 } 522 } 523 524 /** 525 * Resumes playing all currently playing sounds (after pauseAllSounds) 526 */ 527 public void resumeAllSounds() 528 { 529 Iterator var1 = this.playingSounds.iterator(); 530 531 while (var1.hasNext()) 532 { 533 String var2 = (String)var1.next(); 534 sndSystem.play(var2); 535 } 536 } 537 }