見出し画像

iOSアプリ開発 入門 (2) - SFSpeechRecognizer

iOSアプリでの「SFSpeechRecognizer」による「音声認識」の実装方法をまとめました。

・iOS14

前回

1. SFSpeechRecognizer

iOSアプリで「音声認識」を実装するには、「SFSpeechRecognizer」を使います。

2. サーバー版とオンデバイス版の違い

サーバー版とオンデバイス版には、次のような違いがあります。精度ならサーバー版、制限回避やプライバシーならオンデバイス版になります。

画像4

3. UI

今回は、「UILabel」と「UIButton」を1つずつ配置します。

画像1

4. Info.plist

「Info.plist」に以下の項目を設定します。

・Private - Speech Recognition Usage Description : 音声認識の用途の説明。
・Private - Microphone Usabe Description : マイクの用途の説明。

5. コード

コードは、次のとおりです。

import UIKit
import Speech
import AVFoundation

// ViewController
class ViewController: UIViewController {
    // UI
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var button: UIButton!

    // 音声認識
    let recognizer = SFSpeechRecognizer(locale: Locale.init(identifier: "ja_JP"))!
    let audioEngine = AVAudioEngine()
    let recognitionReq = SFSpeechAudioBufferRecognitionRequest()
    var recognitionTask: SFSpeechRecognitionTask!
    var recording: Bool = false

    // ビューのロード時に呼ばれる
    override func viewDidLoad() {
        super.viewDidLoad()
       
        // UI
        self.recording = false
        self.label.text = ""
        self.button.isEnabled = false
        self.button.setTitle("音声認識の開始", for: .normal)
    }
   
    // ビューの表示時に呼ばれる
    override func viewDidAppear(_ animated: Bool) {
        // 音声認識の利用の承認要求
        SFSpeechRecognizer.requestAuthorization {(authStatus) in
            DispatchQueue.main.async {
                // 承認かつ利用可能時
                if authStatus == SFSpeechRecognizerAuthorizationStatus.authorized &&
                    self.recognizer.isAvailable {

                    // オーディオセッションの準備
                    self.setupAudioSession()
        
                    // UI
                     self.button.isEnabled = true
                }
             }
        }
    }
   
    // オーディオセッションの準備
    func setupAudioSession() {
        do {
            // 音声認識の設定
            if self.recognizer.supportsOnDeviceRecognition {
                self.recognitionReq.requiresOnDeviceRecognition = true // オンデバイス音声認識
            }
            self.recognitionReq.shouldReportPartialResults = true // 中間結果の取得

            // オーディオセッションの準備
            let audioSession = AVAudioSession.sharedInstance()
            try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
            try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
        } catch {
            print(error.localizedDescription)
        }
    }
  
    // ボタンクリック時に呼ばれる
    @IBAction func onButtonClick(_ sender: UIButton) {
        // 音声認識の開始
        if (!self.recording) {
            // UI
            self.recording = true
            self.button.setTitle("音声認識の停止", for: .normal)
            self.label.text = ""

            // 音声認識の開始
            self.startSpeechRecognition()
        }
        // 音声認識の停止
        else {
            //UI
            self.recording = false
            self.button.setTitle("音声認識の開始", for: .normal)
           
            // 音声認識の停止
            self.stopSpeechRecognition()
        }
    }
  
    // 音声認識の開始
    func startSpeechRecognition() {
        do {
            // 音声認識タスクの停止
            if (self.recognitionTask != nil) {
                self.recognitionTask.cancel()
                self.recognitionTask.finish()
                self.recognitionTask = nil
            }
            
            // 入力ノードの生成
           let inputNode = self.audioEngine.inputNode
           let recordingFormat = inputNode.outputFormat(forBus: 0)
           inputNode.installTap(onBus: 0, bufferSize: 2048, format: recordingFormat) {(buffer, time) in
               self.recognitionReq.append(buffer)
           }            
            
            // 音声認識の開始
            self.audioEngine.prepare()
            try self.audioEngine.start()
            self.recognitionTask = self.recognizer.recognitionTask(
                with: recognitionReq, resultHandler: {(result, error) in
                if let error = error {
                    print("\(error)")
                } else {
                    DispatchQueue.main.async {
                        self.label.text = result!.bestTranscription.formattedString
                    }
                }
            })
        } catch {
            print(error.localizedDescription)
        }
    }
 
    // 音声認識の停止
    func stopSpeechRecognition() {
        self.audioEngine.stop()
        self.audioEngine.inputNode.removeTap(onBus: 0)
        self.recognitionReq.endAudio()
    }
}

画像3

6. 音声認識が利用可能かどうかの確認

音声認識は、基本的にネットワークを使うので、利用できない場合もあります。

◎ 音声認識が利用可能かどうかの確認

var isAvailable: Bool

◎ 音声認識が利用可能かどうかの変更受信

var delegate: SFSpeechRecognizerDelegate?

受信側には「SFSpeechRecognizerDelegate」を実装します。

func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
    // 音声認識が利用可能かどうかの変更時の処理
}

​7. 音声認識のリクエストの設定

SFSpeechAudioBufferRecognitionRequest」で設定可能です。

◎ 中間結果の取得
音声認識中の中間結果を返すかどうかを指定します。

self.recognitionReq.shouldReportPartialResults = true

◎ オンデバイス音声認識
ネットワークを利用せず、オンデバイスのみで音声認識を行います。

if self.recognizer.supportsOnDeviceRecognition {
    self.recognitionReq.requiresOnDeviceRecognition = true
}

◎ 語彙の追加
音声認識リクエストの語彙を追加することで、システムに含まれていない語彙を認識させやすくなります(体感的では変わってなさそう)。

self.recognitionReq.contextualStrings = ["ヒカキン"] // 語彙の追加

8. レスポンスの情報

self.recognizer.recognitionTask()のコールバックで返ってくる「SFSpeechRecognitionResult」には、以下の情報が含まれています。

◎ 信頼度が最も高い文字起こし

var bestTranscription: SFTranscription

SFTranscription」が持つ情報は、次のとおりです。

var formattedString: String - 文字起こし
var segments: [SFTranscriptionSegment] - セグメント情報(信頼度など)

◎ 信頼度高い順にソートされた文字起こし配列

var transcriptions: [SFTranscription]

◎ 音声認識が完了したかどうか

var isFinal: Bool

◎ メタデータ

var speechRecognitionMetadata: SFSpeechRecognitionMetadata?

SFSpeechRecognitionMetadata」が持つ情報は、次のとおりです。

var averagePauseDuration: TimeInterval
var speakingRate: Double
var speechDuration: TimeInterval
var speechStartTimestamp: TimeInterval
var voiceAnalytics: SFVoiceAnalytics?

9. 参考

次回


この記事が気に入ったらサポートをしてみませんか?