【问题标题】:Storyboard segue causes AVAudioEngine to crashStoryboard segue 导致 AVAudioEngine 崩溃
【发布时间】:2018-05-14 17:56:41
【问题描述】:

我正在使用附加到我的应用程序中的 AVAudioEngine 的 AVAudioUnitSampler。我已经让一切正常工作,除了当我从视图控制器中分离出采样器被实例化的时候。我遇到了一个错误,上面写着:

由于未捕获的异常“com.apple.coreaudio.avfaudio”而终止应用程序,原因:“所需条件为假:outputNode”

我猜这是因为当我转回到我以前的 VC 时引擎以某种方式中断,不确定!

我已尝试停止引擎以及 viewWillDisappear 中的排序器,但它仍然崩溃。

如果我对以前的 VC 使用 UIButton show segue,它可以工作,但我会因展开 segue 和当前导航栏 segue 而崩溃。

我是新手,所以希望我已经解释得足够清楚了!

我从前一个 VC 上的表视图触发的 segue 进入这个 VC。

这里是有问题的 VC 的代码:

import UIKit
import AVFoundation

class PlayerViewController: UIViewController {
    @IBOutlet weak var descriptionLabel: UILabel!
    @IBOutlet weak var playPauseButton: UIButton!
    @IBOutlet weak var musicView: UIImageView!

    let allSounds = SoundBankOnAndOff()
    var currentSoundFile: OnandOff?
    var engine = AVAudioEngine()
    var sampler = AVAudioUnitSampler()

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)

        self.navigationController?.isNavigationBarHidden = false
        setupSequencer()
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(true)
        engine.stop()
        sequencer.stop()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        descriptionLabel.text = exercises[myIndex]
        musicView.image = musicNotes[myIndex]
        engine = AVAudioEngine()
        sampler = AVAudioUnitSampler()

        engine.attach(sampler)
        engine.connect(sampler, to: engine.mainMixerNode, format: nil)
        loadSF2PresetIntoSampler(preset: 0)
        startEngine()
        setSessionPlayback()
    }

    func setSessionPlayback() {
        let audioSession = AVAudioSession.sharedInstance()
        do {
            try
                audioSession.setCategory(AVAudioSessionCategoryPlayback, with:
                    AVAudioSessionCategoryOptions.mixWithOthers)
        } catch {
            print("couldn't set category \(error)")
            return
        }

        do {
            try audioSession.setActive(true)
            print("category is active")
        } catch {
            print("couldn't set category to active \(error)")
            return
        }
    }

    func startEngine() {
        if engine.isRunning {
            print("audio engine has already started")
            return
        }

        do {
            try engine.start()
            print("audio engine started")
        } catch {
            print("oops \(error)")
            print("could not start audio engine")
        }
    }

    var sequencer:AVAudioSequencer!

    func setupSequencer() {
        let allSounds = SoundBankOnAndOff()
        let currentSoundFile = allSounds.list[myIndex]
        self.sequencer = AVAudioSequencer(audioEngine: engine)

        let options = AVMusicSequenceLoadOptions.smfChannelsToTracks

        if let fileURL = Bundle.main.urls(forResourcesWithExtension: "mid", subdirectory: "On & Off MIDI") {
            do {
                try sequencer.load(from: (currentSoundFile.soundFile), options: options)
                print("loaded \(fileURL)")
            } catch {
                print("something messed up \(error)")
                return
            }
        }
        sequencer.prepareToPlay()
    }


    func play() {
        if sequencer.isPlaying {
            stop()
        }

        sequencer.currentPositionInBeats = TimeInterval(0)

        do {
            try sequencer.start()


        } catch {
            print("cannot start \(error)")
        }
    }

    func stop() {
        sequencer.stop()
    }

    func loadSF2PresetIntoSampler(preset:UInt8)  {
        guard let bankURL = Bundle.main.url(forResource: "Pad Sounds", withExtension: "sf2") else {
            print("could not load sound font")
            return
        }

        do {
            try sampler.loadSoundBankInstrument(at: bankURL, program: preset,bankMSB: UInt8(kAUSampler_DefaultMelodicBankMSB),bankLSB: UInt8(kAUSampler_DefaultBankLSB))
        } catch {
            print("error loading sound bank instrument")
        }
    }



    @IBAction func playButtonPressed(sender: UIButton) {
        if sender.currentTitle == "PLAY"{
            play()
            sender.setTitle("STOP", for: .normal)
        } else if sender.currentTitle == "STOP" {
            sender.setTitle("PLAY", for: .normal)

            stop()
        }
    }

    @IBAction func BackButtonPressed(_ sender: Any) {
        performSegue(withIdentifier: "unwindToDetailVC", sender: self)
    }
}

