2017-02-08 10 views
2

iPhoneのマイクから録音し、.cafファイルに記録するiOSアプリケーション(Swift 3とXcode 8.2.1を使用)を構築したいとします。AVAudioFile initはシミュレータでは動作しますが、デバイスでクラッシュします


まず、私は私のプロジェクトののInfo.plistファイルに次の行を追加しました:その後

<key>NSMicrophoneUsageDescription</key> 
<string>Some description to explain why access is required</string> 

、私はAVAudioEngineを設定し、インストールの簡単なUIViewControllerを作成しましたRecordStopボタンをタップするとオーディオタップが削除されます。

ViewController.swift

import UIKit 
import AVFoundation 

class ViewController: UIViewController { 

    var engine = AVAudioEngine() 
    var file: AVAudioFile? 
    var player = AVAudioPlayerNode() 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     guard let url = urlFor(filename: "test.caf") else { return } 
     do { 
      file = try AVAudioFile(forWriting: url, settings: engine.inputNode!.inputFormat(forBus: 0).settings, commonFormat: engine.inputNode!.inputFormat(forBus: 0).commonFormat, interleaved: false) 
     } catch { 
      print("Error: \(error)") 
     } 

     engine.attach(player) 
     engine.connect(player, to: engine.mainMixerNode, format: engine.mainMixerNode.outputFormat(forBus: 0)) 

     do { 
      try engine.start() 
     } catch { 
      print("Error: \(error)") 
     } 
    } 

    @IBAction func record(sender: AnyObject) { 
     engine.inputNode?.installTap(onBus: 0, bufferSize: 1024, format: engine.mainMixerNode.outputFormat(forBus: 0)) { (buffer, time) -> Void in 
      do { 
       try self.file?.write(from: buffer) 
      } catch { 
       print("Error: \(error)") 
      } 
     } 
    } 

    @IBAction func stop(sender: AnyObject) { 
     engine.inputNode?.removeTap(onBus: 0) 
     // Print the url of the saved file 
     let urls = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask) 
     print(urls[urls.endIndex - 1]) 
    } 

    func urlFor(filename: String) -> URL? { 
     // Get url of saved file 
     if let dir = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.allDomainsMask, true).first { 
      let path = dir.appending(filename) 
      return URL(fileURLWithPath: path) 
     } 
     return nil 
    } 

} 

アプリは、シミュレータ上で正常に動作します:私は、私の声を録音し、私の声が正しく記録された記録されたファイルやテストのURLをつかむことができます。しかし、アプリをiOS 10.2.1のiPhone 6sでテストしたデバイスで起動するとすぐにクラッシュします。コンソールは私に次のログを提供します:

2017-02-08 17:09:14.330151 AudioEngineSimpleApp[7100:2659416] Audio files cannot be non-interleaved. Ignoring setting AVLinearPCMIsNonInterleaved YES. 
2017-02-08 17:09:14.333715 AudioEngineSimpleApp[7100:2659416] [central] 54: ERROR: [0x1b7440c40] >avae> AVAudioFile.mm:160: AVAudioFileImpl: error -54 
2017-02-08 17:09:14.333796 AudioEngineSimpleApp[7100:2659416] [central] 54: ERROR: [0x1b7440c40] >avae> AVAudioFile.mm:122: ReadMagicCookie: error -50 
2017-02-08 17:09:14.334369 AudioEngineSimpleApp[7100:2659416] *** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'error -50' 

この問題は、スタックオーバーフローのスレッドAVAudioPlayer working on Simulator but not on Real Deviceに関連しているようだが、私は私の問題を解決するために、自分自身のコードにその応答を実装する方法を見つけ出すことはできません。

答えて

0

私は自分のコードをリファクタリングしてきましたし、次のurlプロパティで私の古いurlFor(filename: String) -> URL?方法を交換した。その結果

let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("test.caf") 

、次のコードは、デバイス上で正常に動作します:

import UIKit 
import AVFoundation 

class ViewController: UIViewController { 

    @IBOutlet weak var recordButton: UIButton! 
    let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("test.caf") 
    //let url = URL(fileURLWithPath: NSTemporaryDirectory().appending("test.caf")) also works 
    var engine = AVAudioEngine() 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     recordButton.setTitle("Record", for: .normal) 

     let file: AVAudioFile 
     do { 
      file = try AVAudioFile(forWriting: url, settings: engine.inputNode!.outputFormat(forBus: 0).settings) 
     } catch { 
      print("Error: \(error)") 
      return 
     } 

     engine.inputNode?.installTap(onBus: 0, bufferSize: 1024, format: engine.inputNode!.outputFormat(forBus: 0)) { (buffer, time) -> Void in 
      do { 
       try file.write(from: buffer) 
      } catch { 
       print("Error: \(error)") 
      } 
     } 
    } 

    @IBAction func toggleAction(_ sender: AnyObject) { 
     engine.isRunning ? stopRecording() : startRecording() 
    } 

    func startRecording() { 
     do { 
      // Start engine 
      try engine.start() 

      // Toggle button title 
      recordButton.setTitle("Stop", for: .normal) 
     } catch { 
      print("Error: \(error)") 
     } 
    } 

    func stopRecording() { 
     // Stop engine 
     engine.stop() 

     // Toggle button title 
     recordButton.setTitle("Record", for: .normal) 

     print(url) 
    } 

} 
関連する問題