Xcode
iOS
WWDC
Swift
ios10

【iOS】Speech Frameworkの実装

More than 1 year has passed since last update.

先日社内のWWDCキャッチアップイベントで調査した、
Speech Frameworkについてまとめます。

詳細な処理に関してはAppleのSampleCodeを参考ください。
Using Speech Recognition with AVAudioEngine

流れ

  1. plistの設定
  2. フレームワークのインポート
  3. SFSpeechRecognizerの作成
  4. ユーザー承認
  5. リクエストの作成
  6. リクエストの開始

plistの設定

plistに以下の項目を加えます。
NSSpeechRecognitionUsageDescription

マイクを利用する場合は次の項目も加えます。
Privacy - Microphone Usage Description

フレームワークのインポート

関連クラスはすべてSpeechフレームワークに入っています。

import Speech

SFSpeechRecognizerの作成

ロケールを指定してSFSpeechRecognizerを作成します。
指定しない場合はユーザーのロケールを使用します。
対応している言語は以下を参照ください。
【iOS 10】Speechフレームワークで音声認識 - 対応言語リスト付き

let speechRecognizer = SFSpeechRecognizer(locale: Locale(localeIdentifier: "en_US"))!

ユーザー承認

SFSpeechRecognizerのrequestAuthorization(_:)メソッドを呼び出し、
ユーザーに許可を求めます。
ハンドラーの引数には認証の状態が入って来るので、
場合に応じて処理します。

SFSpeechRecognizer.requestAuthorization { [weak self] authStatus in
    // some operation
}

リクエストの作成

SFSpeechRecognitionRequestオブジェクトを作成します。
実際には以下の二つのどちらかです。

  • SFSpeechAudioBufferRecognitionRequest(マイク等のオーディオバッファを利用)
  • SFSpeechURLRecognitionRequest(オーディオファイルを利用)

マイクを利用する場合

SFSpeechAudioBufferRecognitionRequestを使用します。

recognitionRequest = SFSpeechAudioBufferRecognitionRequest()

オーディオファイルを使用する場合

SFSpeechURLRecognitionRequestを使用します。
生成時にファイルのパスを指定します。

let audioPath = Bundle.main().pathForResource("steve", ofType: "aiff")!
recognitionRequest = SFSpeechURLRecognitionRequest(url: URL(fileURLWithPath: audioPath))

オーディオファイルの作成は以下を参考にしました。
Macのsayコマンドの使い方

リクエストの開始

はじめに作成したSFSpeechRecognizerに、
先ほど作成したSFSpeechRecognitionRequestを投げると同時に、
結果のコールバックの設定をします。

コールバックの設定には以下の二つの方法があります。

  • recognitionTask(with:resultHandler:)による、クロージャーでの登録
  • recognitionTask(with:delegate:)による、デリゲートでの登録

デリゲートの方が状況に応じて呼ばれるメソッドが分かれているので、
詳細に処理することができます。

クロージャーの場合

SFSpeechRecognitionResult?型のオブジェクトが渡されます。
このオブジェクトのbestTranscription.formattedStringにアクセスし、
文字列を受け取ります。

また、
isFinalには終了かどうかの状態が入っているので、
場合に応じて処理します。

recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { result, error in
    var isFinal = false

    if let result = result {

        let text = result.bestTranscription.formattedString
        isFinal = result.isFinal
    }

    if isFinal {
        // 終了時の処理
    }
}

デリゲートの場合

SFSpeechRecognitionTaskDelegateを実装することで結果を受け取ることができます。

受け取るメソッドはspeechRecognitionTask(_:didHypothesizeTranscription:)です。
これは、中間結果を受け取るメソッドです。
先ほどのSFSpeechRecognitionResultのbestTranscriptionと同じ、
SFTranscription型の引数を受け取るので、formattedStringを取り出します。

func speechRecognitionTask(_ task: SFSpeechRecognitionTask, didHypothesizeTranscription transcription: SFTranscription) {

    let text = transcription.formattedString
}

デリゲートにはこの他、

  • 終了時に呼ばれる、speechRecognitionTask(_:didFinishSuccessfully:)メソッド
  • 処理のキャンセル時に呼ばれる、speechRecognitionTaskWasCancelled(_:)メソッド

など合計7つのメソッドがあります。

その他の処理

オーディオファイルを利用する場合は以上です。
マイクを利用する場合はマイクからの入力を受け取る処理が必要となります。
この処理にはAVAudioEngineクラスを使用します。

AVAudioEngineの作成

let audioEngine = AVAudioEngine()

入力を受けとった際の処理を登録

入力を受け取った際にSFAudioBufferRecognitionRequestに対して、
受け取ったバッファを追加します。

guard let inputNode = audioEngine.inputNode else {
    fatalError("Audio Engine has no input node")
}

let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { [weak self] (buffer: AVAudioPCMBuffer, when: AVAudioTime) in

    self?.recognitionRequest?.append(buffer)
}

収録の開始、終了

収録の開始は

audioEngine.prepare()
try audioEngine.start()

終了は

audioEngine.stop()
recognitionRequest?.endAudio()

制限

Speech Frameworkに関する制限は以下の記事が参考になりました。
【iOS10】Speech Recognition API(音声認識API)の制約まとめ

最後に

想像以上にお手軽に実装できました。

参考

大変お世話になりました。ありがとうございます。

Using Speech Recognition with AVAudioEngine
【iOS 10】Speechフレームワークで音声認識 - 対応言語リスト付き
【iOS10】Speech Recognition API(音声認識API)の制約まとめ
Macのsayコマンドの使い方