Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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...

like image 434
squarehippo10 Avatar asked Dec 07 '17 03:12

squarehippo10


3 Answers

In my opinion, your main problem is that the sound loading is done inside the same function supposed to play the sound itself. Conceptually is wrong, in order to be able to play the sound as fast as you can, you should load once and for all your sounds outside the "play" function. That function should just play, and that's it.

like image 193
Andrea Mugnaini Avatar answered Oct 26 '22 18:10

Andrea Mugnaini


We've just faced the problem in our team. In our case distorted sound bug can be reproduced only if the app is running on a device with debugger attached. Once we detach the app from debugger crackles disappear. It also looks like the problem first appeared in new Xcode 9.2 (9C40b).

like image 30
andriy_fedin Avatar answered Oct 26 '22 18:10

andriy_fedin


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!

like image 1
squarehippo10 Avatar answered Oct 26 '22 18:10

squarehippo10