1

I like the Android Soundpool class for its simplicity and it works well with the standard audio files I am using in my app. Now I want to make it possible for the user to specify certains sounds by specifying audio files on the sd card. Unfortunately I run into limitations of Soundpool, when the sound file is too big i get a

AudioFlinger could not create track. status: -12

response. It seems I have to switch to MediaPlayer yet before getting into the complexity of MediaPlayer again I wanted to ask if there is an audio library available for android which

  • has the simplicity of Soundpool for playing various sounds
  • doesnt have the limitations of Soundpool regarding the size of the files.

Thank you very much.

martin

dorjeduck
  • 7,624
  • 11
  • 52
  • 66
  • [This](http://stackoverflow.com/q/6484574/645270) question could help. I don't think the answers mentioned file size though (but they did mention alternatives) – keyser Oct 09 '12 at 08:09
  • thx Keyser, I saw this post before, Jetplayer seems not what I am looking for – dorjeduck Oct 09 '12 at 08:12

2 Answers2

2

For now I came up with a very simple AudioPool class which plays audio added to it subsequently with the MediaPlayer class. This implementation is for sure not mature yet I just thought to share it as it at least gives some idea how this can be approached easily. If you see any problems with this class please let us know.

Usage:

 AudioPool ap = new AudioPool();

 File root = Environment.getExternalStorageDirectory() ;

 int id1 = ap.addAudio(root + "/gong1.mp3");
 int id2 = ap.addAudio(root + "/gong2.mp3");
 int id3 = ap.addAudio(root + "/gong3.mp3"); 

 ap.playAudio(id1);
 ap.playAudio(id3);
 ap.playAudio(id3);
 ap.playAudio(id2);

which will play gong1 -> gong3 -> gong3 -> gong1 subsequently. As this is basically what I need I leave it here ...

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.util.Log;

public class AudioPool {

static String TAG = "AudioPool";

MediaPlayer mPlayer;

int mAudioCounter;

int mCurrentId;

HashMap<Integer, String> mAudioMap;

LinkedList<Integer> mAudioQueue;

public AudioPool() {

    mAudioMap = new HashMap<Integer, String>();
    mAudioQueue = new LinkedList<Integer>();
    mAudioCounter = 0;

}

public int addAudio(String path) {
    Log.d(TAG, "adding audio " + path + " to the pool");

    if (mAudioMap.containsValue(path)) {
        return getAudioKey(path);
    }
    mAudioCounter++;
    mAudioMap.put(mAudioCounter, path);
    return mAudioCounter;
}

public boolean playAudio(int id) {

    if (mAudioMap.containsKey(id) == false) {
        return false;
    }

    if (mPlayer == null) {
        setupPlayer();
    }

    if (mPlayer.isPlaying() == false) {
        return prepareAndPlayAudioNow(id);
    } else {
        Log.d(TAG, "adding audio " + id + " to the audio queue");

        mAudioQueue.add(id);
    }
    return true;
}

public Integer[] getAudioIds() {
    return (Integer[]) mAudioMap.keySet().toArray(
            new Integer[mAudioMap.keySet().size()]);
}

public void releaseAudioPlayer() {
    if (mPlayer != null) {
        mPlayer.release();
        mPlayer = null;
    }
}


private boolean prepareAndPlayAudioNow(int id) {
    mCurrentId = id;
    try {
        Log.d(TAG, "playing audio " + id + " now");
        mPlayer.reset();
        mPlayer.setDataSource(mAudioMap.get(id));
        mPlayer.prepare();
        mPlayer.start();
        return true;
    } catch (Exception e) {
        Log.d(TAG, "problems playing audio " + e.getMessage());
        return false;
    }
}

private boolean playAudioAgainNow() {
    try {
        mPlayer.seekTo(0);
        mPlayer.start();
        return true;
    } catch (Exception e) {
        Log.d(TAG, "problems playing audio");
        return false;
    }
}

private void setupPlayer() {
    mPlayer = new MediaPlayer();
    mPlayer.setOnCompletionListener(new OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            audioDone();
        }
    });
}

private void audioDone() {

    if (mAudioQueue.size() > 0) {
        Log.d(TAG, mAudioQueue.size() + " audios in queue");
        int nextId = mAudioQueue.removeFirst();

        if (mCurrentId == nextId) {
            playAudioAgainNow();
        } else {
            prepareAndPlayAudioNow(nextId);
        }

    } else {
        releaseAudioPlayer();
    }
}

private int getAudioKey(String path) {
    for (Map.Entry<Integer, String> map : mAudioMap.entrySet()) {
        if (map.getValue().compareTo(path) == 0) {
            return map.getKey();
        }
    }
    return -1;
}

}
dorjeduck
  • 7,624
  • 11
  • 52
  • 66
0

Thanks to dorjeduck for the solution, but his class based on MediaPlayer, which has huge latency. What does it mean? It means that when you call these:

mPlayer.prepare();
mPlayer.start();

and actually hear the sound the delay is very noticable. For example when you need to play one track and immediately play another, you will hear delay even on high-end hardware.

The solution to load all bytes into memory before playing, and use AudioTrack to play that sound bytes.

I have written SoundPoolCompat which uses AudioTrack under the hood. You could pass custom bufferSize, and all data within that buffer will be loaded into memory and played with small latency like SoundPool does. All data that exceed that bufferSize will be loaded on demand (which adds latency, similar to MediaPlayer). Api is very similart to SoundPool, also it is added a feature to load sounds from Uri (for example gdrive). And there is playOnce method, all resources will be unloaded after file is played.

implementation 'com.olekdia:sound-pool:3.0.2'

https://gitlab.com/olekdia/common/libraries/sound-pool

Oleksandr Albul
  • 1,611
  • 1
  • 23
  • 31