質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

Q&A

解決済

1回答

3477閲覧

音声でiOSアプリを動かしたい

sakku727

総合スコア25

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

0グッド

0クリップ

投稿2020/03/20 03:45

現在開発中のアプリを開発中なのですが、そのアプリを音声のみで動かしたいと考えています。
しかし調べてみると、ボタンを押すと音声認識が始まるというものばかりです。
常時音声認証可能にしたいのですがどうすればいいのでしょうか、どなたか教えてください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

常時認識にしたいのであれば、ボタンを押したときに開始する音声認識の処理をアプリの起動時から開始するようにするだけでいいと思います。

ただし、SFSpeechRecognizerを使った音声認識では、次のような制限が発生してしまいます。

一番厄介なのは、一区切りの発話で認識する方法が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
TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sakku727

2020/03/22 23:03

回答ありがとうございます。 ちなみに、あらかじめいくつか単語を用意して、その単語を発音すると一定の動作をするようにはできませんか?
TsukubaDepot

2020/03/22 23:09

私が調べた範囲だと、特定の語彙を登録し、それをキーに何かを行うような処理は見つかりませんでした。 もしかしたら、誰かがそのような機能を実装し、githubなどで公開しているかもしれませんが。 同じような機能を自分で実装することはさほど難しくなさそうですが(認識結果の単語と登録した単語を逐一比較するだけなので)、精度と処理速度のトレードオフを決定することの方が難しいかもしれません。
TsukubaDepot

2020/03/22 23:26

実証するのに使ったサンプルコードを追記しました。ここから先、「その単語を発音すると一定の動作をする」実装が腕の見せ所だとおもいますが、それ自身はさほど難しくはないと思います。
sakku727

2020/03/23 00:07

サンプルコードまでありがとうございます!これを参考に作ってみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問