【问题讨论】:

  • 你在segue之前有没有尝试断开节点输入和输出?每个“附加”还有一个“分离”。
  • 嗨!我在 viewWillAppear 中添加了代码 engine.disconnectNodeInput(engine.mainMixerNode) engine.disconnectNodeOutput(engine.mainMixerNode) engine.stop() ,现在我收到错误:由于未捕获的异常“com.apple.coreaudio.avfaudio”而终止应用程序,原因:'必需的条件是错误的:!IsRunning()' ...也许我们在这里做点什么。
  • 当使用音频引擎(通常是异步的)等 I/O 库时,您需要释放资源等,然后再杀死与其关联的对象。查看 AVAudioEngine 和您正在使用的其他 AV 类的文档。很多时候,如果某些东西在没有停止的情况下运行,它可能会导致问题。 developer.apple.com/documentation/avfoundation/avaudioengine
  • 正确。谢谢回复。我一直在尝试阅读此文档并理解...还有其他方法可以做到吗?我试图断开节点并在 viewWillDisappear 中停止音频引擎,但它仍然崩溃。
  • 堆栈跟踪中的内容是什么?

标签: ios swift avfoundation avaudioengine


【解决方案1】:

此代码与您的代码相似,并且在 segue 或 unwinding 时不会崩溃:

class MP3PlayerVC: UIViewController {

    let audioEngine = AVAudioEngine()
    let audioPlayer = AVAudioPlayerNode()
    let distortion = AVAudioUnitDistortion()

    override func viewDidLoad() {
        super.viewDidLoad()

        audioEngine.attach(audioPlayer)
        audioEngine.attach(distortion)
        audioEngine.connect(audioPlayer, to: distortion, format: nil)
        audioEngine.connect(distortion, to: audioEngine.outputNode, format: nil)
    }

    @IBAction func buttonClicked(_ sender: UIButton) {

        if let filePath = Bundle.main.path(forResource: "test", ofType: "mp3") {

            let url = URL.init(fileURLWithPath: filePath)

            if let audioFile = try? AVAudioFile(forReading: url) {

                if audioPlayer.isPlaying {
                    audioPlayer.stop()
                    audioEngine.stop()
                } else {

                    audioPlayer.scheduleFile(audioFile, at: nil, completionHandler: nil)

                    do {
                        try audioEngine.start()

                        audioPlayer.play()

                    } catch {
                        print("something went wrong")
                    }
                }
            }
        }
    }
}

【讨论】:

  • 感谢您的帮助。这使我能够解决我的问题。干杯!!
  • 嗨特里,你确定的解决方法是什么?
  • 嗨@TheNeil,我发现我需要在点击 segue 按钮之前启动音频播放器。这是一个有点愚蠢的解决方法,但是当我点击按钮时播放了一个 1 毫秒长的静音文件,现在它不会崩溃!
  • 有趣。谢谢回复!确实感觉是一种必须可以避免的解决方法。我遇到了类似的问题,并确定最好的方法是在deinit 启动期间明确将使用音频引擎的任何内容设置为零,否则,iOS 会释放音频引擎first,导致该错误。对我来说,这是一个触发错误的音序器,但听起来是一样的。此处提供有关修复的更多详细信息:stackoverflow.com/questions/58036260/…
  • 太好了,感谢您的跟进。我一定会看看这个。
猜你喜欢
  • 2013-11-28
  • 2019-12-10
  • 1970-01-01
  • 1970-01-01
  • 2020-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-08
相关资源
最近更新 更多