質問編集履歴

2 希望回答を追加

maeta

maeta score 1

2018/07/17 09:23  投稿

[iOS11][Swift]音声認識でエラーとなる
### 前提・実現したいこと
現在、音声でのチャットアプリを作成しようとしており、
まずは音声周りを調査しております。
iOSのSFSpeechRecognizerを使用してサンプルを作成しているのですが
音声認識開始後無言のまま音声認識の停止を実行するとエラーとなっています。
GitHubにある、Sampleをクローンして実行するも同じエラーとなります。
またこのエラーが出ると、AVSpeechSynthesizerのspeakでも
エラーとなってしまいます。
どのようなことを行えばエラーが無くなるのでしょうか?  
### SFSpeechRecognizerのエラー
```
2018-07-13 09:28:20.405082+0900 test[250:11545] [Utility] +[AFAggregator logDictationFailedWithError:] Error Domain=kAFAssistantErrorDomain Code=203 "Retry" UserInfo={NSLocalizedDescription=Retry, NSUnderlyingError=0x1c4450f80 {Error Domain=SiriSpeechErrorDomain Code=1 "(null)"}}
```
### AVSpeechSynthesizerのエラー
```
2018-07-13 09:28:57.917950+0900 test[250:11814] [TTS] Failure starting audio queue ≥˚˛ˇ
2018-07-13 09:28:59.942248+0900 test[250:11814] [TTS] _BeginSpeaking: couldn't begin playback
```
### 該当のソースコード
```Swift
import UIKit
import Speech
class ViewController: UIViewController {
   // "ja-JP"を指定すると日本語になります。
   private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "ja-JP"))!
   private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
   private var recognitionTask: SFSpeechRecognitionTask?
   private let audioEngine = AVAudioEngine()
   private var inputNode:AVAudioInputNode!
   
   @IBOutlet weak var textView: UITextView!
   @IBOutlet weak var recordButton: UIButton!
   // 音声読み上げ
   let talker = AVSpeechSynthesizer()
   
   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view, typically from a nib.
       recordButton.isEnabled = false
   }
   
   override func viewDidAppear(_ animated: Bool) {
       // 音声認識用デリゲートを設定
       speechRecognizer.delegate = self
       
       requestRecognizerAuthorization()
   }
   
   override func didReceiveMemoryWarning() {
       super.didReceiveMemoryWarning()
       // Dispose of any resources that can be recreated.
   }
   // 音声認識が許可されているかどうか確認
   private func requestRecognizerAuthorization() {
       // 認証処理
       SFSpeechRecognizer.requestAuthorization { authStatus in
           // メインスレッドで処理したい内容のため、OperationQueue.main.addOperationを使う
           // コールバックはメインスレッドで呼び出されないことがあります。 追加する
           // 操作をメインキューに送り、レコードボタンの状態を更新します。
           OperationQueue.main.addOperation {
               switch authStatus {
               case .authorized:
                   self.recordButton.isEnabled = true
                   
               case .denied:
                   self.recordButton.isEnabled = false
                   self.recordButton.setTitle("ユーザーが音声認識へのアクセスを拒否しました", for: .disabled)
                   
               case .restricted:
                   self.recordButton.isEnabled = false
                   self.recordButton.setTitle("このデバイスで音声認識が制限されています", for: .disabled)
                   
               case .notDetermined:
                   self.recordButton.isEnabled = false
                   self.recordButton.setTitle("音声認識が承認されていません", for: .disabled)
               }
           }
       }
   }
   private func startRecording() throws {
       
       // 前のタスクが実行中の場合はキャンセルします。
       if let recognitionTask = recognitionTask {
           recognitionTask.cancel()
           self.audioEngine.stop()
           self.audioEngine.inputNode.removeTap(onBus: 0)
           self.recognitionTask = nil
           self.recognitionRequest = nil
       }
       
       let audioSession = AVAudioSession.sharedInstance()
       try audioSession.setCategory(AVAudioSessionCategoryRecord)
       try audioSession.setMode(AVAudioSessionModeMeasurement)
       try audioSession.setActive(true, with: .notifyOthersOnDeactivation)
       recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
       
       let inputNode = audioEngine.inputNode
       guard let recognitionRequest = recognitionRequest else { fatalError("音声認識オブジェクトを作成できません") }
       
       // オーディオ録音が完了する前に結果が返されるようにリクエストを設定する
       // trueだと現在-1回目のリクエスト結果が返ってくる模様。falseだとボタンをオフにしたときに音声認識の結果が返ってくる設定。
       recognitionRequest.shouldReportPartialResults = true
       // 認識タスクは、音声認識セッションを表します。
       // それを取り消すことができるようにタスクへの参照を保持します。
       recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in
           var isFinal = false
           self.textView.text = ""
           if let result = result {
               self.textView.text = result.bestTranscription.formattedString
               isFinal = result.isFinal
           }
           
           // エラーがある、もしくは最後の認識結果だった場合の処理
           if error != nil || isFinal {
               self.audioEngine.stop()
               self.recognitionRequest?.endAudio()
               inputNode.removeTap(onBus: 0)
               
               self.recognitionRequest = nil
               self.recognitionTask = nil
               
               self.recordButton.isEnabled = true
               self.recordButton.setTitle("音声認識開始", for: [])
               
               if error == nil {
                   self.play(text: self.textView.text)
               }
           }
       }
       
       // マイクから取得した音声バッファをリクエストに渡す
       let recordingFormat = inputNode.outputFormat(forBus: 0)
       inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
           self.recognitionRequest?.append(buffer)
           //append(buffer)
       }
       
       // startの前にリソースを確保しておく。
       audioEngine.prepare()
       try audioEngine.start()
       textView.text = "(音声認識状態になっています)"
   }
   @IBAction func recordButtonTapped() {
       if self.audioEngine.isRunning {
           // 認識終了
           self.audioEngine.stop()
           self.audioEngine.inputNode.removeTap(onBus: 0)
           self.recognitionRequest?.endAudio()
           
           self.recordButton.isEnabled = false
           self.recordButton.setTitle("音声認識停止中", for: .disabled)
       } else {
           try! self.startRecording()
           self.recordButton.setTitle("音声認識停止", for: [])
       }
   }
   @IBAction func speechButton() {
       play(text: "音声認識テスト")
   }
   
}
extension ViewController: SFSpeechRecognizerDelegate {
   // 音声認識の可否が変更したときに呼ばれるdelegate
   public func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
       if available {
           recordButton.isEnabled = true
           recordButton.setTitle("音声認識開始", for: [])
       } else {
           recordButton.isEnabled = false
           recordButton.setTitle("音声認識を実行できない状態です", for: .disabled)
       }
   }
   
}
extension ViewController: AVSpeechSynthesizerDelegate {
   
   private func play(text: String) {
       if talker.isSpeaking {
           talker.stopSpeaking(at: .immediate)
       }
       let utterance = AVSpeechUtterance(string: text)
       utterance.volume = 1
       utterance.voice = AVSpeechSynthesisVoice(language: "ja-JP")
       talker.speak(utterance)
   }
}
```
### XcodeとiOS
Xcode:Version 9.4.1
iOS:11.4
  • iOS

    4679 questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

  • Swift

    8727 questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

