vosk/voskpl.py

144 lines
4.5 KiB
Python
Executable File

#!/home/pali112/venv/bin/python3
from vosk import Model, KaldiRecognizer
import sounddevice as sd
import json
import pyperclip # Biblioteka do obsługi schowka
import pyautogui
import sys
import tkinter as tk
from threading import Thread
from pathlib import Path
from pynput import keyboard # Biblioteka do globalnych skrótów
SCRIPT_DIR = Path(__file__).parent
# Ścieżka do modelu
model_path = str(SCRIPT_DIR / "./vosk-model-small-pl-0.22")
model = Model(model_path)
recognizer = KaldiRecognizer(model, 16000)
listening = False # Flaga stanu nasłuchiwania
emacs_mode = False # Flaga trybu Emacs
root = None # Zmienna globalna dla okna głównego
# Funkcja obsługi danych audio
def callback(indata, frames, time, status):
if status:
print(f"Status: {status}", file=sys.stderr)
# Konwersja danych na `bytes`
data = bytes(indata)
if recognizer.AcceptWaveform(data):
result = json.loads(recognizer.Result())
text = result.get("text", "")
if text:
print(f"Rozpoznano: {text}")
insert_text_with_clipboard(text)
# Funkcja kopiowania do schowka i wklejania
def insert_text_with_clipboard(text):
try:
pyperclip.copy(text) # Kopiowanie tekstu do schowka
if emacs_mode:
pyautogui.hotkey('shift', 'insert') # Wklejanie w trybie Emacs
else:
pyautogui.hotkey('ctrl', 'v') # Standardowe wklejanie
pyautogui.press('space') # Dodanie spacji (opcjonalne)
except Exception as e:
print(f"Błąd podczas wklejania tekstu: {e}", file=sys.stderr)
# Funkcja nasłuchiwania i rozpoznawania mowy
def listen_and_type():
global listening
print("Słucham... (naciśnij Stop, aby zatrzymać)")
try:
with sd.RawInputStream(samplerate=16000, blocksize=8000, dtype='int16',
channels=1, callback=callback):
while listening:
pass # Nasłuchiwanie trwa w pętli
except Exception as e:
print(f"Błąd podczas nasłuchiwania: {e}", file=sys.stderr)
# Funkcje sterujące GUI
def toggle_listening():
global listening
if listening:
listening = False
update_button_state()
print("Nasłuchiwanie zatrzymane.")
else:
listening = True
update_button_state()
thread = Thread(target=listen_and_type)
thread.daemon = True
thread.start()
def toggle_emacs_mode():
global emacs_mode
emacs_mode = not emacs_mode
update_emacs_button_state()
mode = "Emacs" if emacs_mode else "Standard"
print(f"Tryb wklejania: {mode}")
# Aktualizacja stanu przycisków
def update_button_state():
# Używamy root.after, aby zapewnić bezpieczeństwo wątków przy zmianie GUI z innego wątku (hotkey)
if root:
if listening:
root.after(0, lambda: toggle_button.config(text="Stop", bg="red", fg="white"))
else:
root.after(0, lambda: toggle_button.config(text="Start", bg="lightgrey", fg="black"))
def update_emacs_button_state():
if emacs_mode:
emacs_button.config(text="Tryb: Emacs", bg="blue", fg="white")
else:
emacs_button.config(text="Tryb: Standard", bg="lightgrey", fg="black")
# Funkcja obsługi skrótu klawiszowego (lokalnego - F1)
def handle_shortcut(event):
if event.keysym == 'F1':
toggle_listening()
# Funkcja obsługi skrótu globalnego (Ctrl+Alt+R)
def start_global_hotkeys():
def on_activate():
print("Skrót globalny wykryty!")
# Wywołujemy toggle w głównym wątku, żeby nie zawiesić GUI
if root:
root.after(0, toggle_listening)
# Definicja skrótu
hotkey = keyboard.GlobalHotKeys({
'<ctrl>+<alt>+r': on_activate
})
hotkey.start()
# Tworzenie GUI
def create_gui():
global toggle_button, emacs_button, root
root = tk.Tk()
root.title("Vosk Rozpoznawanie Mowy")
root.geometry("300x200")
toggle_button = tk.Button(root, text="Start", command=toggle_listening, width=15, bg="lightgrey", fg="black")
toggle_button.pack(pady=10)
emacs_button = tk.Button(root, text="Tryb: Standard", command=toggle_emacs_mode, width=15, bg="lightgrey", fg="black")
emacs_button.pack(pady=10)
exit_button = tk.Button(root, text="Wyjście", command=root.destroy, width=15)
exit_button.pack(pady=10)
# Dodanie skrótu klawiszowego lokalnego
root.bind("<F1>", handle_shortcut)
# Uruchomienie nasłuchiwania globalnych skrótów
start_global_hotkeys()
root.mainloop()
# Uruchomienie GUI
if __name__ == "__main__":
create_gui()