2016-10-26 13 views
7

Zbudowałem więc prostą aplikację, która rozpoznaje mowę za pomocą SFSpeechRecognizer i wyświetla konwersję mowy na tekst w UITextView na ekranie. Teraz próbuję sprawić, by telefon mówił tym wyświetlanym tekstem. Z jakiegoś powodu nie działa. Funkcja mówienia AVSpeechSynthesizer działa tylko przed użyciem SFSpeechRecognizer. Na przykład, gdy aplikacja się uruchamia, ma jakiś tekst powitalny wyświetlany w UITextView, jeśli dotknę przycisku mówienia, telefon będzie wypowiadał tekst powitania. Następnie, jeśli zrobię zapis (dla rozpoznawania mowy), rozpoznana mowa będzie wyświetlana w UITextView. Teraz chcę, żeby telefon mówił w tym tekście, ale niestety tak nie jest.AVSpeechSynthesizer nie mówi po użyciu SFSpeechRecognizer

oto kod

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

Pokaż. Twój. Kod. – matt

+0

@matt Dodałem kod. Oryginalna mowa do kodu tekstowego pochodziła z samouczka o kodowaniu. https://www.appcoda.com/siri-speech-framework/ –

+0

Znalazłem bardzo przydatny [this link] (http://avikam.com/software/sfspeechrecognizer-tutorial). Zawiera kompletny kod źródłowy mowy do tekstu, a następnie tekst na mowę za pomocą 'AVSpeechSynthesizer' –

Odpowiedz

7

Problem polega na tym, że po uruchomieniu funkcji rozpoznawania mowy, trzeba ustawić dźwięku kategorię sesji nagrać. Nie można odtwarzać dźwięku (w tym syntezy mowy) podczas sesji audio Record.

+0

Ale jeśli spojrzysz na tę mikrofonową funkcję wyzwalaną po dotknięciu mikrofonu, jeśli silnik audio jest uruchomiony, zatrzyma go i zakończy audio. Czy coś mi umyka? –

+2

Nie mów, usuń część kategorii sesji audio. Potrzebujesz _more_ zarządzania sesją audio, nie mniej. – matt

7

należy zmienić tę linię metody startRecording od:

try audioSession.setCategory(AVAudioSessionCategoryRecord)    

do:

try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord) 
+0

To działa idealnie. Ale zauważyłem, że dźwięk konwersji tekstu na mowę jest niższy po raz drugi (i kolejnych uruchomień). I nie wiem dlaczego. –

+0

Zgadzam się z Samuelem Méndez. Stoję w obliczu tego samego problemu. –

+0

To powinna być zaakceptowana odpowiedź. Dzięki! – Emilio

-1

spróbuj tego:

audioSession.setCategory(AVAudioSessionCategoryRecord) 
+0

Podaj wyjaśnienie. –

+1

Dlaczego OP powinien "spróbować tego"? ** Dobra odpowiedź ** zawsze będzie zawierała wyjaśnienie tego, co zostało zrobione i dlaczego zostało to zrobione w ten sposób, nie tylko dla OP, ale dla przyszłych gości SO, którzy mogą znaleźć to pytanie i czytać twoją odpowiedź. –

7

Proszę użyć poniższego kodu do mocowania problem :

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

Tutaj Po zatrzymaniu Audioengine jesteśmy ustawienie audioSessionKategoria do AVAudioSessionCategoryPlayback i Tryb audioSession do AVAudioSessionModeDefault .Następnie kiedy zadzwonić następnego tekstu do sposobu mowy, to będzie dobrze pracować.

+0

Ten komentarz pomógł mi rozwiązać mój problem i nie zostawił mnie przy zmianie głośności dźwięku. Wydaje się, że ważną częścią jest resetowanie audioSession i trybu po zakończeniu rozpoznawania. Dzięki za udostępnienie tych informacji. –

+0

dziękuję, zaoszczędziło to wiele czasu, szukałem błędu w Internecie i nie zauważyłem, że działo się to tylko po aktywacji rozpoznawania. chociaż był to błąd w 11.0.1, ale tak nie jest. –

0

podczas korzystania STT, trzeba ustawić tak:

AVAudioSession *avAudioSession = [AVAudioSession sharedInstance]; 

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

Po ponownym użyciem TTS zestaw AudioSession, tak:

[regRequest endAudio]; 

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

Jego praca idealnie dla mnie. Rozwiązany został również problem LOW AUDIO.

+0

Zgadzam się z tym. Korzystanie z "AVAudioSessionModeMeasurement" powinno być sprawdzone, jeśli doświadczamy bardzo niskiego poziomu głośności i/lub problemów z przełączaniem między 'AVSpeechSynthesizer' i' SFSpeechRecognizer' – coco

Powiązane problemy