-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7cb0940
Showing
38 changed files
with
2,489 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/.conda | ||
/dist |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# VoiceInk - 智能语音输入助手 🎙️ | ||
|
||
VoiceInk 是一款简单易用的智能语音输入工具,支持实时语音转文字,让您的输入更加轻松自如。 | ||
|
||
## ✨ 主要特性 | ||
|
||
- 🎯 **便捷操作**:按住 Ctrl 键说话,松开自动转写 | ||
- 🚀 **实时转写**:基于大模型的快速准确的语音识别 | ||
- 💡 **智能优化**:自动优化转写结果 | ||
- 🔄 **历史记录**:支持查看和管理历史记录 | ||
- 🎨 **界面美观**:简洁现代的用户界面,支持音频波形图显示 | ||
- 🔒 **隐私保护**:本地处理,数据安全 | ||
- 🌐 **符号处理**:支持自定义去除句尾符号或emoji表情 | ||
- 📦 **便携设计**:绿色免安装,方便快捷 | ||
|
||
## 🙌 界面截图 | ||
|
||
### 主界面 | ||
|
||
 | ||
|
||
### 设置界面 | ||
|
||
 | ||
|
||
### 历史记录 | ||
|
||
 | ||
|
||
## 🚀 快速开始 | ||
|
||
### 下载安装 | ||
|
||
1. 从 [Releases](https://github.com/yourusername/VoiceInk/releases) 页面下载最新版本 | ||
2. 选择下载方式: | ||
- 便携版(推荐):下载 `VoiceInk_便携版.zip` | ||
- 单文件版:下载 `VoiceInk.exe` | ||
|
||
### 首次使用 | ||
|
||
1. 解压或运行程序 | ||
2. 在设置中配置 API 密钥 | ||
3. 按住 Ctrl 键开始录音 | ||
4. 松开 Ctrl 键完成转写 | ||
|
||
## 💻 使用说明 | ||
|
||
### 基本操作 | ||
|
||
- **开始录音**:按住 Ctrl 键 | ||
- **结束录音**:松开 Ctrl 键 | ||
- **查看历史**:点击系统托盘图标 | ||
- **修改设置**:右键系统托盘图标 | ||
|
||
## ⚙️ 配置说明 | ||
|
||
### API 设置 | ||
|
||
首次使用需要配置 API: | ||
- 转录服务 API Key (OpenAI/Groq/SliconFlow(中文用户推荐)) | ||
- Siliconflow 服务目前只支持 FunAudioLLM/SenseVoiceSmall 模型,在 custom 模式下使用 | ||
|
||
### 其他设置 | ||
|
||
- 语音识别模型选择 | ||
- 界面主题设置 | ||
- 符号处理设置 | ||
- 录音控制设置 | ||
- 日志选项 | ||
- 波形图位置设置 | ||
|
||
## 📝 注意事项 | ||
|
||
- 确保麦克风权限已开启 | ||
- 建议使用较安静的环境 | ||
- 定期备份历史记录 | ||
|
||
## 🔄 更新日志 | ||
|
||
### v1.0.0 (2024-10-24) | ||
- 首个正式版本发布 | ||
- 支持实时语音转写 | ||
- 支持历史记录管理 | ||
- 支持系统托盘操作 | ||
|
||
## 🤝 贡献指南 | ||
|
||
欢迎提交 Issue 和 Pull Request! | ||
|
||
## 📄 许可证 | ||
|
||
本项目采用 [MIT 许可证](LICENSE) | ||
|
||
--- | ||
|
||
Made with ❤️ by [BryceWG](https://github.com/BryceWG) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
import os | ||
import shutil | ||
import subprocess | ||
from pathlib import Path | ||
|
||
def clean_dirs(): | ||
"""清理构建目录""" | ||
dirs_to_clean = ['build', 'dist', 'portable'] | ||
for dir_name in dirs_to_clean: | ||
if os.path.exists(dir_name): | ||
shutil.rmtree(dir_name) | ||
|
||
def create_portable(): | ||
"""创建便携版""" | ||
print("正在创建便携版...") | ||
|
||
subprocess.run([ | ||
'pyinstaller', | ||
'--noconfirm', | ||
'--noconsole', | ||
'--name=VoiceInk', | ||
'--icon=resources/app.ico', | ||
# 添加 qt.conf | ||
'--add-data=qt.conf;.', | ||
# 修改资源文件的打包方式 | ||
'--add-data=resources/app.ico;resources/', | ||
'--add-data=resources/style.qss;resources/', | ||
'--add-data=resources/icons/16x16/app.png;resources/icons/16x16/', | ||
'--add-data=resources/icons/24x24/app.png;resources/icons/24x24/', | ||
'--add-data=resources/icons/32x32/app.png;resources/icons/32x32/', | ||
'--add-data=resources/icons/48x48/app.png;resources/icons/48x48/', | ||
'--add-data=resources/icons/256x256/app.png;resources/icons/256x256/', | ||
# 确保所有资源目录都被包含 | ||
'--add-data=resources;resources', | ||
# PyQt6 相关配置 | ||
'--hidden-import=PyQt6', | ||
'--hidden-import=PyQt6.QtCore', | ||
'--hidden-import=PyQt6.QtGui', | ||
'--hidden-import=PyQt6.QtWidgets', | ||
'--hidden-import=PyQt6.sip', | ||
'--collect-submodules=PyQt6', | ||
'--collect-data=PyQt6', | ||
# 添加系统托盘相关的依赖 | ||
'--hidden-import=PyQt6.QtWidgets.QSystemTrayIcon', | ||
'--hidden-import=PyQt6.QtWidgets.QMenu', | ||
'--hidden-import=PyQt6.QtGui.QIcon', | ||
# 其他必要的依赖 | ||
'--hidden-import=pynput.keyboard._win32', | ||
'--hidden-import=sounddevice', | ||
'--hidden-import=numpy', | ||
'--hidden-import=openai', | ||
'--hidden-import=requests', | ||
'--hidden-import=pyperclip', | ||
'--hidden-import=pyautogui', | ||
'--hidden-import=win32com.client', | ||
'--hidden-import=emoji', | ||
'--hidden-import=google.generativeai', # 添加 Google AI 依赖 | ||
# 排除不需要的模块 | ||
'--exclude-module=matplotlib', | ||
'--exclude-module=scipy', | ||
'--exclude-module=pandas', | ||
'--exclude-module=PIL', | ||
'--exclude-module=cv2', | ||
'--distpath=portable', | ||
# 添加字体相关的依赖 | ||
'--hidden-import=PyQt6.QtGui.QFontDatabase', | ||
'--hidden-import=PyQt6.QtGui.QFont', | ||
'main.py' | ||
], check=True) | ||
|
||
# 复制配置文件模板 | ||
shutil.copy('config.json', 'portable/VoiceInk/config.template.json') | ||
|
||
# 创建启动脚本 | ||
with open('portable/VoiceInk/启动VoiceInk.bat', 'w', encoding='utf-8') as f: | ||
f.write('@echo off\n') | ||
f.write('cd /d "%~dp0"\n') # 切换到脚本所在目录 | ||
f.write('start VoiceInk.exe\n') | ||
|
||
# 创建说明文件 | ||
with open('portable/VoiceInk/说明.txt', 'w', encoding='utf-8') as f: | ||
f.write('VoiceInk - 智能语音输入助手\n\n') | ||
f.write('使用说明:\n') | ||
f.write('1. 双击"启动VoiceInk.bat"运行程序\n') | ||
f.write('2. 按住Ctrl键开始录音\n') | ||
f.write('3. 松开Ctrl键结束录音并转写\n') | ||
f.write('4. 转写文本将自动插入到当前焦点位置\n\n') | ||
f.write('注意事项:\n') | ||
f.write('- 首次运行请在设置中配置API密钥\n') | ||
f.write('- 所有配置和历史记录都保存在程序目录下\n') | ||
f.write('- 如需迁移程序,复制整个文件夹即可\n') | ||
|
||
# 打包为zip | ||
shutil.make_archive('VoiceInk_便携版', 'zip', 'portable/VoiceInk') | ||
print("便携版创建完成:VoiceInk_便携版.zip") | ||
|
||
def create_single_exe(): | ||
"""创建单文件版本""" | ||
print("正在创建单文件版本...") | ||
|
||
subprocess.run([ | ||
'pyinstaller', | ||
'--noconfirm', | ||
'--noconsole', | ||
'--onefile', | ||
'--name=VoiceInk', | ||
'--icon=resources/app.ico', | ||
# 添加 qt.conf | ||
'--add-data=qt.conf;.', | ||
# 修改资源文件的打包方式 | ||
'--add-data=resources/app.ico;resources/', | ||
'--add-data=resources/style.qss;resources/', | ||
'--add-data=resources/icons/16x16/app.png;resources/icons/16x16/', | ||
'--add-data=resources/icons/24x24/app.png;resources/icons/24x24/', | ||
'--add-data=resources/icons/32x32/app.png;resources/icons/32x32/', | ||
'--add-data=resources/icons/48x48/app.png;resources/icons/48x48/', | ||
'--add-data=resources/icons/256x256/app.png;resources/icons/256x256/', | ||
# 确保所有资源目录都被包含 | ||
'--add-data=resources;resources', | ||
# PyQt6 相关配置 | ||
'--hidden-import=PyQt6', | ||
'--hidden-import=PyQt6.QtCore', | ||
'--hidden-import=PyQt6.QtGui', | ||
'--hidden-import=PyQt6.QtWidgets', | ||
'--hidden-import=PyQt6.sip', | ||
'--collect-submodules=PyQt6', | ||
'--collect-data=PyQt6', | ||
# 添加系统托盘相关的依赖 | ||
'--hidden-import=PyQt6.QtWidgets.QSystemTrayIcon', | ||
'--hidden-import=PyQt6.QtWidgets.QMenu', | ||
'--hidden-import=PyQt6.QtGui.QIcon', | ||
# 其他必要的依赖 | ||
'--hidden-import=pynput.keyboard._win32', | ||
'--hidden-import=sounddevice', | ||
'--hidden-import=numpy', | ||
'--hidden-import=openai', | ||
'--hidden-import=requests', | ||
'--hidden-import=pyperclip', | ||
'--hidden-import=pyautogui', | ||
'--hidden-import=win32com.client', | ||
'--hidden-import=emoji', | ||
'--hidden-import=google.generativeai', # 添加 Google AI 依赖 | ||
# 排除不需要的模块 | ||
'--exclude-module=matplotlib', | ||
'--exclude-module=scipy', | ||
'--exclude-module=pandas', | ||
'--exclude-module=PIL', | ||
'--exclude-module=cv2', | ||
'main.py' | ||
], check=True) | ||
|
||
print("单文件版本创建完成:dist/VoiceInk.exe") | ||
|
||
def main(): | ||
# 检查是否在虚拟环境中 | ||
if not os.environ.get('VIRTUAL_ENV'): | ||
print("警告:建议在虚拟环境中运行打包脚本") | ||
input("按Enter继续,或Ctrl+C退出...") | ||
|
||
# 清理旧的构建文件 | ||
clean_dirs() | ||
|
||
# 创建便携版 | ||
create_portable() | ||
|
||
# 创建单文件版本 | ||
create_single_exe() | ||
|
||
print("\n打包完成!") | ||
print("- 便携版: VoiceInk_便携版.zip") | ||
print("- 单文件版: dist/VoiceInk.exe") | ||
|
||
if __name__ == "__main__": | ||
main() |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import sounddevice as sd | ||
import numpy as np | ||
import wave | ||
import io | ||
import threading | ||
import time | ||
|
||
class AudioRecorder: | ||
def __init__(self): | ||
self.sample_rate = 44100 | ||
self.channels = 1 | ||
self.frames = [] | ||
self.recording = False | ||
self.stream = None | ||
self._lock = threading.Lock() | ||
self.audio_callback = None # 添加回调函数 | ||
|
||
def set_audio_callback(self, callback): | ||
"""设置音频数据回调""" | ||
self.audio_callback = callback | ||
|
||
def start_recording(self): | ||
with self._lock: | ||
self.frames = [] | ||
self.recording = True | ||
|
||
def callback(indata, frames, time, status): | ||
if self.recording: | ||
with self._lock: | ||
# 立即处理音频数据 | ||
if self.audio_callback: | ||
self.audio_callback(indata) | ||
self.frames.append(indata.copy()) | ||
|
||
try: | ||
# 使用更激进的低延迟设置 | ||
self.stream = sd.InputStream( | ||
channels=self.channels, | ||
samplerate=self.sample_rate, | ||
callback=callback, | ||
blocksize=256, # 进一步减小块大小 | ||
latency='low', | ||
device=None, | ||
extra_settings=None | ||
) | ||
# 预先启动流并等待一小段时间以确保稳定 | ||
self.stream.start() | ||
time.sleep(0.05) # 短暂等待以确保流启动 | ||
except Exception as e: | ||
self.recording = False | ||
raise Exception(f"录音启动失败: {str(e)}") | ||
|
||
def stop_recording(self): | ||
if not self.stream: | ||
return None | ||
|
||
self.recording = False | ||
try: | ||
self.stream.stop() | ||
self.stream.close() | ||
self.stream = None | ||
|
||
with self._lock: | ||
if not self.frames: # 检查是否有录音数据 | ||
return None | ||
|
||
audio_data = np.concatenate(self.frames, axis=0) | ||
byte_io = io.BytesIO() | ||
|
||
with wave.open(byte_io, 'wb') as wf: | ||
wf.setnchannels(self.channels) | ||
wf.setsampwidth(2) | ||
wf.setframerate(self.sample_rate) | ||
wf.writeframes((audio_data * 32767).astype(np.int16)) | ||
|
||
return byte_io.getvalue() | ||
|
||
except Exception as e: | ||
raise Exception(f"录音停止失败: {str(e)}") | ||
finally: | ||
self.frames = [] |
Oops, something went wrong.