SoundPool sample not ready

Hi there. I am having the following problem when loading sound and playing it immediately after: SoundPool sample x not READY.

Ok, I get why that is happening. I am asking it to play() before it finished loading the sound to memory with loadSound(), since the load happens asynchronously. In this Stack Overflow answer, seems I should listen to the SoundPool.setOnLoadCompleteListener event, which makes a lot of sense.

So, my question is: How can I listen to that SoundPool event in the OpenFrameworks code using the ofSoundPlayer class maybe? All the snippets around are using Java and not the oF in C++.

Thanks!

1 Like
if (soundeffect.isLoaded()) soundeffect.play();

does not work?

ps. please note that, soundpool cant handle long sound files. it is good for bleeps, blops, etc… kind short sounds. if you play longer sound files, you need to use mediaplayer.

Thanks for your reply. I am using it to load 30 second sounds. Is that too much? FYI, checked out the androidSoundPlayerExample and the longer sound they use is 9 seconds.

Also, what makes you say ofSoundPlayer is for shorter sounds? Where do you get this kind of info? Being new to OF I still have some problems finding my way about the documentation. Any hint would be helpful.

if you use setStreaming(true) you can use ofSoundPlayer for long sounds too. setStreaming means that it won’t load the whole file in memory and instead read it directly from the file. on android internally it’ll use mediaplayer instead of soundpool

I’ve tried that too, but I get some errors. The first one appears when starting the sound, and the other two when stopping it. Can’t really understand them… Any ideas?

Should have subtitle controller already set
Attempt to perform seekTo in wrong state: mPlayer=0x5fa45e20, mCurrentState=64
error (-38, 0)

The limitations are not related to OF, but related to soundpool. google the limitations of soundpool and you will find a lot of resources about that.

Oh, and do not use mp3 files for sound effects. mp3 format has a 5ms silence in the beginning which cuases a delay in hearing the sound effect. you need to use wav files (or maybe ogg) for sound effects. mp3s are good for longer sounds like music files.

Anyway streaming may be a different choice. We didn’t use it. What i say here is for the regular usage of soundpool and mediaplayer.

@Rancs: Thanks for the comments and insight!

I’ve been trying to figure out this whole isLoaded() thing and I can’t really understand what is happening with the following minimal (non)working example. Please help me figure this out. The code is really straightforward.

void ofApp::draw() {
	if (sound.isLoaded() && !sound.getIsPlaying()) {
		ofLog() << "playing";
		sound.setVolume(0.7f);
		sound.setPosition(0.0f);
		sound.play();
	}
}
void ofApp::touchDown(int x, int y, int id){
	ofLog() << "touching";
	sound.loadSound("b1.wav");
}

I get the “touching” message on LogCat, followed by the “playing” one, followed by an error from SoundPool “sample 1 not READY”.

My question is: How can the sample not be ready if I am checking it has loaded already?!

EDIT: I actually tried with both a .wav and .mp3 file, 7 seconds long, a similar length to the sound files used in androidSoundPlayerExample.

first of all, can you please try the same code with a short wav file?
with something like that: https://www.freesound.org/people/NoiseCollector/sounds/4359/

Then I think this is a better implementation: (I didnt try the code)

void ofApp::setup() {
    reloadTextures();
    touching = false; //this is a boolean flag
}

void ofApp::draw() {
    if (touching && sound.isLoaded() && !sound.getIsPlaying()) {
        playSound();
    }
    touching = false;
}

void ofApp::touchDown(int x, int y, int id) {
    ofLog() << "touching";
    touching = true;
}

void ofApp::reloadTextures() {
    try {
        if (!sound.isLoaded()) sound.loadSound("b1.wav"); //try a wav file here
    } catch(exception& e) {ofLogNotice("ofApp.cpp") << "EXCEPTION:" << e.what();}
}

void ofApp::playSound() {
    ofLog() << "playing";
    sound.setVolume(0.7f);
    sound.setPosition(0.0f);
    sound.play();
}

Thanks so much for your time @Rancs. Really appreciate it.

I’ve tried my code with the linked wav short sound and still didn’t work. Kept having the same “Sample x not ready” issue.

