What i have:
I have implemented three MediaPlayer.Objects in my App.
All Three are created using a thread:
protected void onResume() {
// Threads
mTT1 = new TrackThread(this, R.raw.audiofile1, 1, mHandler);
mTT2 = new TrackThread(this, R.raw.audiofile2, 2, mHandler);
mTT3 = new TrackThread(this, R.raw.audiofile3, 3, mHandler);
// start thread
mTT1.start();
mTT2.start();
mTT3.start();
super.onResume();
}
"simplified" Code in the Thread for creating:
public class TrackThread extends Thread implements OnPreparedListener {
...
...
...
public void run() {
super.run();
try {
mMp.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getDeclaredLength());
mMp.prepare();
} catch (IllegalArgumentException | IllegalStateException
| IOException e) {
Log.e(TAG, "Unable to play audio queue do to exception: "
+ e.getMessage(), e);
}
}
As I read in several Tutorials the "prepare()" methode takes a little bit of time to finish. Therefore i implemented a "Waiting loop" which waits until all MPs are prepared and created.
When "prepare and create" are done i enable the Start button and i want to start all 3 Mediaplayers SIMULTANEOUSLY.
I again use a Thread for dooing so:
public void onClick(View v) {
// Button 1
if (mBtn.getId() == v.getId()) {
mTT1.startMusic();
mTT2.startMusic();
mTT3.startMusic();
}
Code in the thread:
public class TrackThread extends Thread implements OnPreparedListener {
...
...
...
// start
public void startMusic() {
if (mMp == null)
return;
mMp.start();
}
Please note that the code above is not the full code, but it should be enough to define my problem.
What i want, My problem:
All MPs should play their Music in Sync, unfortunately sometimes when i start the music, there is a time delay between them.
The MPs must start at the exact same time as the 3Audio-files must be played simultaneously (and exactly in sync)
What i have already tried:
+) using SoundPool: My Audio-files are to big(5Megabyte and larger) for SoundPool
+) seekTo(msec): i wanted to seek every MP to a Specific time: eg.: 0, but this did not solve the problem.
+) to reach more Programmers i also asked this question on: coderanch.com
I hope somebody can help me!
Thanks in advance
The bottleneck here will certainly be preparing the mediaplayers to play. The Android framework provides an asynchronous method to perform this loading, and so with a bit of synchronization code you should be able to get these audio sources to play at roughly the same time. To keep from sound artifacting, you'll want less than 10ms of latency.
Initialize an atomic counter, C, to the number of things to load.
Use the prepareAsync() functions within MediaPlayer to prepare all three. Immediately after calling prepareAsync, supply a listener using setOnPreparedListener(listener).
Inside this listener, decrement C and check the value. If the value is greater than 0, wait on an object using the java object .wait() function. If the value is equal to 0, call notifyAll() on the object to wake up all of the other mediaplayer prepared-listener callback threads.
public void startMediaPlayers(List<MediaPlayer> mediaPlayers) {
private AtomicInteger counter = new AtomicInteger(mediaPlayers.size());
Object barrier = new Object();
/* start off all media players */
for (MediaPlayer player : mediaPlayers) {
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(final MediaPlayer mediaPlayer) {
int value = counter.decrementAndGet();
if (value == 0) {
// all media players are done loading.
// wake up all the ones that are asleep
barrier.notifyAll();
} else {
while (value > 0) {
try {
// wait for everyone else to load
barrier.wait();
} catch (InterruptedException e) {
// ignore
}
}
}
mediaPlayer.start();
callback.success(true);
}
player.prepareAsync();
}
}
As nobody could help me I found a solution on my own. MediaPlayer did not fulfill my requirements but Android JETPlayer in combination with JETCreator did.
CAUTION: Installing Python for using JETCreator is very tricky, therfore
follow this tutorial. And be careful with the versions of python and wxpython, not all versions support the JETCreator.
I used:
Python Version 2.5.4 (python-2.5.4.msi)
wxPython 2.8 (wxPython2.8-win32-unicode-2.8.7.1-py25.exe)
For those who do not know how to implement the Jetplayer watch this video
(at min.5 he starts with programming the Jetplayer).
Unfortunately I do not speak French so I just followed the code which worked for me.
Using Android JETCreator you can create your own JET Files and use them as your resource.
Useful links:
Demo data
Manual
Code/class
Related
Recently i have an audio project in Audacity with multiple tracks in it. It has vocals separate and instruments separate.
I have exported each track as a WAV(16bit) file. I have 5 files for 5 tracks. All the files are more than 300MB and lengths of 25 minutes.
I was trying to play them simultaneously using mediaplayer:
I have created Mediaplayer instances mp1, mp2, mp3, mp4 and mp5 for each file
I try to play them simultaneously by:
Default Way to start all media players:
mp1.start()
mp2.start()
mp3.start()
mp4.start()
mp5.start()
What i observed is there is some gaps in between.
I could have mixed them in audacity and played as a single file. the reason i want them to be played seperately is i want to control the volume of each track in android.
I reasearched a lot on net regarding this:
I thought the problem is in .start() commands not starting simultaneously.
Methods 1: Asyntask Method to start all mp simultaneously:
SO i have tried using Asyntask and created 5 separate threads like below:
class PlayThread extends AsyncTask<MediaPlayer, Void, Void>
{
#Override
protected Void doInBackground(MediaPlayer... player) {
player[0].start();
return null;
}
}
then did
PlayThread[] playThreads = new PlayThread[5];
for (int i = 0; i < 5; i++)
playThreads[i] = new PlayThread();
playThreads[0].executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mp1);
playThreads[1].executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mp2);
playThreads[2].executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mp3);
playThreads[3].executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mp4);
playThreads[4].executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mp5);
Method 2: using CyclicBarrier
public void syncedCommand(
MediaPlayer player1,
MediaPlayer player2,
MediaPlayer player3,
MediaPlayer player4,
MediaPlayer player5
) {
final CyclicBarrier commandBarrier = new CyclicBarrier(5, new Runnable() {
#Override
public void run() {
L.m("ALL threads done");
}
});
new Thread(new SyncedCommandService(commandBarrier, player1)).start();
new Thread(new SyncedCommandService(commandBarrier, player2)).start();
new Thread(new SyncedCommandService(commandBarrier, player3)).start();
new Thread(new SyncedCommandService(commandBarrier, player4)).start();
new Thread(new SyncedCommandService(commandBarrier, player5)).start();
}
private class SyncedCommandService implements Runnable {
private final CyclicBarrier mCommandBarrier;
private MediaPlayer mMediaPlayer;
private int lengthseek;
public SyncedCommandService(CyclicBarrier barrier, MediaPlayer player) {
mCommandBarrier = barrier;
mMediaPlayer = player;
}
#Override public void run() {
try {
L.m("Waiting for thread");
mCommandBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
mMediaPlayer.start();
}
}
then play them using
syncedCommand(mp1,mp2,mp3,mp4,mp5);
In both the above methods stil there is some latency among the tracks, there is some gaps in between.
Later i have read in another post that its not possible:
syncing multiple audio tracks on android
The problem is that each MediaPlayer represents a separate stream,
which may start at different times. Even if you call start on all of
them at the same time (which is impossible), there's no way to
guarantee that the OS will load and actually start each one at the
same time.
As far as I know, Android offers no way to synchronize multiple
streams. Even with operating systems that do offer this capability,
it's extremely cumbersome, and usually not 100% accurate.
The correct solution is to open a single stream, eg using the audio
track interface, and do the MP3 decoding and mixing yourself before
sending the data to the stream.
The solution its mentioning is to decode, mix and encode. I am new to do this method using Audio Track. I can still figure out how to do it. BUt will it allow me
1. to change volume of tracks on fly like media player changes volume of each track. Or i have to fix the parameters first and then mix, play and check whether its suitable to my needs
2. Can i seek to particular time and start playing.
for last three weeks I have worked on a Media Player in Android.I am trying to find a solution of how can I make my Media Player to change the song when it's already playing one.
Here is my Listener on the RecyclerView
musicList.addOnItemTouchListener(
new RecyclerItemClickListener(getApplicationContext(), new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, final int position) {
currentPosition = position;
if(!mediaPlayer.isPlaying()){
musicThread.start();
} else {
mediaPlayer.reset();
}
}
})
);
}
and my Thread is this:
final Thread musicThread = new Thread(new Runnable(){
#Override
public void run() {
try {
URL = getMusicURL(myDataset[currentPosition]);
try {
mediaPlayer.setDataSource(URL);
//mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.prepareAsync(); // prepare async to not block main thread
} catch (IOException e) {
e.printStackTrace();
Log.i("TEST","Eroare: "+e.getMessage());
}
} catch (StorageApiException e) {
e.printStackTrace();
Log.i("TEST","Eroare: "+e.getMessage());
}
}
});
I think you have a mess. First of all, you dont need a thread to play music, the own mediaplayer API does it for you when you call mediaPlayer.start(). However, you have to care about the time it takes to prepare the data source if you are for example streaming online music. For this, just use mediaPlayer.prepareAsync() and register a callback. When it has finished preparing, you can automatically start playing or do whatever you want.
If you want to change the data source, just follow the automaton map that you can find in MediaPlayer docs. Essentially, when the user selects another track, you register the call in your button listener, then reset the mediaPlayer, and recall all prepare, start... cycle again. By the way, it is advised to deploy all your mediaplayer code into a service so that it can keep playing even though the user has closed your activity.
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?
I have many short audio fragments which should be played sequentially with minimum latency.
How can I queue these sounds efficiently?
There are two ways I can see this working: using a MediaPlayer or a SoundPool
MediaPlayer
You can use MediaPlayer.OnCompletionListener combined with a list of files to simply listen for completion and play the next one. By and large this works quite well and I have used it to play sequences of very short clips (< 1s) without problems.
SoundPool
Another way is to use the SoundPool class combined with a handler to simply queue up play events in advance. This assumes you know the length of each clip but depending on the format you should be able to find this out.
Which solution you choose depends on a number of factors: how many files, how short they are, what format they are in, where you are getting them from, how fixed or variable the list is, etc.
Personally I would go with MediaPlayer as this is easier to implement and would probably fit your needs.
One way is to concatenate them into a single audio file, then create a MediaPlayer for it and set an OnSeekCompleteListener. Seek to each segment in tern in whichever order you like and then play them in onSeekComplete(). It's timing not exact and MediaPlayer is touchy to use so it may not be the best choice for you but it's good enough for my purposes.
Here's my code:
private MediaPlayer MP = new MediaPlayer();
#Override
public void onCreate(Bundle savedInstanceState) {
//...
FileDescriptor fd = getResources().openRawResourceFd(R.raw.pronounciations).getFileDescriptor();
try {
setVolumeControlStream(AudioManager.STREAM_MUSIC); // Lets the user control the volume.
MP.setDataSource(fd);
MP.setLooping(false);
MP.prepare();
MP.start(); // HACK! Some playing seems required before seeking will work.
Thread.sleep(60); // Really need something like MediaPlayer.bufferFully().
MP.pause();
MP.setOnErrorListener(new OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
return false;
}
});
MP.setOnSeekCompleteListener(new OnSeekCompleteListener() {
public void onSeekComplete(MediaPlayer mp) {
// The clip is queued up and read to play.
// I needed to do this in a background thread for UI purposes.
// You may not need to.
new Thread() {
#Override
public void run() {
MP.start();
try {
Thread.sleep(soundRanges[curWordID][1]);
} catch(InterruptedException e) {}
MP.pause();
}
}.start();
}
});
} catch(Throwable t) {}
}
private void playCurrentWord() {
if(soundRanges != null && !MP.isPlaying() && !silentMode) {
try {
MP.seekTo(soundRanges[curWordID][0]);
}
catch(Throwable t) {}
}
}
You would likely need to concatenate your clips using an external sound editing tool. I used Audacity to find and edit the clip beginnings and lengths that I saved in a file.
soundRanges[curWordID][0] is the offset in the sound file of the beginning of a clip and soundRanges[curWordID][1] is its length.
Hey,
I'm using MediaPlayer to play a regular ShoutCast stream. The code is straightforward with prepareAsync() and a handler to start the playback. While it works flawlessly with some streams like DI.FM or ETN.FM (http://u10.di.fm:80/di_progressive), with others (http://mp3.wpsu.org:8000/) it won't go past the prepare state. No other listeners are called either.
//Uri streamUri = Uri.parse("http://u10.di.fm:80/di_progressive"); /* works */
Uri streamUri = Uri.parse("http://mp3.wpsu.org:8000/"); /* stuck on prepare state */
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(this.getBaseContext(), streamUri);
mediaPlayer.prepareAsync();
Any feedback is appreciated!
I think that there are some compatibility problems with the server end.
This is rather strange since the emulator handles it ok in my case - just not on my Froyo Galaxy S, even though it is the same API version.
It could be a codec issue, http streaming issue, I do not know.
But all the servers that fail tend to be old ones, with "Copyright 1998 - 2004" at the bottom... Not exactly recent or up to date you would think.
One potential workaround (which I have not tried yet) would be to use the StreamProxy, which would also make your code compatible with 2.1 and possibly earlier versions too. At the cost of extra work, extra code, and without doubt extra bugs...
In case you are not aware of it, there is another player bug report for 2.2 which may be relevant too:
Basic streaming audio works in 2.1 but not in 2.2
I'm facing an issue when MP "hangs" at preparing state too long (stream) and i'm trying to stop it using reset(). This causes MP to hang and thus my whole app freezes. Seems like there is no way to stop MP at preparing state. Im thinking on use prepare() wrapped in thread instead of prepareAsync(). Then i'll be able to kill that thread. As for now i did it in following way:
private void actionCancel(){
try {
mp.setDataSource(new String());
} catch (Exception e) {
e.printStackTrace();
android.util.Log.d(TAG,"actionCancel(): mp.setDataSource() exception");
mp.reset();
}
}
and it works 4me.
Additionally i have a following counter:
#Override
public void onBufferingUpdate(final MediaPlayer mp, final int percent) {
if (!mp.isPlaying()){
// android.util.Log.d(TAG,"onBufferingUpdate(): onBufferingUpdateCount = "+onBufferingUpdateCount);
if (onBufferingUpdateCount>MAX_BUFFERING_UPDATES_AT_PREPARING_STATE)
restartMP();
onBufferingUpdateCount++;
return;
}
}
i'd discover this listener always triggers at preparing state. So if it triggers more than 10 times and MP is still not playing i'm just restarting it:
private void restartMP(){
if (mp!=null)
if (mpState==MediaPlayerState.Preparing)
actionCancel();
else
mp.reset();
else
mp = new MediaPlayer();
mpState = MediaPlayerState.Idle;
onBufferingUpdateCount=0;
//isRequestCancelled=false;
requestTrackInfoStartedAt=0;
requestPlay();
}
note MediaPlayerState is my custom enum which has "Preparing" value. Also mpState is a class property/field which holds current MediaPlayerState state. Before starting prepareAsync() im setting mpState to MediaPlayerState.Preparing after it completes im setting it to MediaPlayerState.Started or other corresponding value.