2016-10-26 2 views
7

그래서 SFSpeechRecognizer를 사용하여 음성 인식을 수행하고 변환 된 음성을 화면의 UITextView에 텍스트로 표시하는 간단한 응용 프로그램을 만들었습니다. 이제는 전화가 그 텍스트를 말하게하려고 노력하고 있습니다. 그것은 어떤 이유로 작동하지 않습니다. AVSpeechSynthesizer 말하기 기능은 SFSpeechRecognizer가 사용되기 전에 만 작동합니다. 예를 들어, 앱이 시작되면 UITextView에 환영 텍스트가 표시됩니다. 말하기 버튼을 누르면 전화가 환영 텍스트를 말합니다. 그런 다음 녹음을하면 (음성 인식을 위해) 인식 된 음성이 UITextView에 표시됩니다. 이제는 전화로 텍스트를 말하고 싶지만 안타깝게도 그렇지 않습니다.AVSpeechSynthesizer는 SFSpeechRecognizer를 사용한 후에 말하지 않습니다

여기

import UIKit 
import Speech 
import AVFoundation 


class ViewController: UIViewController, SFSpeechRecognizerDelegate, AVSpeechSynthesizerDelegate { 

    @IBOutlet weak var textView: UITextView! 
    @IBOutlet weak var microphoneButton: UIButton! 

    private let speechRecognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "en-US"))! 

    private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest? 
    private var recognitionTask: SFSpeechRecognitionTask? 
    private let audioEngine = AVAudioEngine() 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     microphoneButton.isEnabled = false 

     speechRecognizer.delegate = self 

     SFSpeechRecognizer.requestAuthorization { (authStatus) in 

      var isButtonEnabled = false 

      switch authStatus { 
      case .authorized: 
       isButtonEnabled = true 

      case .denied: 
       isButtonEnabled = false 
       print("User denied access to speech recognition") 

      case .restricted: 
       isButtonEnabled = false 
       print("Speech recognition restricted on this device") 

      case .notDetermined: 
       isButtonEnabled = false 
       print("Speech recognition not yet authorized") 
      } 

      OperationQueue.main.addOperation() { 
       self.microphoneButton.isEnabled = isButtonEnabled 
      } 
     } 
    } 

    @IBAction func speakTapped(_ sender: UIButton) { 
     let string = self.textView.text 
     let utterance = AVSpeechUtterance(string: string!) 
     let synthesizer = AVSpeechSynthesizer() 
     synthesizer.delegate = self 
     synthesizer.speak(utterance) 
    } 
    @IBAction func microphoneTapped(_ sender: AnyObject) { 
     if audioEngine.isRunning { 
      audioEngine.stop() 
      recognitionRequest?.endAudio() 
      microphoneButton.isEnabled = false 
      microphoneButton.setTitle("Start Recording", for: .normal) 
     } else { 
      startRecording() 
      microphoneButton.setTitle("Stop Recording", for: .normal) 
     } 
    } 

    func startRecording() { 

     if recognitionTask != nil { //1 
      recognitionTask?.cancel() 
      recognitionTask = nil 
     } 

     let audioSession = AVAudioSession.sharedInstance() //2 
     do { 
      try audioSession.setCategory(AVAudioSessionCategoryRecord) 
      try audioSession.setMode(AVAudioSessionModeMeasurement) 
      try audioSession.setActive(true, with: .notifyOthersOnDeactivation) 
     } catch { 
      print("audioSession properties weren't set because of an error.") 
     } 

     recognitionRequest = SFSpeechAudioBufferRecognitionRequest() //3 

     guard let inputNode = audioEngine.inputNode else { 
      fatalError("Audio engine has no input node") 
     } //4 

     guard let recognitionRequest = recognitionRequest else { 
      fatalError("Unable to create an SFSpeechAudioBufferRecognitionRequest object") 
     } //5 

     recognitionRequest.shouldReportPartialResults = true //6 

     recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest, resultHandler: { (result, error) in //7 

      var isFinal = false //8 

      if result != nil { 

       self.textView.text = result?.bestTranscription.formattedString //9 
       isFinal = (result?.isFinal)! 
      } 

      if error != nil || isFinal { //10 
       self.audioEngine.stop() 
       inputNode.removeTap(onBus: 0) 

       self.recognitionRequest = nil 
       self.recognitionTask = nil 

       self.microphoneButton.isEnabled = true 
      } 
     }) 

     let recordingFormat = inputNode.outputFormat(forBus: 0) //11 
     inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer, when) in 
      self.recognitionRequest?.append(buffer) 
     } 

     audioEngine.prepare() //12 

     do { 
      try audioEngine.start() 
     } catch { 
      print("audioEngine couldn't start because of an error.") 
     } 

     textView.text = "Say something, I'm listening!" 

    } 

    func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) { 
     if available { 
      microphoneButton.isEnabled = true 
     } else { 
      microphoneButton.isEnabled = false 
     } 
    } 
} 
+0

표시. 너의. 암호. – matt

+0

@matt 코드를 추가했습니다. 텍스트 코드의 원래 음성은 appcode 자습서에서였습니다. https://www.appcoda.com/siri-speech-framework/ –

+0

