24

I have a .wav file that I'd like to use across my game, currently I am loading the sound in onCreate() of each activity in the game.

 soundCount = soundpool.load(this,R.raw.count, 1);

The sound will be played once the activity starts.

  soundpool.play(soundCount, 0.9f, 0.9f, 1, -1, 1f);

Problem is at times I will hit the error "sample x not ready". Is it possible to load the .wav file once upon starting the game and keep it in memory and use it later across the game? Or is it possible to wait for 1-2 seconds for the sound to load finish?

SteD
  • 13,909
  • 12
  • 65
  • 76

8 Answers8

38

You'll need to wait for it to finish by adding a listener via SoundPool.setOnLoadCompleteListener.

EboMike
  • 76,846
  • 14
  • 164
  • 167
  • 7
    Unfortunately, setOnLoadCompleteListener is not available before API 8 :( Is there any other well known solution to solve this besides using setOnLoadCompleteListener? /thanks – SteD Mar 05 '11 at 08:13
5

Since my project is compatible with Android 1.5 and I couldn't use setOnLoadCompleteListener, I resolved making the play of sound delayed. My source code follows:

    playSound_Delayed(soundId, 100);

// (..)

private void playSound_Delayed (final int soundId, final long millisec) {
    // TIMER
    final Handler mHandler = new Handler();
    final Runnable mDelayedTimeTask = new Runnable() {
    int counter = 0;
    public void run() {
        counter++;

        if (counter == 1) {
            boolean ret = mHandler.postDelayed(this, millisec); 
            if (ret==false) Log.w("playSound_Delayed", "mHandler.postAtTime FAILED!");
       } else {
           playSound(soundId);
       }
    }
    };
    mDelayedTimeTask.run();
}
Lisitso
  • 495
  • 10
  • 14
  • 1
    not exactly in-topic, but you might want to substitute System.err.println with Log.w in order to show it on LogCat – superjos Jan 20 '12 at 15:44
  • 1
    System.err.println does show up in logcat with the System.err tag, actually. But I agree that Log.w is cleaner – entropy Oct 04 '12 at 20:31
4

you should use setOnLoadCompleteListener if possible ... if not, wrap a while loop around your call to 'play'. something like:

int waitLimit = 1000;
int waitCounter = 0;
int throttle = 10;
while(soundPool.play(soundId, 1.f, 1.f, 1, 0, 1.f) == 0 && waitCounter < waitLimit)
 {waitCounter++; SystemClock.sleep(throttle);}

this will retry 'play' 1000 times on a 10ms interval. this should be run on a non-ui thread of course, and is still not ideal. but maybe a little stronger than waiting an arbitrary time and expecting the pool to be ready.

newbyca
  • 1,523
  • 2
  • 13
  • 25
2
private SoundPool soundPool;
private int my_sound;
boolean loaded = false;

// In the constructor

soundPool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
my_sound = soundPool.load(this, R.raw.blast_sound, 1);

soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
    public void onLoadComplete(SoundPool soundPool, int sampleId,int status) {
       loaded = true;
    }
});

// then where ever you want to play the sound, type

if (loaded) {
soundPool.play(my_sound,  0.9f, 0.9f, 1, 0, 1f);
}
Mahesh
  • 3,727
  • 1
  • 39
  • 49
2

I solved with problem with simple do-while cicle. The method play() return non-zero streamID if successful, zero if failed. So, it's sufficient to check the return value.

int streamID = -1;
do {
    streamID = soundPool.play(soundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f);
} while(streamID==0);
DrFred
  • 461
  • 3
  • 6
2

This would work for you definitely !

public void playWavFile() {

    Thread streamThread = new Thread(new Runnable() {           

        @Override
        public void run() {                 
            SoundPool soundPool;
            final int wav;
            String path = "/mnt/sdcard/AudioRecorder/record.wav";
            soundPool = new SoundPool(5,AudioManager.STREAM_MUSIC, 0);
            wav = soundPool.load(path, 1);
            soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
                @Override
                public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
                    // TODO Auto-generated method stub
                    soundPool.play(wav,100, 100, 0, 0, 1f);
                }
            });

        }        
    });

    streamThread.start();
}   
Ben Pearson
  • 7,532
  • 4
  • 30
  • 50
Mahe
  • 21
  • 1
2

The SoundPool library uses the MediaPlayer service to decode the audio into a raw 16-bit PCM mono or stereo stream which we simply can call preparation of Stream, that takes some times depending on size of sound file. And if we try to play the sound before that process is over, we get "sample x not ready" error.

There are two solutions for this

  1. implement setOnLoadCompleteListener or
  2. wait arbitrary amount of time so that preparation is over

and then play it Note that there is no exception for this condition or application is not crashing without any try-catch

CR Sardar
  • 921
  • 2
  • 17
  • 32
1

This sounds crazy, but don't play sounds right away. Give your app a couple of seconds to initialize the SoundPool. In my app, this was exactly the issue; I added a fake "loading" screen of ~3 seconds, and then everything worked. (I didn't even need to preload the sounds.)

ashes999
  • 9,925
  • 16
  • 73
  • 124
  • That's what I'm doing to but I find it really annoying. The game loads in a second. The loading screen is only there for the sounds sake. – W.K.S Apr 16 '12 at 18:40
  • @W.K.S me too. I had an idea to make the loading screen a mini-game or put a bit of interactivity in it as an "easter egg" kinda thing, even though it's only visible for a couple of seconds. – ashes999 Apr 16 '12 at 18:52