Last active
March 22, 2025 14:11
-
-
Save roflsunriz/5a2b611133e2c596203d1d0b42912fcc to your computer and use it in GitHub Desktop.
speech_recognition_app : 日本語と英語対応の音声認識アプリ,Cursorのプロンプト入力用
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import sys | |
import subprocess | |
import tkinter as tk | |
from tkinter import messagebox | |
import datetime | |
# フォルダパスの設定 | |
ASSETS_DIR = os.path.join("misc", "assets") | |
OUTPUT_DIR = os.path.join("misc", "output") | |
# 必須ライブラリのリスト | |
REQUIRED_LIBRARIES = [ | |
("speech_recognition", "speechrecognition"), | |
("pyperclip", "pyperclip"), | |
("pyaudio", "pyaudio") | |
] | |
def create_directories(): | |
"""必要なディレクトリを作成する""" | |
os.makedirs(ASSETS_DIR, exist_ok=True) | |
os.makedirs(OUTPUT_DIR, exist_ok=True) | |
def check_and_install_libraries(): | |
"""必須ライブラリの確認とインストールを行う""" | |
for import_name, package_name in REQUIRED_LIBRARIES: | |
try: | |
__import__(import_name) | |
except ImportError: | |
print(f"{package_name} がインストールされていません。インストールを開始します...") | |
try: | |
subprocess.check_call([sys.executable, "-m", "pip", "install", package_name]) | |
print(f"{package_name} のインストールが完了しました。") | |
except subprocess.CalledProcessError as e: | |
print(f"{package_name} のインストールに失敗しました: {str(e)}") | |
messagebox.showerror("エラー", f"{package_name} のインストールに失敗しました。手動でインストールしてください。") | |
sys.exit(1) | |
# ディレクトリ作成 | |
create_directories() | |
# ライブラリの確認とインストールを実行 | |
check_and_install_libraries() | |
# 必要なライブラリをインポート | |
import speech_recognition as sr | |
import pyperclip | |
class SpeechRecognitionApp: | |
def __init__(self, root): | |
self.root = root | |
self.root.title("音声認識アプリ") | |
self.recognizer = sr.Recognizer() | |
self.setup_ui() | |
def setup_ui(self): | |
# メインフレーム | |
main_frame = tk.Frame(self.root, padx=10, pady=10) | |
main_frame.pack(fill=tk.BOTH, expand=True) | |
# 操作ボタンエリア | |
button_frame = tk.Frame(main_frame) | |
button_frame.pack(fill=tk.X, pady=5) | |
# マイクボタン(美しいボタンにする) | |
self.mic_icon = tk.PhotoImage(file=os.path.join(ASSETS_DIR, "mic_icon.png")).subsample(4, 4) | |
self.mic_button = tk.Button(button_frame, image=self.mic_icon, | |
command=self.start_recognition, | |
bd=0, highlightthickness=0, relief=tk.FLAT) | |
self.mic_button.pack(side=tk.LEFT, padx=5) | |
# キャンセルボタン(美しいボタンにする) | |
self.cancel_icon = tk.PhotoImage(file=os.path.join(ASSETS_DIR, "cancel_icon.png")).subsample(4, 4) | |
self.cancel_button = tk.Button(button_frame, image=self.cancel_icon, | |
command=self.stop_recognition, | |
bd=0, highlightthickness=0, relief=tk.FLAT) | |
self.cancel_button.pack(side=tk.LEFT, padx=5) | |
# 言語選択 | |
lang_frame = tk.Frame(main_frame) | |
lang_frame.pack(fill=tk.X, pady=5) | |
self.lang_var = tk.StringVar(value="ja-JP") | |
tk.Radiobutton(lang_frame, text="日本語", variable=self.lang_var, value="ja-JP").pack(side=tk.LEFT) | |
tk.Radiobutton(lang_frame, text="英語", variable=self.lang_var, value="en-US").pack(side=tk.LEFT) | |
# 追加言語 | |
tk.Radiobutton(lang_frame, text="中国語", variable=self.lang_var, value="zh-CN").pack(side=tk.LEFT) | |
tk.Radiobutton(lang_frame, text="スペイン語", variable=self.lang_var, value="es-ES").pack(side=tk.LEFT) | |
tk.Radiobutton(lang_frame, text="ヒンディー語", variable=self.lang_var, value="hi-IN").pack(side=tk.LEFT) | |
tk.Radiobutton(lang_frame, text="アラビア語", variable=self.lang_var, value="ar-SA").pack(side=tk.LEFT) | |
tk.Radiobutton(lang_frame, text="ポルトガル語", variable=self.lang_var, value="pt-BR").pack(side=tk.LEFT) | |
tk.Radiobutton(lang_frame, text="ロシア語", variable=self.lang_var, value="ru-RU").pack(side=tk.LEFT) | |
tk.Radiobutton(lang_frame, text="フランス語", variable=self.lang_var, value="fr-FR").pack(side=tk.LEFT) | |
# テキスト表示エリア | |
self.text_area = tk.Text(main_frame, height=10, wrap=tk.WORD) | |
self.text_area.pack(fill=tk.BOTH, expand=True, pady=5) | |
# ログ表示エリア | |
self.log_area = tk.Text(main_frame, height=5, wrap=tk.WORD, state=tk.DISABLED) | |
self.log_area.pack(fill=tk.BOTH, expand=True, pady=5) | |
# 下部ボタンエリア | |
bottom_frame = tk.Frame(main_frame) | |
bottom_frame.pack(fill=tk.X, pady=5) | |
# クリアボタン | |
self.clear_text_button = tk.Button(bottom_frame, text="テキストをクリア", | |
command=self.clear_text) | |
self.clear_text_button.pack(side=tk.LEFT, padx=5) | |
self.clear_log_button = tk.Button(bottom_frame, text="ログをクリア", | |
command=self.clear_log) | |
self.clear_log_button.pack(side=tk.LEFT, padx=5) | |
# コピーボタン | |
self.copy_button = tk.Button(bottom_frame, text="クリップボードにコピー", command=self.copy_to_clipboard) | |
self.copy_button.pack(side=tk.LEFT, padx=5) | |
# 保存ボタン | |
self.save_button = tk.Button(bottom_frame, text="ファイルに保存", command=self.save_to_file) | |
self.save_button.pack(side=tk.LEFT, padx=5) | |
# 終了ボタン | |
self.exit_button = tk.Button(bottom_frame, text="終了", command=self.exit_app) | |
self.exit_button.pack(side=tk.RIGHT) | |
def start_recognition(self): | |
# 別スレッドで音声認識を開始 | |
import threading | |
threading.Thread(target=self._recognition_thread, daemon=True).start() | |
def _recognition_thread(self): | |
self.update_log("音声認識を開始しました") | |
self.mic_button.config(state=tk.DISABLED) # ボタンを無効化 | |
try: | |
with sr.Microphone() as source: | |
self.recognizer.adjust_for_ambient_noise(source) | |
self.update_log("ノイズ調整完了。話してください...") | |
# 音声認識の設定を調整 | |
self.recognizer.pause_threshold = 3 # 3秒の沈黙で終了 | |
self.recognizer.phrase_time_limit = 90 # 最大90秒間の音声認識 | |
audio = self.recognizer.listen(source) | |
self.update_log("音声を認識中...") | |
selected_lang = self.lang_var.get() | |
text = self.recognizer.recognize_google(audio, language=selected_lang) | |
self.text_area.insert(tk.END, text + "\n") | |
self.update_log(f"認識結果: {text}") | |
pyperclip.copy(text) | |
self.update_log("クリップボードにコピーしました") | |
except sr.WaitTimeoutError: | |
self.update_log("タイムアウト: 音声が検出されませんでした") | |
except sr.UnknownValueError: | |
self.update_log("エラー: 音声を認識できませんでした") | |
except sr.RequestError as e: | |
self.update_log(f"エラー: 音声認識サービスに接続できませんでした - {str(e)}") | |
except Exception as e: | |
self.update_log(f"予期せぬエラーが発生しました: {str(e)}") | |
finally: | |
self.mic_button.config(state=tk.NORMAL) # ボタンを有効化 | |
def stop_recognition(self): | |
self.update_log("音声認識を停止しました") | |
# ここに音声認識を停止する処理を追加 | |
# (現状のspeech_recognitionライブラリでは強制停止機能がないため、 | |
# 主にユーザーへのフィードバックとして使用) | |
def update_log(self, message): | |
# ログエリアにメッセージを追加 | |
self.log_area.config(state=tk.NORMAL) | |
self.log_area.insert(tk.END, f"[{datetime.datetime.now().strftime('%H:%M:%S')}] {message}\n") | |
self.log_area.config(state=tk.DISABLED) | |
self.log_area.see(tk.END) # 最新のログを表示 | |
def copy_to_clipboard(self): | |
# テキストエリアの内容を取得 | |
text = self.text_area.get("1.0", tk.END).strip() | |
if text: | |
# クリップボードにコピー | |
pyperclip.copy(text) | |
self.update_log("クリップボードにコピーしました") | |
messagebox.showinfo("成功", "クリップボードにコピーしました") | |
else: | |
self.update_log("コピーするテキストがありません") | |
messagebox.showwarning("警告", "コピーするテキストがありません") | |
def save_to_file(self): | |
# テキストエリアの内容を取得 | |
text = self.text_area.get("1.0", tk.END).strip() | |
if text: | |
try: | |
# ファイル名をタイムスタンプで生成 | |
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") | |
filename = os.path.join(OUTPUT_DIR, f"speech_output_{timestamp}.txt") | |
# ファイルに保存 | |
with open(filename, "w", encoding="utf-8") as f: | |
f.write(text) | |
self.update_log(f"ファイルに保存しました: {filename}") | |
messagebox.showinfo("成功", f"ファイルに保存しました: {filename}") | |
except Exception as e: | |
self.update_log(f"ファイル保存エラー: {str(e)}") | |
messagebox.showerror("エラー", f"ファイル保存に失敗しました: {str(e)}") | |
else: | |
self.update_log("保存するテキストがありません") | |
messagebox.showwarning("警告", "保存するテキストがありません") | |
def exit_app(self): | |
# 終了確認 | |
if messagebox.askokcancel("終了", "本当に終了しますか?"): | |
self.root.quit() | |
def clear_text(self): | |
self.text_area.delete("1.0", tk.END) | |
self.update_log("テキストをクリアしました") | |
def clear_log(self): | |
self.log_area.config(state=tk.NORMAL) | |
self.log_area.delete("1.0", tk.END) | |
self.log_area.config(state=tk.DISABLED) | |
self.update_log("ログをクリアしました") | |
if __name__ == "__main__": | |
root = tk.Tk() | |
app = SpeechRecognitionApp(root) | |
root.mainloop() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
mic_icon.pngとcancel_icon.pngが必要。