現在開発中のアプリを開発中なのですが、そのアプリを音声のみで動かしたいと考えています。
しかし調べてみると、ボタンを押すと音声認識が始まるというものばかりです。
常時音声認証可能にしたいのですがどうすればいいのでしょうか、どなたか教えてください。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答1件
0
ベストアンサー
常時認識にしたいのであれば、ボタンを押したときに開始する音声認識の処理をアプリの起動時から開始するようにするだけでいいと思います。
ただし、SFSpeechRecognizerを使った音声認識では、次のような制限が発生してしまいます。
- 精度は高いが、本体の負荷も高い(おそらくバッテリ消費がハンパない)
- 日本語で音声認識するためには、オンライン状態でなければならない
- 1時間に認識できる音声認識の回数の上限は1000回まで
- 一区切りの発話(single utterance)で認識する方法が確立されていない
一番厄介なのは、一区切りの発話で認識する方法がAppleが提供するフレームワークとして存在していなさそうな点です(私の調べ方が足りないだけだと思いますが)。
いろいろ試した感じ、一区切りの発話で認識させる方法(一旦認識を終了させる方法)は不可能ではなさそうです。
ただ、運悪く認識できない場合は現在の結果がその後の認識結果に影響を与えてしまうので、それをどのようにして防ぐか考える必要がありそうです。
決して褒められた例ではないと思いますが、ひとまとまりの発話で認識を完了させるようなサンプルコードをつくってみました。
Qiitaの記事を参考にしています。
Qiitaの記事を含め、多くの例題が認識完了後の処理をクロージャで行っていますが、それだとうまくいかない処理もあるので、この例では delegate で処理させています。
「ひとまとまりで処理を完了する」というメソッドはないので、かわりに「もっともらしさ(confidence)」が0.94を超えた場合には認識を中断するようにしています。
これでもそこそこ認識してくれますが、問題は「もっともらしさ(confidence)が0.94以下の場合には認識を中断せず、認識を継続する」点にあります。
なにが問題かと言うと、「こんにちは」と発話しても認識に成功せず、その後「おはようございます」と続けて発話した場合、2回目に発話した「おはようございます」ではなく、1回目の発話である「こんにちは」も含め、「こんにちはおはようございます」という一つの発話として認識を行なってしまう点です。
当然このような場合、「もっともらしさ(confidence)」はどんどん下がる一方なので、まずその後認識に成功することはありません。
なので、ある一定時間認識に成功しなかった場合には、強制的に認識を中断する(Amazon Alexaであれば
ごめんなさい。認識できませんでした」の状態)方法を実装する必要があります(SFSpeechRecognizer
は無音性が1分続くと認識を中断しますが、それ以外の方法が必要だと言う意味です)。
swift
1import UIKit 2import Speech 3 4class ViewController: UIViewController,SFSpeechRecognizerDelegate, SFSpeechRecognitionTaskDelegate { 5 6 //UIラベルの変数 7 @IBOutlet weak var textView: UITextView! 8 @IBOutlet weak var recordButton: UIButton! 9 //UIボタンの変数 10 @IBAction func recordButton(_ sender: UIButton) { 11 if audioEngine.isRunning { 12 // 音声エンジン動作中なら停止 13 audioEngine.stop() 14 recognitionRequest?.endAudio() 15 recordButton.isEnabled = false 16 recordButton.setTitle("Stopping", for: .disabled) 17 recordButton.backgroundColor = UIColor.lightGray 18 return 19 } 20 // 録音を開始する 21 try! startRecording() 22 recordButton.setTitle("認識を完了する", for: []) 23 recordButton.backgroundColor = UIColor.red 24 } 25 //メンバプロパティでタスクのオブジェクトを宣言 26 private var recognitionTask: SFSpeechRecognitionTask? 27 //認識リクエストのインスタンスを宣言 28 private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest? 29 30 //SFSpeechRecognizerインスタンスを生成 31 //日本語に指定 32 private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "ja-JP"))! 33 34 //端末のマイクを使う準備 35 private let audioEngine = AVAudioEngine() 36 37 override func viewDidLoad() { 38 super.viewDidLoad() 39 // Do any additional setup after loading the view. 40 41 //マイクの許可をデフォルトでは無効にしておく 42 recordButton.isEnabled = false 43 } 44 45 // 画面に表示される直前に呼ばれます。 46 override func viewWillAppear(_ animated: Bool) { 47 speechRecognizer.delegate = self // デリゲート先になる 48 SFSpeechRecognizer.requestAuthorization { (status) in 49 OperationQueue.main.addOperation { 50 switch status { 51 case .authorized: // 許可OK 52 self.recordButton.isEnabled = true 53 self.recordButton.backgroundColor = UIColor.blue 54 case .denied: // 拒否 55 self.recordButton.isEnabled = false 56 self.recordButton.setTitle("録音許可なし", for: .disabled) 57 case .restricted: // 限定 58 self.recordButton.isEnabled = false 59 self.recordButton.setTitle("このデバイスでは無効", for: .disabled) 60 case .notDetermined:// 不明 61 fallthrough 62 @unknown default: 63 self.recordButton.isEnabled = false 64 self.recordButton.setTitle("録音機能が無効", for: .disabled) 65 } 66 } 67 } 68 } 69 70 private func startRecording() throws { 71 //ここに録音する処理を記述 72 //オプショナルバインディング 73 if let recognitionTask = recognitionTask { 74 // 既存タスクがあればキャンセルしてリセット 75 recognitionTask.cancel() 76 self.recognitionTask = nil 77 } 78 79 let audioSession = AVAudioSession.sharedInstance() 80 try audioSession.setCategory(AVAudioSession.Category.record) 81 try audioSession.setMode(AVAudioSession.Mode.measurement) 82 try audioSession.setActive(true) 83 84 //認識開始の前に認識リクエストを初期化 85 recognitionRequest = SFSpeechAudioBufferRecognitionRequest() 86 guard let recognitionRequest = recognitionRequest else { fatalError("リクエスト生成エラー") } 87 88 //録音完了前に途中の結果を報告してくれる 89 recognitionRequest.shouldReportPartialResults = true 90 91 //audioEngineインスタンスのinputNodeプロパティを取得する 92 let inputNode = audioEngine.inputNode 93 94 recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest, delegate: self) 95 96 97 //マイクからの録音フォーマット 98 let recordingFormat = inputNode.outputFormat(forBus: 0) 99 100 inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in 101 self.recognitionRequest?.append(buffer) 102 } 103 104 //オーディオエンジンで録音を開始して、テキスト表示を変更する 105 audioEngine.prepare() // オーディオエンジン準備 106 try audioEngine.start() // オーディオエンジン開始 107 108 textView.text = "Hello World" 109 110 } 111 112 // delegate 113 //音声認識機能の状態が変化するタイミングで呼ばれる 114 //録音ボタンの有効と無効を切り替える 115 public func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) { 116 if available { 117 // 利用可能になったら、録音ボタンを有効にする 118 recordButton.isEnabled = true 119 recordButton.setTitle("Start Recording", for: []) 120 recordButton.backgroundColor = UIColor.blue 121 } else { 122 // 利用できないなら、録音ボタンは無効にする 123 recordButton.isEnabled = false 124 recordButton.setTitle("現在、使用不可", for: .disabled) 125 } 126 } 127 128 // delegate 129 func speechRecognitionTask(_ task: SFSpeechRecognitionTask, didFinishSuccessfully successfully: Bool) { 130 print(#function) 131 print("isCanceled: (task.isCancelled)") 132 // 認識された場合には successfullt は true になる 133 // 1分経って認識がキャンセルされた場合には、successfully は false になる 134 print("successfully: (successfully)") 135 136 audioEngine.stop() 137 audioEngine.inputNode.removeTap(onBus: 0) 138 139 recognitionRequest = nil 140 recognitionTask = nil 141 142 recordButton.isEnabled = true 143 recordButton.setTitle("Start Recording", for: []) 144 recordButton.backgroundColor = UIColor.blue 145 146 // 強制的に再開させるのであれば、ここにコードを記述する 147 // 録音を開始する 148 try! startRecording() 149 recordButton.setTitle("認識を完了する", for: []) 150 recordButton.backgroundColor = UIColor.red 151 } 152 153 func speechRecognitionTask(_ task: SFSpeechRecognitionTask, didFinishRecognition recognitionResult: SFSpeechRecognitionResult) { 154 print(#function) 155 } 156 157 func speechRecognitionDidDetectSpeech(_ task: SFSpeechRecognitionTask) { 158 print(#function) 159 } 160 161 func speechRecognitionTask(_ task: SFSpeechRecognitionTask, didHypothesizeTranscription transcription: SFTranscription) { 162 print(#function) 163 164 switch task.state { 165 case .completed: 166 // 完全に終了 167 print("完全に終了") 168 case .finishing: 169 // 録音は完了したが、認識は継続している 170 print("認識継続中") 171 case .canceling: 172 // キャンセル中 173 print("キャンセル中") 174 case .running: 175 // 認識中 176 print("認識中") 177 case .starting: 178 // 開始 179 print("開始") 180 @unknown default: 181 // 想定外 182 fatalError() 183 } 184 185 // 認識中に呼ばれる 186 if transcription.segments[0].confidence > 0.94 { 187// This property reflects the overall confidence in the recognition of the entire phrase. The value is 0 if there was no recognition, and it is closer to 1 when there is a high certainty that a transcription matches the user's speech exactly. For example, a confidence value of 0.94 represents a very high confidence level, and is more likely to be correct than a transcription with a confidence value of 0.72. 188 if audioEngine.isRunning { 189 dump(transcription) 190 print(transcription.formattedString) 191 textView.text = transcription.formattedString 192 193 // 音声エンジン動作中なら停止 194 audioEngine.stop() 195 recognitionRequest?.endAudio() 196 recordButton.isEnabled = false 197 recordButton.setTitle("Stopping", for: .disabled) 198 recordButton.backgroundColor = UIColor.lightGray 199 } 200 } 201 } 202}
投稿2020/03/20 15:31
編集2020/03/22 23:25総合スコア5086
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/03/22 23:03
2020/03/22 23:09
2020/03/22 23:26
2020/03/23 00:07