[this link] (http://avikam.com/software/sfspeechrecognizer-tutorial) 매우 유용합니다. 'AVSpeechSynthesizer '를 사용하여 완전한 음성 소스 코드와 텍스트를 포함하고 있습니다. –

답변

7

문제는 음성 인식을 시작할 때 녹음 오디오 세션 범주를 설정 한 것입니다 코드입니다. 녹음의 오디오 세션에서 오디오 (음성 합성 포함)를 재생할 수 없습니다.

audioSession.setCategory(AVAudioSessionCategoryRecord) 
+0

그러나 마이크를 두드려서 트리거 한이 microphoneTapped 기능을 보면, 오디오 엔진이 실행 중일 때 멈추고 오디오를 종료합니다. 내가 여기서 뭔가를 놓치고 있니? –

+2

오디오 세션 카테고리 부분을 제거한다고 말하지 않습니다. _more_ 오디오 세션 관리가 필요합니다. – matt

7

당신의 startRecording 방법이 줄을 변경해야합니다 :

여기
let audioSession = AVAudioSession.sharedInstance() 
      do { 

       try audioSession.setCategory(AVAudioSessionCategoryPlayback) 
       try audioSession.setMode(AVAudioSessionModeDefault) 

      } catch { 
       print("audioSession properties weren't set because of an error.") 
      } 

Here, we have to use the above code in the following way: 

@IBAction func microphoneTapped(_ sender: AnyObject) { 

     if audioEngine.isRunning { 
      audioEngine.stop() 
      recognitionRequest?.endAudio() 
      let audioSession = AVAudioSession.sharedInstance() 
      do { 

       try audioSession.setCategory(AVAudioSessionCategoryPlayback) 
       try audioSession.setMode(AVAudioSessionModeDefault) 

      } catch { 
       print("audioSession properties weren't set because of an error.") 
      } 

      microphoneButton.isEnabled = false 
      microphoneButton.setTitle("Start Recording", for: .normal) 
     } else { 
      startRecording() 
      microphoneButton.setTitle("Stop Recording", for: .normal) 
     } 
    } 

, AVAudioSessionCategoryPlayback 및 audioSession 모드 audioengine 우리가 audioSession카테고리을 설정하는 정지 후 당신이 음성 방법 다음 텍스트를 호출 AVAudioSessionModeDefault 그 때는 , 그것은 것입니다 잘 작동합니다. STT를 사용하는 경우

+0

이것은 완벽하게 작동합니다. 그러나 나는 text-to-speech 오디오가 두 번째 시간 (그리고 연속적인 실행)보다 낮다는 것을 알아 차렸다. 그리고 나는 이유를 모른다. –

+0

Samuel Méndez에 동의합니다. 동일한 문제에 직면하고 있습니다. –

+0

이것은 허용 된 대답이어야합니다. 감사! – Emilio

-1

:

try audioSession.setCategory(AVAudioSessionCategoryRecord)    

에 :

+0

약간의 설명을 드리 자면 –

+1

왜 OP가 "이거 해봐"? ** 좋은 답변 **에는 항상 수행 된 작업에 대한 설명이 포함되어 있으며 OP의 경우뿐만 아니라이 질문을 찾고 귀하의 답변을 읽을 수있는 향후 방문객을 위해 그렇게 된 이유에 대해 설명합니다. –

7

문제를 해결하기위한 아래의 코드를 사용하십시오 :이 시도

try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord) 
+0

이 의견은 저의 문제를 해결하는 데 도움이되었고 오디오 변경 음량을 남겨 두지 않았습니다. 중요한 부분은 인식이 끝나면 audioSession 및 모드를 다시 설정하는 것입니다. 이 정보를 공유해 주셔서 감사합니다. –

+0

감사합니다.이 시간이 많이 걸렸습니다. 웹에서 오류를 검색하고 인식기를 활성화 한 후에 만 ​​눈치 채지 못했습니다. 나는 이것이 11.0.1에서 오류 였지만, 그렇지 않다. –

0

, 당신은 다음과 같이 설정해야합니다 :

AVAudioSession *avAudioSession = [AVAudioSession sharedInstance]; 

if (avAudioSession) { 
    [avAudioSession setCategory:AVAudioSessionCategoryRecord error:nil]; 
    [avAudioSession setMode:AVAudioSessionModeMeasurement error:nil]; 
    [avAudioSession setActive:true withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil]; 
} 

과 같이 다시 TTS 세트 AudioSession를 사용하여 : 나를 위해 완벽하게

[regRequest endAudio]; 

AVAudioSession *avAudioSession = [AVAudioSession sharedInstance]; 
if (avAudioSession) { 
    [avAudioSession setCategory:AVAudioSessionCategoryPlayback error:nil]; 
    [avAudioSession setMode:AVAudioSessionModeDefault error:nil]; 
} 

그것의 작동합니다. 또한 LOW AUDIO 문제가 해결되었습니다.

+0

동의합니다. 'AVAudioSessionModeMeasurement'를 사용하는 것은 매우 낮은 볼륨을 경험하거나 'AVSpeechSynthesizer'와 'SFSpeechRecognizer'사이를 전환하는 문제를 조사해야합니다. – coco