14

I'm developing a game in Android and I came across a very annoying, hard-to-find bug. The issue is that when you are using SoundPool to play your sounds, you can actually loop whatever sound you are playing. In this case, the issue is the "running steps" sound; this sound gets executed quite fast and continually (around every 400ms) when the main character is running.

Now when playing the sound on a regular (not so powerful) device e.g. Samsung SII, the sound is played every 500ms - however, if I run the very same code on another device (let's say, Samsung SIV, Samsung SIII), the sound plays twice or even three times faster.

It seems like the more powerful the device hardware specs are, the faster it plays. On some devices, it plays so fast that you almost hear one solid continuous sound. I've been looking for techniques to set a specific ratio on the time period between sound plays, but it doesn't work properly and the issue remains. Does anyone know how to fix it, either using SoundPool, MediaPlayer, or any other sound-controlling API on Android?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Martin Cazares
  • 13,637
  • 10
  • 47
  • 54

5 Answers5

1

You could use an AudioTrack to play a continuous stream of PCM data, since you would pass a stream you could be sure about the interval between sounds. the downside could be a little delay when first starting the sound but it depends on the minimum buffer size, and it depends, I think, on android version and device. On my galaxy s2 android 4.1 it was about 20ms.
if you think this could be an option I can post some code

lelloman
  • 13,883
  • 5
  • 63
  • 85
1

The problem with just looping or using a regular interval for something like footsteps is that you have a possible decoupling of sound and visuals. If your sound gets delays or sped up, or your visuals get delayed or sped up, you would have to adjust for that delay dynamically and automatically. You already have that issue right here

A better solution would be to place a trigger on the exact event which should trigger the sound (in this case, the foot being placed down), which then plays the sound. This also means that if you have multiple sources of the sound (like multiple footsteps), you don't have to manually start the sound with the right interval.

Nzall
  • 3,439
  • 5
  • 29
  • 59
  • This approach have a huge gap, since not all the cases might count with one event each time I want a sound "e.g. an authomatic gun", is one single event to trigger, and the sound must loop at specific time interevals until the finger is released from the gun... – Martin Cazares Oct 15 '13 at 16:16
  • If you got an automatic gun sound, a loop is better, yes, but unless you do an autorunner game or a Myst-style adventure game, looping sound is a bad idea. It's better to link it to the footsteps, because that way the sound is always coupled to the footsteps. I didn't say to loop it all the time. – Nzall Oct 16 '13 at 07:04
0

I can't seem to replicate the issue on Galaxy Nexus and Nexus S, does that mean I fixed it? Or maybe you could show what you're doing differently from this:

SoundPool soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);
Integer sound1 = soundPool.load(this, R.raw.file1, 1);
Integer sound2 = soundPool.load(this, R.raw.file2, 1);
playSound(sound1);

public void playSound(int sound) {
  AudioManager mgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
  float volume = mgr.getStreamVolume(AudioManager.STREAM_MUSIC)
               / mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);  
  soundPool.play(sound, volume, volume, 1, -1, 1.0f);
}
dlamblin
  • 43,965
  • 20
  • 101
  • 140
  • Nothing, I'm doing exactly that, test with an old device, and you will notice a huge difference in gap between one sound and the next one, it will have a bigger delay between each other, that's exactly the same code I have... – Martin Cazares Oct 16 '13 at 18:52
0

If the problem is that you want to control the interval between the discrete sounds, The easiest way to do this is with a handler.

Basically you start a sound playing which is an asynchronous process. Then you use a handler to schedule a message to play the next sound sometime in the future. It will take some trial and error to get it right, but you will be guaranteed that the sound will start at the same interval after the previous sound on every device.

Here is some code to illustrate what I am talking about.

Here is a handler implementation you could use:

    handler = new Handler() {

        /* (non-Javadoc)
         * @see android.os.Handler#handleMessage(android.os.Message)
         */
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == NEXT_ITEM_MSG) {
                playNextSound();
            }
            else if (msg.what == SEQUENCE_COMPLETE_MSG) {
                // notify a listener
                listener.onSoundComplete()
            }
        }
    };

Then you could write playNextSound like this:

private void playNextSound() {
    if (mRunning) {
        // Get the first item
        SoundSequenceItem item = currentSequence.getNextSequenceItem();
        if (item == null) {
            Message msg = handler.obtainMessage(SEQUENCE_COMPLETE_MSG);
            handler.sendMessage(msg);
            return;
        }
        // Play the sound
        int iSoundResId = item.getSoundResId();
        if (iSoundResId != -1) {
            player.playSoundNow(soundResId);
        }

        // schedule a message to advance to next item after duration
        Message msg = handler.obtainMessage(NEXT_ITEM_MSG);
        handler.sendMessageDelayed(msg, item.getDuration());
    }
}

and your SoundSequenceItem could just be a simple class that has a sound file resource id and a duration. If you want to keep playing the sound while the character is moving you could do something like this:

public void onSoundComplete() {
    if (character.isRunning()) {
        currentSequence.addSequenceItem(new SoundSequenceItem(R.id.footsteps,500);
        playNextSound();
    }
}

Or you could modify playNextSound to continually play the same sound. Mine is written this way to be able to play different sounds in sequence.

museofwater
  • 409
  • 5
  • 9
  • I've thought on several ways to do it manually, like ThreadPools/Scheduler/Monitor and even handlers, however I'm looking for a native call as answer, doing it manually wouldn't be a problem I'm just looking for something existing in the API – Martin Cazares Oct 16 '13 at 19:38
0

I have had a lot of problems developing apps which used sounds and stuff like that. I would not suggest you to use SoundPool since it is bug-affected, and also be aware that looping sounds with SoundPool won't work on devices which are 4.3 and higher, see this open issue, at AOSP - Issue tracker. I think that the solution is to go native and use OpenSL ES o similar libraries.

fiipi
  • 663
  • 6
  • 20