Search code examples
androidandroid-mediaplayer

MediaPlayer - Sounds Stop Playing


I am creating a class witch loads up a few sounds. However, isPlaying keeps on throwing an exception after a while and then stops playing that particular sound permanently, while other sounds keep playing OK.

public class MySound {
   int m_IdMyId;
   int m_ResId;
   boolean m_IsLoaded;
   MediaPlayer m_Media;

   public MySound(int idMyId, int resId){
       m_IdMyId = idMyId;
       m_ResId = resId;
       m_IsLoaded = false;
       m_Media = null;
   }
}

In this m_IdMyId is just an id for my game. m_ResId is something like R.raw.mysound1. m_IsLoaded I think is automatically set to true as I am loading synconously. m_Media is the MediaPlayer object.

I am calling stop() very regularly, as it is a game and I need to check every second or so to make sure certain sounds are stopped. It is here that it throws an exception when snd.m_Media.isPlaying() is called.

I cannot seem to access e to see what the error is.

Also I would like to know how I can set m_IsLoaded correctly. How do I know when the sound is fully loaded and ready to use?

Here is my management class:

public class MySoundManager {
    MainActivity m_Context;
    ArrayList<MySound> mySounds;

    public MySoundManager(MainActivity context) {
        m_Context = context;
        mySounds = new ArrayList<MySound>();
        mySounds.add(new MySound(8, R.raw.mysound1));
        mySounds.add(new MySound(10, R.raw.mysound2));
        mySounds.add(new MySound(22, R.raw.mysound3));
        mySounds.add(new MySound(100, R.raw.click));
        mySounds.add(new MySound(101, R.raw.error));

       for(MySound mysound : mySounds) {
           mysound.m_Media = MediaPlayer.create(m_Context, mysound.m_ResId); // no need to call prepare(); create() does that for you
           mysound.m_IsLoaded = true;
        }
    }

    // I call this when the main thread calls onResume
    public void onResume(){
        for(MySound mysound : mySounds) {
            if(mysound.m_Media == null) {
                mysound.m_Media = MediaPlayer.create(m_Context, mysound.m_ResId); // no need to call prepare(); create() does that for you
                mysound.m_IsLoaded = true;
            }
        }
    }

    // I call this when the main thread calls onPause
    public void onPause(){
        for(MySound mysound : mySounds) {
            if(mysound.m_Media != null) {
                mysound.m_Media.stop();
                mysound.m_Media.release();
                mysound.m_Media = null;
            }
        }
    }

    public boolean IsAllLoaded(){
        for(MySound mysound : mySounds) {
            if(!mysound.m_IsLoaded) return false;
        }
        return true;
    }

    public MySound FindMySoundByIdMyId(int idMyId){
        try {
            for(MySound mysound : mySounds) {
                if (mysound.m_IdMyId == idMyId) return mysound;
            }
        }catch(Exception e) {
            MySound snd;
            snd = null; // ToDo
        }
        return null;
    }

    public void play(int idMyId){
        MySound snd;
        try{
            if((snd = FindMySoundByIdMyId(idMyId)) != null)
                snd.m_Media.start();
        }catch(IllegalStateException e) {
            snd = null; // ToDo
        }
    }

    public void pause(int idMyId){
        MySound snd;
        try{
            if((snd = FindMySoundByIdMyId(idMyId)) != null &&
                    snd.m_Media.isPlaying())
               snd.m_Media.pause();
        }catch(IllegalStateException e) {
            snd = null; // ToDo
        }
    }

    public void pauseAll(){
        try{
            for (MySound mysound : mySounds) {
                if(mysound.m_Media.isPlaying())
                    mysound.m_Media.pause();
            }
        }catch(IllegalStateException e) {
            MySound snd;
            snd = null; // ToDo
        }
    }

    public boolean isPlaying(int idMyId, MySound[] fill){
        MySound snd;

        fill[0] = null;
        try{
            if((snd = FindMySoundByIdMyId(idMyId)) != null){
                fill[0] = snd;
                return snd.m_Media.isPlaying();
            }
        }catch(IllegalStateException e) {
            snd = null; // ToDo
        }
        return false;
    }

    public void stop(int idMyId){
        MySound snd;
        try{
            if((snd = FindMySoundByIdMyId(idMyId)) != null &&
                    snd.m_Media.isPlaying())
                snd.m_Media.stop();
        }catch(IllegalStateException e) {
            snd = null; // ToDo
        }
    }

    // The str is in the format
    // number id, 1 = on 0 = off,dont play if this id playing;
    public void PlaySound(String str) {
        boolean isplaying;
        int i, len, id, idDontPlay, milliNow;
        String[] strARR = str.split(";");
        String[] strARR2;
        Integer[] tmpIntARR;
        ArrayList<Integer[]> onARR = new ArrayList<Integer[]>();
        ArrayList<Integer> offARR = new ArrayList<Integer>();
        MySound snd;

        for (i = 0, len = strARR.length; i < len; i++) {
            if(strARR[i].length() <= 0) continue;
            if((strARR2 = strARR[i].split(",")) != null &&
                strARR2.length >= 3 &&
                strARR2[0].length() > 0 &&
                strARR2[1].length() > 0 &&
                strARR2[2].length() > 0){
                id = Integer.parseInt(strARR2[0]);
                idDontPlay = Integer.parseInt(strARR2[2]);
                tmpIntARR = new Integer[2];
                tmpIntARR[0] = id;
                tmpIntARR[1] = idDontPlay;
                if(Integer.parseInt(strARR2[1]) == 1){
                    onARR.add(tmpIntARR);
                } else offARR.add(id);
            }
        }

        // Turn off all sounds that need to be turned off
        for (i=0,len=offARR.size();i<len;i++) {
           id = offARR.get(i);
           stop(id);
        }

        // Turn all sounds that need to be turned on,
        // but only if the sound that blocks a new sound is not playing
        for (i=0,len=onARR.size();i<len;i++) {
            tmpIntARR = onARR.get(i);
            id = tmpIntARR[0];
            idDontPlay = tmpIntARR[1];

            // We dont play if the idDontPlay sound is already playing
            if((snd = FindMySoundByIdMyId(idDontPlay)) != null &&
                    snd.m_Media.isPlaying())
                continue;
            if((snd = FindMySoundByIdMyId(id)) != null){
                isplaying = snd.m_Media.isPlaying();
                milliNow = snd.m_Media.getCurrentPosition();
                if(milliNow > (snd.m_Media.getDuration() - 1000) ||
                    (!isplaying && milliNow > 0)){
                    snd.m_Media.seekTo(0); // Half a second inside
                }

                if(!isplaying) snd.m_Media.start();
            }
        }
    }
}

Solution

  • Creating a MediaPlayer instance for every sound is not a good practice to get low latency, especially for short clips. MediaPlayer is for longer clips such as Music files it uses large buffer so, larger buffer means high latency. Also, there is AudioFocus mechanism on Android that may interfere your sound playing session. So, I strongly recommend you to use SoundPool to play short clips like game sounds.