Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
BryceWG committed Oct 24, 2024
0 parents commit 7cb0940
Show file tree
Hide file tree
Showing 38 changed files with 2,489 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/.conda
/dist
Binary file added Clip_2024-10-24_15-38-38.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Clip_2024-10-24_15-39-00.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Clip_2024-10-24_15-39-22.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
96 changes: 96 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# VoiceInk - 智能语音输入助手 🎙️

VoiceInk 是一款简单易用的智能语音输入工具,支持实时语音转文字,让您的输入更加轻松自如。

## ✨ 主要特性

- 🎯 **便捷操作**:按住 Ctrl 键说话,松开自动转写
- 🚀 **实时转写**:基于大模型的快速准确的语音识别
- 💡 **智能优化**:自动优化转写结果
- 🔄 **历史记录**:支持查看和管理历史记录
- 🎨 **界面美观**:简洁现代的用户界面,支持音频波形图显示
- 🔒 **隐私保护**:本地处理,数据安全
- 🌐 **符号处理**:支持自定义去除句尾符号或emoji表情
- 📦 **便携设计**:绿色免安装,方便快捷

## 🙌 界面截图

### 主界面

![](Clip_2024-10-24_15-38-38.png)

### 设置界面

![](Clip_2024-10-24_15-39-00.png)

### 历史记录

![](Clip_2024-10-24_15-39-22.png)

## 🚀 快速开始

### 下载安装

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)
174 changes: 174 additions & 0 deletions build_scripts/build.py
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 added core/__pycache__/audio_recorder.cpython-312.pyc
Binary file not shown.
Binary file added core/__pycache__/config_manager.cpython-312.pyc
Binary file not shown.
Binary file not shown.
Binary file added core/__pycache__/logger.cpython-312.pyc
Binary file not shown.
Binary file added core/__pycache__/text_processor.cpython-312.pyc
Binary file not shown.
Binary file not shown.
81 changes: 81 additions & 0 deletions core/audio_recorder.py
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 = []
Loading

0 comments on commit 7cb0940

Please sign in to comment.