iOS Stop Audio Playback At Certain Time (AVAudioPlayer End Time)

10,381

Solution 1

The AVAudioPlayer gives you some neat properties to work with:

  • currentTime: during playback you can rely on this property.

  • playAtTime : starts playing from a pre-defined time.


But first of all I would write some helpers for that:

@interface Word {
    double startTime;
    double endTime;
}

@property double startTime;  
@property double endTime;  

@end

This is just a class to simply working with the following method.

- (void)playWord:(Word *)aWord {

    self.avPlayer.playAtTime = aWord.startTime;
    [avPlayer prepareToPlay];
    [avPlayer play];

    while (avPlayer.playing) {
        /*
        This while loop could be dangerous as it could go on for a long time.
        But because we're just evaluating words, it won't be as much power draining
        */

        if(avPlayer.currentTime >= aWord.endTime;
        [avPlayer stop];
    }
}

I would suggest you to use an array or any other mechanism to automatically switch to the next word. Maybe your could also add a Previous and Next button for handling user input.

Please let me know if that worked for you.

HTH

Solution 2

This should do what you want. you don't need a repeating timer that's thrashing the player.

NSString *myAudioFileInBundle = @"words.mp3";
NSTimeInterval wordStartsAt = 1.8;
NSTimeInterval wordEndsAt = 6.5;
NSString *filePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:myAudioFileInBundle];
NSURL *myFileURL = [NSURL fileURLWithPath:filePath];
AVAudioPlayer *myPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:myFileURL error:nil];
if (myPlayer) 
{
    [myPlayer setCurrentTime:wordStartsAt];
    [myPlayer prepareToPlay];
    [myPlayer play];
    [NSTimer scheduledTimerWithTimeInterval:wordEndsAt-wordStartsAt target:myPlayer.autorelease selector:@selector(stop) userInfo:nil repeats:NO];

}

Solution 3

Try using AVPlayer instead and make use of

addBoundaryTimeObserverForTimes:queue:usingBlock:

What it does: Requests invocation of a block when specified times are traversed during normal playback.

https://developer.apple.com/library/prerelease/ios/documentation/AVFoundation/Reference/AVPlayer_Class/index.html#//apple_ref/occ/instm/AVPlayer/addBoundaryTimeObserverForTimes:queue:usingBlock:

Share:
10,381
dciso
Author by

dciso

Updated on June 21, 2022

Comments

  • dciso
    dciso almost 2 years

    I'm wanting to play just part of an audio file. This audio file contains 232 spoken words, I have a dictionary that contains the start time of each word. So I have the start and stop times I just can't find a way to stop at a given time or play the file for a certain duration. Any advice and/or sample code to help me would be much appreciated, thanks Jason.

    So I've found a solution, there's a problem with how I get endTime but I sure I can fix it.

    //Prepare to play
    [audioPlayer prepareToPlay];
    
    //Get current time from the array using selected word
    audioPlayer.currentTime = [[wordsDict objectForKey:selectedWord] floatValue]; 
    
    //Find end time  - this is a little messy for now
    int currentIndex = 0;
    int count = 0;
    
    for (NSString* item in wordsKeys) {
        if ([item isEqualToString: selectedWord]) {
            currentIndex = count+1;
        }
        count++;
    }
    
    //Store found end time
    endTime  = [[wordsDict objectForKey:[wordsKeys objectAtIndex:currentIndex]] floatValue];
    
    //Start Timer
    NSTimer * myAudioTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
                                                                 target:self
                                                               selector:@selector(checkCurrentTime)
                                                               userInfo:nil
                                                                repeats:YES]
    //Now play audio
    [audioPlayer play];
    
    
    
    //Stop at endTime
    - (void) checkCurrentTime {
    if(audioPlayer.playing && audioPlayer.currentTime >= endTime)
        [audioPlayer stop];
    }
    
  • unsynchronized
    unsynchronized over 12 years
    yes it is true playAtTime is a nifty METHOD that does neat tricks, but in this case it will not help achieve what the OP wants to do. playAtTime is not a property, but a command telling the player when, in relation to it's device clock to start playback from the start of the media file (default) or from the time previously assigned to the currentTime property. to stop it, providing you have called prepareToPlay, a single fire from a timer sent to the players stop method should suffice. make it autoreleased, the timer will retain the player until it fires, then ... goneburger. see my answer.
  • JamesB
    JamesB over 11 years
    This idea of setting NSTimer to a specific interval is good, for anyone else doing something similar, if you implement a scrub-bar or anything that changes the currentTime value during playback you will need to clear and reset the NSTimer event to the new offset which would be equivalent to wordEndsAt-mplayer.currentTime
  • chakrit
    chakrit almost 10 years
    This is still not ideal as it is possible for NSTimer to slip in time and miss the stop point. The passed in interval is only approximately guaranteed. With audio, microseconds time matter.
  • unsynchronized
    unsynchronized almost 10 years
    sure, it is a pragmatic solution that is not ideal in every circumstance. sometimes you just have to weigh up the effort required vs the benefit gained finding textbook perfect solutions, when something that handles 98% of situations will suffice.
  • Massmaker
    Massmaker about 9 years
    You could use GCD also: dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((wordEndsAt-wordStartsAt) * NSEC_PER_SEC)), self.playerQueue, ^ { [weakSelf.audioPlayer stop]; });