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