LinuxでWhisperを使った音声入力アプリ作った
はじめに
最近、Aqua Voiceやsuperwhisperといった音声入力ツールが流行っている気がします。
しかし、Linuxに対応しているものが少なかったり、ローカル環境で動作するWhisperモデルを活用したツールが見当たらなかったりすることに気づきました。
そこで、OpenAIのWhisperモデルを利用した無料の音声入力システムを開発しましたので、本記事で紹介します。
サーバー・クライアント構成にすることで、家の高性能GPUを活用しながら、軽量なノートPCからでも快適に音声入力できる仕組みを実現しました。
特徴
- サーバー・クライアント分離
- Ollama統合
- 複数の出力モード: クリップボードへのコピーまたは直接入力
- シンプルな操作: シグナルによるトグル制御
- システムトレイ対応: 視覚的な状態表示
動作環境
このツールは、以下の環境を想定しています。
- OS: Linux
-
ディスプレイサーバー: Wayland環境を推奨します。
- X11環境でもクリップボードモードは利用可能ですが、直接入力モードは
wtypeコマンドに依存しているためWaylandが必要です。
- X11環境でもクリップボードモードは利用可能ですが、直接入力モードは
-
ハードウェア:
- サーバー側: 高速な文字起こしのために、NVIDIA製GPUとCUDA環境の利用を強く推奨します。
- クライアント側: 特別な要件はありません。
アーキテクチャ
全体構成
簡単に説明すると、
-
- ユーザーがクライアントにシグナルを送る(
キー入力)\fallingdotseq
- ユーザーがクライアントにシグナルを送る(
-
- クライアントが録音する
-
- もう一度シグナルを送る
-
- 録音が止まる
-
- サーバーに音声データが送られ文字起こしされる
-
- 起こされた文章が入力される
工夫したポイント
サーバー・クライアント分離
最初は全てを一つのファイルで実行していました。ですが、GPUのないノートパソコンでも使いたいなーと思った時デスクトップに文字起こしを任せられれば実用的なスピードで処理できると思いました。
デバイス間の通信はポート開放等をしなくてもtailscaleを使えば可能です。
シグナルを使った録音の制御
音声入力の開始・終了を、無音状態の検知に頼る方式はあまり好みではありませんでした。無音部分を文字起こしすると、学習データの影響で「ご視聴ありがとうございました」といった意図しないテキストが出力されることがあり、使い勝手が悪かったためです。
毎回コマンドを打つ手間を省きつつ、自分のタイミングで正確に録音を制御したいと考え、シグナルによるトグル方式を採用しました。これにより、クライアントを常駐させたまま、簡単なキー操作で録音の開始と停止を切り替えられます。
私は Windowsキー + D のショートカットでシグナルを送信するように設定しています。
サーバー側の実装
FastAPIを使いシンプルなREST APIで音声ファイルを受け取り、テキストを返します。
faster-whisperを使用して文字起こししています。
Ollamaによる整形
上で挙げた音声入力ソフトはカスタムプロンプトがあると聞いたので、Ollamaを使ってllmで整形できるようにしてみました。とは言ってもOllamaのAPIを使って設定したプロンプトと文章を送ってその出力を返すだけですが。
クライアント側の実装
音声録音の制御
このプロセスにSIGUSR1シグナルが来ると録音がトグルされます。
setproctitleでタイトルをつけているのでpkill -SIGUSR1 whisper-typingで送ることができます。
録音はsounddeviceライブラリを使用しました。
出力モードの選択
2つの出力モードが選択可能です。
-
クリップボードモード:
pyperclipでクリップボードにコピー -
直接入力モード:
wtypeコマンドで直接テキスト入力
クリップボードの場合は自分でペーストする必要があるので手間だと思います。wtypeが動かないとき用です。
wtypeはWaylandでしか動かないので汎用的な方法があれば修正したいです。
システムトレイ機能
pystrayで視覚的な状態確認のためのトレイアイコンを実装しました。
- 🟢 緑: 待機中(録音可能)
- 🔴 赤: 録音中
- ⚫ グレー: 停止/エラー
おわり
私自身はPythonでの開発経験が豊富なわけではありませんが、faster-whisper を利用する上でPythonの採用は避けられませんでした。また、依存関係をシンプルに保つため、クライアント側もPythonで実装することにしました。
そのため、コードには改善の余地があるかもしれません。より良い実装方法や改善点など、お気づきの点があれば、ぜひGitHubのIssueなどでフィードバックをいただけると嬉しいです!
Discussion