Stopping a track (at a certain point) on Android - java

I know you can seekto() with Mediaplayer, to start at a certain point.
But is there a way to make a track (the audio playing) stop at a certain point? Or would an if statement on a timer loop have to be used?

Doesn't seem possible (correct me if I'm wrong) to do this with media player without resorting to seekto() in a timer loop. However you could try using an AudioTrack in conjunction with setting a notification marker:
AudioTrack.setNotificationMarkerPosition
Sets the position of the notification marker.
and overriding the playback position update listener AudioTrack.OnPlaybackPositionUpdateListener
Interface definition for a callback to be invoked when the playback head position of an AudioTrack has reached a notification marker or has increased by a certain period.

You have to make threat that will trigger getCurrentPosition().
When it will reach stop point, you have to stop MediaPlayer.
public void run() {
while (mp != null && mPosition < mTotal) {
try {
Thread.sleep(500); // you can modify sleep time for better accuracy
if (mp.isPlaying()) {
mPosition = mp.getCurrentPosition();
if (mPosition == mYourStopPoint) { //remember to set mYourStopPoint
mp.stop();
break;
}
}
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
}
}
Start this Thread in onPreapared callback.
public void onPrepared(MediaPlayer genericPlayer) {
mTotal = mp.getDuration();
new Thread(this).start();
}

Sadly, AudioTrack's position callbacks appear to be fairly seriously broken. http://code.google.com/p/android/issues/detail?id=2563

Related

CountDownTimer freezes UI when trying to reproduce sound

I'm trying to reproduce the beep.wav sound each second from 3 to 1 , for example, reproduce 3 , 2, and 1 the beep sound, and then when finish reproduce the beependsound.
For some reason only the beependsound is playing but when reaching second 3 it seems the ui freezes for a sec and then the numbers decrease fast to 0
private void stopPlaying(){
if(mp!=null){
try {
mp.reset();
mp.prepareAsync();
mp.stop();
mp.release();
mp=null;
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
private void startCountDown() {
aCounter = new CountDownTimer(10000, 100) {
public void onTick(long millisUntilFinished) {
if (Math.round((float) millisUntilFinished / 1000.0f) != secondsLeft) {
countDownTxt.setTextColor(getResources().getColor(R.color.white));
secondsLeft = Math.round((float) millisUntilFinished / 1000.0f);
countDownTxt.setText(String.valueOf(secondsLeft));
}
if (secondsLeft <= 3) {
countDownTxt.setTextColor(getResources().getColor(R.color.colorAccent));
stopPlaying();
mp = MediaPlayer.create(MainActivity.this, R.raw.beep);
mp.start();
}
}
public void onFinish() {
secondsLeft = 0;
stopPlaying();
mp = MediaPlayer.create(MainActivity.this, R.raw.beepend);
mp.start();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
aCounter.cancel();
startCountDown();
}
}, 1000);
}
};
aCounter.start();
}
I spect this to work as described above, any hint ?
MediaPlayer.create() can be a fairly expensive call. Consider what happens if it takes approximately 100ms (or even more):
The timer calls onTick().
onTick() blocks for about 100ms inside MediaPlayer.create().
mp starts playing, and onTick() returns. (So far, so good!)
Immediately, the timer realizes another onTick() call is due! The last one started over 100ms ago!
onTick() is called again, almost immediately. Very quickly, it reaches the stopPlaying() call. But you only started playing about 1ms ago!
This leads to a situation where your timer spends all its time in MediaPlayer.create(), and almost no time actually playing the sound.
Note that, with the code as written, it will attempt to play the sound approximately 30 times in the last 3 seconds of the countdown (since the ticks are ideally 100ms apart). If your intent was to play the sound only 3 times, you may want to move your second if block to inside the first. That way, you only attempt play when secondsLeft actually changes. This will actually ameliorate the original problem, and you may not need any further changes.
But if you want to optimize further, note that you can prepare mp in advance -- say, when the app starts up -- and simply reuse it: Instead of release()-ing it each time, just stop() it, and prepare() it (and don't reset() it). That'll leave it ready for the next play. You could even create a separate MediaPlayer just for beepend, and you could prepare them both during app initialization.

Seekbar 'unhooking' from media player on orientation change

I have a seekbar in my fragment that I use to show the progress of a MediaPlayertrack that is playing. Everything works as expected until I change orientation of the device. I save my fragment instance and load it back, and all of the other views are repopulated as they should be, and the music keeps right on playing as it should, but the seekbar goes right to the max, as if the track has reached it's max duration. The runnable that is updating the seekbar based on the mediaplayer's position keeps running after the orientation change as well, and logging the currentPos value in the runnable shows that it stays in sync with the mMediaPlayer.getCurrentPosition() as it should, even after the orientation change...So basically, it continues to feed the correct position to the seekbar; the seekbar just isn't updating after the orientation change. It makes me wonder if the seekbar instance stays the same after the change. If it doesn't stay the same instance, I can't figure out why or how to fix it.
The onCLick method of my play button calls the following to start the track and hook up the seekbar to it:
private void startPlayer(String url){
try {
mMediaPlayer.setDataSource(url);
} catch (IOException e) {
e.printStackTrace();
}
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mMediaPlayer.start();
playerState = PlayerState.PLAYING;
setButtonsForPlayerState();
setSeekBarValues();
mHandler = new Handler();
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if (mMediaPlayer != null ) {
int currentPos = mMediaPlayer.getCurrentPosition();
Log.e(LOG_TAG, "Current pos is " + currentPos);
seekBar.setProgress(currentPos);
if (currentPos<10000){
seekBarStartTime.setText("0:0" + currentPos / 1000);
}else{
seekBarStartTime.setText("0:" + currentPos / 1000);
}
}
if (mMediaPlayer.isPlaying()){
mHandler.postDelayed(this, 50);
}
}
}
);
}
});
}
and here is the setSeekBarValues() method that is called in the above method:
private void setSeekBarValues() {
seekBar.setMax(mMediaPlayer.getDuration());
seekBarFinishTime.setText("0:" + seekBar.getMax()/1000);
seekBar.setProgress(0);
}
seekBarStartTime and seekBarFinishTime are just TextViews that show the current position and end time of the track, respectivily.
Since all of my other views, streaming music, etc. seems to be working as it should on the re-creation of Activity and Fragment, I don't think it's an issue with saving instance state, but if you want me to post that code as well please let me know.
Any ideas are appreciated.

