[ローカル環境]faster-whisperを使ったリアルタイム文字起こしツールを作った話

自分の備忘録として残しておきます。

PC上で再生されているシステム音声をリアルタイムに文字起こしするツールを作成しました。
具体的には、`faster-whisper`という高速版のWhisperモデルを利用し、マイク入力ではなく、PCのシステム音声（ブラウザや動画再生ソフトの音など）を直接キャプチャして文字起こしを行う点が特徴です。

コード全文はGitHubで公開しております。
ぜひ見に来てください。
[コード全文](https://github.com/ando1109/faster-whisper-gui)

## 要件
・リアルタイムで文字起こしを行いたい
・物理マイクの音声ではなくPCのシステム音声に対して行いたい
・音声の推論はローカル環境で行いたい
### 環境
- Windows 11
- cuDNN 9.6
- CUDA 12.6
[参考記事：【新規環境/既存環境対応】最新のCUDA及びcuDNNのインストール方法](https://qiita.com/harmegiddo/items/86b295ccf96eff489e02)

## 実装の概要
### 使用技術

- **faster-whisper**（Whisperの高速実装版）
- **sounddevice**（音声キャプチャ）
- **librosa**（音声リサンプリング）
- **Tkinter**（GUI）
- **VBCABLE_Driver_Pack43** (仮想オーディオケーブル)

### faster-whisperの特徴
WhisperはOpenAIが開発した音声認識モデルですが、通常版では処理速度が遅いためリアルタイム用途には向きません。今回使用した`faster-whisper`は、その処理を最適化したバージョンであり、CUDA環境を利用することでほぼリアルタイムの文字起こしを可能にしています。
モデルの初期化は以下のように行います。

```python
model_path = "large-v3-turbo"
model = WhisperModel(model_path, device="cuda", compute_type="float32")
```

## システム音声のキャプチャについて
通常、マイク入力の音声を認識するのは簡単ですが、PCから出力される音を取得するには仮想オーディオデバイス（例: VB-CABLE）が必要です。以下のコードでその設定をしています。
```python
device_index = 24  # 「sd.query_devices()」で仮想オーディオのインデックスを確認
wasapi_settings = sd.WasapiSettings(auto_convert=True)

audio_stream = sd.InputStream(
    device=device_index,
    samplerate=48000,
    channels=2,
    callback=audio_callback,
    extra_settings=wasapi_settings
)
audio_stream.start()
```

ここで`channels=2`はステレオ入力を指定し、`wasapi_settings`でWASAPI経由で自動的にフォーマット変換を行っています。

またシステム音声を取り込むのにWindows標準でサウンドミキサーという機能があります。
なので当方もサウンドミキサーを使用してみましたが音がうまく取れなかった。。。。。
そこでVBCABLE_Driver_Pack43というフリーソフトを使用させていただきました。
参考記事：[[Windows] 文字起こしにデスクトップ音を利用する試み](https://qiita.com/reriiasu/items/1b4cae7205458cf78ade)

下記コード実行すると使用可能なデバイス一覧が出ます。
```python
import sounddevice as sd
print(sd.query_devices())
```
ここから`
CABLE Output (VB-Audio Virtual Cable), Windows WASAPI (2 in, 0 out)
`を選択します。

## 音声処理とリサンプリング
Whisperが要求する音声のサンプルレートは16kHzですが、キャプチャした音声は48kHzで取得されます。そこで`librosa`を使ってリサンプリングを行っています。

```python
process_data = librosa.resample(process_data, orig_sr=48000, target_sr=16000)
```

この処理により、精度の高い文字起こしが可能になります。
## リアルタイム性の向上
下記コードで音声認識を別スレッドにすることでUIがフリーズすることを防止しています。
（Pythonのスレッドはスレッドではないという方もいらっしゃると思ますが、見逃してください）

```python
threading.Thread(target=process_recording, args=(text_widget, audio_data), daemon=True).start()
```

## 実際の動作画面
GUIにはTkinterを使用しており、直感的に操作できます。

- 「聞き取り開始」で録音を開始（無音状態を検出して自動的に終了するときもある）
- 「聞き取り終了」で録音を終了し、文字起こし結果を表示

以下は実際のスクショです。
[ヨビノリさんの動画](https://www.youtube.com/watch?v=IOsluvDNQH4&ab_channel=%E4%BA%88%E5%82%99%E6%A0%A1%E3%81%AE%E3%83%8E%E3%83%AA%E3%81%A7%E5%AD%A6%E3%81%B6%E3%80%8C%E5%A4%A7%E5%AD%A6%E3%81%AE%E6%95%B0%E5%AD%A6%E3%83%BB%E7%89%A9%E7%90%86%E3%80%8D)を部分的に文字起こしさせていただいた結果です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819645/e457a7b5-49bf-4d0f-a61b-a6f6ac965f5a.png)

## 課題
無音状態だと以下の写真のように`ご視聴ありがとうございました。` や`Thank you`
が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3819645/36063057-8e38-4a10-9f7e-0d7e014e3af3.png)

こちらはWisperの学習データによるものだそうです。[参照：discussions](https://github.com/openai/whisper/discussions/905)

## 今後の展望
- さらにリアルタイム性を高めるため、音声データのバッファリング方式の改良
- 認識結果の編集や保存機能の追加
- Webアプリ化やクラウド連携の検討
- 認識精度の向上