简体   繁体   中英

Why does my SoundPool sound not play the first time on Android?

I've got a got a curious problem with playing Ogg Vorbis sounds on Android that I'm having difficulty correcting. I'm using the SoundPool class to play sounds. It works great with the exception of the first sound that I play after a pause.

Here's the issue I'm encountering: I have a menu with several buttons on it. If I hit a button, I play a tick sound. However, the very first time that I attempt to play one, it does not emit a sound. Then, if I hit the same menu button again, it works. If I then delay the next press for several seconds, the issue re-appears. Specifically, I see the following behavior:

  • wait several seconds
  • touch UI button which should emit a sound ... it doesn't
  • log cat reports "AudioFlinger: write blocked for 165 msecs, 66 delayed writes, thread 0xec40" (happens immediately when I touch the UI button)
  • wait several seconds
  • log cat reports "AudioHardwareQSD: AudioHardware pcm playback is going to standby"
  • touch UI button which should emit a sound... it doesn't.
  • logcat reports "AudioFlinger: write blocked for 166 msecs, 67 delayed writes, thread 0xec40" (happens immediately when I touch the UI button)
  • touch UI button again, but before AudioHardwareQSD standby message appears... SOUND WORKS!

I've seen this behavior on a few different devices: HTC Incredible, rooted Nook Color, and HTC Thunderbolt, so I don't believe it is device specific.

I'm GUESSING this is a function of the AudioHardwareQSD going to standby. Once it is in standby, the first sound out of the chute gets ignored, while subsequent sounds work until it hits standby once again. There is roughly a 3 second delay between the 'write blocked' message and the AudioHardwareQSD message on my HTC Incredible.

There's an answer on StackOverflow here that discusses using WakeLock to prevent this from happening. I tried that solution and it did not fix my problem.

Why is this happening? Is there a way to prevent AudioHardwareQSD from going into standby? Does anybody have any tips on troubleshooting this?

Thanks!

UPDATE: I've been able to implement a rather crude workaround that corrects this problem. In my application (a game), I have a sounder rendering class. I added a new method present() that is executed by the game logic on a periodic basis. The method uses a 1.5s timeout timer. When the timer bottoms out, the sound renderer plays an audio file that consists of 50ms of silence and then resets the timer.

This has corrected my issue... Far from optimal, but it works.

Android SoundPool really sucks. I have to

  int streamid = soundPool.play(sid, soundVolume, soundVolume, 0, loopVal, 1.0f);
  if (streamid == 0) {
    try {
     Thread.sleep(50);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
  }
} while(streamid == 0 && retry++<3);

But this isn't ideal, it really slows down the framerate.

You can initialize the SoundPool in a background thread and set a SoundPool.OnLoadCompleteListener to get informed when ready.

Also take a look to the example: specific sound must play when item is clicked

Four years since the original post and I'm having this same issue! In my case, it's on a Samsung Galaxy S3 using Android API 19. But I found a different solution that doesn't require playing a silent sound at a timed interval.

Instead, I found that it's only the first portion (50-100ms) of the audio file that isn't being played the first time. So editing my sound file so the resulting mp3 has about 100ms of silence at the beginning, it then plays correctly. The 100ms delay is barely perceptible for a button click.

Issue still exists in 2021.

The only solution, that really gets rid of that initial delay, is to use a thread that continuously plays a sound with volume 0 every 5 seconds or so.

Kotlin code:

class SoundThread(soundPool: SoundPool) : Thread() {

    private var soundPool: SoundPool = soundPool;
    var sounds: BlockingQueue<SoundEntity> = LinkedBlockingQueue<SoundEntity>();

    override fun run() {
        var sound: SoundEntity;
        var prevTime = System.currentTimeMillis();

        while(true){
            var now = System.currentTimeMillis();
            if(now - prevTime > 5000){
                this.soundPool.play(1, 0.0f, 0.0f, 1, 0, 1.0f);
                prevTime = now;
            }

            if(!sounds.isEmpty()) {
                sound = sounds.take();
                this.soundPool.play(sound.soundId, sound.volume, sound.volume, 1, 0, 1.0f);
            }
        }
    }
}

Modified Andreas Linden's answer from here .

(I write this answer for the ones who overlook that workaround part in the question, like I did.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM