6

I want to play multiple MP3 files, in sequence (one after the other) , using AVAudioPlayer. I tried it, and it stops after playing the first MP3. However, if I go into debugger, it works fine.. any ideas? I read somewhere AVAudioPlayer plays audio in the background.. how do I prevent it from doing this? Vas

Sam Baumgarten
  • 2,231
  • 3
  • 21
  • 46
  • This needs an iPhoneSDK tag please – John Fricker Mar 11 '09 at 00:28
  • It would help to see your code. Somethings to look at - are you using one AVAudioPlayer per sound? When you play mp3 files only one can play at a time so any over lap may not give expected results. Not sure why the debugger would make a difference unless you mean in the simulator too. Maybe the simulator can play multiple mp3 files concurrently (since that is a result of mp3 decoding hardware in the iPhone). Well more info will help us find a solution so feel free to post som code. – John Fricker Mar 11 '09 at 00:30

5 Answers5

11

I think the AVQueuePlayer (subclass of AVPlayer) does exactly this job (play a sequence of items) since iOS 4.1 : http://developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVQueuePlayer_Class/Reference/Reference.html

I didn't try it myself however but will definitely give a try to it.

yonel
  • 7,855
  • 2
  • 44
  • 51
  • While this answer addresses an alternative to using AVAudioPlayer, it is a far more elegant and 'built in' solution than using the Looper.* code examples above. It makes me cringe having to continuously allocate a new instance of AVAudioPlayer. While I initially took that approach I ended up subclassing AVQueuePlayer which gave me the functionality I needed for sequential audio file playback. So yes, this is the correct answer. – manderson Feb 20 '14 at 15:08
4

Use one AVAudioPlayer per sound.

3

Well, your code example didn't work out of the box for me. Soooo, I figured I'd respond with a fixed version:

Looper.h:

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@interface Looper : NSObject <AVAudioPlayerDelegate> {
    AVAudioPlayer* player;
    NSArray* fileNameQueue;
    int index;
}

@property (nonatomic, retain) AVAudioPlayer* player;
@property (nonatomic, retain) NSArray* fileNameQueue;

- (id)initWithFileNameQueue:(NSArray*)queue;
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;
- (void)play:(int)i;
- (void)stop;

@end

Looper.m:

#import "Looper.h"
@implementation Looper
@synthesize player, fileNameQueue;

- (id)initWithFileNameQueue:(NSArray*)queue {
    if ((self = [super init])) {
        self.fileNameQueue = queue;
        index = 0;
        [self play:index];
    }
    return self;
}

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    if (index < fileNameQueue.count) {
        [self play:index];
    } else {
        //reached end of queue
    }
}

- (void)play:(int)i {
    self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:[fileNameQueue objectAtIndex:i] ofType:nil]] error:nil];
    [player release];
    player.delegate = self;
    [player prepareToPlay];
    [player play];    
    index++;
}

- (void)stop {
    if (self.player.playing) [player stop];
}

- (void)dealloc {
    self.fileNameQueue = nil;
    self.player = nil;        
    [super dealloc];
}

@end

And here's how I would call it:

 Looper * looper = [[Looper alloc] initWithFileNameQueue:[NSArray arrayWithObjects: audioFile, audioFile2, nil ]];

I only have slightly over a year of experience with iPhone/iPad development using Objective-C so feel free to respond with additional criticism.

1

It is a good idea to initialize, prepare the items, and queue ahead of time, for example on the viewDidLoad method.

If you are working on Swift,

    override func viewDidLoad() {
        super.viewDidLoad()
        let item0 = AVPlayerItem.init(URL: NSBundle.mainBundle().URLForResource("url", withExtension: "wav")!)

        let item1 = AVPlayerItem.init(URL: NSBundle.mainBundle().URLForResource("dog", withExtension: "aifc")!)
        let item2 = AVPlayerItem.init(URL: NSBundle.mainBundle().URLForResource("GreatJob", withExtension: "wav")!)


        let itemsToPlay:[AVPlayerItem] = [item0, item1, item2] 
        queuePlayer = AVQueuePlayer.init(items: itemsToPlay)
    }

and then when an event occurs,

 queuePlayer.play()

Notice that if you use the queue, you still might have some gaps between the sounds.

You can find the Objective-C version in the question How to do something when AVQueuePlayer finishes the last playeritem

Hope it helps.

Community
  • 1
  • 1
1

I've implemented a class to handle this.

To use just do something like this:

[looper playAudioFiles:[NSArray arrayWithObjects:
    @"add.mp3",
    [NSString stringWithFormat:@"%d.mp3", numeral1.tag],
    @"and.mp3",
    [NSString stringWithFormat:@"%d.mp3", numeral2.tag],
    nil
]];

Looper.m

#import "Looper.h"
@implementation Looper
@synthesize player, fileNameQueue;

- (id)initWithFileNameQueue:(NSArray*)queue {
    if ((self = [super init])) {
        self.fileNameQueue = queue;
        index = 0;
        [self play:index];
    }
    return self;
}

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    if (index < fileNameQueue.count) {
        [self play:index];
    } else {
        //reached end of queue
    }
}

- (void)play:(int)i {
    self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:[fileNameQueue objectAtIndex:i] ofType:nil]] error:nil];
    [player release];
    player.delegate = self;
    [player prepareToPlay];
    [player play];    
    index++;
}

- (void)stop {
    if (self.player.playing) [player stop];
}

- (void)dealloc {
    self.fileNameQueue = nil;
    self.player = nil;        
    [super dealloc];
}

@end

Looper.h

#import <Foundation/Foundation.h>


@interface Looper : NSObject <AVAudioPlayerDelegate> {
    AVAudioPlayer* player;
    NSArray* fileNameQueue;
    int index;
}

@property (nonatomic, retain) AVAudioPlayer* player;
@property (nonatomic, retain) NSArray* fileNameQueue;

- (id)initWithFileNameQueue:(NSArray*)queue;
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;
- (void)play:(int)i;
- (void)stop;


@end
jarrold
  • 71
  • 4