🎤
Windows上でVoskを動かしてリアルタイム音声認識
通話やPCの音声をリアルタイムで字幕化したいときに便利なのが Vosk。
この記事では、マイク入力だけでなく VB-Audio Virtual Cableを利用して通話アプリの音声も認識できる環境を構築する手順をまとめます。
1. 事前準備
Python インストール
- Windows 向けにPython公式 から3.12をインストール。
-
PATHに追加するオプションにチェックを入れてください。
必要ライブラリのインストール
PowerShell で以下を実行:
pip install sounddevice vosk
2. Vosk 日本語モデルの準備
Vosk公式から日本語モデルをダウンロードします
解凍したフォルダを C:\models\vosk-model-ja-0.22 に配置してください。
smallモデルもありますが、ある程度の精度を求めるなら大モデルを推奨。
3. VB-Audio Virtual Cable(通話音声取り込み用)
通話アプリの音声を直接取得するために VB-Audio Virtual Cable を使います。
-
公式サイト から
VB-CABLE_Setup_x64.exeをダウンロード - 管理者権限で実行し Install → 再起動
- 通話アプリ(例:MicroSIP)で「再生デバイス」を
CABLE Inputに設定
4. Python スクリプト
マイク入力と Virtual Cable の両方に対応し、リアルタイムに字幕を表示します。
import tkinter as tk
from tkinter import ttk, messagebox
import threading, queue, json
import sounddevice as sd
from vosk import Model, KaldiRecognizer
# ===== 設定 =====
MODEL_PATH = r"C:\models\vosk-model-ja-0.22"
SAMPLE_RATE = 16000
BLOCKSIZE = 8000
MODE = "mic" # "mic" または "vb-cable"
VB_CABLE_NAME = "CABLE Output"
# =================
def find_vb_cable_input():
devs = sd.query_devices()
for i, d in enumerate(devs):
name = d.get("name", "")
if d.get("max_input_channels", 0) >= 1 and VB_CABLE_NAME.lower() in name.lower():
return i
return None
class RecognizerThread(threading.Thread):
def __init__(self, q, device_index=None):
super().__init__(daemon=True)
self.q = q
self.running = True
self.model = Model(MODEL_PATH)
self.rec = KaldiRecognizer(self.model, SAMPLE_RATE)
self.device_index = device_index
def _cb(self, indata, frames, time_info, status):
if status:
print(status)
self.q.put(bytes(indata))
def run(self):
with sd.RawInputStream(
samplerate=SAMPLE_RATE, blocksize=BLOCKSIZE,
dtype='int16', channels=1,
callback=self._cb, device=self.device_index
):
while self.running:
sd.sleep(50)
def stop(self):
self.running = False
class App:
def __init__(self, root):
self.root = root
root.title("Live Captions (Mic/VB-Cable – Vosk)")
frm = ttk.Frame(root, padding=8); frm.grid(sticky="nsew")
root.rowconfigure(0, weight=1); root.columnconfigure(0, weight=1)
ttk.Label(frm, text="Partial(リアルタイム)").grid(row=0, column=0, sticky="w")
self.partial = tk.Text(frm, height=6, wrap="word"); self.partial.grid(row=1, column=0, sticky="nsew")
ttk.Label(frm, text="Final(確定文/ログ)").grid(row=2, column=0, sticky="w", pady=(8,0))
self.final = tk.Text(frm, height=12, wrap="word"); self.final.grid(row=3, column=0, sticky="nsew")
btns = ttk.Frame(frm); btns.grid(row=4, column=0, sticky="e", pady=(8,0))
self.start_btn = ttk.Button(btns, text="Start", command=self.on_start)
self.stop_btn = ttk.Button(btns, text="Stop", command=self.on_stop, state="disabled")
self.start_btn.grid(row=0, column=0, padx=4); self.stop_btn.grid(row=0, column=1, padx=4)
frm.rowconfigure(1, weight=1); frm.rowconfigure(3, weight=2); frm.columnconfigure(0, weight=1)
self.q_audio = queue.Queue()
self.rec_thread = None
self.model = Model(MODEL_PATH)
self.rec = KaldiRecognizer(self.model, SAMPLE_RATE)
self.root.after(50, self._pump)
def on_start(self):
dev_index = None
if MODE.lower() == "vb-cable":
dev_index = find_vb_cable_input()
if dev_index is None:
messagebox.showerror("デバイス未検出", f"'{VB_CABLE_NAME}' が見つかりません")
return
self.rec_thread = RecognizerThread(self.q_audio, device_index=dev_index)
self.rec_thread.start()
self.start_btn.config(state="disabled"); self.stop_btn.config(state="normal")
def on_stop(self):
if self.rec_thread:
self.rec_thread.stop()
self.rec_thread = None
self.start_btn.config(state="normal"); self.stop_btn.config(state="disabled")
def _pump(self):
try:
while True:
data = self.q_audio.get_nowait()
if self.rec.AcceptWaveform(data):
res = json.loads(self.rec.Result())
text = res.get("text", "").strip()
if text:
self.final.insert("end", text + "\n")
self.final.see("end")
self.partial.delete("1.0", "end")
else:
pres = json.loads(self.rec.PartialResult())
ptxt = pres.get("partial", "").strip()
self.partial.delete("1.0", "end")
self.partial.insert("end", ptxt)
self.partial.see("end")
except queue.Empty:
pass
self.root.after(50, self._pump)
if __name__ == "__main__":
root = tk.Tk()
app = App(root)
root.protocol("WM_DELETE_WINDOW", lambda: (app.on_stop(), root.destroy()))
root.mainloop()
5. 実行方法
マイク入力で試す
py .\ファイル名.py
- Start ボタン →
話すと「Partial」にリアルタイム表示、「Final」に確定文がログされます
通話音声(VB-Cable)を使う場合
-
MODE = "vb-cable"に変更して保存 - 通話アプリの出力デバイスを
CABLE Inputに設定 - スクリプト実行 → 通話の音声が字幕化されます
まとめ
- Vosk は軽量かつ高精度に日本語音声を認識できるOSSライブラリ
- VB-Audio Virtual Cable を組み合わせれば、通話アプリやシステム音声も字幕化可能
- GUI付きで Partial/Final を分けて表示することで、会話中の利用にも適します
この仕組みをベースに、後段で自然言語処理や要約、検索連携を加えることも可能です。
Discussion