Search code examples
iosswiftavaudioplayer

AVAudioPlayer crackles and pops


I have a Simon-like memory game where a sequence of tones is played and the user attempts to repeat the tones. The problem is that I get a horrible crackly, popping sound from time to time. It could be after just a few notes or as many as twenty. The sounds are all wav files that are two seconds in length. I cycle through ten players so that none of the sounds are clipped. (I've tried up to 50 but that didn't help anything.) I have also tried implementing audioPlayerDidFinishPlaying so that I could stop the player once the sound is complete but that didn't help either. And finally, I added prepareToPlay() - sadly, no difference.

One interesting issue that may or may not be related is that after a sequence of about 15 notes, the audio stops working all together. The app continues like normal, but without any sound.

Here is the audio portion of the code:

func playSound(_ soundID: Int) {
        var soundName = ""

        switch soundID {
        case 0:
            soundName = "beep1"
        case 1:
            soundName = "beep2"
        case 2:
            soundName = "beep3"
        case 3:
            soundName = "beep4"
        case 4:
            soundName = "swish3"
        default:
            soundName = "clank"
        }

        guard let url = Bundle.main.url(forResource: soundName, withExtension: "wav") else { return }

        do {
            buttonSound.insert(try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.wav.rawValue), at: playerIndex)
            buttonSound[playerIndex].prepareToPlay()
            buttonSound[playerIndex].delegate = self

            if playerIndex < 10 {
                buttonSound[playerIndex].play()
                playerIndex += 1
            } else {
                buttonSound[playerIndex].play()
                playerIndex = 0
            }

        } catch {
            // error
        }
    }

    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        if flag {
            player.stop()
        }
    }

/******** UPDATE *********/

I moved the AVAudioPlayer creation to its own class based on an outdated example I ran across...somewhere. It solved the audio completely cutting out after 15 tones problem, however, the cracks and pops are still there! I also tried rerecording the sounds with no luck.

Here is the class for creating AVAudioPlayers as needed:

import UIKit
import AVFoundation

private var players: [AVAudioPlayer] = []

class AVAudioPlayerPool: NSObject {

    class func playerWithURL(url: URL) -> AVAudioPlayer? {

        let availablePlayers = players.filter { (player) -> Bool in
            return player.isPlaying == false && player.url == url
        }

        if let playerToUse = availablePlayers.first {
            playerToUse.prepareToPlay()
            return playerToUse
        }

        do {
            let newPlayer = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.wav.rawValue)
            players.append(newPlayer)
            newPlayer.prepareToPlay()
            return newPlayer

        } catch {
            return nil
        }
    }
}

Playing a sound is simple:

func playSound(_ soundID: Int) {
    var soundName = ""

    switch soundID {
    case 0:
        soundName = "tt1"
    case 1:
        soundName = "tt2"
    case 2:
        soundName = "tt3"
    case 3:
        soundName = "tt4"
    case 4:
        soundName = "swish3"
    default:
        soundName = "clank"
    }

    guard let url = Bundle.main.url(forResource: soundName, withExtension: "wav") else { return }
    let player = AVAudioPlayerPool.playerWithURL(url: url)
    player?.play()

}

If anyone has any thoughts - even if it's just the next step you might try...


Solution

  • After many hours of experimentation, I'm going to say that the problem was caused by the sound I had chosen - a big, hollow, square wave synth sound with reverb. I first tried reducing the level thinking that might help. Next I tried all sorts of different sounds, but everything crackled. I think that had more to do with the way I was assigning players. Once I fixed the player problem, I didn't think to go back and try different sounds again. Until, I did...and it works!