使用Swift中的AudioEngine重复播放声音时崩溃

5

我试图播放不同效果的声音。在之前的视图控制器中,我录制了一个声音,然后在下一个屏幕上,可以使用效果来播放它。第一次它能够正常工作,但第二次会出现以下错误导致崩溃:

2015-08-07 13:00:45.900 Pitch Perfect[9643:1121173] 13:00:45.900 ERROR: AVAudioEngine.mm:253: AttachNode: required condition is false: !nodeimpl->HasEngineImpl() 2015-08-07 13:00:45.953 Pitch Perfect[9643:1121173] Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is 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个回答

13

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

实际上,它崩溃的原因在错误信息中已经概述:

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

换句话说,在附加节点时,必须确保该节点尚未附加到引擎上(!nodeimpl->HasEngineImpl())。

解决方法是使用 audioEngine.detachNode 将节点移除,然后再尝试重新添加。


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

1
在我的情况下,我的计时器一遍又一遍地调用,而这段代码是写在我的计时器内部的。
self.recognitionTask?.finish()
node.removeTap(onBus: 0)
self.request.endAudio()

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

如果您已经停止了请求,移除了水龙头并关闭了引擎,那么这些代码行就不应该再被调用。

希望这能帮助到某些人。


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时,它会崩溃。

即使注释掉所有的autoEngine.reset()行,它仍然会崩溃。 - Juan Presa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接