-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
144 lines (113 loc) · 4.11 KB
/
Copy pathmain.py
File metadata and controls
144 lines (113 loc) · 4.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import os
import sys
import time
import threading
import signal
from dotenv import load_dotenv
# --- CUDA Fix for Windows ---
# Try to add torch's lib directory to PATH so CTranslate2 can find cublas64_12.dll
try:
import torch
torch_lib = os.path.join(os.path.dirname(torch.__file__), "lib")
if os.path.exists(torch_lib):
os.environ["PATH"] = torch_lib + os.pathsep + os.environ.get("PATH", "")
except Exception:
pass
# ----------------------------
from pynput import keyboard
import pyperclip
import pyautogui
# Load environment variables from .env
load_dotenv()
# Verify GROQ_API_KEY
if not os.environ.get("GROQ_API_KEY"):
print("ERROR: GROQ_API_KEY environment variable or .env file not found!")
print("Please set your Groq API key in the .env file or environment.")
sys.exit(1)
from PySide6.QtCore import QObject, Signal, Slot
from PySide6.QtWidgets import QApplication
from audio_capture import AudioRecorder
from transcriber import Transcriber
from text_cleaner import TextCleaner
from ui import FlowUI
# --- Configuration ---
HOTKEY = '<f9>'
WHISPER_MODEL = "turbo"
GROQ_MODEL = "llama-3.3-70b-versatile"
# ---------------------
class FlowBridge(QObject):
toggle_sig = Signal()
status_sig = Signal(str)
volume_sig = Signal(float)
print("Initializing Flow Pipeline...")
bridge = FlowBridge()
recorder = AudioRecorder(volume_callback=lambda v: bridge.volume_sig.emit(v))
transcriber = Transcriber(model_size=WHISPER_MODEL)
cleaner = TextCleaner(model=GROQ_MODEL)
is_recording = False
def run_pipeline(audio_data):
try:
if audio_data is None or len(audio_data) < 8000: # less than 0.5s of audio
print("[-] Audio too short, skipping.")
bridge.status_sig.emit("idle")
return
# 1. Transcribing
bridge.status_sig.emit("transcribing")
raw_text = transcriber.transcribe(audio_data)
print(f" [Raw Text]: {raw_text}")
if not raw_text.strip():
print("[-] No speech detected.")
bridge.status_sig.emit("idle")
return
# 2. Cleaning Text
bridge.status_sig.emit("cleaning")
cleaned_text = cleaner.clean(raw_text)
print(f" [Cleaned Text]: {cleaned_text}")
# 3. Pasting text
original_clipboard = pyperclip.paste()
pyperclip.copy(cleaned_text)
time.sleep(0.1)
pyautogui.hotkey('ctrl', 'v')
time.sleep(0.1)
pyperclip.copy(original_clipboard)
bridge.status_sig.emit("done")
except Exception as e:
print(f"Error in processing pipeline: {e}")
bridge.status_sig.emit("idle")
@Slot()
def handle_toggle():
global is_recording
if not is_recording:
print("\n[*] Recording started...")
recorder.start_recording()
is_recording = True
bridge.status_sig.emit("listening")
else:
print("\n[*] Recording stopped. Processing...")
is_recording = False
audio_data = recorder.stop_recording()
# Run heavy processing in background thread
threading.Thread(target=run_pipeline, args=(audio_data,), daemon=True).start()
def on_activate():
bridge.toggle_sig.emit()
if __name__ == "__main__":
# Allow Python's default Ctrl+C behavior to exit PySide6
signal.signal(signal.SIGINT, signal.SIG_DFL)
# Let Qt6 handle high DPI scaling natively
os.environ["QT_SCALE_FACTOR_ROUNDING_POLICY"] = "PassThrough"
app = QApplication(sys.argv)
# Initialize overlay UI
ui = FlowUI()
ui.show_centered_top()
# Connect signals
bridge.toggle_sig.connect(handle_toggle)
bridge.status_sig.connect(ui.set_state)
bridge.volume_sig.connect(ui.update_volume)
# Start global hotkey listener thread
listener = keyboard.GlobalHotKeys({HOTKEY: on_activate})
listener.start()
print("\n========================================================")
print(f"Flow is ready with UI!")
print(f"Press {HOTKEY} anywhere to start/stop dictating.")
print("========================================================")
sys.exit(app.exec())