Skip to content

Instantly share code, notes, and snippets.

@roflsunriz
Last active March 22, 2025 14:11
Show Gist options
  • Save roflsunriz/5a2b611133e2c596203d1d0b42912fcc to your computer and use it in GitHub Desktop.
Save roflsunriz/5a2b611133e2c596203d1d0b42912fcc to your computer and use it in GitHub Desktop.
speech_recognition_app : 日本語と英語対応の音声認識アプリ,Cursorのプロンプト入力用
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()
@roflsunriz
Copy link
Author

mic_icon.pngとcancel_icon.pngが必要。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment