0
votes

I'm trying to play a background song in my iPhone game and also have sound effects, using the AVFoundation framework and AVPlayerItem. I've scoured the Internet for help with AVPlayerItem and AVPlayer but I'm only finding stuff about AVAudioPlayer.

The background song plays fine, but when the character jumps, I have 2 problems:

1) On the initial jump ([player play] inside jump method), the jump sound effect interrupts the background music.

2) If I try to jump again, the game crashes with the error "AVPlayerItem cannot be associated with more than one instance of AVPlayer"

My professor told me to create a new instance of AVPlayer for each sound I want to play, so I'm confused.

I'm doing data driven design, so my sound files are listed in a .txt and then loaded into a NSDictionary.

Here's my code:

- (void) storeSoundNamed:(NSString *) soundName 
        withFileName:(NSString *) soundFileName
{
    NSURL *assetURL = [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:soundName ofType:soundFileName]];

    AVURLAsset *mAsset = [[AVURLAsset alloc] initWithURL:assetURL options:nil];

    AVPlayerItem *mPlayerItem = [AVPlayerItem playerItemWithAsset:mAsset];

    [soundDictionary setObject:mPlayerItem forKey:soundName];

    NSLog(@"Sound added.");
}

- (void) playSound:(NSString *) soundName
{
    // from .h: @property AVPlayer *mPlayer;
    // from .m: @synthesize mPlayer = _mPlayer;       

    _mPlayer = [[AVPlayer alloc] initWithPlayerItem:[soundDictionary valueForKey:soundName]];

    [_mPlayer play];
    NSLog(@"Playing sound.");
}

If I move this line from the second method into the first:

_mPlayer = [[AVPlayer alloc] initWithPlayerItem:[soundDictionary valueForKey:soundName]];

The game does not crash, and the background song will play perfectly, but the jump sound effect does not play, even though the console is showing "Playing sound." on each jump.

Thank you

1

1 Answers

0
votes

I figured it out.

The error message was telling me everything I needed to know: you can't have more than one AVPlayer per AVPlayerItem, which is contrary to what I was taught.

Anyway, instead of storing my AVPlayerItems in the soundDictionary, I stored the AVURLAssets in the soundDictionary, with the soundName as the key for each Asset. Then I created a new AVPlayerItem and AVPlayer each time I wanted to play the sound.

Another problem was ARC. I wasn't able to keep track of the AVPlayerItem for each different item, so I had to make a NSMutableArray to store the AVPlayerItem and AVPlayer in.

Here's the fixed code:

- (void) storeSoundNamed:(NSString *) soundName 
        withFileName:(NSString *) soundFileName
{
    NSURL *assetURL = [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:soundName ofType:soundFileName]];

    AVURLAsset *mAsset = [[AVURLAsset alloc] initWithURL:assetURL options:nil];

    [_soundDictionary setObject:mAsset forKey:soundName];

    NSLog(@"Sound added.");
}

- (void) playSound:(NSString *) soundName
{
    // beforehand: @synthesize soundArray;
    // in init: self.soundArray = [[NSMutableArray alloc] init];

    AVPlayerItem *mPlayerItem = [AVPlayerItem playerItemWithAsset:[_soundDictionary valueForKey:soundName]];

    [self.soundArray addObject:mPlayerItem];

    AVPlayer *tempPlayer = [[AVPlayer alloc] initWithPlayerItem:mPlayerItem];

    [self.soundArray addObject:tempPlayer];

    [tempPlayer play];

    NSLog(@"Playing Sound.");
}