How to control MediaPlayer starting delay? - android

Firstly, my aim is to play a sound on my devices with the same start time. Even a delay of 100ms is very important for me. So i must be very careful on that delay management.
Assume that i synchronize my all devices with using network time protocol. I am connecting 0.tr.pool.ntp.org and with added delays i have a data like that:
Your system time: 14:45:50.255000 (a) On my device
NTP time: 14:46:01.255000 (b)
It is working fine. I have a BroadcastReceiver class as an Alarm Receiver. Again for example, i will play a sound at 14:50:00.000 and that given to AlarmManager like ((14:50:00.000) - (a - b)) for synchronization with NTP server.
Up to here everything is great. My problem is beginning with a delay of almost 250ms (c) on BroadcastReceiver. This delay is never goes 1000ms++ . I solved this issue with changing Alarm time to ((14:50:00.000) - (a - b) - 1000ms) then on BroadcastReceiver i add a delay of (1000ms - c) . It is still great thank god :)
Final step is playing sound, it is very easy just add this code block:
MediaPlayer player = MediaPlayer.create(getBaseContext(), R.raw.mysound);
player.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer player) {
player.release();
}
});
player.start();
This code works but what about delay? I decide to measure this delay on the beginning of my app and save it to use when the time comes. I use that method:
private void synchronizeMediaPlayer(){
long startMs = new Date().getTime();
MediaPlayer player = MediaPlayer.create(getBaseContext(), R.raw.mysound);
player.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer player) {
player.release();
}
});
player.start();
long finishMs = new Date().getTime();
delayPlayer = finishMs - startMs; // this is static variable for using in any class
player.stop();
player.release();
}
Ok. Again this delayPlayer is never goes 1000ms++ . Then as you can estimate, i changed Alarm time to ((14:50:00.000) - (a - b) - 1000ms - 1000ms) then added a delay of (1000ms - delayPlayer) before playing sound. I think this is the most reasonable way.
Result: Now after all these steps, my sound is starting with a little delay(like 50ms-250ms) between the devices. I measured all the delays and controlled every step. Everything is right but the only problem is delayPlayer. I thought, it is changing always with respect to devices state(RAM usage, Background Services, etc.). Frankly, i started to think this changes are not depending on device state. Because i measured it with an interval of 1 minute, then saw that it is always changing. This changings are very different like; now 150ms, after 1 min it goes 300ms.
How can i solve that MediaPlayer delay issue? I think you awesome programmers can help me.
Note: My English may not be good. Sorry for that...

Related

Play sound precisely every one second

I am learning to code for Android and I have a problem with... maybe performance?
I want to play very short sound every second. I have created a CountDownTimer (with tick interval 20ms so very accurate) and putted there in onTick to play it. But the sound is played not precisely after one second and I can hear this - this is the problem...
fragment of my code:
private class ExerciseCountDownTimer extends CountDownTimer
{
...
public void onTick(long millisUntilFinished)
{
...
if(/*this is a next second not just a tick*/)
playSound(R.raw.quick_rest, true);
}
}
private void playSound(int resId, boolean releaseAfter)
{
if (currentMediaPlayerRes != resId || mediaPlayer == null)
{
if (mediaPlayer != null)
{
if (mediaPlayer.isPlaying())
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.release();
}
currentMediaPlayerRes = resId;
mediaPlayer = MediaPlayer.create(this, resId);
if (releaseAfter)
{
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
{
#Override
public void onCompletion(MediaPlayer mp)
{
mediaPlayer.release();
mediaPlayer = null;
}
});
}
}
mediaPlayer.start();
}
Is it possible to have counter like this?
use this code to play a music one time
MediaPlayer AlarmMusic;
AlarmMusic = MediaPlayer.create(G.context, R.raw.music1);
AlarmMusic.setLooping(false);
AlarmMusic.start();
this worked for me !
Because of the lack of real time guarantees in Java/Android, using one thread to cue another probably will always have some inaccuracy.
I can think of two approaches that might work for you:
(1) make the sound file exactly 1 second long, e.g., 44100 frames at 44100 fps, if you are using the standard "CD-quality" format that Java supports. The sound file can probably be edited with Audacity to an exact frame length. Then, use a looping playback.
(2) Open a line for streaming audio. I can't recall exactly what the Android-supported command is for this, but I know it exists. With it, count elapsing frames while sending silence (bytes with 0 value). When you get to the 44100th frame, start feeding the PCM data that you wish to hear. When it is done, go back to feeding 0's until the next 44100 frame arrives. Never stop the line--keep it running in its own thread, probably with a high priority. It is generally okay to give audio a high priority (as long as you are doing nothing else on the line) as audio spends a vast majority of its time in a "blocked" state during which it is yielding to other lines.
A fellow on java-gaming.org made a metronome and got it working on Android. I bet if you search for "metronome" on that site, his thread will pop up and have some useful info. He basically used the second approach that I described above.

How to play sounds at accurate periods of time across different devices in Android

