AVAudioPlayer在模拟器上可以工作,但在真实设备上不能工作

5
当我播放录制的音频时,出现以下错误:

致命错误:在解包可选值时意外地发现了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的故事板文件:

enter image description here

附加信息:

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

注意:我已经寻找了类似的SO解决方案,但它们都没有帮助到我。其中一些是由于较旧的Swift版本和已弃用的方法。一些解决方案涉及从应用程序包中播放音频文件,而另一些则使用Web URL。因此,这种情况是不同的(真实设备与模拟器)。

这是源代码的链接,存储在Google Drive上。

1个回答

10

在真实设备上,需要为 AVAudioSession.sharedInstance() 设置 setCategory()

固定项目也可以从这里下载。

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音频行为以允许音频输入(录制)和输出(播放)。

引用自:音频会话编程指南


你在真实设备上试过了吗? - Allen
1
@Mr.UB,你能帮我检查一下我修改后的答案吗?我刚刚在我的设备上测试过了。 - Allen
是的,我也测试了。在 @Allen 的修复下,它在我的设备上可以工作。 - euvs
如果您能解释一下这里的“会话(session)”是做什么用的,那就太好了。 - Bista
1
音频会话是您的应用程序和iOS之间的中介,用于配置应用程序的音频行为。如果我们将类别设置为“AVAudioSessionCategoryPlayAndRecord”,则定义iOS音频行为以允许音频输入(录制)和输出(播放)。 - Allen
显示剩余2条评论

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