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.prevRotationYaw + (par1EntityLiving.rotationYaw - par1EntityLiving.prevRotationYaw) * par2;
207 double var4 = par1EntityLiving.prevPosX + (par1EntityLiving.posX - par1EntityLiving.prevPosX) * (double)par2;
208 double var6 = par1EntityLiving.prevPosY + (par1EntityLiving.posY - par1EntityLiving.prevPosY) * (double)par2;
209 double var8 = par1EntityLiving.prevPosZ + (par1EntityLiving.posZ - par1EntityLiving.prevPosZ) * (double)par2;
210 float var10 = MathHelper.cos(-var3 * 0.017453292F - (float)Math.PI);
211 float var11 = MathHelper.sin(-var3 * 0.017453292F - (float)Math.PI);
212 float var12 = -var11;
213 float var13 = 0.0F;
214 float var14 = -var10;
215 float var15 = 0.0F;
216 float var16 = 1.0F;
217 float var17 = 0.0F;
218 sndSystem.setListenerPosition((float)var4, (float)var6, (float)var8);
219 sndSystem.setListenerOrientation(var12, var13, var14, var15, var16, var17);
220 }
221 }
222 }
223
224 /**
225 * Stops all currently playing sounds
226 */
227 public void stopAllSounds()
228 {
229 Iterator var1 = this.playingSounds.iterator();
230
231 while (var1.hasNext())
232 {
233 String var2 = (String)var1.next();
234 sndSystem.stop(var2);
235 }
236
237 this.playingSounds.clear();
238 }
239
240 public void playStreaming(String par1Str, float par2, float par3, float par4)
241 {
242 if (loaded && (this.options.soundVolume != 0.0F || par1Str == null))
243 {
244 String var5 = "streaming";
245
246 if (sndSystem.playing(var5))
247 {
248 sndSystem.stop(var5);
249 }
250
251 if (par1Str != null)
252 {
253 SoundPoolEntry var6 = this.soundPoolStreaming.getRandomSoundFromSoundPool(par1Str);
254 var6 = SoundEvent.getResult(new PlayStreamingEvent(this, var6, par1Str, par2, par3, par4));
255
256 if (var6 != null)
257 {
258 if (sndSystem.playing("BgMusic"))
259 {
260 sndSystem.stop("BgMusic");
261 }
262
263 float var7 = 16.0F;
264 sndSystem.newStreamingSource(true, var5, var6.soundUrl, var6.soundName, false, par2, par3, par4, 2, var7 * 4.0F);
265 sndSystem.setVolume(var5, 0.5F * this.options.soundVolume);
266 MinecraftForge.EVENT_BUS.post(new PlayStreamingSourceEvent(this, var5, par2, par3, par4));
267 sndSystem.play(var5);
268 }
269 }
270 }
271 }
272
273 /**
274 * Updates the sound associated with the entity with that entity's position and velocity. Args: the entity
275 */
276 public void updateSoundLocation(Entity par1Entity)
277 {
278 this.updateSoundLocation(par1Entity, par1Entity);
279 }
280
281 /**
282 * Updates the sound associated with soundEntity with the position and velocity of trackEntity. Args: soundEntity,
283 * trackEntity
284 */
285 public void updateSoundLocation(Entity par1Entity, Entity par2Entity)
286 {
287 String var3 = "entity_" + par1Entity.entityId;
288
289 if (this.playingSounds.contains(var3))
290 {
291 if (sndSystem.playing(var3))
292 {
293 sndSystem.setPosition(var3, (float)par2Entity.posX, (float)par2Entity.posY, (float)par2Entity.posZ);
294 sndSystem.setVelocity(var3, (float)par2Entity.motionX, (float)par2Entity.motionY, (float)par2Entity.motionZ);
295 }
296 else
297 {
298 this.playingSounds.remove(var3);
299 }
300 }
301 }
302
303 /**
304 * Returns true if a sound is currently associated with the given entity, or false otherwise.
305 */
306 public boolean isEntitySoundPlaying(Entity par1Entity)
307 {
308 if (par1Entity != null && loaded && this.options.musicVolume != 0.0F)
309 {
310 String var2 = "entity_" + par1Entity.entityId;
311 return sndSystem.playing(var2);
312 }
313 else
314 {
315 return false;
316 }
317 }
318
319 /**
320 * Stops playing the sound associated with the given entity
321 */
322 public void stopEntitySound(Entity par1Entity)
323 {
324 if (par1Entity != null && loaded && this.options.musicVolume != 0.0F)
325 {
326 String var2 = "entity_" + par1Entity.entityId;
327
328 if (this.playingSounds.contains(var2))
329 {
330 if (sndSystem.playing(var2))
331 {
332 sndSystem.stop(var2);
333 }
334
335 this.playingSounds.remove(var2);
336 }
337 }
338 }
339
340 /**
341 * Sets the volume of the sound associated with the given entity, if one is playing. The volume is scaled by the
342 * global sound volume. Args: the entity, the volume (from 0 to 1)
343 */
344 public void setEntitySoundVolume(Entity par1Entity, float par2)
345 {
346 if (par1Entity != null && loaded && this.options.musicVolume != 0.0F)
347 {
348 if (loaded && this.options.soundVolume != 0.0F)
349 {
350 String var3 = "entity_" + par1Entity.entityId;
351
352 if (sndSystem.playing(var3))
353 {
354 sndSystem.setVolume(var3, par2 * this.options.soundVolume);
355 }
356 }
357 }
358 }
359
360 /**
361 * Sets the pitch of the sound associated with the given entity, if one is playing. Args: the entity, the pitch
362 */
363 public void setEntitySoundPitch(Entity par1Entity, float par2)
364 {
365 if (par1Entity != null && loaded && this.options.musicVolume != 0.0F)
366 {
367 if (loaded && this.options.soundVolume != 0.0F)
368 {
369 String var3 = "entity_" + par1Entity.entityId;
370
371 if (sndSystem.playing(var3))
372 {
373 sndSystem.setPitch(var3, par2);
374 }
375 }
376 }
377 }
378
379 /**
380 * If a sound is already playing from the given entity, update the position and velocity of that sound to match the
381 * entity. Otherwise, start playing a sound from that entity. Args: The sound name, the entity, the volume, the
382 * pitch, unknown flag
383 */
384 public void playEntitySound(String par1Str, Entity par2Entity, float par3, float par4, boolean par5)
385 {
386 if (par2Entity != null)
387 {
388 if (loaded && (this.options.soundVolume != 0.0F || par1Str == null))
389 {
390 String var6 = "entity_" + par2Entity.entityId;
391
392 if (this.playingSounds.contains(var6))
393 {
394 this.updateSoundLocation(par2Entity);
395 }
396 else
397 {
398 if (sndSystem.playing(var6))
399 {
400 sndSystem.stop(var6);
401 }
402
403 if (par1Str == null)
404 {
405 return;
406 }
407
408 SoundPoolEntry var7 = this.soundPoolSounds.getRandomSoundFromSoundPool(par1Str);
409
410 if (var7 != null && par3 > 0.0F)
411 {
412 float var8 = 16.0F;
413
414 if (par3 > 1.0F)
415 {
416 var8 *= par3;
417 }
418
419 sndSystem.newSource(par5, var6, var7.soundUrl, var7.soundName, false, (float)par2Entity.posX, (float)par2Entity.posY, (float)par2Entity.posZ, 2, var8);
420 sndSystem.setLooping(var6, true);
421 sndSystem.setPitch(var6, par4);
422
423 if (par3 > 1.0F)
424 {
425 par3 = 1.0F;
426 }
427
428 sndSystem.setVolume(var6, par3 * this.options.soundVolume);
429 sndSystem.setVelocity(var6, (float)par2Entity.motionX, (float)par2Entity.motionY, (float)par2Entity.motionZ);
430 sndSystem.play(var6);
431 this.playingSounds.add(var6);
432 }
433 }
434 }
435 }
436 }
437
438 /**
439 * Plays a sound. Args: soundName, x, y, z, volume, pitch
440 */
441 public void playSound(String par1Str, float par2, float par3, float par4, float par5, float par6)
442 {
443 if (loaded && this.options.soundVolume != 0.0F)
444 {
445 SoundPoolEntry var7 = this.soundPoolSounds.getRandomSoundFromSoundPool(par1Str);
446 var7 = SoundEvent.getResult(new PlaySoundEvent(this, var7, par1Str, par2, par3, par4, par5, par6));
447
448 if (var7 != null && par5 > 0.0F)
449 {
450 this.latestSoundID = (this.latestSoundID + 1) % 256;
451 String var8 = "sound_" + this.latestSoundID;
452 float var9 = 16.0F;
453
454 if (par5 > 1.0F)
455 {
456 var9 *= par5;
457 }
458
459 sndSystem.newSource(par5 > 1.0F, var8, var7.soundUrl, var7.soundName, false, par2, par3, par4, 2, var9);
460 sndSystem.setPitch(var8, par6);
461
462 if (par5 > 1.0F)
463 {
464 par5 = 1.0F;
465 }
466
467 sndSystem.setVolume(var8, par5 * this.options.soundVolume);
468 MinecraftForge.EVENT_BUS.post(new PlaySoundSourceEvent(this, var8, par2, par3, par4));
469 sndSystem.play(var8);
470 }
471 }
472 }
473
474 /**
475 * Plays a sound effect with the volume and pitch of the parameters passed. The sound isn't affected by position of
476 * the player (full volume and center balanced)
477 */
478 public void playSoundFX(String par1Str, float par2, float par3)
479 {
480 if (loaded && this.options.soundVolume != 0.0F)
481 {
482 SoundPoolEntry var4 = this.soundPoolSounds.getRandomSoundFromSoundPool(par1Str);
483 var4 = SoundEvent.getResult(new PlaySoundEffectEvent(this, var4, par1Str, par2, par3));
484
485 if (var4 != null)
486 {
487 this.latestSoundID = (this.latestSoundID + 1) % 256;
488 String var5 = "sound_" + this.latestSoundID;
489 sndSystem.newSource(false, var5, var4.soundUrl, var4.soundName, false, 0.0F, 0.0F, 0.0F, 0, 0.0F);
490
491 if (par2 > 1.0F)
492 {
493 par2 = 1.0F;
494 }
495
496 par2 *= 0.25F;
497 sndSystem.setPitch(var5, par3);
498 sndSystem.setVolume(var5, par2 * this.options.soundVolume);
499 MinecraftForge.EVENT_BUS.post(new PlaySoundEffectSourceEvent(this, var5));
500 sndSystem.play(var5);
501 }
502 }
503 }
504
505 /**
506 * Pauses all currently playing sounds
507 */
508 public void pauseAllSounds()
509 {
510 Iterator var1 = this.playingSounds.iterator();
511
512 while (var1.hasNext())
513 {
514 String var2 = (String)var1.next();
515 sndSystem.pause(var2);
516 }
517 }
518
519 /**
520 * Resumes playing all currently playing sounds (after pauseAllSounds)
521 */
522 public void resumeAllSounds()
523 {
524 Iterator var1 = this.playingSounds.iterator();
525
526 while (var1.hasNext())
527 {
528 String var2 = (String)var1.next();
529 sndSystem.play(var2);
530 }
531 }
532 }