I'm developing a game in Android and I came across a very annoying, hard-to-find bug. The issue is that when you are using SoundPool to play your sounds, you can actually loop whatever sound you are playing. In this case, the issue is the "running steps" sound; this sound gets executed quite fast and continually (around every 400ms) when the main character is running.
Now when playing the sound on a regular (not so powerful) device e.g. Samsung SII, the sound is played every 500ms - however, if I run the very same code on another device (let's say, Samsung SIV, Samsung SIII), the sound plays twice or even three times faster.
It seems like the more powerful the device hardware specs are, the faster it plays. On some devices, it plays so fast that you almost hear one solid continuous sound. I've been looking for techniques to set a specific ratio on the time period between sound plays, but it doesn't work properly and the issue remains. Does anyone know how to fix it, either using SoundPool, MediaPlayer, or any other sound-controlling API on Android?
You could use an AudioTrack to play a continuous stream of PCM data, since you would pass a stream you could be sure about the interval between sounds. the downside could be a little delay when first starting the sound but it depends on the minimum buffer size, and it depends, I think, on android version and device. On my galaxy s2 android 4.1 it was about 20ms.if you think this could be an option I can post some code
The problem with just looping or using a regular interval for something like footsteps is that you have a possible decoupling of sound and visuals. If your sound gets delays or sped up, or your visuals get delayed or sped up, you would have to adjust for that delay dynamically and automatically. You already have that issue right here
A better solution would be to place a trigger on the exact event which should trigger the sound (in this case, the foot being placed down), which then plays the sound. This also means that if you have multiple sources of the sound (like multiple footsteps), you don't have to manually start the sound with the right interval.
I can't seem to replicate the issue on Galaxy Nexus and Nexus S, does that mean I fixed it? Or maybe you could show what you're doing differently from this:
SoundPool soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);
Integer sound1 = soundPool.load(this, R.raw.file1, 1);
Integer sound2 = soundPool.load(this, R.raw.file2, 1);
playSound(sound1);
public void playSound(int sound) {
AudioManager mgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
float volume = mgr.getStreamVolume(AudioManager.STREAM_MUSIC)
/ mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
soundPool.play(sound, volume, volume, 1, -1, 1.0f);
}
If the problem is that you want to control the interval between the discrete sounds, The easiest way to do this is with a handler.
Basically you start a sound playing which is an asynchronous process. Then you use a handler to schedule a message to play the next sound sometime in the future. It will take some trial and error to get it right, but you will be guaranteed that the sound will start at the same interval after the previous sound on every device.
Here is some code to illustrate what I am talking about.
Here is a handler implementation you could use:
handler = new Handler() {
/* (non-Javadoc)
* #see android.os.Handler#handleMessage(android.os.Message)
*/
#Override
public void handleMessage(Message msg) {
if (msg.what == NEXT_ITEM_MSG) {
playNextSound();
}
else if (msg.what == SEQUENCE_COMPLETE_MSG) {
// notify a listener
listener.onSoundComplete()
}
}
};
Then you could write playNextSound like this:
private void playNextSound() {
if (mRunning) {
// Get the first item
SoundSequenceItem item = currentSequence.getNextSequenceItem();
if (item == null) {
Message msg = handler.obtainMessage(SEQUENCE_COMPLETE_MSG);
handler.sendMessage(msg);
return;
}
// Play the sound
int iSoundResId = item.getSoundResId();
if (iSoundResId != -1) {
player.playSoundNow(soundResId);
}
// schedule a message to advance to next item after duration
Message msg = handler.obtainMessage(NEXT_ITEM_MSG);
handler.sendMessageDelayed(msg, item.getDuration());
}
}
and your SoundSequenceItem could just be a simple class that has a sound file resource id and a duration. If you want to keep playing the sound while the character is moving you could do something like this:
public void onSoundComplete() {
if (character.isRunning()) {
currentSequence.addSequenceItem(new SoundSequenceItem(R.id.footsteps,500);
playNextSound();
}
}
Or you could modify playNextSound to continually play the same sound. Mine is written this way to be able to play different sounds in sequence.
I have had a lot of problems developing apps which used sounds and stuff like that. I would not suggest you to use SoundPool since it is bug-affected, and also be aware that looping sounds with SoundPool won't work on devices which are 4.3 and higher, see this open issue, at AOSP - Issue tracker.
I think that the solution is to go native and use OpenSL ES o similar libraries.

Pause/Stop MediaPlayer Android at given time programmatically

I researched a little bit, but couldn't find any solutions to this problem:
I would like to play a MediaPlayer and pause/stop it at a given time.. (ie: play from second 6 to second 17).
I know that I can set its starting point with seekTo() method, but can I pause/stop it from playing by setting an end point (of course, before reaching the file end limit)?
There are different ways you could do this, here's one:
int startFrom = 6000;
int endAt = 11000;
MediaPlayer mp;
Runnable stopPlayerTask = new Runnable(){
#Override
public void run() {
mp.pause();
}};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mp = MediaPlayer.create(this, R.raw.my_sound_file);
mp.seekTo(startFrom);
mp.start();
Handler handler = new Handler();
handler.postDelayed(stopPlayerTask, endAt);
}
The mediaplayer will start playing 6 seconds in and pause it 11 seconds after that (at second 17).
I think you can create Timer and call seekTo() directly from its task. Then call stop()/pause() inside of that Timer Task.
Maybe this post will be helpfull for you.
Or you can use handler for this task, like Ken Wolf shows you.
Best wishes.
You can use CountDownTimer
new CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
mp.stop;
mp.relese();
}
}.start();
I know this already has an answer but here is an alternative for anyone wanting to do this efficiently for a video with controls.
I saw a different answer that involved constantly checking the position.
Note that this assumes that you do not need a lot of different portions of one video. Even in that case I suggest to follow the below.
If you only need to play a portion of the video and want it to end somewhere, then why not just use free video editing software and clip the end? You can still start it from anywhere using seekTo() but this way you don't have to waste resources checking for a certain position (video with controls).
If you do not have video player controls the accepted answer will work. But if you do have player controls then it would not since a user could pause and play the video.
There's a time difference between videoView.start() and video prepared to play depend on video format, those time difference is probably more than 10 frames.
so the best way to do is to start the timer inside OnPreparedListener to minimise the time difference, or even more to get the current playing duration and set a postDelayed timer at that point.

