Audiotrack, Soundpool or Mediaplayer Which Should I Use

Soundpool vs Mediaplayer for playing multiple sound effects

It does not depend by the number of sounds, but from their size in terms of bytes. Every sound is loaded into memory and if they are too big, you could not have enough memory to load them all.

I would consider using AudioTrack. You instantiate it and when you have to play a sound you can read it from file system or assets or resources and add it to the play queue. It works only with PCM.

Soundpool or MediaPlayer ? - Android

I have done it on my way using MediaPlayer, Thanks for @blipinsk, After i read this answer StackOverFlow suggested by him in the comment above.

My files are a bit larger that SoundPool can tolerate, as well as, I want to play many files sequentially. Which i had to implement it myself using threads in SoundPool. On the contrary, It is ready in MediaPlayer using OnCompletionListener. So that, I used MediaPlayer.

Actually i tried SoundPool with threads, it works but since it does not support large media files, i used Media Player.

I wrote this class which wrap the MediaPlayer to run a playList, you can add to the playlist and the media player will run them one after another. so here is the class:

import android.media.MediaPlayer;
import android.os.Environment;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/**
* Created by MBH on 01.08.2015.
*/
public class THSafeListMediaPlayer {
private static final String TAG_DEBUG = "MBH";
private String PATH_OF_SOUND_FILES; // I use it because i will put all sound clips in one folder
// , then i will pass the name of the folder only.
private LinkedBlockingQueue<String> playList;
private MediaPlayer mediaPlayer; // The media player to play the sounds, even in background
private ExecutorService executorService; // For making sure there is only one thread at a time
// adding to the queue
private boolean isPaused = false;
private int pausedPosition = -1;

/**
* Constructor will take care of initializing all the important variables
*/
public THSafeListMediaPlayer() {
// initializing the variables
executorService = Executors.newSingleThreadExecutor();
playList = new LinkedBlockingQueue<>();
mediaPlayer = new MediaPlayer();
PATH_OF_SOUND_FILES = Environment.getExternalStorageDirectory().getPath() + "/mbh/sounds/";
}

/**
* It will only add file to the PlayList
*
* @param fileName: The file name
*/
public void addFile(String fileName) {
// you may add executorService here for safer threads adding here
// here i use offer, because it is thread safe
playList.offer(fileName);
}

/**
* It will add file and play the last add file and continue to the play list
*
* @param fileName: the file name, playing the soundtrack will start from this file
*/
public void addFileAndPlay(final String fileName) {
// For MultiThreaded
// executorService.submit(new Runnable() {
// @Override
// public void run() {
// playList.offer(fileName);
// if (!mediaPlayer.isPlaying())
// play(playList.poll());
// }
// });
// For single threaded
playList.offer(fileName);
if (!mediaPlayer.isPlaying())
play(playList.poll());
}

/**
* Start playing the play list if there is files in the playlist
*
* @return: True if playing successfully done, otherwise, false;
*/
public boolean play() {
if (mediaPlayer != null) {
if (!mediaPlayer.isPlaying()) {
if (isPaused) {
mediaPlayer.seekTo(pausedPosition);
mediaPlayer.start();
pausedPosition = -1;
isPaused = false;
return true;
} else if (!playList.isEmpty()) {
play(playList.poll());
return true;
}
}
}
return false;
}

/**
* Pause the current played track, if there is track playing
*/
public void pause() {
if(isPaused)
return;
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
pausedPosition = mediaPlayer.getCurrentPosition();
isPaused = true;
}
}
}

/**
* it will play the given file, when it finishes, or fails, it will play the next from the list
*
* @param fileName: the file name to start playing from it
*/
private void play(String fileName) {
if (mediaPlayer != null) {
if (!mediaPlayer.isPlaying()) {
try {
mediaPlayer.reset();
mediaPlayer.setDataSource(fileName);
mediaPlayer.prepare();
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
playNextSoundTrack();
}
});
mediaPlayer.start();
} catch (Exception e) {
// TODO: Remove this error checking before publishin

// If the current file is not found, play the next track if there is
playNextSoundTrack();
}
}
}
}

/**
* this function will be called recursively to play the next track
*/
private void playNextSoundTrack() {
if (playList.size() != 0) {
play(playList.poll());
}
}

}

I struggled for a while with it. I hope it will help others.

NOTE: I used LinkedBlockingQueue to keep the playList tracks in it, because it is implemented to be thread safe.

If you want to use this class in threads, i suggest you to use the executorService if u will use it in multithreaded app.

How bad is Android SoundPool? What alternative to use?

Just to add some more recent feedback on this issue. I've been using SoundPool for some time in an app with a fairly large user base for key press sounds. Our use case:

  • Must be played immediately
  • Up to 3+ sounds in parallel
  • We make use of the setRate across it's full range [0.5f-2.0f]

I've now experienced two major device specific issue and have decided to cut my losses and switch away from SoundPool

  • A large number of 4.4 LG devices (mostly the LG G2/G3 line) were having a native crash with their implementation of SoundPool. This was fixed in an update (eventually) but we still have a lot of users with un-upgraded devices
  • Sony Xperia devices currently have all sorts of issue with SoundPool as reported by others. In my case, I've discovered that if you use setRate with rate > 1.0f the SoundPool with start throwing exceptions until your app quits (and burn through a bunch of battery in the process).

TL;DR; I no longer think it's worth the danger/hassle of debugging SoundPool

Difference between Audiomanager and MediaPlayer

AudioManager doesn't play sound at all. It provides access to sound settings, but to play sounds easily, you should use MediaPlayer, SoundPool, or possibly AudioTrack.

From the docs:

AudioManager provides access to volume and ringer mode control.

android audio - soundpool alternatives

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;
}

}

SoundPool - What position in a sound file

Sound File Duration

MediaPlayer is probably your best bet, here:

MediaPlayer player = MediaPlayer.create(ctxt, R.raw.mysound);
int duration = player.getDuration();
player.release(); //(If not using this player later on)

Current Playback Position

SoundPool does not support this functionality. Your three main options:

  1. Use MediaPlayer for playback. MediaPlayer supplies a getCurrentPosition() method.
  2. Use AudioTrack -- manually sending uncompressed audio to the stream on a byte-by-byte basis. AudioTrack has some basic functionality for determining playback position (usually accurate to within a few tens of milliseconds, on most devices), and besides that you can know exactly how much audio's been added to the playback buffer.
  3. Stick with SoundPool, but time the playback yourself. That is, take a time hack (SystemClock.UptimeMillis()) or set a CountDownTimer, and estimate your playback position by assuming that playback began as soon as play() returned. This approach is rife with problems and inaccuracies, but has the benefit of not involving AudioTrack's complexity.


Related Topics



Leave a reply



Submit