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    }