iOS10から登場した音声認識API(Speech Recognition)の挙動について調べてみました。
60秒を超えた時の挙動
テストプロジェクトを作り、ViewControllerを下のようにして検証しました。
このコードでは5秒時点と55秒時点と62秒時点でのタスクステータスを見ています。
import UIKit import Speech class ViewController: UIViewController { private var recognitionRequest = SFSpeechAudioBufferRecognitionRequest() private var speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "ja-JP"))! private var recognitionTask: SFSpeechRecognitionTask? private var audioEngine = AVAudioEngine() override func viewDidLoad() { super.viewDidLoad() SFSpeechRecognizer.requestAuthorization { _ in } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) start() } func start() { recognitionRequest = SFSpeechAudioBufferRecognitionRequest() speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "ja-JP"))! audioEngine = AVAudioEngine() let recordingFormat = audioEngine.inputNode.outputFormat(forBus: 0) audioEngine.inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in self.recognitionRequest.append(buffer) } try! audioEngine.start() recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in print("resultHandler") print(result?.bestTranscription.formattedString ?? "") if let error = error { print("ERROR!") print(error.localizedDescription) } } print(recognitionTask!.state.rawValue) DispatchQueue.main.asyncAfter(deadline: .now() + 5.0, execute: { print(self.recognitionTask!.state.rawValue) print("5!") }) DispatchQueue.main.asyncAfter(deadline: .now() + 55.0, execute: { print(self.recognitionTask!.state.rawValue) print("55!") }) DispatchQueue.main.asyncAfter(deadline: .now() + 62.0, execute: { print(self.recognitionTask!.state.rawValue) print("62!") }) } }
検証結果は下の通りです。
- 60秒経過するとresultHandlerが「kAFAssistantErrorDomain Code=203 "Retry"」エラーを引数にして呼ばれる
- 5秒時点でのタスクのステータスはrunning
- 55秒時点でのタスクのステータスはrunning
- 62秒時点でのタスクのステータスはcompleted
それとこの挙動は「マイクに一度でも話しかけたかどうか」で変化がありました。
一度でもマイクに話しかけると下のような挙動になりました。
- 60秒経過してもresultHandlerは呼ばれない
- 55秒時点でのタスクのステータスはrunningではなくcompleted
自分でキャンセルした場合の挙動
制限ではないのですが、自分でタスクをキャンセルした時はresultHandlerが下エラーを引数に呼ばれます。
The operation couldn’t be completed. (kAFAssistantErrorDomain error 216.)
ユーザー辺りの制限
下のように延々と音声認識をして、どのくらいで制限が引っかかるか試しました。
連続で実施すると60秒制限に引っかかるので、5秒おきに開始と終了を繰り返しています。
import UIKit import Speech class ViewController: UIViewController { private var recognitionRequest = SFSpeechAudioBufferRecognitionRequest() private var speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "ja-JP"))! private var recognitionTask: SFSpeechRecognitionTask? private var audioEngine = AVAudioEngine() var startCount = 0 override func viewDidLoad() { super.viewDidLoad() SFSpeechRecognizer.requestAuthorization { _ in } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) start() } func start() { startCount += 1 print(startCount) recognitionRequest = SFSpeechAudioBufferRecognitionRequest() speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "ja-JP"))! audioEngine = AVAudioEngine() let recordingFormat = audioEngine.inputNode.outputFormat(forBus: 0) audioEngine.inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in self.recognitionRequest.append(buffer) } try! audioEngine.start() recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in print("resultHandler") print(result?.bestTranscription.formattedString ?? "") if let error = error { print("ERROR!") print(error.localizedDescription) } } DispatchQueue.main.asyncAfter(deadline: .now() + 5.0, execute: { self.recognitionTask?.cancel() self.recognitionTask?.finish() self.audioEngine.stop() print("RESTART") self.start() }) } }
結果としては2時間ほどは音声認識を使う事ができました。
時間がなかったのでそこまでしか検証しなかったのですが、結構長い時間使う事ができそうです。