iOS 26 SpeechAnalyzerのマイク入力:準備・結果処理・問題の切り分け
このページは英語の原文を機械翻訳したものです。 英語の原文を読む
iOS 26でリアルタイムのマイク入力を扱うには、端末・言語の対応を確認し、モデルを準備し、対応するPCM形式を取得し、音声を渡しながら認識結果を受け取ります。最後の言葉まで確定できるよう、正常終了とキャンセルを分けます。このガイドはAppleのAPI資料と現在のSimpleMemoマイク入力サンプルに沿い、別途ダウンロードできる準備用ヘルパーも紹介します。
まず完全なサンプルから始め、次にパイプラインを検査してください
MITライセンスのリポジトリでは、iOS 26のホストアプリ向けに、SpeechSessionをSwiftパッケージとして公開しています。Xcodeのパッケージ依存関係に追加し、SpeechAnalyzerLiveMicプロダクトをリンクします。READMEには、finalizedTextを表示し、volatileTextを別に描画し、セッションの準備中はStartを無効にするSwiftUIビューがあります。
完全なアプリプロジェクトを完成させるには、README の XcodeGen ルートに従ってください。 代わりに、提供されたアプリソースを新しい iOS 26 アプリにコピーしてください。 同じ型を二度定義しないように、統合ルートを1つ使用してください。 このパッケージは、ノートの保存、メール配信、または Obsidian の統合を実装していません。
リンク先のサンプルは、マイクと音声認識の使用許可を求めます。ホストアプリにはNSMicrophoneUsageDescriptionとNSSpeechRecognitionUsageDescriptionが必要で、パッケージを追加するだけではその説明文は用意されません。権限の用途を説明し、拒否・準備中の状態を表示し、いつ録音するかをユーザーに選んでもらいます。以下のヘルパー自身は許可を求めず、マイクも開始しません。
Appleの公式リアルタイム文字起こしサンプルとWWDC25セッションは、構成を考える参考になります。SpeechAnalyzerはモジュールを取りまとめる、対応OS向けに追加されたAPIです。既存のSFSpeechRecognizerコードがすべて不要になったという意味ではありません。
録音前に対応状況・モデル・音声形式を準備する
SpeechTranscriber はランタイムのデバイスおよびロケールチェックを提供します。 iOS のバージョンチェックだけでは不十分です。 以下のヘルパーは、設定された文字起こし器、アナライザー、およびオーディオ形式を返すか、特定の準備エラーをスローします。
完全なSwift準備ヘルパーをダウンロードしてください. フルファイルには、インポート、結果およびエラータイプ、そして以下で説明する DictationTranscriber ヒントヘルパーが含まれています。 これはセットアップコンポーネントであり、完全な録音アプリではありません。
@available(iOS 26.0, *)
@MainActor
public func prepareSpeechAnalyzer(
requestedLocale: Locale,
naturalFormat: AVAudioFormat? = nil
) async throws -> PreparedSpeechAnalyzer {
guard SpeechTranscriber.isAvailable else {
throw SpeechPreparationError.deviceUnavailable
}
guard let locale = await SpeechTranscriber.supportedLocale(
equivalentTo: requestedLocale
) else {
throw SpeechPreparationError.unsupportedLocale
}
let transcriber = SpeechTranscriber(
locale: locale,
preset: .progressiveTranscription
)
// The system may already have the asset; nil means no install is needed.
// A request can automatically reserve the locale or throw if no slot is free.
if let request = try await AssetInventory.assetInstallationRequest(
supporting: [transcriber]
) {
try await request.downloadAndInstall()
}
try Task.checkCancellation()
guard await AssetInventory.status(forModules: [transcriber]) == .installed else {
throw SpeechPreparationError.assetsNotInstalled
}
guard let format = await SpeechAnalyzer.bestAvailableAudioFormat(
compatibleWith: [transcriber], considering: naturalFormat
) else {
throw SpeechPreparationError.noCompatibleFormat
}
let analyzer = SpeechAnalyzer(modules: [transcriber])
try await analyzer.prepareToAnalyze(in: format)
return PreparedSpeechAnalyzer(
locale: locale, transcriber: transcriber,
analyzer: analyzer, audioFormat: format
)
}
AssetInventoryはシステムで共有するモデルを管理します。モデルはOSや別のアプリによってすでに導入されていることがあります。インストール要求は、インストール不要ならnilを返し、必要ならロケールを予約してダウンロードを始められます。予約数の上限によるエラーもあります。予約はアプリの機能全体で管理し、不要になったロケールだけを解放します。ほかの機能が使っているモデルを解放しないようにしてください。
オフラインになる前に、必要なロケールを準備し、準備状況を確認してください。 モデルアセットはアプリバンドルの外部にありますが、依然としてシステムストレージを消費し、時間とともに変化する可能性があります。 ホストUIにダウンロードの進行状況とキャンセルを表示します。 「オンデバイス認識」は「準備やネットワークアクセスが全く必要ない」ことを意味するわけではありません。
アナライザーが受け取れる形式でPCMを渡す
AVAudioEngine microphone tap
-> owned PCM / conversion for the selected format
-> AsyncStream<AnalyzerInput>
-> SpeechAnalyzer + SpeechTranscriber
-> result consumer and UI state
オーディオセッションを有効化した後、入力ノードの実際の形式を確認してください。 デバイスやルートが以前にそのレートを使用したことがあるため、サンプルレートをハードコードしないでください。 適切な場合は、bestAvailableAudioFormat(compatibleWith:considering:) に自然な形式を渡し、そのオプション結果をアンラップしてください。 Appleは、アナライザが入力を透過的に再サンプリングまたは変換しないことを記録しています。
リポジトリの AudioBufferConverter は、既に一致しているバッファを変更せずに返し、形式が異なる場合に AVAudioConverter を使用します。 変換は、すべてのバッファに対して包括的な追加ステップではなく、不一致のフォーマットに対して必要です。 出力フレームの長さを確認し、変換エラーを報告してください;失敗しているバッファをすべて静かにドロップすると、テキストが生成されずに録画UIが正常に見えることがあります。
キューに入れる音声バッファの所有権を明確にします。更新後のサンプルは、形式が一致していても独立したPCM領域を返すため、入力バッファが再利用されても待機中の音声は上書きされません。音声コールバックから渡されたバッファが、コールバック終了後も不変だとは仮定できません。後で使うためにキューへ入れるなら、所有するコピーを作るか、バッファの寿命を管理します。バッファごとのUI更新は音声コールバックの外で行います。本番用途では、待機処理の上限、過負荷、音声経路の変更への対応も決めます。
Swift 6では、ローカル変数をキャプチャしただけでスレッド安全性が保証されるわけではありません。現在のサンプルのコンバーターは単一の処理スレッドを前提にし、一つの変換コールバック用に、ロックで保護した非公開の@unchecked Sendableホルダーを持ちます。すべてのPCMバッファやコンバーターをスレッド間で安全に共有できるという意味ではありません。流用前にこの前提を確認してください。
未確定の結果は置き換え、入力を終えてから確定する
逐次結果を有効にすると、未確定の結果は同じ音声範囲のテキストを更新することがあります。部分結果を毎回新しい文として追加しないでください。サンプルでは、確定済みテキストと置き換え可能な未確定の末尾を分けています。より高度な編集では、音声範囲に対応させて更新し、確定した部分を別に保持します。Appleはこの動きをWWDCセッションの結果処理の説明で紹介しています。
- 通常のStop操作では、新しいマイク音声の生成を止め、tapを1回だけ解除します。
AsyncStreamの入力継続を完了し、アナライザが入力の終了を確認できるようにしてください。- 結果を受け取るタスクを動かしたまま、
finalizeAndFinishThroughEndOfInput()の完了を待ちます。 - 結果の受け取りが完了してから、確定テキストを反映し、セッションのリソースを解放します。
確定処理は、入力の終了と処理完了を待ちます。入力側が終了しないまま呼ぶと、Stopが待ち続けることがあります。結果を受け取るタスクをすぐキャンセルすると、保存したかった末尾を失うこともあります。発話を破棄するCancelは、正常に確定したように扱わず、専用のキャンセル経路を使います。
公開パッケージは学習用サンプルです。現在のstop()は確定処理後に結果受信タスクをキャンセルし、受信完了を別途確認する仕組みは公開していません。すべての確定結果を残すことが重要なら、この終了処理を調整してテストしてください。準備中のStop、連打、画面を閉じる操作、割り込み、エラー後の再開も確認します。
語彙サポートは文字起こしモジュールに依存します
新しいSpeech APIに文脈語彙のサポートがないと言うのは不正確です。 Apple は AnalysisContext.contextualStrings を DictationTranscriber 用に文書化します。 ドキュメントでは、短いフレーズと、タグ全体で最大でも 100 のフレーズを推奨しています。 それは、同じヒントがSpeechTranscriberをカスタマイズするという約束ではありません。
// For an analyzer configured with DictationTranscriber.
let context = AnalysisContext()
context.contextualStrings[.general] = ["Obsidian", "Yurica"]
try await analyzer.setContext(context)
setContext(_:)は現在のコンテキストを置き換えるため、既存の内容を必要に応じて保持・統合します。ダウンロードできるヘルパーは、100件を超えるフレーズを黙って切り捨てず、エラーにします。フレーズのヒントと、DictationTranscriberに記載されているカスタム言語モデルは別の機能で、どちらもすべての名前の正確な認識を保証しません。用途に合うモジュールと言語を選んで試してください。
録音でテキストが生成されない場合は、失敗しているステージを特定してください
| 症状 | 次をご確認ください | 有用な証拠 |
|---|---|---|
| 準備に失敗しました | デバイスサポート、正規化されたロケール、アセットステータス、予約/ダウンロードエラー | OS とデバイス、要求された/選択されたロケール、エラードメインとコード |
| マイクUIがアクティブで、バッファがありません | 許可、オーディオセッションの有効化、ルート、エンジン始動およびタップのライフサイクル | コールバックが届くか、入力のサンプルレートとチャンネル数 |
| バッファが到着しました、テキストはありません | 互換フォーマット、非ゼロコンバータ出力、生成された入力およびアクティブ結果タスク | 入力/出力フレーム数と最初の実際のパイプラインエラー |
| 単語が繰り返されます | 未確定の更新結果を、置き換えずに追加していないか | 結果の音声範囲と、確定・未確定のフラグ |
| 最後の言葉が消えます | 入力の終了と、確定処理中の結果受信タスクの寿命 | 最終入力時刻、最終完了および最終結果の受領 |
| オフライン起動に失敗しました | そのデバイスとロケールで必要なモデルの準備状況 | 切断前のアセット状態および表面化されたセットアップエラー |
問題の調査では、各段階の時間、フレーム数、ロケール、エラー情報から確認します。すべてのログに録音原本や認識テキストが必要なわけではありません。ユーザーから再現情報をもらう場合は、私的な会話ではなく、機密性のない短いテスト文と関連設定を依頼します。
何がチェックされ、デバイスで何を測定すべきか
この記事は、2026年9月9日にAppleの資料とXcode 26.6で確認しました。準備用ヘルパーは、iOS 26シミュレーターを対象に、Swift 6のcomplete strict concurrency設定で型チェックを通過しています。ヘルパーは録音を行わず、この確認だけで認識精度、モデル導入の成功、マイクから結果までの一連の動作を実証したことにはなりません。
公開サンプルのリビジョン1befcceは、9月9日に4件のPCM変換テストとiOSシミュレーターのビルドを通過しました。うち2件は変換後に元のバッファを再利用し、モノラルの浮動小数点出力とインターリーブされたステレオ整数出力が壊れないことを確かめます。どちらも、形式一致時に入力をそのまま通す以前の実装では失敗します。READMEには、パッケージ化後にiOSの実機で再テストしていないと明記されています。このページに以前掲載した遅延の数値は、現在のパッケージのベンチマークではありません。
自分のアプリでは、端末、OS、ロケール、音声経路、モデルの準備状態、計測区間を記録します。最初の未確定テキスト、最初の確定テキスト、正常停止の完了を別々に測ります。モデル準備済みの場合と準備が必要な場合を比較し、機密性のない固定の文を繰り返します。速さだけでなく、末尾の言葉の欠落や重複も確認してください。一つの時間計測からNeural Engineの動作やハードウェアの限界を推定しないでください。
Apple Watchでは、アプリに適合する場合は別のシステムテキスト入力フローを使用してください。TextFieldLinkはシステムインターフェースを開きますが、ディクテーションがすでに録音されていることは確立されません。 SpeechAnalyzer は watchOS では利用できず、iOS パッケージは Watch マイクロフォンの実装ではありません。
よくある質問
SpeechAnalyzerはオフラインで動作しますか?
SpeechTranscriberは端末内で音声を処理しますが、必要なモデルが使える状態であることが条件です。モデルはアプリの初回起動前から存在する場合もあります。オフラインで使う前に確認・準備し、「初回は必ずダウンロードが必要」とも「一度導入すれば常に使える」とも仮定しないでください。
カスタム語彙を追加できますか?
Apple は DictationTranscriber 用に AnalysisContext.contextualStrings を文書化しており、そのモジュールはカスタマイズされた言語のコンテンツヒントも公開しています。 これはSpeechTranscriberの同じ機能のドキュメントではありません。 モジュールを慎重に選択し、必要な単語とロケールをテストしてください。
録音しているのにテキストが出ないのはなぜですか?
端末・言語の対応、モデルの状態、マイク入力、Optionalの解析用形式、変換結果、入力シーケンス、結果受信処理を確認します。空の結果をすべて音声形式の問題と決めつけず、実際のエラーを表示してください。
停止時に最後の言葉を保持するにはどうすればよいですか?
音声の生成を止め、入力continuationを終了し、アナライザーが入力末尾まで確定するのを待ちます。確定結果を受け取れるよう、結果受信タスクを維持してください。キャンセルは発話を破棄する別の経路で、正常終了の代わりにはなりません。
このサンプルはApple Watchで動作しますか?
いいえ。 リンクされたマイクロフォンセッションは iOS 26 の例であり、SpeechAnalyzer は watchOS では利用できません。 Watch アプリは、TextFieldLink のようなシステムテキスト入力を使用できます。これはシステム入力インターフェースを提示しますが、音声入力の録音が開始されたことを証明するものではありません。
サンプルに対して何が検証されましたか?
リンク先のリポジトリには、2026年9月9日の4件の変換テストとiOSシミュレーターのビルドが記録されています。元バッファの再利用に関する2件の回帰テストも含みます。実機で再テストしたとはしていません。このガイドの準備用ヘルパーは、9月9日にXcode 26.6とSwift 6のcomplete strict concurrency設定で型チェックしました。これらの確認は、マイクの認識精度、モデルのダウンロード動作、遅延を実証するものではありません。
これがSimple Memoとどのように関係しているか
このガイドと無料サンプルはSimpleMemoの開発者が管理しています。製品としての使い方は音声入力ガイドで説明しています。サンプル自体はメールを送らず、Obsidianにも保存しません。Simple Memoのメール送信には宛先設定と確認が必要で、Obsidianへの保存には連携と保存先の設定が必要です。端末内の音声認識という説明だけでは、その後アプリが行う処理のプライバシーや配送の動作までは分かりません。
関連: オフライン優先メモアーキテクチャ 開発者の次のステップは、テストフレーズを使用して完全なサンプルを実行し、サポートする予定のデバイスとロケールで準備、結果、停止を確認することです。