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.
Related
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...
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.
I have two issues I would like understand.
I am using soundPool for my sound effects and its working with no problem.
However, when I try to play cetain file (25 sec , about 400K) it doesn't play the whole file only 3-4 seconds from it .
Why and how can I fix it ?
and the second question is, should I play each effect from thread ? many threads are good ?
this is the current code :
static void play(final int soundID ){
if(loaded){
handler.post(new Runnable()
{
public void run()
{
soundpool.play(soundID, 1, 1, 1, 0, 1);
}
});
My understanding is that SoundPool cannot be used for sounds longer than several seconds or audio files >1MB. Use MediaPlayer in those cases.
Either use this for each play of a sound:
MediaPlayer.create(YourActivity.this, R.raw.your_sound).start();
or create MediaPlayer object, play same sound as many times as needed, then release() the object.
Such Problem: I have video file recorded with two sound channels. I tried to switch off left sound channel by this code:
MediaPlayer mp;
....
mp.setVolume(0.f, 1f);
... and on Tablet this work good (right volume channel sounds well). But then I tried it on googleTv which I connect to Samsung UE46ES6307U and this code did not work, sound swichs off.
Maybe it is bounds to Dolby Digital Plus / Dolby Pulse audio? Can I somehow programmatically discover how sound channels device has, and what volume in each chanels setuped?
Update:
On this forum http://www.googletvforum.org/forum/logitech-revue/375-audio-problems-logitech-revue.html in one of replies such message: "Logitech has not yet figured out how to pipe multichannel audio thru hdmi.you have to use the optical output. Which is ok."
"How are you constructing the MediaPlayer?"
Videoview vv;
...............
vv.setOnPreparedListener(new OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mp.setVolume(0.f, 1f);
}
});
Update:
public class MainActivity extends Activity {
MediaPlayer mp = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (mp != null) {
mp.reset();
mp.release();
}
mp = MediaPlayer.create(this, R.raw.test);
mp.start();
}
public void onTurnOffLeft(View v){
mp.setVolume(0.f, 1.f);
}
public void onTurnOffRight(View v){
mp.setVolume(1.f, 0.f);
}
}
Method onTurnOffLeft switchs off all sound, and onTurnOffRight method has no effect.
Update2
I tried to play .ogg audio file codded with Vorbis codec - channels turns off well. But I tried to play video files codded with mp3, ac3, pcm, aac - and problem with turning off channels is still there... I need to turn off audio channels in video, but how to solve that problem, I do not know yet.
The MediaPlayer object is backed by different libraries across the devices (not the same between a tablet and a Google TV). How are you constructing the MediaPlayer?
One thing you may want to try is calling #reset() on the MediaPlayer right after it is constructed. By default when you use a "new" operator to construct a MediaPlayer instance it is in an IDLE state (at least on Google TV). By calling reset you allow your own OnErrorListener.onError() handler to be invoked. This will let you see if there is some underlying error that is not visible otherwise.
You may also want to look at AudioManager#setStreamVolume(int, int, int) which sets the volume of ALL streams of a particular type.
Edit 1:
Since you are just grabbing the VideoView from layout (I'm guessing since that code was omitted) after you setup the listener you should call reset on the video view.
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);
}
});