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 }