I’ve tried your code and it did work! Seems there is hope. I still have a small concern though, which is, actually two:

  1. I would really like to understand why my code isn’t working and issuing that error. Logicaly I don’t see how it is possible that is happening. Any ideas?

  2. I am developing an iOS and Android project with this. And the reloadTextures() method seems to be Android only, so that would be a problem, or at least not a very good solution. Also, doesn’t seem to be any documentation on that on the docs, no idea what that is supposed to do really. If you would be so kind as to further explain your approach, that would be much appreciated.

Thanks.

  1. In reality, the only difference between 2 codes is; in your code you load the sound just before playing it and it can not be ready in that short time. In the second one, i loaded it in the beginning.

  2. Normally you can load sound also in the setup() void instead of putting it to reloadTextures().
    Andoid has a pause-resume system in OF. When the app goes to pause state, it invokes pause(). Then when resuming, it first invoke resume(), then just after that it invokes reloadTextures().

If there are some resources which could not be loaded again, you reload it here in the reloadTextures() void. So in the production, you need to write the loader code inside setup and reloadTextures(). But to not write it twice, i write it only inside reloadTextures() and reference it from setup(). But again, you can write loader code inside setup() as well.

Ok, this one is maybe more comprehensible:

void ofApp::setup() {
    try {
        if (!sound.isLoaded()) sound.loadSound("b1.wav"); //try a wav file here
    } catch(exception& e) {ofLogNotice("ofApp.cpp") << "EXCEPTION:" << e.what();}
    touching = false; //this is a boolean flag
}

void ofApp::draw() {
    if (touching && sound.isLoaded() && !sound.getIsPlaying()) {
        ofLog() << "playing";
        sound.setVolume(0.7f);
        sound.setPosition(0.0f);
        sound.play();
    }
    touching = false;
}

void ofApp::touchDown(int x, int y, int id) {
    ofLog() << "touching";
    touching = true;
}

Anyway, please note that the first one is better for final production.

@Rancs, I am working with @nunos in the app development. Thank you so much for your time and your comments, they’ve been really helpful. We are definitely incorporating some of the tips.

Regarding the loading of the sounds, we are facing a trade-off:

  1. We have also tried loading the sounds during setup, as you mentioned, but, as we need to load about 15 different soundscapes (ca. 30’’ each) which can then be triggered (rarely more than 2 or 3 at the same time), the load time is really an issue. We are dealing with about 8-9 seconds extra because of that. It works very well after that.

  2. So we tried loading the sound only when we need it (a 200-300ms latency would be fine in that context). We were expecting that the sound.isLoaded() method would only return true when the sound is actually loaded in the memory. So, on nunos’s code:

     void ofApp::draw() {
     if (sound.isLoaded() && !sound.getIsPlaying()) {
     	ofLog() << "playing";
     	sound.setVolume(0.7f);
     	sound.setPosition(0.0f);
     	sound.play();
     }
    

    }

Our sound.play() would only be called once the sound is loaded into the memory (sound.isLoaded()), right? Or is that some issue with Android’s SoundPool?

Thanks again!

Hi @walkingmetaphor, nice to talk to you.

I checked OFAndroidSoundPlayer.java code and Android’s soundpool reference documentation now. It seems like Android’s soundpool implementation doesn’t have an isLoaded() method. This facility is added by OF.

When OF calls pool.load, it automatically set bisLoaded flag to true in Java side, with no ability of checking that from Android.

So maybe you can try putting a delay between your load and play commands to test the issue. For example, add a simple counter. In the first touch, invoke loadSound() and start the counter counting from 0. Then play the sound when the counter reaches 100 etc…

Sorry i don’t have the ability of checking it now myself. Please share the result with us too.

I thought that may be the case, thanks for checking! We had already tried setting a delayed trigger and it worked, but we decided not to go with it because it might be heavily device and sample dependent. The best way might be to have some kind of trigger coming from when the memory is loaded, but for my part I will only be able to check it tomorrow.

Just for information, the iOS version in already in the App Store as a free download and it works the way we expected it to…

Thanks again :smile:

ok i could try something now.

inside setup() i wrote these 3 consecutive lines:

ofxAndroidSoundPlayer sound;
sound.loadSound("soundfile.wav");
sound.play();

it works.
(Test device Samsung S4)

i for the life of me could not get wav files playing on my kindle fire hdx… re-encoded to mp3s and things seem fine.