こんにちは、皆さん。
会議やメモの文字起こしがMacの中だけで動けば、音声を外部サービスへ送らずに済みます。では、日本語を話しながら表示する用途で、精度と待ち時間はどの程度なのでしょうか。
さて、今日はAppleのSpeechAnalyzerへ日本語音声を少しずつ入力し、認識精度と途中結果の遅延を測ります。ブラウザのマイクから試せるローカルツールも作りました。
先に結果を書くと、14.171秒の合成会話では、2つの設定を各3回試して 文字誤り率(CER)はすべて0% でした。一方、逐次表示では最初の文字が届くまで約1.09秒、途中結果の配送遅延はp95で約1.01秒でした。内容は正確でしたが、話した直後に文字が出る速さではありません。
SpeechAnalyzerとは
SpeechAnalyzerは、Appleが2025年6月のWWDC25で発表した、音声を文字へ変換するための新しいAPIです。iOS 26、macOS 26などの世代で導入されました。
文字起こしを担当するSpeechTranscriberは、一般的な会話向けの新しい音声認識モデルを使います。Appleは、従来モデルより高速で柔軟になり、会議や講義のような長い音声や、マイクから離れた話者にも向くと説明しています。処理は端末上で完結します。
このモデルは、重みをダウンロードして自由に再配布するOSSモデルではありません。AssetInventoryがAppleのサーバーから必要なモデルを取得し、OSが保存と更新を管理します。モデル単体のオープンソースライセンスは提示されておらず、Speech frameworkとモデルの利用にはAppleのOS、Xcode、SDKなどの適用される契約が関係します。今回作成したlabのコード自体はMIT Licenseです。
役割とデータの流れ
今回は複数の認識モデルを組み合わせていません。認識に使うのは、ja_JP向けのSpeechTranscriberモデル1つです。周辺の処理を含む流れは次のとおりです。
ブラウザのマイク(通常48 kHz Float32)
-> AudioWorklet
-> WebSocket(localhost)
-> Python bridge(16 kHzへ変換)
-> Swift CLI(Int16 PCMへ変換)
-> SpeechAnalyzer + SpeechTranscriber
-> 途中結果 / 確定結果
-> ブラウザへ表示
PCMは、音の波を数値として並べた基本的な音声形式です。Python bridgeとSwift CLIは形式を整える役割で、音声認識はしません。音声はlocalhost内だけを流れ、外部サービスへ送信も保存もしません。
今回検証する内容
確認するのは次の3点です。
- 既知の日本語会話を正しく文字にできるか
- 途中結果を返す設定で、最初の表示と更新に何秒かかるか
- ブラウザのマイクから端末内のSpeechAnalyzerまで接続できるか
完全なコードとJSON形式の測定結果は、kiarina/labsのapple-speech-analyzer-streaming-asr labで公開しています。
検証環境の再現
Apple Silicon Mac、macOS 26以降、Xcode Command Line Tools、mise、uv、FFmpegが必要です。次のコマンドは共有音声の取得、test、release build、3回ずつの測定まで実行します。
git clone --depth 1 --filter=blob:none --sparse \
https://github.com/kiarina/labs.git
cd labs
git sparse-checkout set .gitignore .mise/tasks Makefile mise.toml \
2026/07/20/apple-speech-analyzer-streaming-asr
mise -C 2026/07/20/apple-speech-analyzer-streaming-asr run
ブラウザからライブマイクを試す場合は、次を実行します。
mise -C 2026/07/20/apple-speech-analyzer-streaming-asr run demo
途中結果はライム色、確定した文字は白色で表示します。ローカルサーバーには認証がないため、外部へ公開せず127.0.0.1のまま使ってください。
検証条件
2話者による14.171秒の合成会話を、100 msずつ実時間と同じ間隔で入力しました。比較した設定は次の2つです。
-
transcription: 正確さを優先し、確定結果を返す -
progressiveTranscription: 認識途中の累積文字列も返す
主な環境はMacBook Pro、Apple M1 Max、64 GB、macOS 26.5.2、Swift 6.3.3です。日本語localeはja_JP、入力は16 kHz・mono・Int16 PCMです。
正解との違いはCERで測りました。CERは、正解に対して何文字の置換・削除・追加が必要かを表す割合です。今回は空白、改行、句読点を除いて比較しています。
検証結果
| 設定 | CER | 途中結果数 | 最初の途中結果 | 途中結果の遅延 p50 / p95 | 最終結果の遅延 |
|---|---|---|---|---|---|
| transcription | 0.0% × 3 | 0 | — | — | 1.160〜1.215秒 |
| progressive | 0.0% × 3 | 93、93、94 | 1.082〜1.101秒 | 0.526 / 1.014秒 | 1.033〜1.223秒 |
入力開始から最終確定までのend-to-end RTFは1.072〜1.086でした。RTF 1.0は音声の長さと処理全体の時間が同じという意味です。ただし今回は意図的に実時間と同じ速度で入力を待っているため、この値はモデル単体の計算速度ではありません。
内容は全6発話で一致
句読点と空白を除くと、6発話の内容は全試行で正解と一致しました。数字を含む「あと5分くらいかな」も保持されています。
ただし、句読点は試行や設定によって変わりました。たとえば「そっちはこっちは」と続く場合と、「そっちは。こっちは」と区切る場合があります。CER 0%は、文章として常に読みやすいことを意味しません。
逐次表示は約1秒遅れる
progressiveTranscriptionは約1.09秒で最初の途中結果を返し、その後は文字列を更新しました。全280件の途中結果をまとめると、音声上の時刻から結果が届くまでの遅延はp50で0.526秒、p95で1.014秒です。
p95は、結果の95%がその時間以内に届いたという意味です。事前目標の「初回1秒未満」「p95 1秒未満」には、どちらもわずかに届きませんでした。
ライブマイク用の経路も動作
自動testでは、ブラウザ相当の48 kHz音声を128 frameずつ送り、16 kHzへ変換してからSpeechAnalyzerへ入力しました。93件の途中結果と1件の確定結果を受信し、最終内容はbenchmarkと一致しました。
実際にChromeとMacBook Proの内蔵マイクで自由に話したときも、体感の表示遅延は約1秒でした。誤認識は少なめでしたが、十分が10分になる例や、句読点が少ない場面がありました。これは1名の主観的な確認で、CERの測定とは分けて考える必要があります。
結果を簡単に読む
-
短くきれいな会話では、話した内容を正確に取れました。
ただし、1本の合成音声だけの結果です。雑音のある部屋や固有名詞でも0%になるとは限りません。
-
話しながら文字を更新できますが、表示はおよそ1秒後です。
会議メモや字幕の下書きには使えそうですが、単語を発した直後に反応する音声コマンドには遅く感じる可能性があります。
-
音声を外へ送らずに処理できます。
モデルはMac上で動きます。ただし、モデルassetの初回取得や更新にはAppleのサーバーを使います。
制限と実装上の注意
- cleanな合成会話1本、14.171秒、各設定3回だけを評価した
- 句読点の品質を数値では評価していない
- 雑音、残響、方言、固有名詞、長時間streamは試していない
- Appleが管理するモデルの正確なversionやhashは固定できない
- benchmarkはマイクやbrowserのpermission待ちを含まない
実装では、sample rateだけでなくPCMの型もモデルに合わせる必要がありました。Float32のまま渡すと失敗し、SpeechAnalyzerが要求する16 kHz Int16へ変換すると動作しました。追試では、OS build、Speech framework、localeを一緒に記録するのが重要です。
検証後の感想
短い1本の会話とはいえ、端末上の認識だけで全試行CER 0%になったのは予想より良い結果でした。約1秒の表示待ちは見えますが、自由発話でも極端に遅い印象ではありませんでした。
一方、句読点は内容認識ほど安定しておらず、実用時には読みやすく整える後処理が欲しくなります。音声を外へ出せない会議メモや、ローカル字幕の下書きには使えそうです。
