3
votes

I am developing an Music player App. And using Play/Pause button and a Progress bar while playing an audio. and I am calling a method with NSTimer to update progress bar. What I need is to pause the progress bar and resume it where it was left off.

I can pause the timer as well the progress bar with the code referred from the links: 1. How to pause a NSTimer? 2. How to Pause/Play NSTimer?

But I am struggling in resuming it where ii was left off. I also need to update progress bar based on the audio.

My code is as below:

AVAudioPlayer *audioPlayer;
NSTimer *audioTimer;
BOOL play;

BOOL ispaused;
int seconds1;
int minutes1;
int hours1;

NSDate *pauseStart, *previousFireDate;

Play/Pause Button action:

- (IBAction)playSoundTapped:(id)sender
{
songUrl = [NSURL URLWithString:@"http://www.stephaniequinn.com/Music/Allegro%20from%20Duet%20in%20C%20Major.mp3"];

self.playerItem = [AVPlayerItem playerItemWithURL:songUrl];
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
self.player = [AVPlayer playerWithURL:songUrl];

if (play==YES)
{
    if (ispaused == YES)
    {
        float pauseTime = -1*[pauseStart timeIntervalSinceNow];
        [audioTimer setFireDate:[NSDate dateWithTimeInterval:pauseTime sinceDate:previousFireDate]];
        audioTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];

        [self.player play];

        ispaused = NO;
    }
    else
    {
        audioTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];

        [self.player play];

        ispaused = NO;
    }

    _playImg.image=[UIImage imageNamed:@"Pause.png"];
    play=NO;
}
else
{
    [self.player pause];

    ispaused = YES;

    pauseStart = [NSDate dateWithTimeIntervalSinceNow:0];
    previousFireDate = [audioTimer fireDate];
    [audioTimer setFireDate:[NSDate distantFuture]];

    _playImg.image=[UIImage imageNamed:@"Play.png"];
    play=YES;
}
}

Updates ProgressBar:

- (void)updateProgress
{
    if (ispaused == NO)
    {
        seconds1 += 1;

    if (seconds1 == 60)
    {
        seconds1 = 0;
        minutes1++;

        if (minutes1 == 60)
        {
            minutes1 = 0;
            hours1++;
        }
    }

    float duration = CMTimeGetSeconds([_playerItem duration]);
    float actDuration = duration/100;
    float currentTime = CMTimeGetSeconds([_player currentTime]);
    float minutes = currentTime/60;
    //float seconds = (currentTime - minutes) * 60;
    NSLog(@"Duration: %.2f",actDuration);
    NSLog(@"Current Time: %.2f",minutes);

    NSString *durStr = [NSString stringWithFormat: @"%f",duration];

    if ([durStr  isEqual: @"nan"])
    {
        NSLog(@"Nan");
    }
    else
    {
        [self setProgress:minutes animated:YES];
    }
}
else
{
      NSLog(@“Paused”);        
}
}

It is restarting the audio instead of resuming. As I am a beginner to iOS I don’t know where I'm doing mistake. Can Anyone help me please….?

2
You don't need to use the current time or keep track of paused time. Simply increment your elapsed time when the audio is running and not when it is paused.Paulw11
@Paulw11 Can you share me sample code to use it...?Abirami Bala
Ketan's answer looks good. You just use the current time from the audio,player. invalidate the timer when you pause and start it when you playPaulw11
yeah, you just need to maintain time from your audio player as @Paulw11 said. For that you can refer my answer. And don't initialize player in play-pause action method. Otherwise it will create new player every time and start with initial position!Ketan Parmar

2 Answers

2
votes

What i am doing in my one of project,

 - (void)playPauseAudio : (UIButton*)sender{

if (!playPause.selected) {


    [audioPlayer play];

    sliderTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateSlider) userInfo:nil repeats:YES];
    [mySlider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
    mySlider.maximumValue = audioPlayer.duration;

}
else{

    [audioPlayer pause];
    [sliderTimer invalidate];
}



playPause.selected = !playPause.selected;

}

I have common button for play and pause, it just change it's title on normal state and selected state to play and pause respectively.

So in my method, playPause is button, audioPlayer is AVAudioPlayer object, sliderTimer is NSTimer object, mySlider is UISlider object to show progress. That's it. You can manage your case some how as i mentioned.

And updateSlider and sliderChanged method is something like below,

 - (void)updateSlider{

mySlider.value = audioPlayer.currentTime;
timerLabel.text = [self timeFormatted:audioPlayer.duration - audioPlayer.currentTime];
NSLog(@"update slider call");

}

- (void)sliderChanged : (UISlider*) sender{


[audioPlayer setCurrentTime:mySlider.value];
timerLabel.text = [self timeFormatted:audioPlayer.duration - mySlider.value];


}

So, If user manually change UISlider then sliderChanged will get called and your player will reach at that point.

1
votes

I got it!

i just add the below in pause function

rate = CMTimeGetSeconds([_player currentTime])/60;

and setting it as a current time to the audio player with the reference from ketan Parmer's Answer

[audioPlayer setCurrentTime:rate];

and when stoping

rate = 0.0;

and rate is a float which is globally declared. Thanks to Ketan..!