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