はじめに
macOS向けのオフライン文字起こしアプリを開発していると、ここ1〜2年でApple Silicon向けの音声認識環境が大きく変化したことを実感します。
私も当初はPythonとMLX Whisperを組み合わせた構成で実装していましたが、現在はFluidAudioとWhisperKit、そしてSpeechAnalyzerを組み合わせたSwiftネイティブの構成へ移行しました。
本記事では、その移行の経緯と、実際に得られた知見を紹介します。
Python版の構成
最初の実装は話者分離がPythonでしか実質難しかったため、Python+PySide6ベースで構築しました。
ただ、FFmpeg依存はライセンス関連や配布形態からどうしても避けたかったので、一般的なWhisper環境とは少し違い、音声の読み込みにはFFmpegではなくAVFoundationを利用していました。
PyObjCを介してAVFoundationを呼び出し、MLX Whisper内部で使用されていたFFmpeg依存部分についてもAVFoundationへ置き換えていました。
当時の構成は概ね次のような形です。
AVFoundation
↓
pyannote audio
↓
MLX Whisper
↓
SRT
MLX WhisperはApple Siliconとの相性も良く、十分高速でしたがLarge-V3だとやはりメモリ消費が多く、metal memory APIの活用やモデルの事前ロードなどの改造を経て、なんとかpyannote audioとの併走を16GBメモリで安定させていた形です。
Swiftへ移行した理由
しばらくこの構成で運用していましたが、Apple Silicon向けライブラリの成熟によって状況が変わりました。
決定的だったのは、
- SpeechAnalyzer
- FluidAudio
- WhisperKit
この3つです。
これらを組み合わせることで、Pythonへ橋渡しすることなく、Swiftだけで文字起こしパイプラインを構築できるようになりました。
特にFluidAudioで話者分離とSpeaker Embedding取得が利用できるようになったことは、Swiftへ移行する大きな理由になりました。
話者分離はFluidAudioへ
後述するApple純正のSpeechAnalyzerは現時点では話者分離までは行いません。(次OSでサポートするらしいですが。)
WhisperKitにもSpeakerKitはありますが、Speaker Embeddingが取り出せないため、話者記憶を実装できず、FluidAudioをチョイスすることになりました。
FluidAudioにも当然ASRは装備されていますが、こちらはParakeetなどで言語ごとにモデルを使い分ける必要があるケースが多く、多言語混じりへの対応を踏まえてSTT部分はWhisperKitを使うという変則的な形を取っています。
FluidAudioでは
- Speaker Diarization
- Known Speaker Recognition(extractEmbedding)
を使っています。
Pythonと比べると量子化で多少精度は落ちるものの、Swiftだけでここまで完結できるようになったことで、Python環境への依存は不要になりました。
WhisperKitと量子化モデル
文字起こしにはまずはこれまでの流れを継承して、WhisperKitを利用しました。
Whisper.cppや先ほどのFluidAudioでの文字起こしも検討しましたが、WhisperKitではApple Silicon向けに最適化されたモデルが利用でき、Apple Neural Engineを積極的に活用できるため、こちらを採用することとしました。
ただ、モデルによって速度やメモリ使用量も変わるため、用途に応じた選択が重要になります。
- large-v3-v20240930_626MB ANE : 48秒58
- large-v3-v20240930_626MB GPU : 1分7秒52
- large-v3 ANE : 2分36秒
- Medium ANE : 1分24秒90
(M2 Maxにて計測。参考:FFTrans Pro(MLX Whisper Large-V3) : 1分17秒)
これらの結果からメインのモデルはlarge-v3-v20240930_626MBとしました。
蒸留されていることで英語のみにの対応と誤解されやすいですが、実はこのモデルは多言語対応もちゃんとされています。
精度もMediumよりは良いですし、メモリ消費も抑えられていて良い事づくめです。
Apple Neural Engineを活かす
今回特に意識したのは、Apple Neural Engineを中心に処理を行うことです。
GPUを併用すべきかどうかの判断も迷いましたが、最終的にはNeural Engineのみで処理することとしました。
Mac Studio M2 MaxではGPU併用(audioencoderのみGPU)のほうが若干高速になりますが、MacBook Air M3 8GBでは全てANEのほうが40秒と、M2 Maxをも上回るスピードを実現できたことが大きいです。
SiliconScopeで確認すると、推論中はANEが主に使用され、CPUやGPUへの負荷は比較的低く抑えられており、消費電力も概ね8W以下で推移しています。
比較のため、PythonでのMLX Whisper実行時の負荷も見てみると、GPUフル活動で16W以上の消費となっていますし、メモリ消費も大幅に増加しているのが分かります。
Swift側の実装ではANEのみの活用により、GPUを専有しないため、Metalを利用する他のアプリケーションとの競合も少なく、Apple Siliconらしい構成になりました。
SpeechAnalyzerを導入してみる
WhisperKitでも十分高速でしたが、macOS標準の「メモ」アプリなどでSpeechAnalyzerベースと思われる文字起こしを試したところ、その処理速度は非常に印象的でした。そこでSpeechAnalyzerを既存パイプラインへ組み込めないか検証することにしました。
前述のように話者分離はできませんし、ASR対象の言語を識別する手段も用意されていません。
そのため、話者分離はこれまで通りFluidAudioを、言語識別はWhisperKitのdetectLanguageを用いて、先頭部分5区間の多数決処理で決定後、SpeechAnalyzerには決定したロケールを渡す形で互換を保っています。
そして驚くべきはその速度です。
- SpeechAnalyzer : 16秒
- large-v3-v20240930_626MB:38秒
- large-v3:2分02秒
(いずれも6分動画、言語識別あり。MacBook Air M4にて計測)
ロケールを最初から指定してあれば、なんと12.8秒で文字起こしが終わってしまいます。
話者分離の処理は共通ですから、SpeechAnalyzerがいかに爆速かは分かっていただけるでしょう。
精度に関しても多少の差こそあれ、救いようがないような品質では全くありません。
[ SpeechAnalyzer ]
周辺ではこんな現象も。
子供の増加のスピードが想像以上に早い。
学校の教室が足りない。
去年4月に開校した小中学校は生徒数が合わせて1500人を超えているといいます。
今後さらに増える見込みで新興社を建設予定です。
[ large-v3-v20240930_626MB ]
周辺ではこんな現象も。
子供の増加のスピードが想像以上に速い。
学校の教室が足りない。
去年4月に開校した小中学校は生徒数が合わせて1500人を超えているといいます。
今後さらに増える見込みで新校舎を建設予定です。
[ large-v3 ]
周辺ではこんな現象も。
子供の増加のスピードが想像以上に速い。
学校の教室が足りない。
去年4月に開校した小中学校は生徒数が合わせて1500人を超えているといいます。
今後さらに増える見込みで新校舎を建設予定です。
SpeechAnalyzerとWhisperKitで多少の差はありますが、この結果は後述のカスタム辞書を未登録の状態ですので、たとえば上記であれば「新校舎」を単語登録すれば同等にできます。
カスタム辞書を後処理で適用
どんなに精度の高い文字起こしでも、固有名詞や専門用語の修正が必要になることは避けられません。
そこでWhisper自体を再実行するのではなく、後処理としてカスタム辞書を適用する方式を採用しました。
これはPythonの頃から実装してありましたが、それをSwiftにも移植した形です。
この方法であれば、
- 辞書追加後すぐ反映
- 再推論不要
- 表記ゆれの修正
が容易になります。
PythonではSudachiを使って読み戻しをし、その連結トークンと前後の品詞で辞書適用していましたが、SwiftではNLTaggerを活用しています。
現在の構成
最終的なパイプラインは次のようになりました。
AVFoundation
│
├── FluidAudio
│
└── WhisperKit(Language Detect)
│
▼
SpeechAnalyzer
│
▼
Custom Dictionary
│
▼
SRT
Apple純正フレームワークとSwiftライブラリだけで完結する構成になり、保守性や配布のしやすさも大きく向上しました。
まとめ
Python版からSwift版への移行は単なる言語の書き換えではありませんでした。
1つのライブラリですべて解決しようとするのではなく、Apple純正APIと各ライブラリの得意分野を組み合わせることで、速度・精度・保守性を両立できました。
Apple Silicon向け音声認識では、「1つのライブラリですべてを解決する」のではなく、「各コンポーネントの得意分野を組み合わせる」ことが重要だと感じています。
現在、この構成を利用したオフライン文字起こしアプリとして「FFTrans Neu」をリリースしています。
本記事が、Apple Silicon向け音声認識を検討している方の参考になれば幸いです。

