    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}