AVAudioPlayer 在模拟器上工作但不在真实设备上工作

AVAudioPlayer working on Simulator but not on Real Device

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

fatal error: unexpectedly found nil while unwrapping an Optional value

在这行代码中:

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的故事板文件:

附加信息:

注意: 我一直在寻找类似的 SO 解决方案,但其中 none 帮助了我。其中一些是由于旧的 Swift 版本和不推荐使用的方法。一些解决方案是关于从应用程序包中播放音频文件,一些是使用 web URL。所以,这种情况是不同的(真实设备与模拟器

这里是Google Drive上的link源代码。

在真实设备上,它需要 setCategory() for 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