12

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

squarehippo10
  • 1,855
  • 1
  • 15
  • 45
  • how about playing the files on a background queue? main thread might be busy from processing your game, which could cause the crackles you are experiencing – hhanesand Dec 07 '17 at 07:16
  • I tried moving it off the main thread - still crackly. But thanks for the suggestion. – squarehippo10 Dec 08 '17 at 02:42
  • Have you use OALSimpleAudio? https://github.com/kstenerud/ObjectAL-for-iPhone I'm using it in my current game project, that has a lot of different sounds . It is an Objective-C code, but it's simple to add into a swift project by using a bridging header. Let me know if you need some help – Woof Dec 11 '17 at 02:20
  • @Woof, I appreciate the suggestion, however, this is just a simple learning project. I'm really interested in figuring out AVAudioPlayer. If this were for an actual project I would have moved on weeks ago. Thanks! – squarehippo10 Dec 11 '17 at 05:03

3 Answers3

4

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.

mugx
  • 9,869
  • 3
  • 43
  • 55
  • 1
    That makes sense. I'm still trying to figure out how to separate the dynamic player creation from the play function. But I'm working on it... – squarehippo10 Dec 13 '17 at 15:40
  • As good as this answer is, it is not a solution to the question. I created separate audioPlayers for each sound, initialised in the VC. The call to a method that played the sound from the game scene still resulted in the "Pop/Crackle noise described" – hoboBob Sep 18 '19 at 15:34
2

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

andriy_fedin
  • 175
  • 2
  • 9
  • That's interesting and would have been great if it fixed my problem. When I improved the player creation process the distortion disappeared from the simulator. Unfortunately, it's still on the device, connected or not. – squarehippo10 Dec 13 '17 at 15:36
  • I’m having exacltly the same problem. I’ve implemented the sound creation and play methods into a seperate class that I create and instance of in the gamescene. Each time I call a method to play a sound I get this crackle and popping sound. I’ve tried loads of different sounds and the problem persists. Also tried removing the debug in the run schema but no luck ! – hoboBob Apr 02 '18 at 12:27
  • Detaching the debugger also fixed the issue here. Thanks. But is there a way to fix it when the debugger is attached? – Stéphane de Luca May 11 '20 at 22:22
1

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!

squarehippo10
  • 1,855
  • 1
  • 15
  • 45