在Swift中使用AudioEngine重复声音时崩溃

时间:2015-08-07 11:10:35

标签: swift avaudioengine

我试图播放具有不同效果的声音。在之前的viewController中我录制了一个声音,然后在下一个屏幕中,它可以用效果播放。第一次它工作正常但第二次崩溃时出现如下错误:

  

2015-08-07 13:00:45.900 Pitch Perfect [9643:1121173] 13:00:45.900   错误:AVAudioEngine.mm:253:AttachNode:必需条件是   false:!nodeimpl-> HasEngineImpl()2015-08-07 13:00:45.953 Pitch   完美[9643:1121173]由于未捕获的异常而终止应用程序   ' com.apple.coreaudio.avfaudio',原因:'必需条件为false:   !nodeimpl-> HasEngineImpl()'

import UIKit
import AVFoundation

class PlaySoundsViewController: UIViewController, AVAudioPlayerDelegate {

    var receivedAudio:RecordedAudio!
    var audioPlayer: AVAudioPlayer!
    var disabledButton:UIButton!
    var firstTime:Bool = true

    var audioEngine:AVAudioEngine!
    var audioFile:AVAudioFile!
    var audioPlayerNode:AVAudioPlayerNode!

    var audioStopped:Bool!
    var typeOfSound:IntegerLiteralType!


    @IBOutlet weak var stopButton: UIButton!

    @IBOutlet weak var reverbButton: UIButton!

    @IBOutlet weak var echoButton: UIButton!

    @IBOutlet weak var darthButton: UIButton!

    @IBOutlet weak var chipmonkButton: UIButton!

    @IBOutlet weak var snailButton: UIButton!

    @IBOutlet weak var rabbitButton: UIButton!



    override func viewDidLoad() {
        super.viewDidLoad()

        audioPlayer = AVAudioPlayer(contentsOfURL: receivedAudio.filePathUrl, error: nil)
        audioPlayer.enableRate=true
        audioPlayer.delegate=self

        var session = AVAudioSession.sharedInstance()
        session.setCategory(AVAudioSessionCategoryPlayback, error: nil)
        audioPlayerNode=AVAudioPlayerNode();
        audioEngine = AVAudioEngine()
        audioFile = AVAudioFile(forReading: receivedAudio.filePathUrl, error: nil)
        audioStopped=true;


    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func playAnimalSound(animal: String) {
        if audioStopped==false {
            resetAudio(typeOfSound)
        }
        typeOfSound = 1
        audioPlayer.currentTime=0

        switch animal {
            case "snail":
                audioPlayer.rate=0.5
            case "rabbit":
                 audioPlayer.rate=2.0
        default:
            showMessage("Sound not found. How can it be?")
        }

        audioPlayer.play()
        stopButton.hidden=false
        stopButton.enabled=true

    }



    @IBAction func playSnailSound(sender: UIButton) {
        highlightButton(sender)
       playAnimalSound("snail")
    }

    @IBAction func playRabbitSound(sender: UIButton) {
        highlightButton(sender)
        playAnimalSound("rabbit")
    }

    func soundEnded() {
        stopButton.hidden=true
        disabledButton.enabled=true;
        if(audioEngine.running) {
            audioEngine.stop()
            audioEngine.reset();
        }

    }


    func playAudioWithVariablePitch(pitch: Float, type: String) {
        if audioStopped==false {
             resetAudio(typeOfSound)
        }

        audioEngine.attachNode(audioPlayerNode)


        switch type {
        case "normal":
             typeOfSound = 2
            var changePitchEffect = AVAudioUnitTimePitch()
            changePitchEffect.pitch = pitch
            audioEngine.attachNode(changePitchEffect)

            audioEngine.connect(audioPlayerNode, to: changePitchEffect, format: nil)
            audioEngine.connect(changePitchEffect, to: audioEngine.outputNode, format: nil)

        case "reverb":
             typeOfSound = 3
            var changeReverbEffect = AVAudioUnitReverb()
            changeReverbEffect.loadFactoryPreset(AVAudioUnitReverbPreset(rawValue: 4)!)
            changeReverbEffect.wetDryMix=50;
            audioEngine.attachNode(changeReverbEffect)

            audioEngine.connect(audioPlayerNode, to: changeReverbEffect, format: nil)
            audioEngine.connect(changeReverbEffect, to: audioEngine.outputNode, format: nil)

        case "delay":
             typeOfSound = 3
            var changeDelayEffect = AVAudioUnitDelay()
            audioEngine.attachNode(changeDelayEffect)

            audioEngine.connect(audioPlayerNode, to: changeDelayEffect, format: nil)
            audioEngine.connect(changeDelayEffect, to: audioEngine.outputNode, format: nil)
        default:
            showMessage("oops, there was an internal problem. Never mind")

        }


       audioPlayerNode.scheduleFile(audioFile, atTime: nil, completionHandler: soundEnded)
        audioEngine.startAndReturnError(nil)
        stopButton.hidden=false
        stopButton.enabled=true
        audioPlayerNode.play()


    }



    func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool) {
        if flag {
            stopButton.hidden=true
            disabledButton.enabled=true;
            audioStopped=true
            println("I hid stopButton and enabled the disabled one")

        }

    }



