speech-recognition について
Speech フレームワークで音声認識機能を実装する Apple アプリ開発者向けのガイドです。ライブ音声やファイルの文字起こし、SpeechAnalyzer の新しい処理フローと SFSpeechRecognizer の代替手段を扱います。
主な機能
- 01マイク音声のリアルタイム文字起こし
- 02音声ファイルの文字起こし
- 03iOS 26+ の SpeechAnalyzer フロー
- 04SFSpeechRecognizer の代替指針
- 05途中・確定・候補結果の処理
- 06オンデバイス認識の対応確認
活用シーン
- 01録音中の文字起こし表示
- 02保存済み音声の文字起こし
- 03iOS 26+ での長時間音声の文字起こし
- 04オンデバイス認識とネットワーク認識の選択
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill speech-recognitionインストール後、Claude Code で/speech-recognitionを入力して読み込まれたか確認します。
同梱ファイルevals/evals.jsonreferences/speechanalyzer-patterns.md
詳細
音声認識
Apple の Speech フレームワークを使用して、ライブ音声や録音済み音声をテキストに書き起こします。
SpeechAnalyzer / SpeechTranscriber(iOS 26 以降)と
SFSpeechRecognizer(iOS 10 以降)のフォールバック方法を扱います。
対象範囲: 音声からテキストへの認識、音声認識の許可、マイク音声の取り込み処理、結果の処理には、このスキルを使用してください。テキスト分析、書き起こし後の言語識別、感情分析、埋め込み表現、翻訳は natural-language に引き継いでください。音声再生 UI は avkit に引き継いでください。書き起こしの要約や生成は apple-on-device-ai に引き継いでください。
目次
- SpeechAnalyzer の方針(iOS 26 以降)
- SFSpeechRecognizer の設定
- 許可
- マイク音声のライブ書き起こし
- 録音済み音声ファイルの認識
- オンデバイス認識とサーバー認識
- 結果の処理
- よくある間違い
- 確認チェックリスト
- 参考資料
SpeechAnalyzer の方針(iOS 26 以降)
最新の iOS 26 以降での音声分析には SpeechAnalyzer を使用してください。特に、長時間の録音、ライブ書き起こし、時間インデックス付きの書き起こし、完全なオンデバイス処理に適しています。iOS 10 以降を対象とする場合、サーバー対応のロケールが必要な場合、または既存のコールバック/デリゲート実装がある場合は、SFSpeechRecognizer を使用してください。
iOS 26 以降の書き起こしパイプライン、モデルアセットの処理、揮発性の結果、またはファイル/バッファーの例を実装する場合は、SpeechAnalyzer のパターンをお読みください。
SpeechAnalyzer の設定チェックリスト
- モジュールを選択します。
- 新しい汎用オンデバイスモデルには
SpeechTranscriberを使用します。 - 現在のデバイスまたはロケールで
SpeechTranscriberを利用できず、ディクテーション互換のサポートで問題ない場合は、DictationTranscriberを使用します。 - 音声アクティビティ検出による精度と消費電力のトレードオフを許容できる場合に限り、書き起こしモジュールと併せて
SpeechDetectorを使用します。
- 新しい汎用オンデバイスモデルには
- セッションを作成する前に、サポート状況を確認します。
SpeechTranscriber.isAvailableSpeechTranscriber.supportedLocale(equivalentTo:)- 言語の選択肢を表示する場合は
SpeechTranscriber.installedLocales/supportedLocales
- ドキュメントに記載されたプリセットを選択します。
- 基本的で精度の高い書き起こしには
.transcriptionを使用します。 - ライブ UI の更新には
.progressiveTranscriptionを使用します。 - 再生位置のハイライトに
audioTimeRangeが必要な場合は.timeIndexedProgressiveTranscriptionを使用します。
- 基本的で精度の高い書き起こしには
AssetInventory.assetInstallationRequestを使用して、必要なアセットをインストールします。- ライブ音声バッファーを
SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith:)に変換してから、AnalyzerInputを渡します。 - 別のタスクで、モジュールの結果を
AsyncSequenceから取得します。 finalizeAndFinish(through:)、finalizeAndFinishThroughEndOfInput()、またはcancelAndFinishNow()を明示的に呼び出して終了します。
Apple がドキュメント化していないため、offlineTranscription プリセットは使用しないでください。
AsyncStream の入力シーケンスを終了しても、アナライザーのセッションは終了しません。
SFSpeechRecognizer の設定
ロケールを指定して認識器を作成する
import Speech
// Default locale (user's current language)
let recognizer = SFSpeechRecognizer()
// Specific locale
let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))
// Check if recognition is available for this locale
guard let recognizer, recognizer.isAvailable else {
print("Speech recognition not available")
return
}
利用可能状況の変化を監視する
final class SpeechManager: NSObject, SFSpeechRecognizerDelegate {
private let recognizer = SFSpeechRecognizer()!
override init() {
super.init()
recognizer.delegate = self
}
func speechRecognizer(
_ speechRecognizer: SFSpeechRecognizer,
availabilityDidChange available: Bool
) {
// Update UI — disable record button when unavailable
}
}
許可
ライブ書き起こしを開始する前に、音声認識とマイクの両方の許可をリクエストしてください。Info.plist に次のキーを追加します。
NSSpeechRecognitionUsageDescriptionNSMicrophoneUsageDescription
import Speech
import AVFoundation
func requestPermissions() async -> Bool {
let speechStatus = await withCheckedContinuation { continuation in
SFSpeechRecognizer.requestAuthorization { status in
continuation.resume(returning: status)
}
}
guard speechStatus == .authorized else { return false }
let micStatus: Bool
if #available(iOS 17, *) {
micStatus = await AVAudioApplication.requestRecordPermission()
} else {
micStatus = await withCheckedContinuation { continuation in
AVAudioSession.sharedInstance().requestRecordPermission { granted in
continuation.resume(returning: granted)
}
}
}
return micStatus
}
マイク音声のライブ書き起こし
標準的なパターン:AVAudioEngine でマイク音声を取り込み、バッファーを
SFSpeechAudioBufferRecognitionRequest に追加し、結果を順次受け取ります。
import Speech
import AVFoundation
final class LiveTranscriber {
private let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))!
private let audioEngine = AVAudioEngine()
private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
private var recognitionTask: SFSpeechRecognitionTask?
func startTranscribing() throws {
// Cancel any in-progress task
recognitionTask?.cancel()
recognitionTask = nil
// Configure audio session
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
// Create request
let request = SFSpeechAudioBufferRecognitionRequest()
request.shouldReportPartialResults = true
self.recognitionRequest = request
// Start recognition task
recognitionTask = recognizer.recognitionTask(with: request) { result, error in
if let result {
let text = result.bestTranscription.formattedString
print("Transcription: \(text)")
if result.isFinal {
self.stopTranscribing()
}
}
if let error {
print("Recognition error: \(error)")
self.stopTranscribing()
}
}
// Install audio tap
let inputNode = audioEngine.inputNode
let recordingFormat = inputNode.outputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) {
buffer, _ in
request.append(buffer)
}
audioEngine.prepare()
try audioEngine.start()
}
func stopTranscribing() {
audioEngine.stop()
audioEngine.inputNode.removeTap(onBus: 0)
recognitionRequest?.endAudio()
recognitionRequest = nil
recognitionTask?.cancel()
recognitionTask = nil
}
}
録音済み音声ファイルの認識
ディスク上の音声ファイルには SFSpeechURLRecognitionRequest を使用します。
func transcribeFile(at url: URL) async throws -> String {
guard let recognizer = SFSpeechRecognizer(), recognizer.isAvailable else {
throw SpeechError.unavailable
}
let request = SFSpeechURLRecognitionRequest(url: url)
request.shouldReportPartialResults = false
return try await withCheckedThrowingContinuation { continuation in
var didResume = false
recognizer.recognitionTask(with: request) { result, error in
guard !didResume else { return }
if let error {
didResume = true
continuation.resume(throwing: error)
} else if let result, result.isFinal {
didResume = true
continuation.resume(
returning: result.bestTranscription.formattedString
)
}
}
}
}
デバイス上での認識とサーバーでの認識
SFSpeechRecognizer は、iOS 13+ で対応しているロケールの場合、デバイス上での認識を使用できます。supportsOnDeviceRecognition が false の場合、認識にはネットワーク接続が必要です。requiresOnDeviceRecognition は、認識がデバイス上での認識に対応している場合にのみ効果があります。
let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))!
// Check if on-device is supported for this locale
if recognizer.supportsOnDeviceRecognition {
let request = SFSpeechAudioBufferRecognitionRequest()
request.requiresOnDeviceRecognition = true // Force on-device
}
SFSpeechRecognizer のリクエストは、長時間の録音には適さない場合があります。Apple は、音声認識におけるタスクの制限時間をおよそ 1 分とし、ほかにもサービスの制限があることを説明しています。iOS 26+ で長時間録音を行う場合は、SpeechAnalyzer を使用してください。それ以外の場合は、制限に達する前に認識を分割または再開し、タスクをまたいで文字起こしの状態を保持してください。
結果の処理
部分結果と確定結果
shouldReportPartialResults を指定した場合は、SFSpeechRecognitionResult.isFinal によって確定されるまで、表示中の部分的な文字起こしを置き換えてください。これは SpeechTranscriber.Result.isFinal とは別のもので、後者では、揮発性の属性付き範囲を、その範囲の確定結果で置き換える必要があります。ライブの例とreferences/speechanalyzer-patterns.mdに標準的なループが記載されています。
代替の文字起こし候補と信頼度へのアクセス
recognizer.recognitionTask(with: request) { result, error in
guard let result else { return }
// Best transcription
let best = result.bestTranscription
// All alternatives (sorted by confidence, descending)
for transcription in result.transcriptions {
for segment in transcription.segments {
print("\(segment.substring): \(segment.confidence)")
}
}
}
句読点の追加(iOS 16+)
let request = SFSpeechAudioBufferRecognitionRequest()
request.addsPunctuation = true
コンテキスト文字列
特定の分野に固有の用語の認識精度を高めます。
let request = SFSpeechAudioBufferRecognitionRequest()
request.contextualStrings = ["SwiftUI", "Xcode", "CloudKit"]
よくある間違い
| 間違い | 修正方法 |
|---|---|
| ライブ音声のリクエストで音声認識の許可のみを求める | 音声認識とマイクの両方の許可を求めます。 |
| 認識の利用可能性を一度だけ確認する | デリゲートの利用可能性の変更を監視し、サービスが利用できなくなった場合に対応します。 |
| 最終処理、エラー処理、ロールオーバーで異なるクリーンアップを行う | エンジンの停止、タップの削除、endAudio、タスクのキャンセルを行う単一の stopTranscribing() に処理を集約します。 |
| すべてのロケールでデバイス上モードを強制する | supportsOnDeviceRecognition を確認し、フォールバックを用意します。 |
長時間録音に単一の SFSpeechRecognizer タスクを使用する | iOS 26+ では SpeechAnalyzer を優先するか、確定済みの文字起こしを保持しながら、長さを制限したセグメントに分割します。 |
| アナライザーへの入力を終了すればセッションも完了すると見なす | 最後のサンプルまで明示的に確定するか、キャンセルして終了します。 |
| 揮発性の SpeechAnalyzer 結果を追記する | 最終結果で確定されるまで、揮発性の範囲を置き換えます。 |
| 1 つ目の認識タスクが終了する前に 2 つ目を開始する | アクティブなタスクをキャンセルまたは終了し、置き換える前にクリーンアップを完了します。 |
アナライザーの終了処理と揮発性結果のコード全体については、references/speechanalyzer-patterns.mdを読み込んでください。
レビューチェックリスト
-
NSSpeechRecognitionUsageDescriptionが Info.plist に設定されている - ライブ音声を使用する場合、
NSMicrophoneUsageDescriptionが Info.plist に設定されている - 認識を開始する前に許可を求めている
-
SFSpeechRecognizerDelegateを設定し、availabilityDidChangeを処理している - 認識の終了時にオーディオエンジンを停止し、タップを削除している
- 録音の完了時に
recognitionRequest.endAudio()を呼び出している - 新しい認識タスクを開始する前に、前の
recognitionTaskをキャンセルしている - デバイス上モードを必須にする前に
supportsOnDeviceRecognitionを確認している - 部分結果を確定結果(
isFinal)とは別に処理している -
SFSpeechRecognizerの 1 分間の制限とサービスの制限を考慮している - iOS 26+:
SpeechAnalyzerを使用する前にAssetInventoryのアセットをインストールしている - iOS 26+:
SpeechTranscriber.isAvailableとロケールの対応状況を確認している - iOS 26+:ライブバッファーをアナライザーに対応した形式に変換している
- iOS 26+:アナライザーセッションを明示的に終了またはキャンセルしている
- iOS 26+:揮発性の結果を重複させず、確定結果で置き換えている
参考資料
- Speech フレームワーク
- SpeechAnalyzer
- SpeechTranscriber
- SpeechTranscriber.Preset
- DictationTranscriber
- SpeechDetector
- SFSpeechRecognizer
- SFSpeechAudioBufferRecognitionRequest
- SFSpeechURLRecognitionRequest
- SFSpeechRecognitionResult
- SFSpeechRecognitionRequest
- AssetInventory
- 音声認識の使用許可を求める
- ライブオーディオで音声を認識する
- SpeechAnalyzerで高度な音声テキスト変換をアプリに導入する