2017-12-03 2 views
-2

DispatchSource를 사용하여 녹음 된 오디오 (녹음 및 재생이 잘 작동 함) 타이머를 구현했으며 타이머가 작동하지만 내 탐색 컨트롤러 탭에서 뒤로 버튼을 누르면 앱이 다운됩니다. 아무런 문제없이 다음 화면으로 넘어갈 수 있습니다. 오류 : 스레드 1 : EXC_BAD_INSTRUCTION (코드 = EXC_I386_INVOP, 서브 코드 = 0x0)뒤로 버튼을 누르면 DispatchSource.makeTimerSource가 응용 프로그램을 중단합니다. Swift

누군가가이 이유에 대한 힌트를 제공해 줄 수 있습니까?

내보기 컨트롤러 코드 :

import Foundation 
import UIKit 
import AVFoundation 

class RecordGreetingController: UIViewController { 

@IBOutlet weak var timeLabel: UILabel! 
var audioPlayer: AVAudioPlayer! 
var updateTimer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main) 

override func viewDidLoad() { 
    super.viewDidLoad() 
} 

@IBAction func toggleRecordButton(_ sender: UIButton) { 
    if AudioRecorderManager.shared.recorder == nil { 
     if AudioRecorderManager.shared.recordAudio(fileName: "NewRecording") { 
      updateTimer.resume() 
     } 

     let formatter = DateComponentsFormatter() 
     formatter.zeroFormattingBehavior = .pad 
     formatter.includesApproximationPhrase = false 
     formatter.allowedUnits = [.minute, .second] 
     formatter.calendar = Calendar.current 

     updateTimer.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.milliseconds(100)) 

     updateTimer.setEventHandler { [weak self] in 
      self?.timeLabel.text = formatter.string(from: AudioRecorderManager.shared.recorder!.currentTime) 
     } 
    } 
    else { 
     AudioRecorderManager.shared.finishRecording() 
     AudioRecorderManager.shared.recorder = nil 
     updateTimer.suspend() 
    } 
} 

}

내 AudioRecorderManager 클래스 :

import Foundation 
import AVFoundation 

class AudioRecorderManager: NSObject, AVAudioRecorderDelegate { 


static let shared = AudioRecorderManager() 

var recordingSession: AVAudioSession! 
var recorder: AVAudioRecorder? 


func setup() { 

    recordingSession = AVAudioSession.sharedInstance() 

    do { 
     try recordingSession.setCategory(AVAudioSessionCategoryPlayAndRecord, with: .defaultToSpeaker) 
     try recordingSession.setActive(true) 

     // Request permission from user 
     recordingSession.requestRecordPermission{ (allowed) in 
      if allowed { 
       print("Mic authorised") 
      } 
      else { 
       print("Mic not authorised") 
       // TODO display alert to allow microphone access in settings to operate 
      } 
     } 
    } 
    catch { 
     print("Failed to set category", error.localizedDescription) 
    } 
} 

var meterTimer: Timer? 
var recorderApc0: Float? = 0 
var recorderPeak0: Float? = 0 

// Starts the recording session 
func recordAudio(fileName:String) -> Bool { 
    let url = getUserPath().appendingPathComponent(fileName+".m4a") 
    let audioURL = URL.init(fileURLWithPath: url.path) 
    let recordSettings: [String: Any] = [ 
     AVFormatIDKey: NSNumber(value: kAudioFormatAppleLossless), 
     AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue, 
     AVEncoderBitRateKey: 12000.0, 
     AVNumberOfChannelsKey: 1, 
     AVSampleRateKey: 44100.0 
    ] 

    do { 
     recorder = try AVAudioRecorder(url: audioURL, settings: recordSettings) 
     recorder?.delegate = self 
     recorder?.isMeteringEnabled = true 
     recorder?.prepareToRecord() 
     recorder?.record() 

     self.meterTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { (timer: Timer) in 
      // Update recorder meter values 
      if let recorder = self.recorder { 
       recorder.updateMeters() 
       self.recorderApc0 = recorder.averagePower(forChannel: 0) 
       self.recorderPeak0 = recorder.peakPower(forChannel: 0) 
      } 
     }) 
     print("Recording") 
     return true 
    } 
    catch { 
     print("Error recording") 
     return false 
    } 
} 

// Stop recording 
func finishRecording() { 
    self.recorder?.stop() 
    self.meterTimer?.invalidate() 
} 

// Gets path for the folder the file is being saved to 
func getUserPath() -> URL { 
    return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] 
} 

func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { 
    print("Audio Manager did finish recording", flag) 
} 

func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) { 
    print("Error encoding ", error?.localizedDescription ?? "") 
} 

}

+0

는 관련없는,하지만 그것을 수행하기 어렵다 동안 전에 타이머를 재개 할 수있는 실행 경로가처럼, 그것은 본다 핸들러와 스케줄을 설정하십시오 ... – Rob

+0

안녕하세요 Rob, 명확히하기 위해 toggleRecordButton IBAction 블록의 시작 부분에 updateTimer.resume()이 있습니다. 처리기와 일정 전에 타이머를 다시 시작하는 것이 잘못된 것이라고 제안 하시겠습니까? – elarcoiris

+0

예, 타이머를 시작하기 전에 타이머를 구성해야한다고 제안합니다. 구성을 완료하기 전에 시작하면 무엇을 기대합니까? 운이 좋다면 아무 것도 잘못 될 것이지만 왜이 문서화되지 않은 행동에 의존하고 있습니까? 시작하기 전에 설정을 완료하는 것이 가장 안전합니다. – Rob

답변

-1

이 내 문제에 대한 해결책 었죠. 문제가 해결되지 못한 위의 두 가지 중 하나에서 다운 불만 투표 무시 :

var updateTimer: DispatchSourceTimer? 
@IBOutlet weak var timeLabel: UILabel! 

func startTimer() { 
    let formatter = DateComponentsFormatter() 
    formatter.zeroFormattingBehavior = .pad 
    formatter.includesApproximationPhrase = false 
    formatter.allowedUnits = [.minute, .second] 
    formatter.calendar = Calendar.current 
    let queue = DispatchQueue.main 
    updateTimer = DispatchSource.makeTimerSource(queue: queue) 
    updateTimer!.schedule(deadline: .now(), repeating: .milliseconds(100)) 
    updateTimer!.setEventHandler { [weak self] in 
     self?.timeLabel.text = formatter.string(from: AudioRecorderManager.shared.recorder!.currentTime) 
    } 
    updateTimer!.resume() 
} 

func stopTimer() { 
    updateTimer?.cancel() 
    updateTimer = nil 
} 

deinit { 
    self.stopTimer() 
} 

@IBAction func toggleRecordButton(_ sender: UIButton) { 
    if AudioRecorderManager.shared.recorder == nil { 
     if AudioRecorderManager.shared.recordAudio(fileName: "NewRecording") { 
       startTimer() 
     } 
    } 
    else { 
     stopTimer() 
     AudioRecorderManager.shared.finishRecording() 
     AudioRecorderManager.shared.recorder = nil 
    } 
} 
관련 문제