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 }