I am using AVAudioplayer to play audio files, play/pause button to toggle between play and pause and NSTimer with audio player to synchronize view controllers one after another. I have a total of 10 or 11 view controllers to load one after the other.
When play button is pressed the very first time it starts NSTimer, starts playing an audio file and based on NSTimer, view controllers will load one after the other. When pause button is pressed again to resume it resumes everything from that point audio file, NSTimer and view controllers from the same point where it was paused which is good. However, at the same time it starts loading the view controllers again one after the other from the beginning based on NSTimer which is not good.
It is reloading view controllers with this statement
self.timer = [NSTimer scheduledTimerWithTimeInterval:11.0
target:self
selector:#selector(displayviewsAction:)
userInfo:nil
repeats:NO];
So my issue is that I need to stop the loading of view controllers again from the beginning when pause button is pressed again to resume playback.
-(void)playpauseAction:(id)sender
{
if ([audioPlayer isPlaying]) {
[sender setImage:[UIImage imageNamed:#"Play Icon.png"] forState:UIControlStateSelected];
[audioPlayer pause];
[self pauseTimer];
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
[self resumeTimer];
self.timer = [NSTimer scheduledTimerWithTimeInterval:11.0
target:self
selector:#selector(displayviewsAction:)
userInfo:nil
repeats:NO];
}
}
-(void)pauseTimer{
pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFireDate = [[timer fireDate] retain];
[timer setFireDate:[NSDate distantFuture]];
}
-(void)resumeTimer{
float pauseTime = -1*[pauseStart timeIntervalSinceNow];
[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];
[pauseStart release];
[previousFireDate release];
}
- (void)displayviewsAction:(id)sender
{
First *firstController = [[First alloc] init];
firstController.view.frame = CGRectMake(0, 0, 320, 480);
CATransition *transitionAnimation = [CATransition animation];
[transitionAnimation setDuration:1];
[transitionAnimation setType:kCATransitionFade];
[transitionAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
[self.view.layer addAnimation:transitionAnimation forKey:kCATransitionFade];
[self.view addSubview:firstController.view];
[self.view addSubview:toolbar];
[firstController release];
self.timer = [NSTimer scheduledTimerWithTimeInterval:23 target:self selector:#selector(Second) userInfo:nil repeats:NO];
}
Can anyone give an idea on how to stop the loading of view controllers again from the beginning when pause button is pressed again to resume playback.
I gather you're trying to display FirstViewController after 11 seconds have elapsed in the audio track.
You can set you timer up to poll (every .5 - 1.0 seconds) and if the audioPlayer's elapsed time is greater than 11.0, perform your action.
-(void)playpauseAction:(id)sender
{
if ([audioPlayer isPlaying]){
[sender setImage:[UIImage imageNamed:#"Play Icon.png"] forState:UIControlStateSelected];
[audioPlayer pause];
[self.timer invalidate]; // stop the polling timer
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
// start/resume the timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 /* polling interval */
target:self
selector:#selector(tryToDisplayViews:)
userInfo:nil
repeats:YES]; /* REPEAT = YES */
}
}
// the new polled method
-(void)tryToDisplayViews:(id)sender {
// need last current time as a new variable to ensure that we only add the view once
// and not every time the method fires with currentTime > 11.0
if( audioPlayer.currentTime > 11.0 && lastCurrentTime < 11.0) {
[self displayviewsAction:nil];
}
// you can add the second view here in a similar fashion
// if(audioPlayer.currentTime > (23.0+11.0) && lastCurrentTime < (23.0+11.0) ) {}
lastCurrentTime = audioPlayer.currentTime;
}
This also get's rid of your resumeTimer/pauseTimer actions, you don't need to count the time paused.
Related
Have a UISlider ProgressBar on UIToolbar when audio plays wants UISlider ProgressBar to display the duration of the audio file and currenttime of the audio file.
- (void)playAction:(id)sender
{
if([player isPlaying])
{
[sender setImage:[UIImage imageNamed:#"1play.png"] forState:UIControlStateSelected];
[player pause];
//[self pauseTimer];
}else{
[sender setImage:[UIImage imageNamed:#"audiopause.png"] forState:UIControlStateNormal];
[player play];
//[self resumeTimer];
}
[self updateProgressBar:timer];
}
- (void)updateProgressBar:(NSTimer *)timer
{
NSTimeInterval playTime = [self.player currentTime];
NSTimeInterval duration = [self.player duration];
float progress = playTime/duration;
[_progressBar setProgress:progress];
}
But it is not working.
Thanks for help.
You're probably going to want to call the updateProgressBar method using a timer, rather than what you're doing right now (calling it in the playAction method). Instead, you can use the playAction method to create the timer that calls updateProgressBar, or pause/stop existing timers.
Looks like you already have an instance variable to keep track of the timer, which is good. Here is how you would create a new timer:
[timer invalidate]; // stop the old timer
// this timer runs once per second, perhaps you want to make it something shorter which would look less choppy
NSTimer *progressTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(updateProgressBar:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:progressTimer forMode:NSRunLoopCommonModes];
timer = progressTimer;
If you have a method to pause the audio, you can invalidate the timer there too.
[timer invalidate];
timer = nil;
Altogether, in your code, this would look something like:
- (void)playAction:(id)sender
{
if([player isPlaying])
{
[sender setImage:[UIImage imageNamed:#"1play.png"] forState:UIControlStateSelected];
[player pause];
[timer invalidate];
timer = nil;
} else {
[sender setImage:[UIImage imageNamed:#"audiopause.png"] forState:UIControlStateNormal];
[player play];
[timer invalidate];
NSTimer *progressTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(updateProgressBar:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:progressTimer forMode:NSRunLoopCommonModes];
timer = progressTimer;
}
}
- (void)updateProgressBar:(NSTimer *)timer
{
NSTimeInterval playTime = [self.player currentTime];
NSTimeInterval duration = [self.player duration];
float progress = playTime/duration;
[_progressBar setProgress:progress];
}
Try this:
This is for the update slider..
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateSlider) userInfo:nil repeats:YES];
slider.maximumValue = avAudioPlayer.duration;
[slider addTarget:self action:#selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
- (void)updateSlider {
slider.value = avAudioPlayer.currentTime;
}
- (IBAction)sliderChanged:(UISlider *)sender {
[avAudioPlayer stop];
[avAudioPlayer setCurrentTime:slider.value];
[avAudioPlayer prepareToPlay];
[avAudioPlayer play];
}
Declared variable in m file.
BOOL isFirstTime;
I want that if play button is pressed the very first time then it should toggle to pause button and check the function isFirstTime then it should start the timer and execute the displayviewsAction method and if the pause button is resumed to play back then it should check again the function isFirstTime or second time and if the hit is for the second time then it should resume timer.
-(void)playpauseAction:(id)sender {
if ([audioPlayer isPlaying]){
[sender setImage:[UIImage imageNamed:#"Play Icon.png"] forState:UIControlStateSelected];
[audioPlayer pause];
[self pauseTimer];
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(AfterOneSecondsFuction) userInfo:nil repeats:YES];
}
}
-(void)pauseTimer{
pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFireDate = [[timer fireDate] retain];
[timer setFireDate:[NSDate distantFuture]];
}
-(void)resumeTimer{
float pauseTime = -1*[pauseStart timeIntervalSinceNow];
[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];
[pauseStart release];
[previousFireDate release];
}
-(void)AfterOneSecondsFuction
{
if(!isFirstTime)
{
isFirstTime=YES;
return;
self.timer = [NSTimer scheduledTimerWithTimeInterval:11.0
target:self
selector:#selector(displayviewsAction:)
userInfo:nil
repeats:NO];
} else if (!isFirstTime){
isFirstTime=NO;
return;
[self resumeTimer];
}
}
- (void)displayviewsAction:(id)sender
{
First *firstController = [[First alloc] init];
firstController.view.frame = CGRectMake(0, 0, 320, 480);
CATransition *transitionAnimation = [CATransition animation];
[transitionAnimation setDuration:1];
[transitionAnimation setType:kCATransitionFade];
[transitionAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
[self.view.layer addAnimation:transitionAnimation forKey:kCATransitionFade];
[self.view addSubview:firstController.view];
[self.view addSubview:toolbar];
[firstController release];
}
-(void)Second
{
Second *secondController = [[Second alloc] init];
secondController.view.frame = CGRectMake(0, 0, 320, 480);
CATransition *transitionAnimation = [CATransition animation];
[transitionAnimation setDuration:1];
[transitionAnimation setType:kCATransitionReveal];
[transitionAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
[self.view.layer addAnimation:transitionAnimation forKey:kCATransitionReveal];
[self.view addSubview:secondController.view];
[self.view addSubview:toolbar];
[secondController release];
self.timer = [NSTimer scheduledTimerWithTimeInterval:27 target:self selector:#selector(Third) userInfo:nil repeats:NO];
}
Looks like when play is pressed first it is only toggling to pause button not checking the function isFirstTime or not and not starting the timer and executing displayviewsaction.
If anyone can tell why it is not happening and what i m doing wrong.
Thanks for help.
BOOL isFirstTime
//Somewhere where the object is set up, isFirstTime should be set to YES
- (void)playpauseAction:(id)sender {
//When the button is pressed
if(isFirstTime) {
//Load the sound file
//Start playing the sound
//Start view controller timers
//Change the button image to paused
isFirstTime = FALSE;
}
else {
//The button has already been pressed
if ([audioPlayer isPlaying]) {
//If the sound is playing
//Stop playing the sound
//Stop view controller timers
//Change the button image to playing
}
else {
//Resume playing the sound
//Start view controller timers
//Change the button image to paused
}
}
}
In playpauseAction Method execution it will play audio file as well as load multiple viewcontrollers
-(void)playpauseAction:(id)sender
{
if
([audioPlayer isPlaying]){
[sender setImage:[UIImage imageNamed:#"play.png"] forState:UIControlStateSelected];
[audioPlayer pause];
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
self.timer = [NSTimer scheduledTimerWithTimeInterval:11 target:self selector:#selector(displayviewsAction:) userInfo:nil repeats:NO];
}
}
While loading these view controllers just want to check in every viewcontroller that if audioplayer is paused then invalidate timer if not then continue loading of viewcontroller with NSTimer
- (void)displayviewsAction:(id)sender
{
FirstViewController *viewController = [[FirstViewController alloc] init];
viewController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:viewController.view];
[self.view addSubview:toolbar];
if
([audioPlayer pause]){
[timer invalidate];
} else {
self.timer = [NSTimer scheduledTimerWithTimeInterval:23 target:self selector:#selector(secondViewController) userInfo:nil repeats:NO];
}
[viewController release];
}
My problem is when i add if statement in displayviewAction method it gives me red warning message that statement requires expression of scalar type (void invalid).
But if statement in the playpauseAction works fine.
Why the if statement of displayviewAction is giving red warning message.
AVAudioPlayer's pause is defined as:
-(void)pause
so writing:
if([audioPlayer pause]) {...}
corresponds to write:
if(void) {...}
and void is not a scalar type, exactly what the error message tells you.
Probably your intention was to check if the audio player was paused, that is it is not playing :
if([audioPlayer isPlaying]==NO) {...}
I m struggling with this issue for so long but not able to find any solution for this
The issue i have is when i pause the audioplayer i want views to be paused from updating so i used timer invalidate to pause the views from loading but when i resume play only Audioplayer is resumed not the views cause i invalidated the timer.
Now i how i can resume updating views from that point where view was paused. By starting the timer again i can start updating views again but how program will know that at what view it was paused and it should resume updating views from that point. There are multiple views involved displayed code for only two.
-(void)playpauseAction:(id)sender
{
if
([audioPlayer isPlaying]){
[sender setImage:[UIImage imageNamed:#"play.png"] forState:UIControlStateSelected];
[audioPlayer pause];
[timer invalidate];
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
self.timer = [NSTimer scheduledTimerWithTimeInterval:11 target:self selector:#selector(displayviewsAction:) userInfo:nil repeats:NO];
}
}
- (void)displayviewsAction:(id)sender
FirstViewController *viewController = [[FirstViewController alloc] init];
viewController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:viewController.view];
[self.view addSubview:toolbar];
[viewController release];
self.timer = [NSTimer scheduledTimerWithTimeInterval:23 target:self selector:#selector(secondViewController) userInfo:nil repeats:NO];
-(void)secondViewController {
SecondViewController *secondController = [[SecondViewController alloc] init];
secondController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:secondController.view];
[self.view addSubview:toolbar];
[secondController release];
self.timer = [NSTimer scheduledTimerWithTimeInterval:27 target:self selector:#selector(ThirdviewController) userInfo:nil repeats:NO];
}
Thanks and appreciate for your answers and helping me out with this issue.
Use a background thread to basically bind the view to the player, something like this:
UIImageView* imageView; //the view that gets updated
int current_selected_image_index = 0;
float time_for_next_image = 0.0;
AVAudioPlayer* audioPlayer;
NSArray* image_times; //an array of float values representing each
//time when the view should update
NSArray* image_names; //an array of the image file names
-(void)update_view
{
UIImage* next_image_to_play = [image_names objectAtIndex:current_selected_image_index];
imageView.image = next_image_to_play;
}
-(void)bind_view_to_audioplayer
{
while(audioPlayer.isPlaying)
{
float currentPlayingTime = (float)audioPlayer.currentTime;
if(currentPlayingTime >= time_for_next_image)
{
current_selected_image_index++;
[self performSelectorOnMainThread:#selector(update_view) withObject:nil waitUntilDone:NO];
time_for_next_image = [image_times objectAtIndex:[current_selected_image_index+1)];
}
[NSThread sleep:0.2];
}
}
-(void)init_audio
{
current_selected_image_index = 0;
time_for_next_image = [image_times objectAtIndex:1];
}
-(void)play_audio
{
[audioPlayer play];
[self performSelectorInBackground:#selector(bind_view_to_audioplayer) withObject:nil];
}
-(void)pause_audio
{
[audioPlayer pause];
//that's all, the background thread exits because audioPlayer.isPlaying == NO
//the value of current_selected_image_index stays where it is, so [self play_audio] picks up
//where it left off.
}
also, add self as observer for the audioPlayerDidFinishPlaying:successfully: notification to reset when the audioplayer finishes playing.
Time is invalidated to pause the views from updating because pause button was hit to pause the audio file.
-(void)playpauseAction:(id)sender
{
if
([audioPlayer isPlaying]){
[sender setImage:[UIImage imageNamed:#"play.png"] forState:UIControlStateSelected];
[audioPlayer pause];
[timer invalidate];
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
self.timer = [NSTimer scheduledTimerWithTimeInterval:11 target:self selector:#selector(displayviewsAction:) userInfo:nil repeats:NO];
}
}
- (void)displayviewsAction:(id)sender
{
FirstViewController *viewController = [[FirstViewController alloc] init];
viewController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:viewController.view];
[self.view addSubview:toolbar];
self.timer = [NSTimer scheduledTimerWithTimeInterval:23 target:self selector:#selector(secondViewController) userInfo:nil repeats:NO];
[viewController release];
}
-(void)secondViewController {
SecondViewController *secondController = [[SecondViewController alloc] init];
secondController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:secondController.view];
[self.view addSubview:toolbar];
self.timer = [NSTimer scheduledTimerWithTimeInterval:27 target:self selector:#selector(ThirdviewController) userInfo:nil repeats:NO];
[secondController release];
}
It goes like this for multiple views. As per the application logic user can hit pause on any view to pause the audio file and timer invalidate statement will also pause views from updating. When user will hit pause again to resume audio file it should start updating views from that point onwards. So problem i m having is how to restart the timer again so that it keep on updating views onwards no matter at which view user pauses.
Anyideas how i can do that.
The timers are not supposed to be reused, simply create another one.
-(void)countDown{
counter--;
if (counter == 0){
[self.timer invalidate];
[self secondViewController];
}
}
Launch your timer:
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(countDown) userInfo:nil repeats:YES];
And relaunch your timer:
counter = 23;
[self.timer fire];
you should adjust this answer for your purposes, because as you can see, the timer is scheduled only for launching -(void)secondViewController method