001package net.minecraft.client.audio;
002
003import net.minecraftforge.client.*;
004import net.minecraftforge.client.event.sound.*;
005import net.minecraftforge.common.MinecraftForge;
006import static net.minecraftforge.client.event.sound.SoundEvent.*;
007import cpw.mods.fml.relauncher.Side;
008import cpw.mods.fml.relauncher.SideOnly;
009import java.io.File;
010import java.util.ArrayList;
011import java.util.HashSet;
012import java.util.Iterator;
013import java.util.List;
014import java.util.Random;
015import java.util.Set;
016import net.minecraft.client.settings.GameSettings;
017import net.minecraft.entity.Entity;
018import net.minecraft.entity.EntityLiving;
019import net.minecraft.util.MathHelper;
020import paulscode.sound.SoundSystem;
021import paulscode.sound.SoundSystemConfig;
022import paulscode.sound.codecs.CodecJOrbis;
023import paulscode.sound.codecs.CodecWav;
024import paulscode.sound.libraries.LibraryLWJGLOpenAL;
025
026@SideOnly(Side.CLIENT)
027public 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 f = this.options.soundVolume;
092            float f1 = 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 = f;
104            this.options.musicVolume = f1;
105            this.options.saveOptions();
106        }
107        catch (Throwable throwable)
108        {
109            throwable.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 soundpoolentry = this.soundPoolMusic.getRandomSound();
190                soundpoolentry = ModCompatibilityClient.audioModPickBackgroundMusic(this, soundpoolentry);
191                soundpoolentry = SoundEvent.getResult(new PlayBackgroundMusicEvent(this, soundpoolentry));
192
193                if (soundpoolentry != null)
194                {
195                    this.ticksBeforeMusic = this.rand.nextInt(MUSIC_INTERVAL) + MUSIC_INTERVAL;
196                    sndSystem.backgroundMusic("BgMusic", soundpoolentry.soundUrl, soundpoolentry.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 f1 = par1EntityLiving.prevRotationPitch + (par1EntityLiving.rotationPitch - par1EntityLiving.prevRotationPitch) * par2;
214                float f2 = par1EntityLiving.prevRotationYaw + (par1EntityLiving.rotationYaw - par1EntityLiving.prevRotationYaw) * par2;
215                double d0 = par1EntityLiving.prevPosX + (par1EntityLiving.posX - par1EntityLiving.prevPosX) * (double)par2;
216                double d1 = par1EntityLiving.prevPosY + (par1EntityLiving.posY - par1EntityLiving.prevPosY) * (double)par2;
217                double d2 = par1EntityLiving.prevPosZ + (par1EntityLiving.posZ - par1EntityLiving.prevPosZ) * (double)par2;
218                float f3 = MathHelper.cos(-f2 * 0.017453292F - (float)Math.PI);
219                float f4 = MathHelper.sin(-f2 * 0.017453292F - (float)Math.PI);
220                float f5 = -f4;
221                float f6 = -MathHelper.sin(-f1 * 0.017453292F - (float)Math.PI);
222                float f7 = -f3;
223                float f8 = 0.0F;
224                float f9 = 1.0F;
225                float f10 = 0.0F;
226                sndSystem.setListenerPosition((float)d0, (float)d1, (float)d2);
227                sndSystem.setListenerOrientation(f5, f6, f7, f8, f9, f10);
228            }
229        }
230    }
231
232    /**
233     * Stops all currently playing sounds
234     */
235    public void stopAllSounds()
236    {
237        Iterator iterator = this.playingSounds.iterator();
238
239        while (iterator.hasNext())
240        {
241            String s = (String)iterator.next();
242            sndSystem.stop(s);
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 s1 = "streaming";
253
254            if (sndSystem.playing(s1))
255            {
256                sndSystem.stop(s1);
257            }
258
259            if (par1Str != null)
260            {
261                SoundPoolEntry soundpoolentry = this.soundPoolStreaming.getRandomSoundFromSoundPool(par1Str);
262                soundpoolentry = SoundEvent.getResult(new PlayStreamingEvent(this, soundpoolentry, par1Str, par2, par3, par4));
263
264                if (soundpoolentry != null)
265                {
266                    if (sndSystem.playing("BgMusic"))
267                    {
268                        sndSystem.stop("BgMusic");
269                    }
270
271                    float f3 = 16.0F;
272                    sndSystem.newStreamingSource(true, s1, soundpoolentry.soundUrl, soundpoolentry.soundName, false, par2, par3, par4, 2, f3 * 4.0F);
273                    sndSystem.setVolume(s1, 0.5F * this.options.soundVolume);
274                    MinecraftForge.EVENT_BUS.post(new PlayStreamingSourceEvent(this, s1, par2, par3, par4));
275                    sndSystem.play(s1);
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 s = "entity_" + par1Entity.entityId;
296
297        if (this.playingSounds.contains(s))
298        {
299            if (sndSystem.playing(s))
300            {
301                sndSystem.setPosition(s, (float)par2Entity.posX, (float)par2Entity.posY, (float)par2Entity.posZ);
302                sndSystem.setVelocity(s, (float)par2Entity.motionX, (float)par2Entity.motionY, (float)par2Entity.motionZ);
303            }
304            else
305            {
306                this.playingSounds.remove(s);
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 s = "entity_" + par1Entity.entityId;
319            return sndSystem.playing(s);
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 s = "entity_" + par1Entity.entityId;
335
336            if (this.playingSounds.contains(s))
337            {
338                if (sndSystem.playing(s))
339                {
340                    sndSystem.stop(s);
341                }
342
343                this.playingSounds.remove(s);
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 s = "entity_" + par1Entity.entityId;
359
360                if (sndSystem.playing(s))
361                {
362                    sndSystem.setVolume(s, 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 s = "entity_" + par1Entity.entityId;
378
379                if (sndSystem.playing(s))
380                {
381                    sndSystem.setPitch(s, 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 s1 = "entity_" + par2Entity.entityId;
399
400                if (this.playingSounds.contains(s1))
401                {
402                    this.updateSoundLocation(par2Entity);
403                }
404                else
405                {
406                    if (sndSystem.playing(s1))
407                    {
408                        sndSystem.stop(s1);
409                    }
410
411                    if (par1Str == null)
412                    {
413                        return;
414                    }
415
416                    SoundPoolEntry soundpoolentry = this.soundPoolSounds.getRandomSoundFromSoundPool(par1Str);
417
418                    if (soundpoolentry != null && par3 > 0.0F)
419                    {
420                        float f2 = 16.0F;
421
422                        if (par3 > 1.0F)
423                        {
424                            f2 *= par3;
425                        }
426
427                        sndSystem.newSource(par5, s1, soundpoolentry.soundUrl, soundpoolentry.soundName, false, (float)par2Entity.posX, (float)par2Entity.posY, (float)par2Entity.posZ, 2, f2);
428                        sndSystem.setLooping(s1, true);
429                        sndSystem.setPitch(s1, par4);
430
431                        if (par3 > 1.0F)
432                        {
433                            par3 = 1.0F;
434                        }
435
436                        sndSystem.setVolume(s1, par3 * this.options.soundVolume);
437                        sndSystem.setVelocity(s1, (float)par2Entity.motionX, (float)par2Entity.motionY, (float)par2Entity.motionZ);
438                        sndSystem.play(s1);
439                        this.playingSounds.add(s1);
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 soundpoolentry = this.soundPoolSounds.getRandomSoundFromSoundPool(par1Str);
454            soundpoolentry = SoundEvent.getResult(new PlaySoundEvent(this, soundpoolentry, par1Str, par2, par3, par4, par5, par6));
455
456            if (soundpoolentry != null && par5 > 0.0F)
457            {
458                this.latestSoundID = (this.latestSoundID + 1) % 256;
459                String s1 = "sound_" + this.latestSoundID;
460                float f5 = 16.0F;
461
462                if (par5 > 1.0F)
463                {
464                    f5 *= par5;
465                }
466
467                sndSystem.newSource(par5 > 1.0F, s1, soundpoolentry.soundUrl, soundpoolentry.soundName, false, par2, par3, par4, 2, f5);
468                sndSystem.setPitch(s1, par6);
469
470                if (par5 > 1.0F)
471                {
472                    par5 = 1.0F;
473                }
474
475                sndSystem.setVolume(s1, par5 * this.options.soundVolume);
476                MinecraftForge.EVENT_BUS.post(new PlaySoundSourceEvent(this, s1, par2, par3, par4));
477                sndSystem.play(s1);
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 soundpoolentry = this.soundPoolSounds.getRandomSoundFromSoundPool(par1Str);
491            soundpoolentry = SoundEvent.getResult(new PlaySoundEffectEvent(this, soundpoolentry, par1Str, par2, par3));
492
493            if (soundpoolentry != null)
494            {
495                this.latestSoundID = (this.latestSoundID + 1) % 256;
496                String s1 = "sound_" + this.latestSoundID;
497                sndSystem.newSource(false, s1, soundpoolentry.soundUrl, soundpoolentry.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(s1, par3);
506                sndSystem.setVolume(s1, par2 * this.options.soundVolume);
507                MinecraftForge.EVENT_BUS.post(new PlaySoundEffectSourceEvent(this, s1));
508                sndSystem.play(s1);
509            }
510        }
511    }
512
513    /**
514     * Pauses all currently playing sounds
515     */
516    public void pauseAllSounds()
517    {
518        Iterator iterator = this.playingSounds.iterator();
519
520        while (iterator.hasNext())
521        {
522            String s = (String)iterator.next();
523            sndSystem.pause(s);
524        }
525    }
526
527    /**
528     * Resumes playing all currently playing sounds (after pauseAllSounds)
529     */
530    public void resumeAllSounds()
531    {
532        Iterator iterator = this.playingSounds.iterator();
533
534        while (iterator.hasNext())
535        {
536            String s = (String)iterator.next();
537            sndSystem.play(s);
538        }
539    }
540
541    public void func_92071_g()
542    {
543        if (!this.field_92072_h.isEmpty())
544        {
545            Iterator iterator = this.field_92072_h.iterator();
546
547            while (iterator.hasNext())
548            {
549                ScheduledSound scheduledsound = (ScheduledSound)iterator.next();
550                --scheduledsound.field_92064_g;
551
552                if (scheduledsound.field_92064_g <= 0)
553                {
554                    this.playSound(scheduledsound.field_92069_a, scheduledsound.field_92067_b, scheduledsound.field_92068_c, scheduledsound.field_92065_d, scheduledsound.field_92066_e, scheduledsound.field_92063_f);
555                    iterator.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}