Speechフレームワーク
Speech
Apple公式のサンプルコード
音声をマイクから認識して文字列に変換するという機能を提供してくれています。
標準のキーボードのマイクボタンを押した時と同じ機能をアプリ上から利用できます。
処理の流れ
- 音声認識機能利用の権限の確認、取得
- リクエスト作成、設定
- タスク作成
- マイクインプット作成、設定
- 録音開始
- テキスト変換結果の取得
カスタムクラス実装
以下のクラスを実装していきます。
SpeechRecognizerクラス
- Speechフレームワークを利用するクラス
ListenViewModelクラス
- SpeechRecognizerクラスを保持
ListenViewクラス
- ListenViewModelを保持
事前準備
info.plistに以下を追加します。
Privacy - Microphone Usage Description
Privacy - Speech Recognition Usage Description
コード
SpeechRecognizer.swift
- パーミッション取得
- 音声認識開始
- 認識結果テキスト取得
SpeechRecognizerは
Apple公式のサンプルコード
のSpeechFramework利用部分を切り出しているだけなのでほぼ内容は同じになっているのでここでは省略しますが、少し変更している部分だけ紹介します。
// ...
/// 音声認識結果のSubject,Publisher
private let speechResultSubject: PassthroughSubject<String, Never> = .init()
lazy var speechResult: AnyPublisher<String, Never> = {
speechResultSubject.eraseToAnyPublisher()
}()
/// 録音開始可能判定のSubject,Publisher
private let recordEnabledSubject: PassthroughSubject<Bool, Never> = .init()
lazy var recordEnabled: AnyPublisher<Bool, Never> = {
recordEnabledSubject.eraseToAnyPublisher()
}()
// ...
// ...
if let result = result {
// 認識結果取得
self?.speechResultSubject.send(result.bestTranscription.formattedString)
// ...
認識結果テキストと録音可能判定の部分をCombineのSubjectとPublisherで切り出して外から値を取得できるようにしています。
// ...
// 認識結果を部分的に取得するようにするか
recognitionRequest.shouldReportPartialResults = true
// 音声認識処理に通信を利用しないようにするか
recognitionRequest.requiresOnDeviceRecognition = true
// ...
SFSpeechAudioBufferRecognitionRequestのプロパティで設定を入れています。
音声認識の部分的な更新の取得と、通信の利用をしないようにしています。
ただ、通信を利用した方が精度は高くなり、他言語への対応もできます。
ListenViewModel.swift
- SpeechRecognizerクラスを保持
- 認識結果テキストデータを保持
- Viewへの橋渡し
class ListenViewModel: ObservableObject {
private var speechRecognizer = SpeechRecognizer()
@Published var recordButtonEnabled: Bool = false
@Published var speechResult: String = ""
private var cancellables: Set<AnyCancellable> = .init()
deinit {
cancellables.forEach { cancellable in
cancellable.cancel()
}
}
init() {
speechRecognizer.recordEnabled
.receive(on: DispatchQueue.main)
.sink { [weak self] in
self?.recordButtonEnabled = $0
}
.store(in: &cancellables)
speechRecognizer.speechResult
.receive(on: DispatchQueue.main)
.sink { [weak self] in
self?.speechResult = $0
}
.store(in: &cancellables)
}
/// 音声認識権限確認、取得
func authorizeRecord() {
speechRecognizer.authorizeRecord()
}
/// 録音開始
func tapRecordButton() {
do {
try speechRecognizer.startRecording()
} catch {
speechResult = "\(error)"
}
}
}
ListenView.swift
- ListenViewModelを保持
- 表示開始時にViewModelのパーミッション取得を呼び出し
- 録音開始ボタン
- 認識状態、結果に合わせてボタン、テキストUIの表示更新
struct ListenView: View {
@ObservedObject var viewModel: ListenViewModel
init(viewModel: ListenViewModel = ListenViewModel()) {
self.viewModel = viewModel
}
var body: some View {
VStack {
Text(viewModel.speechResult)
Spacer()
Button("Start") {
viewModel.tapRecordButton()
}
.disabled(!viewModel.recordButtonEnabled)
}
.onAppear {
viewModel.authorizeRecord()
}
}
}
試してみる
喋る内容
アイオーエスのスピーチフレームワークをつかってリアルタイムおんせいにんしき
かっこスピーチトゥーテキストかっことじるをじっそうしてみましたまる
かいぎょうあいふぉんてんアイパッドてんえあぽっど
結果
まとめ
試してみたところ、音声認識の精度やスピードは端末の標準で利用できるものと同等のものを使うことができるようです。
コード自体も複雑なことをしなくても気軽に実装できて良かったです。
サンプルコードではテキストに出力する、というキーボード的な用途で利用しましたが、それはキーボードを使えば良いというだけのことですので、このフレームワークの本質的な使い道は例えば音声コマンド入力とか手を使えない状態でアプリを利用できるようにする(次のページという言葉を聞き取ったら次のページに遷移、下にスクロールという言葉を聞き取ったら下にスクロールする処理をする)ことだと思います。
新しいアプリのUXを提供できるという点でこのフレームワークをしっかりと習得できると今後プラスになるかなと思いました。