2017-02-08 12 views
2

iPhone의 마이크에서 기록하는 iOS 앱 (Swift 3 및 Xcode 8.2.1 사용)을 만들고 .caf 파일에 레코드를 저장하고 싶습니다.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? 방법을 대체했다 :

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) 
    } 

} 
:

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

결과, 다음 코드는 장치에서 제대로 작동