-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
256 lines (206 loc) · 8.48 KB
/
main.py
File metadata and controls
256 lines (206 loc) · 8.48 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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#!/usr/bin/env python3
"""
ezA2T - 实时语音转字幕软件主程序
Easy Audio to Text - Real-time Speech to Subtitle Application
"""
import sys
import os
import threading
import time
import argparse
from pathlib import Path
# 添加src目录到路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
class RealTimeSubtitleApp:
"""实时字幕应用主类"""
def __init__(self, config_file: str = "config.json"):
# 延迟导入模块
from src.config import Config
self.config = Config(config_file)
self.audio_capture = None
self.speech_recognizer = None
self.subtitle_window = None
self.is_running = False
print("ezA2T - 实时语音转字幕软件")
print("=" * 40)
def initialize_components(self):
"""初始化各个组件"""
# 延迟导入模块
from src.wasapi_loopback_capture import WASAPILoopbackCapture
from src.speech_recognizer import SpeechRecognizer, SimpleSpeechRecognizer, VOSK_AVAILABLE
from src.subtitle_ui import SubtitleWindow
print("正在初始化组件...")
# 初始化WASAPI loopback音频捕获(专门捕获电脑播放的音频)
try:
sample_rate = self.config.get('audio.sample_rate', 16000)
chunk_size = self.config.get('audio.chunk_size', 1024)
print("初始化WASAPI loopback音频捕获...")
self.audio_capture = WASAPILoopbackCapture(sample_rate, chunk_size)
# 列出可用设备
print("\\n可用音频设备:")
self.audio_capture.list_all_devices()
except Exception as e:
print(f"音频捕获初始化失败: {e}")
return False
# 初始化语音识别
print("\\n初始化语音识别...")
model_path = self.config.get('recognition.model_path', './models/vosk-model-cn-0.22')
if VOSK_AVAILABLE:
self.speech_recognizer = SpeechRecognizer(model_path, sample_rate)
if not self.speech_recognizer.load_model():
print("VOSK模型加载失败,请检查模型路径或下载模型")
print("\\n模型下载说明:")
print("1. 访问 https://alphacephei.com/vosk/models")
print("2. 下载中文模型(推荐 vosk-model-small-cn-0.22)")
print("3. 解压到 models/ 目录下")
# 使用简单识别器作为备用
print("\\n使用模拟识别器(仅用于测试界面)")
self.speech_recognizer = SimpleSpeechRecognizer()
else:
print("VOSK未安装,使用模拟识别器")
self.speech_recognizer = SimpleSpeechRecognizer()
# 初始化字幕界面
print("\\n初始化字幕界面...")
self.subtitle_window = SubtitleWindow(self.config)
print("组件初始化完成!")
return True
def start_recognition(self):
"""开始语音识别"""
if not self.audio_capture or not self.speech_recognizer:
print("组件未正确初始化")
return False
print("\\n开始实时语音识别...")
self.is_running = True
# 开始语音识别
self.speech_recognizer.start_recognition(self.on_recognition_result)
# 开始音频捕获
if not self.audio_capture.start_recording(self.on_audio_data):
print("无法开始捕获音频")
return False
print("语音识别已启动")
print("提示:")
print("- 右键点击字幕窗口可以打开设置菜单")
print("- 双击字幕窗口可以切换置顶状态")
print("- 拖拽字幕窗口可以移动位置")
print("- 按 Ctrl+S 保存字幕,Ctrl+Q 或 ESC 退出")
return True
def stop_recognition(self):
"""停止语音识别"""
if not self.is_running:
return
print("\\n正在停止语音识别...")
self.is_running = False
if self.audio_capture:
self.audio_capture.stop_recording()
if self.speech_recognizer:
self.speech_recognizer.stop_recognition()
print("语音识别已停止")
def on_audio_data(self, audio_data):
"""音频数据回调"""
if self.is_running and self.speech_recognizer:
self.speech_recognizer.process_audio(audio_data)
def on_recognition_result(self, text: str, is_final: bool = True):
"""语音识别结果回调"""
if self.subtitle_window:
self.subtitle_window.update_subtitle(text, is_final)
# 打印识别结果到控制台
if is_final and text.strip():
print(f"识别结果: {text}")
def run(self):
"""运行应用"""
try:
# 初始化组件
if not self.initialize_components():
print("初始化失败")
return
# 开始识别
if not self.start_recognition():
print("启动识别失败")
return
# 运行界面(阻塞)
self.subtitle_window.run()
except KeyboardInterrupt:
print("\\n用户中断")
except Exception as e:
print(f"运行出错: {e}")
finally:
self.stop_recognition()
print("程序已退出")
def download_model_guide():
"""显示模型下载指南"""
print("\\nezA2T 模型下载指南")
print("=" * 30)
print("\\n为了使用离线语音识别,您需要下载VOSK模型:")
print("\\n1. 访问官方模型库:")
print(" https://alphacephei.com/vosk/models")
print("\\n2. 推荐的中文模型:")
print(" - vosk-model-small-cn-0.22 (约40MB,适合一般使用)")
print(" - vosk-model-cn-0.22 (约1.2GB,更高精度)")
print("\\n3. 英文模型:")
print(" - vosk-model-small-en-us-0.15 (约40MB)")
print(" - vosk-model-en-us-0.22 (约1.8GB)")
print("\\n4. 下载后解压到项目的 models/ 目录下")
print("\\n5. 修改 config.json 中的 model_path 指向对应模型")
print("\\n示例目录结构:")
print("ezA2T/")
print("├── models/")
print("│ ├── vosk-model-small-cn-0.22/")
print("│ │ ├── am/")
print("│ │ ├── graph/")
print("│ │ └── ...")
print("│ └── vosk-model-small-en-us-0.15/")
print("└── ...")
def check_dependencies():
"""检查依赖项"""
missing_deps = []
try:
import soundcard
except ImportError:
missing_deps.append('soundcard')
try:
import vosk
except ImportError:
missing_deps.append('vosk')
try:
import numpy
except ImportError:
missing_deps.append('numpy')
if missing_deps:
print("缺少以下依赖项:")
for dep in missing_deps:
print(f" - {dep}")
print("\\n请运行以下命令安装:")
print(f"pip install {' '.join(missing_deps)}")
return False
return True
def main():
"""主函数"""
parser = argparse.ArgumentParser(description='ezA2T - 实时语音转字幕软件')
parser.add_argument('--config', '-c', default='config.json', help='配置文件路径')
parser.add_argument('--check-deps', action='store_true', help='检查依赖项')
parser.add_argument('--download-guide', action='store_true', help='显示模型下载指南')
parser.add_argument('--list-devices', action='store_true', help='列出音频设备')
args = parser.parse_args()
if args.check_deps:
if check_dependencies():
print("所有依赖项已安装")
return
if args.download_guide:
download_model_guide()
return
if args.list_devices:
try:
from src.wasapi_loopback_capture import WASAPILoopbackCapture
audio = WASAPILoopbackCapture()
audio.list_all_devices()
except Exception as e:
print(f"列出设备失败: {e}")
return
# 检查依赖项
if not check_dependencies():
return
# 创建并运行应用
app = RealTimeSubtitleApp(args.config)
app.run()
if __name__ == "__main__":
main()