How to effectively re-start the preview having taken a photo inside an Android app?

I am attempting to code a tool that takes pictures at a dedicated interval (like a timelapse) and am having difficulty getting the camera to reset following the first capture so that it can then start on the next photo.
Here is a sample of the code used:
// gets called with the oncreate method and loads a preview fine
public void startUpPreview(){
mCamera = getCameraInstance();
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
}
// gets called from a later loop
public void getPicture() {
mCamera.takePicture(null, null, mPicture);
releaseCamera();
startUpPreview();
}
With the code above, it throws the error:
java.lang.RuntimeException: Method called after release()
I am using the release code taken directly from the SDK guide which works otherwise:
private void releaseCamera(){
if (mCamera != null){
mCamera.release();
mCamera = null;
}
}
For a single picture adding a delay with a sleeping thread makes it work:
try {
Thread.sleep(1000);
releaseCamera();
startUpPreview();
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
But this code does not work within a loop when attempting to take more than one picture. I assume that the loop is completing before all of that photo taking can catch up.
Thanks in advance for any help that any one can provide.
takePicture() starts the capture process which ends with an (asynchronous) call to onPictureTaken() callback. You should not release camera before it is over.
So the pseudo-code that will perform a loop look like this:
onCLick() { // <-- start the loop
count = 0;
takePicture()
}
onPictureTaken() {
savePicture(count);
count++;
if (count < maxCount) {
mCamera.startPreview();
mHandler.postDelayed(runTakePicture(), 1000);
}
else {
releaseCamera();
}
}
runTakePicture() {
return new Runnable() {
public void run() {
mCamera.takePicture();
}
}
}

Android Media Player Seek bar not updating on start after stop

I'm having a hard time working with this.
I have a Media player, and I can play, pause, stop, play again after pause, stop... whatever.
Now I wanted to have a SeekBar to give a visual component. My problem is:
When I start the player for the first time everything works well. Music plays, and seek bar updates. also works when I pause.
Now, if I stop the player and start it again, the player starts, the run() method executes, but the seekbar doesn't update, and soon the app gives a not responging error.
What am I missing here?
The run method is an implementation from the Runnable interface, and with a simple log, I can see it's being executed, even after the stop/play case. The only thing that seems not to be working is the seek.setProgress(...).
some help, please? :)
Here's my code:
public class MediaPlayerTestingActivity extends Activity
implements OnClickListener, OnPreparedListener, Runnable {
private MediaPlayer mp;
private boolean paused = false;
private SeekBar seek;
private boolean threadStarted = false;;
private Thread thread = new Thread(this);
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
seek = (SeekBar)findViewById(R.id.seek);
mp = new MediaPlayer();
Button start = (Button)findViewById(R.id.start);
start.setOnClickListener(this);
Button pause = (Button)findViewById(R.id.pause);
pause.setOnClickListener(this);
Button stop = (Button)findViewById(R.id.stop);
stop.setOnClickListener(this);
}
//click handlers
public void onClick(View v)
{
int buttonId = v.getId();
switch (buttonId)
{
case R.id.start:
if(mp != null)
{
if(!mp.isPlaying() && !paused)
prepareTostartPlayer();
else
mp.start(); //if it was just paused
}
else
{
mp = new MediaPlayer();
prepareTostartPlayer();
}
break;
case R.id.pause:
if(mp.!= null && mp.isPlaying())
{
mp.pause();
paused = true;
}
break;
case R.id.stop:
if(mp != null && mp.isPlaying())
{
mp.stop();
mp.release();
mp = null;
}
break;
default:
break;
}
}
//When the player is ready to play
public void onPrepared(MediaPlayer arg0)
{
seek.setMax(mp.getDuration());
mp.start();
if(!threadStarted)
thread.start();
else
thread.run();
}
//Method to prepare to start the player
private void prepareTostartPlayer()
{
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mp.setDataSource("http://feedproxy.google.com/~r/rc-cadernetadecromos/~3/JH1kfZCmP3M/cdc_190112_2.mp3");
mp.prepareAsync();
mp.setOnPreparedListener(this);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void run()
{
threadStarted = true;
int current = 0;
while(mp != null && current < mp.getDuration())
{
try {
current = mp.getCurrentPosition();
seek.setProgress(current);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I see three issues here:
First, this part:
public void onPrepared(MediaPlayer arg0)
{
seek.setMax(mp.getDuration());
mp.start();
if(!threadStarted)
thread.start();
else
thread.run();
}
The first time your run the thread you set threadStarted to true in the thread code, but you never set it back to false when the thread finishes. So after the first run it will be true forever and thus the else above will always execute, running the player code sequentially and thus blocking your user interface.
Second, you are updating the user interface (the SeekBar) from a different thread than the UI thread. See this related question for how to correctly update the UI in this case: Update UI from Thread
Third, this may be just a recommendation, but since your threadStarted variable is modified between threads, it's better to declare it volatile to prevent some optimizations from breaking your code:
private volatile boolean threadStarted = false;
According to my experience and what is written in docs, threads in Android work only in purely algorythmic parts of a prog. Any try to use them in activities, or anything connected to UI, goes to error. This way it simply won't work. Never can you repair it!
Use AsyncTask (or child Activity/Fragment for greater things) instead.

Stop button when recording audio with AudioRecord

At the moment I have the code from this site (link text) working great. In the example on the site it starts the recording for 10 seconds and then immediately plays the audio back in reverse. I have modified the code to start recording when a button is pressed, but can only get it to record for the 10 seconds and then save that file. I want to be able to start the recording by pressing a button and then stop the recording on a different button press. I have an idea that it could be an interrupt to the wait() method of the thread object but have no idea how to implement this. My code is as follows:
public void onClickRecord(View v){
text.setText("Recording");
Thread thread = new Thread(new Runnable() {
public void run() {
record();
}
});
thread.start();
synchronized(this) {
try {
wait(10000); //This is the 10 second waiting period while recording
}
catch (InterruptedException e) {
}
}
isRecording = false;
try {
thread.join();
} catch (InterruptedException e) {
}
}
public void onClickStop(View v){
//Here is what needs to be implemented to stop the recording.
}
There is quite a bit of code so I have only posted the bits I think are relevant. If any more is needed just ask.
Thanks.
try the AudioRecord Class !
I think that should be helpful.
Also look at AudioCapture here
http://developer.android.com/guide/topics/media/index.html

Categories

Resources