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.
Related
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
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.
I'm writing an application to play audio from remote server. I tried several ways to implement streaming audio, but they all are not good enough for me.
That's what I've tried:
Naive using of MediaPlayer
Something like:
MediaPlayer player = new MediaPlayer();
player.setDataSource(context, Uri.parse("http://whatever.com/track.mp3"));
player.prepare();
player.start();
(or prepareAsync, no matter)
But standard MediaPlayer is quite unstable when playing remote content. It is often falls or stops playback and I can't process this. On the other side, I want to implement media caching. But I haven't found any way to get buffered content from MediaPlayer to save it somewhere on device.
Implementing custom buffering
Then there became an idea to download media file by chunks, combine them into one local file and play this file. Downloading the whole file can be slow because of bad connection, so it will be fine to download enough initially piece, then start playback and continue downloading and appending local file. Besides, we get caching functionality.
Sounds like a plan, but it didn't always work. It works perfectly on HTC Sensation XE but didn't on 4.1 tablet playback stopped after finishing this initial piece. Don't know, why is so. I've asked question about this, but received no answers.
Using two MediaPlayers
I've created two MediaPlayer instances and tried to make them change each other. The logic is following:
Start downloading initial piece of media
When it is downloaded, start playback via currentMediaPlayer. The rest of media continues
downloading
When downloaded piece is almost played (1 sec before finish), prepare secondaryMediaPlayer with the same source file (as it was appended during playback)
261 ms before finish of currentMediaPlayer – pause it, start secondary, set secondary as current, schedule preparing of next secondary player.
The source:
private static final String FILE_NAME="local.mp3";
private static final String URL = ...;
private static final long FILE_SIZE = 7084032;
private static final long PREPARE_NEXT_PLAYER_OFFSET = 1000;
private static final int START_NEXT_OFFSET = 261;
private static final int INIT_PERCENTAGE = 3;
private MediaPlayer mPlayer;
private MediaPlayer mSecondaryPlayer;
private Handler mHandler = new Handler();
public void startDownload() {
mDownloader = new Mp3Downloader(FILE_NAME, URL, getExternalCacheDir());
mDownloader.setDownloadListener(mInitDownloadListener);
mDownloader.startDownload();
}
private Mp3Downloader.DownloadListener mInitDownloadListener = new Mp3Downloader.DownloadListener() {
public void onDownloaded(long bytes) {
int percentage = Math.round(bytes * 100f / FILE_SIZE);
// Start playback when appropriate piece of media downloaded
if (percentage >= INIT_PERCENTAGE) {
mPlayer = new MediaPlayer();
try {
mPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath());
mPlayer.prepare();
mPlayer.start();
mHandler.postDelayed(prepareSecondaryPlayerRunnable, mPlayer.getDuration() - PREPARE_NEXT_PLAYER_OFFSET);
mHandler.postDelayed(startNextPlayerRunnable, mPlayer.getDuration() - START_NEXT_OFFSET);
} catch (IOException e) {
Log.e(e);
}
mDownloader.setDownloadListener(null);
}
}
};
// Starting to prepare secondary MediaPlayer
private Runnable prepareSecondaryPlayerRunnable = new Runnable() {
public void run() {
mSecondaryPlayer = new MediaPlayer();
try {
mSecondaryPlayer.setDataSource(mDownloader.getDownloadingFile().getAbsolutePath());
mSecondaryPlayer.prepare();
mSecondaryPlayer.seekTo(mPlayer.getDuration() - START_NEXT_OFFSET);
} catch (IOException e) {
Log.e(e);
}
}
};
// Starting secondary MediaPlayer playback, scheduling creating next MediaPlayer
private Runnable startNextPlayerRunnable = new Runnable() {
public void run() {
mSecondaryPlayer.start();
mHandler.postDelayed(prepareSecondaryPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - PREPARE_NEXT_PLAYER_OFFSET);
mHandler.postDelayed(startNextPlayerRunnable, mSecondaryPlayer.getDuration() - mPlayer.getCurrentPosition() - START_NEXT_OFFSET);
mPlayer.pause();
mPlayer.release();
mPlayer = mSecondaryPlayer;
}
};
Again – sounds, like a plan, but works not perfectly. The moments of switching MediaPlayers are quite hearable. Here I have opposite situation: on 4.1 tablet it's ok, but on HTC Sensation there are evident lags.
I also tried to implement different download techniques. I've implemented download by 10Kb chunks and by MP3 frames. I don't know exactly, but it seems that in case of MP3 frames seekTo and start work better. But it's just a feeling, I don't know explanation.
StreamingMediaPlayer
I saw to this word several times while googling, and found this implementation: https://code.google.com/p/mynpr/source/browse/trunk/mynpr/src/com/webeclubbin/mynpr/StreamingMediaPlayer.java?r=18
It is a solution everybody use?
If yes, it's sad, because it is not working good for me too. And I don't see any fresh ideas in implementation.
So, the question
How do you guys implement audio streaming in your applications? I don't beleive I am the only person who faced problems like this. There should be some good practices.
In my case I use FFMPEG with OpenSL ES. The disadvantage is complexity. You must be familiar with a lot of things: JNI, OpenSL, FFMPEG. It's also hard to debug(comparing with pure java android app). In your case I suggest you to try low level Media API. The only thing is lack of examples. But there is a unit test which shows how you can handle audio(you need to change InputStream reference - line 82).
I've created an app that uses MediaPlayer to play a random (short) sound when a button is clicked. The sounds are played correctly on android devices < 2.2. This is the code responsible for playing sounds.
r = new Random();
sounds = new ArrayList<MediaPlayer>();
sounds.add(MediaPlayer.create(this, R.raw.sound1));
sounds.add(MediaPlayer.create(this, R.raw.sound2));
sounds.add(MediaPlayer.create(this, R.raw.sound3));
sounds.add(MediaPlayer.create(this, R.raw.sound4));
sounds.add(MediaPlayer.create(this, R.raw.sound5));
theButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
playSound();
}
});
private void playSound() {
Thread thread = new Thread() {
public void run() {
MediaPlayer soundPlayer = sounds.get(r.nextInt(sounds.size()));
while (soundPlayer.isPlaying())
{
soundPlayer = sounds.get(r.nextInt(sounds.size()));
}
soundPlayer.seekTo(0);
soundPlayer.start();
}
};
thread.start();
}
The sounds are all .wav files. I tried converting them to .mp3, but then they wouldn't play at all. Am I doing something extremely wrong, or is the MediaPlayer in 2.2 buggy? Anyone else had this problem and know of a fix? Keep in mind that the sounds are played normally on all other devices with an android version below 2.2.
I think you shouldn't create a ArrayList for MediaPlayer. Instead that, you use only a MediaPlayer object and a ArrayList to contain all music resources.
When you next other song, you update only the info of MediaPlayer. For example,
Release the previous MediaPlayer object.
Create other MediaPlayer object
Finally, start this song
Seems there was a problem with the sampling rate of the mp3's that the 2.2 Framework frowned upon. I fixed it by opening up the sounds in a sound editor, resampling them and adding silence to the first and last second of the sounds.