iOS, swift: проигрывайте фоновую музыку и звуковые эффекты без задержки

Я пытаюсь создать игру в iOS без использования SpriteKit. Я застрял в том, чтобы заставить звуковые эффекты играть своевременно. Я использовал следующий код, который нашел в Интернете, и фоновая музыка играет великолепно. Однако, когда я использую метод "playSoundEffect", он воспроизводится нормально с первого раза, но затем начинает отставать и становится не синхронизированным. Я думаю, это происходит потому, что он каждый раз инициализирует AVAudioPlayer. У кого-нибудь есть хорошая идея о том, как своевременно воспроизводить звуковые эффекты и одновременно играть фоновую музыку? Спасибо!

import AVFoundation

public class SKTAudio: NSObject, AVAudioPlayerDelegate {
public var backgroundMusicPlayer: AVAudioPlayer?
public var soundEffectPlayer: AVAudioPlayer?

private var mainLoopFileName:String! {

    let randomSong = Int(arc4random_uniform(3))

    switch randomSong {
    //case 0: return "Test.mp3"
    //case 1: return "Test2.mp3"
    case 0: return "SneakySnitch.mp3"
    case 1: return "FasterDoesIt.mp3"
    case 2: return "MonkeysSpinningMonkeys.mp3"
    default:
        break
    }

    return "SneakySnitch.mp3"
}

public class func sharedInstance() -> SKTAudio {
    return SKTAudioInstance
}

public func playBackgroundMusic() {
    let filename = mainLoopFileName
    let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
    if (url == nil) {
        println("Could not find file: \(filename)")
        return
    }

    var error: NSError? = nil
    backgroundMusicPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
    if let player = backgroundMusicPlayer {
        player.numberOfLoops = 0
        player.delegate = self
        player.prepareToPlay()
        player.play()
    } else {
        println("Could not create audio player: \(error!)")
    }
}

public func pauseBackgroundMusic() {
    if let player = backgroundMusicPlayer {
        if player.playing {
            player.pause()
        }
    }
}

public func resumeBackgroundMusic() {
    if let player = backgroundMusicPlayer {
        if !player.playing {
            player.play()
        }
    }
}

public func playSoundEffect(filename: String) {
    let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
    if (url == nil) {
        println("Could not find file: \(filename)")
        return
    }

    var error: NSError? = nil
    soundEffectPlayer = AVAudioPlayer(contentsOfURL: url, error: &error)
    if let player = soundEffectPlayer {
        player.numberOfLoops = 0
        player.prepareToPlay()
        player.play()
    } else {
        println("Could not create audio player: \(error!)")
    }
}

// MARK: AVAudioPlayerDelegate
public func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool) {
    println("finished playing \(flag)")

    delay(5.0, {
        self.playBackgroundMusic()
    })
}

public func audioPlayerDecodeErrorDidOccur(player: AVAudioPlayer!, error: NSError!) {
    println("\(error.localizedDescription)")
}

}

2 ответа

Вы можете использовать AVPlayer для воспроизведения вашего звукового файла. Оставьте одного игрока, но поменяйте его AVPlayerItem на новый предмет, когда вам нужно воспроизвести новый звук. Это может быть быстрее, чем воссоздание плеера каждый раз.

В то время как AVAudioPlayer/AVPlayer это самый простой вариант, он не даст вам ни малейшей задержки, ни идеальной синхронизации при воспроизведении аудио файлов. Для более точного воспроизведения звука вы должны изучить аудио-очереди или аудиоустройства в Core Audio.

Проблема в том, что вы играете второй звуковой эффект до того, как первый закончен. Вы убиваете ссылку на первый, когда устанавливаете новый AVAudioPlayer на ваш soundEffectPlayer переменная и первый перестанет играть.

Если вы не против потерять возможность управления громкостью звука, вы можете использовать это:

var mySound: SystemSoundID = 0
AudioServicesCreateSystemSoundID(url, &mySound)
// Play
AudioServicesPlaySystemSound(mySound)

В противном случае вы можете использовать AVAudioPlayer если вы добавляете каждый звук, который вы создаете, в массив, сохраняя ссылку. Затем вы можете удалить его, когда звук сделан путем реализации AVAudioPlayer делегировать.

func playSound(){
    let path : NSString? = NSBundle.mainBundle().pathForResource("sound", ofType: "wav")!
    let url = NSURL(fileURLWithPath: path!)

    var error : NSError?
    let sound = AVAudioPlayer(contentsOfURL: url, error: &error)
    sound.delegate = self
    sound.volume = 0.5

    self.soundsEffectsArray.addObject(sound)
    sound.prepareToPlay()
    sound.play()
}

func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool) {
    self.soundsEffectsArray.removeObject(player)
}
Другие вопросы по тегам