    @IBAction func playDelaySound(sender: UIButton) {
        highlightButton(sender)
        playAudioWithVariablePitch(0, type: "delay")

    }


    @IBAction func playReverbSound(sender: UIButton) {
        highlightButton(sender)
        playAudioWithVariablePitch(0, type: "reverb")

    }

    @IBAction func playChipmunkSound(sender: UIButton) {
        highlightButton(sender)
        playAudioWithVariablePitch(1000.0, type: "normal")

    }

    @IBAction func playDarthVaderSound(sender: UIButton) {
        highlightButton(sender)
        playAudioWithVariablePitch(-900.0, type: "normal")

    }

    @IBAction func stopPlaying(sender: UIButton) {
        resetAudio(typeOfSound)
        stopButton.hidden=true
        stopButton.enabled=false;
        disabledButton.enabled=true;
    }

    func highlightButton(button: UIButton) {
        if firstTime {
            firstTime=false
        } else {
            disabledButton.enabled=true;
        }
        button.enabled=false;
        disabledButton=button;

    }

    func resetAudio(type: IntegerLiteralType) {
        switch type {
        case 1 :
            audioPlayer.stop()
            println("case 1")
        case 2 :
            println("case 2")
            if audioEngine.running {
                audioEngine.stop()
            }
            audioEngine.reset()
        case 3 :
             audioEngine.stop()

        default:
            break
        }

        audioStopped=true;

    }

    func showMessage(msg: String) {
        var message=UIAlertView(title: "Alert", message: msg, delegate: nil, cancelButtonTitle: "ok I won't panic")
    }


}

有人知道它崩溃的原因吗?我研究过没有结果的AVAudioEngine,AVAudioPlayer和AVAudioPlayerNode类。

由于

4 个答案:

答案 0 :(得分:9)

我知道这是一个老问题,但我没有看到上面的正确答案。

崩溃的原因实际上在错误消息中列出:

  

AttachNode:必需条件为false:!nodeimpl-> HasEngineImpl()

换句话说,在附加节点时,必须将该节点连接到引擎(!nodeimpl->HasEngineImpl())。

解决方案是在尝试再次添加节点之前使用audioEngine.detachNode删除节点。

答案 1 :(得分:3)

最后,崩溃是由viewDidLoad函数中的audioPlayerNode和audioEngine对象初始化引起的。它们需要在每次使用它们时进行实例化,显然,或者可能在被停止和重置之后。 直接将这些行放在playAudioWithVariablePitch函数的开头,而不是在viewDidLoad函数中,解决了崩溃问题。我仍然有播放音调,混响和回声的问题。它们会在它到期前被切断,我仍然不知道为什么。它与audioPlayerNode.scheduleFile方法的completionHandler有关。

答案 2 :(得分:0)

看起来你在播放可变音高效果后重置引擎。

func soundEnded() {
    stopButton.hidden=true
    disabledButton.enabled=true;
    if(audioEngine.running) {
        audioEngine.stop()
        audioEngine.reset();
    }
}

audioPlayerNode.scheduleFile(audioFile, atTime: nil, completionHandler: soundEnded)
audioEngine.startAndReturnError(nil)
stopButton.hidden=false
stopButton.enabled=true
audioPlayerNode.play()

因此,添加节点并链接链接后,引擎尚未重新设置。当你尝试播放playerNode时,它会崩溃。

答案 3 :(得分:0)

在我的情况下,我的计时器一次又一次地调用,并且这段代码被写入了我的计时器

self.recognitionTask?.finish()
node.removeTap(onBus: 0)
self.request.endAudio()

self.recognitionTask = nil
//Maybe this line was causing the issue
self.audioEngine.stop()

因此,如果您已经停止了该请求,卸下了水龙头并停止了引擎,那么这些行将不再被调用。

希望这对某些人有帮助