Binding MediaPlayer to be played at a specific time

When using MediaPlayer, I noticed that whenever my phone stucks, the MediaPlayer glitches and then continues playing from the position in the audio it glitched.
This is bad for my implementation since I want the audio to be played at a specific time.
If I have a song of 1000 millisecond length, I want is the ability to set MediaPlayer to start playing at some specific time t, and then exactly stop at at time t+1000.
This means that I actually need two things:
1) Start MediaPlayer at a specific time with a very small delay.
2) Making MediaPlayer glitches ignore the audio they glitched on and continue playing in order to finish the song on time.
The delay of the functions is very important to me and I need the audio to be played exactly(~) at the time it was supposed to be played.
Thanks!
You will need to use possibly mp.getDuration(); and/or mp.getCurrentPosition(); although it's impossible to know exactly what you mean by "I need the audio to be played exactly(~) at the time it was supposed to be played."
Something like this should get you started:
int a = (mp.getCurrentPosition() + b);
Thanks for the answer Mike. but unfortunately this won't help me. Let's say that I asked MediaPlayer to start playing a song of length 3:45 at 00:00. At 01:00 I started using the phone's resources, due to the heavy usage my phone glitched making MediaPlayer pause for 2 seconds.
Time:
00:00-01:00 - I heard the audio at 00:00-01:00
01:00-01:02 - I heard silence because the phone glitched
01:02-03:47 - I heard the audio at 01:00-03:45 with 2 second time skew
Now from what I understood MediaPlayer is a bad choice of usage on this problem domain, since MediaPlayer provides a high level API.I am currently experimenting with the
AudioTrack class which should provide me with what I need:
//Creating a new audio track
AudioTrack audioTrack = new AudioTrack(...)
//Get start time
long start = System.currentTimeMillis();
// loop until finished
for (...) {
// Get time in song
long now = System.currentTimeMillis();
long nowInSong = now - start;
// get a buffer from the song at time nowInSong with a length of 1 second
byte[] b = getAudioBuffer(nowInSong);
// play 1 second of music
audioTrack.write(b, 0, b.length);
// remove any unplayed data
audioTrack.flush();
}
Now if I glitch I only glitch for 1 second and then I correct myself by playing the right audio at the right time!
NOTE
I haven't tested this code but it seems like the right way to do it. If it will actually work I will update this post again.
P.S. seeking in MediaPlayer is:
1. A heavy operation that will surely delay my music (every millisecond counts here)
2. Is not thread safe and cannot be used from multiple threads (seeks, starts etc...)

Reliable sequential audio playback on Android

I am writing an android game for teaching kids to count. The instructions are read to the player through sound clips that are put together to form sentences (for instance "Place", "one", "cow", "in the", "barn". This requires a certain amount of reliability when it comes to latency so that the flow of the instructions sounds natural.
Currently I am using MediaPlayer, playing each sound in a OnCompletionListener. Each sound has it's own MediaPlayer that is created and prepared before playback of any sound starts (to reduce latency) - but still I get a significant delay before each sound the first time it is played (the second time it seems some sort of caching has taken place and it works fine).
The sounds are not many and very short and it should probably work better with SoundPool, but SoundPool has no way of knowing when an audio is complete and thus not an option.
Does anyone have any experience with similar problems and a viable solution?
I have used handler with OnCompletionListener and it worked fine for me to give delay between two sounds.
this way,
CommonMethod.player.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
// /will use count and loop as per number of
// repetition chosen.
handler.postDelayed(new Runnable() {
public void run() {
if (counter >= limit) {
CommonMethod.player.stop();
} else {
CommonMethod.player.start();
}
counter++;
}
}, 3000);
}
});

Categories

Resources