【问题标题】:AVAudioPlayer working on Simulator but not on Real DeviceAVAudioPlayer 在模拟器上工作但不在真实设备上
【发布时间】:2016-05-29 04:56:20
【问题描述】:

播放录制的音频时出现此错误:

致命错误:在展开可选值时意外发现 nil

在这行代码上:

SoundPlayer = try AVAudioPlayer(contentsOfURL: getFileURL())

但它在模拟器上运行良好,除了真实设备。

ViewController.Swift:

import UIKit
import AVFoundation

class ViewController: UIViewController, AVAudioPlayerDelegate, AVAudioRecorderDelegate {

    @IBOutlet var PlayBTN: UIButton!
    @IBOutlet var RecordBTN: UIButton!


    var soundRecorder : AVAudioRecorder!
    var SoundPlayer : AVAudioPlayer!

    var fileName = "audioFile.m4a"

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        setupRecorder()
    }

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

    func setupRecorder(){

        let recordSettings = [AVSampleRateKey : NSNumber(float: Float(44100.0)),
            AVFormatIDKey : NSNumber(int: Int32(kAudioFormatMPEG4AAC)),
            AVNumberOfChannelsKey : NSNumber(int: 1),
            AVEncoderAudioQualityKey : NSNumber(int: Int32(AVAudioQuality.Max.rawValue))]

        do{
            try soundRecorder = AVAudioRecorder(URL: getFileURL(), settings: recordSettings)
                soundRecorder.delegate = self
                soundRecorder.prepareToRecord()
        }
        catch let error as NSError {
            error.description
        }

    }

    func getCacheDirectory() -> String {

        let paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true) 

        return paths[0]

    }

    func getFileURL() -> NSURL{
        let path  = (getCacheDirectory() as NSString).stringByAppendingPathComponent(fileName)

        let filePath = NSURL(fileURLWithPath: path)


        return filePath
    }


    @IBAction func Record(sender: UIButton) {

        if sender.titleLabel?.text == "Record"{

            soundRecorder.record()
            sender.setTitle("Stop", forState: .Normal)
            PlayBTN.enabled = false

        }
        else{

            soundRecorder.stop()
            sender.setTitle("Record", forState: .Normal)
            PlayBTN.enabled = false
        }

    }

    @IBAction func PlaySound(sender: UIButton) {

        if sender.titleLabel?.text == "Play" {

            RecordBTN.enabled = false
            sender.setTitle("Stop", forState: .Normal)

            preparePlayer()
            SoundPlayer.play()

        }
        else{

            SoundPlayer.stop()
            sender.setTitle("Play", forState: .Normal)

        }

    }

    func preparePlayer(){
        do{
            SoundPlayer = try AVAudioPlayer(contentsOfURL: getFileURL())
            SoundPlayer.delegate = self
            SoundPlayer.volume = 1.0
            SoundPlayer.prepareToPlay()
        }
        catch let error as NSError {
            error.description
        }
    }

    func audioRecorderDidFinishRecording(recorder: AVAudioRecorder, successfully flag: Bool) {
        PlayBTN.enabled = true
    }

    func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) {
        RecordBTN.enabled = true
        PlayBTN.setTitle("Play", forState: .Normal)
    }

    @IBAction func actDone(sender: AnyObject) {
        //viewRecorder.hidden = true

        let fm = NSFileManager.defaultManager()
        let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String

        do {
            let items = try fm.contentsOfDirectoryAtPath(documentsDirectory)

            for item in items {
                print(item)
            }
        }
        catch let error as NSError {
            error.description
        }
    }
}

ViewController.Swift 的故事板文件:

其他信息:

  • Xcode 版本:7.2.1
  • 设备:iPhone 5s
  • iOS版本:9.2.1

注意:我一直在寻找类似的 SO 解决方案,但没有一个对我有帮助。其中一些是由于较旧的 Swift 版本和不推荐使用的方法。一些解决方案是关于从应用程序包中播放音频文件,一些解决方案是使用 Web URL。所以,这种情况是不同的(Real Device vs Simulator

这里是Google Drive上的源代码链接。

【问题讨论】:

    标签: ios swift avaudioplayer fatal-error


    【解决方案1】:

    在真机上,需要setCategory()换成AVAudioSession.sharedInstance()

    也可以下载固定项目here

    func setupRecorder(){
    
            let audioSession = AVAudioSession.sharedInstance()
            do {
                try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
            } catch let error as NSError {
                print(error.description)
            }
    
            let recordSettings = [AVSampleRateKey : NSNumber(float: Float(44100.0)),
                AVFormatIDKey : NSNumber(int: Int32(kAudioFormatMPEG4AAC)),
                AVNumberOfChannelsKey : NSNumber(int: 1),
                AVEncoderAudioQualityKey : NSNumber(int: Int32(AVAudioQuality.Max.rawValue))]
    
            do{
                try soundRecorder = AVAudioRecorder(URL: getFileURL(), settings: recordSettings)
                    soundRecorder.delegate = self
                    soundRecorder.prepareToRecord()
            }
            catch let error as NSError {
                error.description
            }
    
        }
    

    注意:音频会话是您的应用和 iOS 之间的中介,用于配置应用的音频行为。如果我们为AVAudioSessionCategoryPlayAndRecord 设置类别,我们定义iOS 音频行为以允许音频输入(录制)和输出(播放)。 引用自:Audio Session Programming Guide

    【讨论】:

    • 你在真机上试过了吗?
    • @Mr.UB 你能检查我修改后的答案吗?我刚刚在我的设备上进行了测试。
    • 是的,我也测试过。通过@Allen 的修复,它适用于我的设计。
    • 如果你能在这里解释一下“会话”是什么,那就太好了。
    • 音频会话是您的应用和 iOS 之间的中介,用于配置应用的音频行为。如果我们为AVAudioSessionCategoryPlayAndRecord 设置类别,我们定义iOS 音频行为以允许音频输入(录制)和输出(播放)。
    猜你喜欢
    • 2011-03-04
    • 2014-09-03
    • 1970-01-01
    • 1970-01-01
    • 2016-02-14
    • 1970-01-01
    • 2019-05-09
    • 2017-11-14
    • 1970-01-01
    相关资源
    最近更新 更多