3

I know very little about using background threads, but this seems to play my sound in the way that I need it to as follows:

1) I need this very short sound effect to play repeatedly even if the sound overlaps. 2) I need the sound to be played perfectly on time. 3) I need the loading of the sound to not affect the on-screen graphics by stuttering.

I am currently just trying out this method with one sound, but if successful, I will roll it out to other sound effects that need the same treatment. My question is this: Am I using the background thread properly? Will there be any sort of memory leaks?

Here's the code:

-(void) playAudio {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSString *path = [NSString stringWithFormat:@"%@/metronome.mp3", [[NSBundle mainBundle] resourcePath]];
        NSURL *metronomeSound = [NSURL fileURLWithPath:path];
        _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:metronomeSound error:nil];
        [_audioPlayer prepareToPlay];
        [_audioPlayer play];
    });
}

//handles collision detection
-(void) didBeginContact:(SKPhysicsContact *)contact {
    uint32_t categoryA = contact.bodyA.categoryBitMask;
    uint32_t categoryB = contact.bodyB.categoryBitMask;

    if (categoryA == kLineCategory || categoryB == kLineCategory) {
        NSLog(@"line contact");
        [self playAudio];
    }
}
02fentym
  • 1,762
  • 2
  • 16
  • 29

1 Answers1

2

I use the AVAudioPlayer and use it asynchronously and in background threads without any problems and no leaks as far as I can tell. However, I have implemented a singleton class that handles all the allocations and keeps an array of AVAudioPlayer instances that also play asynchronously as needed. If you need to play a sound repeatedly, you should allocate an AVAudioPlayer instance for every time you want to play it. In that case, latency will be negligible and you can even play the same sound simultaneously.

Concerning your strategy I think it needs some refinements, in particular if you want to prevent any delays. The main problem is always reading from disk, which is the slowest operation of all and your limiting step.

Thus, I would also implement an array of AVAudioPlayers each already initialized to play a specific sound, in particular if this sound is played often and repeatedly. You could remove those instances of players that are played less often from the array if memory starts to grow and reload them a few seconds before if you can tell which ones will be needed.

And one more thing... Don't forget to lock and unlock the array, if you are going to access it from multiple threads or better yet, create a GCD queue to handle all accesses to the array.

jvarela
  • 3,744
  • 1
  • 22
  • 43
  • Ok. In your solution are you "throwing away" the `AVAudioPlayer` each time you use it? The reason I ask is that when I was keeping the same one around, it wouldn't replay the sound at the frequency that I needed since it was probably still playing the sound. – 02fentym Jun 22 '17 at 17:19
  • No, I'm not, that's why I keep an array of them. I only throw them away if a) I know I don't need it for a while and b) when I allocate another AVAudioPlayer instance. – jvarela Jun 22 '17 at 17:21
  • Hmmm...as I said in my comment, when I reuse the player it doesn't play sometimes. Any ideas as to why? – 02fentym Jun 22 '17 at 17:22
  • Not at all or at a later time? That can happen if you don't preload the sounds and if they are too short. Try to preload them first in array and keep them ready. – jvarela Jun 22 '17 at 17:24
  • Not at all. Yes, the sound is extremely short (0.5 seconds maybe). I haven't been able to find a good solution for this. The code I have above solved by creating a new player each time, but I'm concerned that it's not the optimal solution. – 02fentym Jun 22 '17 at 17:25
  • Yes, that used to happen to me when I used sounds that were too short. Perhaps a way to avoid that is to preload a sound with no sound in it at the beginning and then keep it in memory and then play the rest that has actually some sound in it from a particular time point. But I don't know if that solves the problem, because I don't use sounds so short anymore. – jvarela Jun 22 '17 at 17:29
  • 1
    Perhaps having silence (or near silence) at the end of the sound would also work right? I thought the issue was that it wouldn't play because it's currently playing a sound, but it seems like what you're saying that it doesn't replay because the sound is too short. – 02fentym Jun 22 '17 at 17:31
  • Perhaps, I don't know. As I said, I never tried that solution. Anyway, here is a solution that seems to work and what I am trying to say: https://stackoverflow.com/questions/24043904/creating-and-playing-a-sound-in-swift/24073071#24073071. Try to lengthen your sound and see if that helps. If it does, then you know that you need to create some silence before or after. – jvarela Jun 22 '17 at 17:41
  • I just made the sound effect 8 seconds (the initial sound + a bunch of silence) and it seems that the player waits 8 seconds now between playing the sound again. Looks like it's NOT due to the length of the sound, but due to the fact that the sound is currently playing. – 02fentym Jun 22 '17 at 17:41
  • Sorry, but that does not happen to me. I can play the same sound several times even simultaneously and with some delay. I just tried it and it works. – jvarela Jun 22 '17 at 17:46
  • But you need then to create another instance of the AVAudioPlayer pointing to the same file for that. – jvarela Jun 22 '17 at 17:53
  • So let's say that you had a player for some sort of explosion sound. You would have to have multiple players just in case the explosions happen very frequently? – 02fentym Jun 22 '17 at 17:57
  • So how many players would you have to have for a single sound? Seems like a waste. Also, it doesn't work with the method you earlier prescribed where you have an array of players, one per sound. – 02fentym Jun 22 '17 at 18:16
  • Yes, I was wrong. I actually use multiple instances in the same array if I need to play that sound simultaneously with or without delay. I just create them and destroy them as needed in order to keep memory usage down. – jvarela Jun 22 '17 at 18:20