1 エラーになる条件を詳細に書き直しました。

maeta

maeta score 1

2018/07/13 13:32  投稿

[iOS11][Swift]音声認識でエラーとなる
### 前提・実現したいこと
現在、音声でのチャットアプリを作成しようとしており、
まずは音声周りを調査しております。
iOSのSFSpeechRecognizerを使用してサンプルを作成しているのですが
音声認識の停止時にエラーとなっています。
音声認識開始後無言のまま音声認識の停止を実行するとエラーとなっています。
GitHubにある、Sampleをクローンして実行するも同じエラーとなります。
またこのエラーが出ると、AVSpeechSynthesizerのspeakでも
エラーとなってしまいます。
### SFSpeechRecognizerのエラー
```
2018-07-13 09:28:20.405082+0900 test[250:11545] [Utility] +[AFAggregator logDictationFailedWithError:] Error Domain=kAFAssistantErrorDomain Code=203 "Retry" UserInfo={NSLocalizedDescription=Retry, NSUnderlyingError=0x1c4450f80 {Error Domain=SiriSpeechErrorDomain Code=1 "(null)"}}
```
### AVSpeechSynthesizerのエラー
```
2018-07-13 09:28:57.917950+0900 test[250:11814] [TTS] Failure starting audio queue ≥˚˛ˇ
2018-07-13 09:28:59.942248+0900 test[250:11814] [TTS] _BeginSpeaking: couldn't begin playback
```
### 該当のソースコード
```Swift
import UIKit
import Speech
class ViewController: UIViewController {
   // "ja-JP"を指定すると日本語になります。
   private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "ja-JP"))!
   private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
   private var recognitionTask: SFSpeechRecognitionTask?
   private let audioEngine = AVAudioEngine()
   private var inputNode:AVAudioInputNode!
   
   @IBOutlet weak var textView: UITextView!
   @IBOutlet weak var recordButton: UIButton!
   // 音声読み上げ
   let talker = AVSpeechSynthesizer()
   
   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view, typically from a nib.
       recordButton.isEnabled = false
   }
   
   override func viewDidAppear(_ animated: Bool) {
       // 音声認識用デリゲートを設定
       speechRecognizer.delegate = self
       
       requestRecognizerAuthorization()
   }
   
   override func didReceiveMemoryWarning() {
       super.didReceiveMemoryWarning()
       // Dispose of any resources that can be recreated.
   }
   // 音声認識が許可されているかどうか確認
   private func requestRecognizerAuthorization() {
       // 認証処理
       SFSpeechRecognizer.requestAuthorization { authStatus in
           // メインスレッドで処理したい内容のため、OperationQueue.main.addOperationを使う
           // コールバックはメインスレッドで呼び出されないことがあります。 追加する
           // 操作をメインキューに送り、レコードボタンの状態を更新します。
           OperationQueue.main.addOperation {
               switch authStatus {
               case .authorized:
                   self.recordButton.isEnabled = true
                   
               case .denied:
                   self.recordButton.isEnabled = false
                   self.recordButton.setTitle("ユーザーが音声認識へのアクセスを拒否しました", for: .disabled)
                   
               case .restricted:
                   self.recordButton.isEnabled = false
                   self.recordButton.setTitle("このデバイスで音声認識が制限されています", for: .disabled)
                   
               case .notDetermined:
                   self.recordButton.isEnabled = false
                   self.recordButton.setTitle("音声認識が承認されていません", for: .disabled)
               }
           }
       }
   }
   private func startRecording() throws {
       
       // 前のタスクが実行中の場合はキャンセルします。
       if let recognitionTask = recognitionTask {
           recognitionTask.cancel()
           self.audioEngine.stop()
           self.audioEngine.inputNode.removeTap(onBus: 0)
           self.recognitionTask = nil
           self.recognitionRequest = nil
       }
       
       let audioSession = AVAudioSession.sharedInstance()
       try audioSession.setCategory(AVAudioSessionCategoryRecord)
       try audioSession.setMode(AVAudioSessionModeMeasurement)
       try audioSession.setActive(true, with: .notifyOthersOnDeactivation)
       recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
       
       let inputNode = audioEngine.inputNode
       guard let recognitionRequest = recognitionRequest else { fatalError("音声認識オブジェクトを作成できません") }
       
       // オーディオ録音が完了する前に結果が返されるようにリクエストを設定する
       // trueだと現在-1回目のリクエスト結果が返ってくる模様。falseだとボタンをオフにしたときに音声認識の結果が返ってくる設定。
       recognitionRequest.shouldReportPartialResults = true
       // 認識タスクは、音声認識セッションを表します。
       // それを取り消すことができるようにタスクへの参照を保持します。
       recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in
           var isFinal = false
           self.textView.text = ""
           if let result = result {
               self.textView.text = result.bestTranscription.formattedString
               isFinal = result.isFinal
           }
           
           // エラーがある、もしくは最後の認識結果だった場合の処理
           if error != nil || isFinal {
               self.audioEngine.stop()
               self.recognitionRequest?.endAudio()
               inputNode.removeTap(onBus: 0)
               
               self.recognitionRequest = nil
               self.recognitionTask = nil
               
               self.recordButton.isEnabled = true
               self.recordButton.setTitle("音声認識開始", for: [])
               
               if error == nil {
                   self.play(text: self.textView.text)
               }
           }
       }
       
       // マイクから取得した音声バッファをリクエストに渡す
       let recordingFormat = inputNode.outputFormat(forBus: 0)
       inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
           self.recognitionRequest?.append(buffer)
           //append(buffer)
       }
       
       // startの前にリソースを確保しておく。
       audioEngine.prepare()
       try audioEngine.start()
       textView.text = "(音声認識状態になっています)"
   }
   @IBAction func recordButtonTapped() {
       if self.audioEngine.isRunning {
           // 認識終了
           self.audioEngine.stop()
           self.audioEngine.inputNode.removeTap(onBus: 0)
           self.recognitionRequest?.endAudio()
           
           self.recordButton.isEnabled = false
           self.recordButton.setTitle("音声認識停止中", for: .disabled)
       } else {
           try! self.startRecording()
           self.recordButton.setTitle("音声認識停止", for: [])
       }
   }
   @IBAction func speechButton() {
       play(text: "音声認識テスト")
   }
   
}
extension ViewController: SFSpeechRecognizerDelegate {
   // 音声認識の可否が変更したときに呼ばれるdelegate
   public func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
       if available {
           recordButton.isEnabled = true
           recordButton.setTitle("音声認識開始", for: [])
       } else {
           recordButton.isEnabled = false
           recordButton.setTitle("音声認識を実行できない状態です", for: .disabled)
       }
   }
   
}
extension ViewController: AVSpeechSynthesizerDelegate {
   
   private func play(text: String) {
       if talker.isSpeaking {
           talker.stopSpeaking(at: .immediate)
       }
       let utterance = AVSpeechUtterance(string: text)
       utterance.volume = 1
       utterance.voice = AVSpeechSynthesisVoice(language: "ja-JP")
       talker.speak(utterance)
   }
}
```
### XcodeとiOS
Xcode:Version 9.4.1
iOS:11.4
  • iOS

    4679 questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

  • Swift

    8727 questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

思考するエンジニアのためのQ&Aサイト「teratail」について詳しく知る