fix(Git): 提交 README.md
This commit is contained in:
parent
5db2d81f4d
commit
3e51867523
103
README.md
103
README.md
|
|
@ -1,28 +1,89 @@
|
||||||
# ollama-voice-mac
|
# x-Ollama-Voice-Mac
|
||||||
A completely offline voice assistant using Mistral 7b via Ollama and Whisper speech recognition models. This builds on the [excellent work of maudoin](https://github.com/maudoin/ollama-voice) by adding Mac compatibility with various improvements.
|
一款完全离线运行的语音助手,集成了 Whisper、Ollama 和 pyttsx3 模型,支持离线语音识别、自然语言处理及文本转语音(TTS)功能。
|
||||||
|
结合 **Mistral 7b**(通过 Ollama 实现)和 **Whisper** 语音识别模型构建而成。此项目基于 [maudoin 的优秀工作](https://github.com/apeatling/ollama-voice-mac),添加了对 Mac 的兼容性并进行了一系列改进。
|
||||||
|
|
||||||
https://github.com/apeatling/ollama-voice-mac/assets/1464705/996abeb7-7e99-451b-8d3b-feb3fecbb82e
|
https://github.com/apeatling/ollama-voice-mac/assets/1464705/996abeb7-7e99-451b-8d3b-feb3fecbb82e
|
||||||
|
|
||||||
## Installing and running
|
---
|
||||||
|
|
||||||
1. Install [Ollama](https://ollama.ai) on your Mac.
|
## 安装与运行
|
||||||
2. Download the Mistral 7b model using the `ollama pull mistral` command.
|
### 0. 在mac上安装python的虚拟环境
|
||||||
3. Download an [OpenAI Whisper Model](https://github.com/openai/whisper/discussions/63#discussioncomment-3798552) (base.en works fine).
|
python3.11 -m venv myenv_311
|
||||||
4. Clone this repo somewhere.
|
source myenv_311/bin/activate
|
||||||
5. Place the Whisper model in a /whisper directory in the repo root folder.
|
deactivate
|
||||||
6. Make sure you have [Python](https://www.python.org/downloads/macos/) and [Pip](https://pip.pypa.io/en/stable/installation/) installed.
|
|
||||||
7. For Apple silicon support of the PyAudio library you'll need to install [Homebrew](https://brew.sh) and run `brew install portaudio`.
|
|
||||||
8. Run `pip install -r requirements.txt` to install.
|
|
||||||
9. Run `python assistant.py` to start the assistant.
|
|
||||||
|
|
||||||
## Improving the voice
|
### 1. 安装 Ollama
|
||||||
|
在 Mac 上安装 [Ollama](https://ollama.ai)。
|
||||||
|
|
||||||
You can improve the quality of the voice by downloading a higher quality version. These instructions work on MacOS 14 Sonoma:
|
### 2. 下载 Mistral 7b 模型
|
||||||
|
运行以下命令下载模型:
|
||||||
|
```bash
|
||||||
|
ollama pull mistral
|
||||||
|
```
|
||||||
|
|
||||||
1. In System Settings select Accessibility > Spoken Content
|
### 3. 下载 Whisper 模型
|
||||||
2. Select System Voice and Manage Voices...
|
访问 [Whisper 模型库](https://github.com/openai/whisper/discussions/63#discussioncomment-3798552),选择适合的模型(`base` 即可)。
|
||||||
3. For English find "Zoe (Premium)" and download it.
|
|
||||||
4. Select Zoe (Premium) as your System voice.
|
|
||||||
|
|
||||||
## Other languages
|
### 4. 克隆本项目
|
||||||
You can set up support for other languages by editing `assistant.yaml`. Be sure to download a different Whisper model in your language and change the default `modelPath`.
|
将项目代码克隆到本地计算机:
|
||||||
|
```bash
|
||||||
|
git clone <仓库地址>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 配置 Whisper 模型路径
|
||||||
|
将 Whisper 模型放入项目根目录的 `/whisper` 文件夹中。
|
||||||
|
|
||||||
|
### 6. 安装 Python 和 Pip
|
||||||
|
确保已安装 [Python](https://www.python.org/downloads/macos/) 和 [Pip](https://pip.pypa.io/en/stable/installation/)。
|
||||||
|
|
||||||
|
### 7. 配置 PyAudio 库(Apple Silicon 特别步骤)
|
||||||
|
对于 Apple Silicon 用户,需安装 **Homebrew** 并运行以下命令:
|
||||||
|
```bash
|
||||||
|
brew install portaudio
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. 安装依赖
|
||||||
|
运行以下命令安装所需依赖:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9. 启动助手
|
||||||
|
运行以下命令启动语音助手:
|
||||||
|
```bash
|
||||||
|
python assistant.py
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### 10. 中间使用的要点:
|
||||||
|
语音识别:使用 Whisper 模型进行本地语音识别,完全离线运行。
|
||||||
|
自然语言处理:使用 Ollama 平台运行 Mistral 7b 模型,实现高效的本地大语言模型推理,无需联网。
|
||||||
|
文本转语音(TTS):基于 pyttsx3 实现的离线文本语音合成功能,支持多种语言和语音优化。但是 pyttsx3 太机械了 增加了edge-tts
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 提升语音质量
|
||||||
|
|
||||||
|
在 MacOS 14 Sonoma 中,可以通过以下步骤提升语音质量:
|
||||||
|
|
||||||
|
1. 打开 **系统设置** > **辅助功能** > **语音内容**。
|
||||||
|
2. 选择 **系统语音** 并点击 **管理语音**。
|
||||||
|
3. 在英文语音中找到 **"Zoe (Premium)"** 并下载。
|
||||||
|
4. 下载完成后,将系统语音更改为 **Zoe (Premium)**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 支持其他语言
|
||||||
|
|
||||||
|
要支持其他语言,可以通过以下方式配置:
|
||||||
|
|
||||||
|
1. 编辑 `assistant.yaml` 文件。
|
||||||
|
2. 下载目标语言的 Whisper 模型并将其路径更新到 `modelPath` 配置项。
|
||||||
|
|
||||||
|
**示例(中文配置)**:
|
||||||
|
- 下载适合中文的 Whisper 模型,例如 `medium.zh`。
|
||||||
|
- 在 `assistant.yaml` 中将 `modelPath` 修改为下载的模型路径,例如:
|
||||||
|
```yaml
|
||||||
|
modelPath: /path/to/medium.zh.pt
|
||||||
|
```
|
||||||
BIN
assistant.png
BIN
assistant.png
Binary file not shown.
|
Before Width: | Height: | Size: 394 KiB After Width: | Height: | Size: 822 B |
32
assistant.py
32
assistant.py
|
|
@ -45,7 +45,10 @@ class Assistant:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
logging.info("Initializing Assistant")
|
logging.info("Initializing Assistant")
|
||||||
self.config = self.init_config()
|
self.config = self.init_config()
|
||||||
|
|
||||||
|
# 预初始化 pygame mixer,避免每次语音播放时的初始化开销
|
||||||
|
pygame.mixer.init()
|
||||||
|
|
||||||
programIcon = pygame.image.load('assistant.png')
|
programIcon = pygame.image.load('assistant.png')
|
||||||
|
|
||||||
self.clock = pygame.time.Clock()
|
self.clock = pygame.time.Clock()
|
||||||
|
|
@ -271,30 +274,32 @@ class Assistant:
|
||||||
logging.info(f"Using edge-tts with voice: {self.edge_voice}")
|
logging.info(f"Using edge-tts with voice: {self.edge_voice}")
|
||||||
communicate = edge_tts.Communicate(text, self.edge_voice)
|
communicate = edge_tts.Communicate(text, self.edge_voice)
|
||||||
|
|
||||||
# 添加调试信息
|
# 使用异步方式并行处理音频生成
|
||||||
logging.info("Starting audio generation...")
|
audio_task = asyncio.create_task(communicate.save("temp_speech.mp3"))
|
||||||
await communicate.save("temp_speech.mp3")
|
|
||||||
logging.info("Audio file generated successfully")
|
# 在音频生成的同时执行其他初始化
|
||||||
|
if not pygame.mixer.get_init():
|
||||||
|
pygame.mixer.init()
|
||||||
|
|
||||||
|
# 等待音频生成完成
|
||||||
|
await audio_task
|
||||||
|
|
||||||
if not os.path.exists("temp_speech.mp3") or os.path.getsize("temp_speech.mp3") == 0:
|
if not os.path.exists("temp_speech.mp3") or os.path.getsize("temp_speech.mp3") == 0:
|
||||||
raise Exception("Generated audio file is empty or does not exist")
|
raise Exception("Generated audio file is empty or does not exist")
|
||||||
|
|
||||||
pygame.mixer.init()
|
|
||||||
pygame.mixer.music.load("temp_speech.mp3")
|
pygame.mixer.music.load("temp_speech.mp3")
|
||||||
pygame.mixer.music.play()
|
pygame.mixer.music.play()
|
||||||
|
|
||||||
while pygame.mixer.music.get_busy():
|
while pygame.mixer.music.get_busy():
|
||||||
pygame.time.wait(100)
|
await asyncio.sleep(0.1) # 使用异步等待替代 pygame.time.wait
|
||||||
|
|
||||||
pygame.mixer.quit()
|
|
||||||
|
|
||||||
|
# 不要每次都退出 mixer,只清理文件
|
||||||
if os.path.exists("temp_speech.mp3"):
|
if os.path.exists("temp_speech.mp3"):
|
||||||
os.remove("temp_speech.mp3")
|
os.remove("temp_speech.mp3")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"An error occurred during edge-tts speech playback: {str(e)}")
|
logging.error(f"An error occurred during edge-tts speech playback: {str(e)}")
|
||||||
logging.error(f"Voice being used: {self.edge_voice}")
|
logging.error(f"Voice being used: {self.edge_voice}")
|
||||||
# 如果 edge-tts 失败,回退到 pyttsx3
|
|
||||||
logging.info("Falling back to pyttsx3...")
|
logging.info("Falling back to pyttsx3...")
|
||||||
self.tts_engine.say(text)
|
self.tts_engine.say(text)
|
||||||
self.tts_engine.runAndWait()
|
self.tts_engine.runAndWait()
|
||||||
|
|
@ -305,9 +310,7 @@ class Assistant:
|
||||||
|
|
||||||
def play_speech():
|
def play_speech():
|
||||||
try:
|
try:
|
||||||
logging.info("Starting speech playback")
|
# 移除不必要的延迟
|
||||||
time.sleep(0.5) # Short delay before speaking
|
|
||||||
|
|
||||||
if self.config.tts.engine == "edge-tts":
|
if self.config.tts.engine == "edge-tts":
|
||||||
asyncio.run(self.edge_tts_speak(text))
|
asyncio.run(self.edge_tts_speak(text))
|
||||||
else: # pyttsx3
|
else: # pyttsx3
|
||||||
|
|
@ -318,7 +321,8 @@ class Assistant:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"An error occurred during speech playback: {str(e)}")
|
logging.error(f"An error occurred during speech playback: {str(e)}")
|
||||||
|
|
||||||
speech_thread = threading.Thread(target=play_speech)
|
# 使用守护线程,这样主程序退出时不会被阻塞
|
||||||
|
speech_thread = threading.Thread(target=play_speech, daemon=True)
|
||||||
speech_thread.start()
|
speech_thread.start()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user