オフラインでVosk を使ってマイクに喋りかけて音声を文字起こしする

2025年3月21日

インストール

pip install vosk

VOSK Models

vosk-model-ja-0.22

コード

import tkinter as tk
from tkinter import scrolledtext as tk_scrolledtext
import time
from tkinter import messagebox
import threading
import queue
from vosk import Model, KaldiRecognizer
import pyaudio
import json

SAMPLE_RATE = 44100
MODEL_PATH = "model/vosk-model-ja-0.22"  # VOSKのモデルパス

# ハルシネーションで出力される可能性があるテキストを定義
hallucinationTexts = [
    "ご視聴ありがとうございました",
    "Thanks for watching!",
]

# VOSKモデルの初期化
model = Model(MODEL_PATH)
recognizer = KaldiRecognizer(model, SAMPLE_RATE)

# 録音スレッドの処理
def record_audio():
    global recording
    global th2
    global q

    p = pyaudio.PyAudio()
    stream = p.open(format=pyaudio.paInt16, channels=1, rate=SAMPLE_RATE, input=True, frames_per_buffer=4000)
    stream.start_stream()

    while recording:  # recordingがTrueの間、録音を継続
        print('record_audio')
        data = stream.read(4000, exception_on_overflow=False)
        q.put(data)
        print(str(q.qsize()) + ' Queue')
        if not th2.is_alive():
            th2 = threading.Thread(target=audio_to_text)
            th2.start()

    stream.stop_stream()
    stream.close()
    p.terminate()

# 音声を文字に変換する関数
def audio_to_text():
    global q

    print('audio_to_text')

    while not q.empty():
        print('audio_to_text2')
        print(str(q.qsize()) + ' Queue')

        data = q.get()

        if recognizer.AcceptWaveform(data):
            result = json.loads(recognizer.Result())
            text = result.get("text", "")
        else:
            text = ""

        print(f'VOSK Result: {text}')

        if text in hallucinationTexts or not text:
            continue
        else:
            output_text.insert(tk.END, f'{text}\n')
            output_text.see(tk.END)

        time.sleep(0.1)

# 録音を開始する関数
def start_recording():
    global recording
    global th
    global th2
    recording = True
    th = threading.Thread(target=record_audio)
    th.start()
    print("start_recording ...")

    th2 = threading.Thread(target=audio_to_text)
    th2.start()

# 録音を停止する関数
def stop_recording():
    global recording
    recording = False
    print("stop_recording ...")

# 録音ボタンがクリックされたときの処理
def toggle_recording():
    global recording
    if recording:
        # 録音を停止
        stop_recording()
        record_button.config(text="Start Recording")
    else:
        # 録音を開始
        start_recording()
        record_button.config(text="Stop Recording")

def delete_window():
    global recording
    global starting

    global th
    global th2
    global q
    # 録音を停止
    stop_recording()
    record_button.config(text="Start Recording")
    record_button["state"] = "disabled"

    if not starting:
        root.destroy()
    elif q.empty() and not th.is_alive() and not th2.is_alive():
        print(str(q.qsize()) + ' Queue')
        root.destroy()
    else:
        messagebox.showerror('error', str(q.qsize()) + ' Queue or running. Wait a minute')
# クリアボタンとコピー用関数
def clear_text():
    output_text.delete("1.0", tk.END)

def copy_text():
    root.clipboard_clear()
    root.clipboard_append(output_text.get("1.0", tk.END))
    root.update()  # クリップボードを更新
#    messagebox.showinfo("コピー完了", "テキストがクリップボードにコピーされました!")


if __name__ == "__main__":
    # オーディオキューを作成
    q = queue.Queue()

    # 録音中かどうかを示すフラグ
    recording = False
    starting = False

    # Tkinterウィンドウを作成
    root = tk.Tk()
    root.title("Speech Recognition App")

    # 録音ボタンを作成
    record_button = tk.Button(root, text="Start Recording", command=toggle_recording)
    record_button.pack(fill="x")
# 新しいボタンを追加
    clear_button = tk.Button(root, text="クリア", command=clear_text)
    clear_button.pack(fill="x")

    copy_button = tk.Button(root, text="コピー", command=copy_text)
    copy_button.pack(fill="x")

    # 認識結果を表示するテキストウィジェットを作成
    output_text = tk_scrolledtext.ScrolledText(root, width=70, height=10, font=('Arial', 14))
    output_text.pack(pady=10)
    root.protocol("WM_DELETE_WINDOW", delete_window)

    print('Loading model...')
    print('Done')
    # GUIを表示
    root.mainloop()

Python

Posted by eightban