I am so confused of those two classes.
I have a problem that I have 1000 of .wav files, it depends on user to load different sounds.
as well as, user can play many sounds in a row, like 4 sounds sequentially.
so which should I use? SoundPool is better for wav files but it is not good that it loads and keep the files loaded.
any recommendation for this situation?
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.
Related
I'm using Media Player in my android application, which plays audio files from given url. I also want to update progress bar while playing the audio file. For that I'm using seekbar.setMax(mediaplayer.getDuration()) in onPrepated() method, but mediaplayer.getDuration() is throwing exception of illegalStateException & showing mediaplayer state = 0 or 1 while same code is working for android 7 & lower versions
My code goes below :
Map<String, String> map = new HashMap<>();
map.put("X-CSRF-Token",preferences.getString("token",null));
map.put("Cookie",preferences.getString("sessid",null));
map.put("Content-Type","application/json");
mediaPlayer.setDataSource(context,Uri.parse(mediaUrl),map);
mediaPlayer.prepareAsync();
#Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
seekBarProgress.setMax(mediaPlayer.getDuration());
seekBarProgress.setProgress(0);
primarySeekBarProgressUpdater(mediaPlayer.getCurrentPosition());
}
// method for updating seekbar
public void primarySeekBarProgressUpdater(final int i) {
try {
if (mediaPlayer.isPlaying()) {
handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
if (mediaPlayer != null && seekBarProgress != null) {
if (mediaPlayer.getDuration() > 0) {
seekBarProgress.setProgress(mediaPlayer.getCurrentPosition());
}
}
if (handler != null) {
handler.postDelayed(this, 1000);
}
}
}, 1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
According to this answer, postDelayed may causes the mediaPlayer to be in invalid state, so calling mediaPlayer.getDuration() will cause IllegalStateException.
For example:
if you press back so your media player released and your handler is still running and call mediaPlayer.getDuration()
Another Assumption is mediaPlayer.isPlaying(), according to open source:
/**
* Checks whether the MediaPlayer is playing.
*
* #return true if currently playing, false otherwise
* #throws IllegalStateException if the internal player engine has not been
* initialized or has been released.
*/
public native boolean isPlaying();
I am able to play an mp3 file using android's MediaPlayer object. But I would like to play between a range of milliseconds for example between 30000 ms to 40000 ms ( 10 seconds only ). How can I achieve this?
Currently the following code is what I have,
private MediaPlayer mPlayer;
public void play() {
try {
mPlayer = MediaPlayer.create(getApplicationContext(), R.raw.mp3_file);
if (mPlayer != null) {
int currentPosition = mPlayer.getCurrentPosition();
if (currentPosition + 30000 <= mPlayer.getDuration()) {
mPlayer.seekTo(currentPosition + 30000);
} else {
mPlayer.seekTo(mPlayer.getDuration());
}
mPlayer.start();
}
}
catch(Exception e) {
}
}
Any help is greatly appreciated. Thank you!
You can use the method:
public int getCurrentPosition ()
to obtain the current time in milSeconds maybe inside a Handler that runs every 1000 milSeconds and tests to see:
if(mPlayer.getCurrentPosition() >= (mPlayer.getDuration + 40000));
Dont forget to release the media file when you're done using it:
public void release();
mPlayer.release();
Releases resources associated with this MediaPlayer object. It is
considered good practice to call this method when you're done using
the MediaPlayer. In particular, whenever an Activity of an application
is paused (its onPause() method is called), or stopped (its onStop()
method is called), this method should be invoked to release the
MediaPlayer object, unless the application has a special need to keep
the object around. In addition to unnecessary resources (such as
memory and instances of codecs) being held, failure to call this
method immediately if a MediaPlayer object is no longer needed may
also lead to continuous battery consumption for mobile devices, and
playback failure for other applications if no multiple instances of
the same codec are supported on a device. Even if multiple instances
of the same codec are supported, some performance degradation may be
expected when unnecessary multiple instances are used at the same
time.
The best approach is to use a Handler to time the stopping of the playback. Start the player and then use the Handler's postDelayed to schedule the execution of a Runnable that will stop the player. You should also start the player only after the initial seek completes. Something like this:
public class PlayWord extends Activity implements MediaPlayer.OnSeekCompleteListener {
Handler mHandler;
MediaPlayer mPlayer;
int mStartTime = 6889;
int mEndTime = 7254;
final Runnable mStopAction = new Runnable() {
#Override
public void run() {
mPlayer.stop();
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final TextView tv = new TextView(this);
tv.setText("Playing...");
setContentView(tv);
mHandler = new Handler();
mPlayer = MediaPlayer.create(this, R.raw.nicholas);
mPlayer.setOnSeekCompleteListener(this);
mPlayer.seekTo(mStartTime);
}
#Override
public void onDestroy() {
mPlayer.release();
}
#Override
public void onSeekComplete (MediaPlayer mp) {
mPlayer.start();
mHandler.postDelayed(mStopAction, mEndTime - mStartTime);
}
}
Note also that the MediaPlayer.create method you are using returns a MediaPlayer that has already been prepared and prepare should not be called again like you are doing in your code.on the screen. I also added a call to release() when the activity exits.
Also, if you want to update the UI when the seek completes, be aware that this method is usually called from a non-UI thread. You will have to use the handler to post any UI-related actions.
I'm copied this from: Android: How to stop media (mp3) in playing when specific milliseconds come?
Is there any way to reference programmatically a very small video file adn include it in teh package - i.e. I don't want to have it separate on the SD card. I am thinking of putting it in the 'raw' package directory.
E.g. MPEG4 called 'video' in 'raw'
Am trying to work out what the correct format for Uri.parse() but it has beaten me. I thought it should be something like R.raw (as used when setting up a media player for audio myMediaPlayer = MediaPlayer.create(this, R.raw.audiocameralive1) - but it doesn't seem to be.
Any suggestions
Oliver
I see there have been a number of views, so in case anyone is looking for a solution, this is what I eventually did - and seems to work fine. There is probably cleaner way of doing the same but, this one makes sense to me ...
Oliver
public class ShowVideoActivity extends Activity
implements SurfaceHolder.Callback,
OnErrorListener,
OnPreparedListener,
OnCompletionListener
{
/** Called when the activity is first created. */
private MediaPlayer myMediaPlayer;
boolean bolMediaPlayerIsReleased = false;
// The SurfaceHolder and SurfaceView are used to display the video
// By implementing the SurfaceHolder.Callback interface means that we have
// to implement surfaceChanged(), surfaceCreated() and surfaceDestroyed()
private SurfaceView videoSurface;
private SurfaceHolder videoHolder;
Display currentDisplay;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.showvideo); // Inflate ShowVideo
// Identify the Surface that will be used to hold the camera image
videoSurface = (SurfaceView)findViewById(R.id.videosurface);
// The SurfaceHolder 'monitors' activity on the Surface
videoHolder = videoSurface.getHolder();
videoHolder.setKeepScreenOn(true);
// Data will be Pushed onto the buffers external to the surface
videoHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
videoHolder.setKeepScreenOn(true);
// Let the monitor know that 'this' activity is responsible for
// all the callback functions.
videoHolder.addCallback(this);
// It is now up to the 'callbacks' to do any further processing
myMediaPlayer = MediaPlayer.create(this,R.raw.filename);
myMediaPlayer.setOnCompletionListener(this);
myMediaPlayer.setOnErrorListener(this);
myMediaPlayer.setOnPreparedListener(this);
myMediaPlayer.setOnCompletionListener(this);
currentDisplay = getWindowManager().getDefaultDisplay();
}
// Set up a listener to wait for MediaPlayer End (Is this PlaybackCompleted()?)
public void onCompletion(MediaPlayer mp)
{
Wrapup(mp);
}
public void surfaceCreated(SurfaceHolder CreatedHolder) {
// Surface created, now it is possible to set the preview
myMediaPlayer.setDisplay(CreatedHolder);
}
public void surfaceDestroyed(SurfaceHolder DestroyedHolder)
{
if (myMediaPlayer != null)
{
if (myMediaPlayer.isPlaying() )
myMediaPlayer.stop();
myMediaPlayer.release();
bolMediaPlayerIsReleased = true;
}
}
public void surfaceChanged(SurfaceHolder ChangedHolder, int intFormat, int intWidth, int intHeight)
{
if (myMediaPlayer.isPlaying())
return;
else
{
setVideoSurfaceSize(myMediaPlayer);
myMediaPlayer.start();
}
}
public boolean onError(MediaPlayer mPlayer, int intError, int intExtra)
{
return false;
}
public void onPrepared(MediaPlayer mPlayer)
{
setVideoSurfaceSize(mPlayer);
mPlayer.start();
// From the 'Started' mode, the player can either be 'Stopped', 'Paused' or PlaybackCompleted'
} // End onPrepared
public void Wrapup(MediaPlayer mp)
{
if (mp != null)
{
if (myMediaPlayer.isPlaying() )
mp.stop();
mp.release();
bolMediaPlayerIsReleased = true;
}
// Now clean up before terminating. This is ESSENTIAL
// If cleanup is NOT done then the surfaceDestroyed will get called
// and screw up everything
// Firstly remove the callback
videoHolder.removeCallback(this); // Prevents callbacks when the surface is destroyed
ShowVideoActivity.this.finish();
}
}
Use Activity.getAssets() to get an AssetManager. The load the file with open.
On Android 2.2+ there is something called SoundPool.OnLoadCompleteListener allowing to know whether a sound has been loaded successfully or not.
I am targeting lower API version (ideally 1.6 but could go for 2.1) and I need to know whether a sound has been loaded correctly (as it is selected by the user). What's the proper way to do it?
I hope not load the sound once with MediaPlayer and if correct with SoundPool?!
I implemented a kind-of-compatible OnLoadCompleteListener class that works at least for Android 2.1.
The constructor takes a SoundPool object, and sounds for which the SoundPool.load(..) has been called must be registered with OnLoadCompleteListener.addSound(soundId). After this, the listener periodically attempts to play the requested sounds (at zero volume). If successful, it calls your implementation of onLoadComplete, like in the Android 2.2+ version.
Here's a usage example:
SoundPool mySoundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
OnLoadCompleteListener completionListener = new OnLoadCompleteListener(mySoundPool) {
#Override
public void onLoadComplete(SoundPool soundPool, int soundId, int status) {
Log.i("OnLoadCompleteListener","Sound "+soundId+" loaded.");
}
}
int soundId=mySoundPool.load(this, R.raw.funnyvoice,1);
completionListener.addSound(soundId); // tell the listener to test for this sound.
And here's the source:
abstract class OnLoadCompleteListener {
final int testPeriodMs = 100; // period between tests in ms
/**
* OnLoadCompleteListener fallback implementation for Android versions before 2.2.
* After using: int soundId=SoundPool.load(..), call OnLoadCompleteListener.listenFor(soundId)
* to periodically test sound load completion. If a sound is playable, onLoadComplete is called.
*
* #param soundPool The SoundPool in which you loaded the sounds.
*/
public OnLoadCompleteListener(SoundPool soundPool) {
testSoundPool = soundPool;
}
/**
* Method called when determined that a soundpool sound has been loaded.
*
* #param soundPool The soundpool that was given to the constructor of this OnLoadCompleteListener
* #param soundId The soundId of the sound that loaded
* #param status Status value for forward compatibility. Always 0.
*/
public abstract void onLoadComplete(SoundPool soundPool, int soundId, int status); // implement yourself
/**
* Method to add sounds for which a test is required. Assumes that SoundPool.load(soundId,...) has been called.
*
* #param soundPool The SoundPool in which you loaded the sounds.
*/
public void addSound(int soundId) {
boolean isFirstOne;
synchronized (this) {
mySoundIds.add(soundId);
isFirstOne = (mySoundIds.size()==1);
}
if (isFirstOne) {
// first sound, start timer
testTimer = new Timer();
TimerTask task = new TimerTask() { // import java.util.TimerTask for this
#Override
public void run() {
testCompletions();
}
};
testTimer.scheduleAtFixedRate(task , 0, testPeriodMs);
}
}
private ArrayList<Integer> mySoundIds = new ArrayList<Integer>();
private Timer testTimer; // import java.util.Timer for this
private SoundPool testSoundPool;
private synchronized void testCompletions() {
ArrayList<Integer> completedOnes = new ArrayList<Integer>();
for (Integer soundId: mySoundIds) {
int streamId = testSoundPool.play(soundId, 0, 0, 0, 0, 1.0f);
if (streamId>0) { // successful
testSoundPool.stop(streamId);
onLoadComplete(testSoundPool, soundId, 0);
completedOnes.add(soundId);
}
}
mySoundIds.removeAll(completedOnes);
if (mySoundIds.size()==0) {
testTimer.cancel();
testTimer.purge();
}
}
}
SoundPool does load the file asynchronously. Before API8 level there is unfortunately no API to check if the loading has been completely.
As you said as of Android API8 it is possible to check if the loading complete via a OnLoadCompleteListener. Here is a small example for this: Android sounds tutorial.
I have a small (200kb) mp3 in the res/raw folder of my android app. I am trying to run it in an emulator from Eclipse. It is recognized as a resource in the R file but when I try to prepare/start, my activity crashes! Was there something else I needed to change, perhaps in the manifest?
MediaPlayer mPlayer = MediaPlayer.create(FakeCallScreen.this, R.raw.mysoundfile);
try {
mPlayer.prepare();
mPlayer.start();
} catch (IOException e) {
// handle this later
}
When starting the activity i.e on onCreate put the following code.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MediaPlayer mPlayer = MediaPlayer.create(FakeCallScreen.this, R.raw.mysoundfile);
mPlayer.start();
}
When stopping the activity i.e on onDestroy put the following code.
public void onDestroy() {
mPlayer.stop();
super.onDestroy();
}
Hope it helps :)
You'll likely prefer to use the SoundPool class. It reduces latency when it's time to play the sound, and offers other niceties like being able to prioritise sounds when there are too many to play at once.
From the docs:
A SoundPool is a collection of samples that can be loaded into memory from a resource inside the APK or from a file in the file system. The SoundPool library uses the MediaPlayer service to decode the audio into a raw 16-bit PCM mono or stereo stream. This allows applications to ship with compressed streams without having to suffer the CPU load and latency of decompressing during playback.
For example:
/**
* How many sounds can be played at once.
*/
private static final int MAX_SOUND_POOL_STREAMS = 4;
/**
* Modify this as part of your own priority scheme. Higher numbers mean higher
* priority. If you don't care, it's okay to use the same priority for every
* sound.
*/
private static final int NORMAL_PRIORITY = 10;
private int mySoundId;
#Override
public void setupContent() {
this.soundPool = new SoundPool(MAX_SOUND_POOL_STREAMS,
AudioManager.STREAM_MUSIC, 100);
this.mySoundId = this.soundPool.load(this.getApplicationContext(),
R.raw.mySound, 1);
}
#Override
private void playMySound() {
this.soundPool.play(this.mySoundId, 1, 1, NORMAL_PRIORITY, 0, 1);
}
this is a static method I use in my projects.
I add it to my Utils class:
public static void playSound(final Context context, final SoundType type)
{
new Thread(new Runnable()
{
#Override
public void run()
{
MediaPlayer mediaPlayer = new MediaPlayer();
int resId = -1;
switch (type)
{
case INCOMING_NOTIFICATION:
resId=R.raw.noti_sound;
break;
case SEND_BETTING_SLIP:
resId=R.raw.slip_sent;
break;
case TRIVIA_RIGHT_ANSWER:
resId=R.raw.game_bonus;
break;
case TRIVIA_WRONG_ANSWER:
resId=R.raw.whistle_referee_trivia_bad_answer;
break;
}
if (resId != -1)
{
mediaPlayer = MediaPlayer.create(context, resId);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setLooping(false);
mediaPlayer.start();
while (mediaPlayer.isPlaying() == true)
{
}
}
}
}).start();
}
}
now I defind an Enum (SoundType) and placed the mp3 files in raw folder under
res folder.