Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PiTalk - 树莓派智能音箱

一个用 Rust 实现的智能音箱系统,支持多种大模型后端、语音合成和语音识别。

功能特性

  • 🗣️ LLM 抽象层 - 支持 OpenAI、DeepSeek、Qwen 等多种大模型
  • 🔊 TTS 语音合成 - 支持 Edge TTS (免费)、Sherpa 和 Mock Provider
  • 🎤 ASR 语音识别 - 支持 Whisper API、Sherpa 和 Mock Provider
  • 👂 Wake 唤醒词检测 - 支持 Porcupine、Sherpa 和 Mock Provider
  • 🎭 声纹识别 - 支持说话人识别和声纹管理
  • 🔌 可插拔架构 - 所有模块均支持自定义 Provider
  • 🛠️ 工具系统 - 支持定时器、音乐播放、天气查询等工具

项目结构

src/
├── main.rs         # 入口和演示
├── lib.rs          # 库入口
├── config/         # 配置管理
├── database/       # SQLite 数据库
├── llm/            # 大语言模型模块
├── tts/            # 语音合成模块
├── asr/            # 语音识别模块
├── wake/           # 唤醒词检测模块
├── audio/          # 音频 I/O 和 VAD
├── speaker/        # 声纹识别模块
├── pipeline/       # 语音交互管道
├── tools/          # 工具执行器
└── state/          # 状态机管理

快速开始

1. 安装依赖

# 克隆项目
git clone https://github.com/iabetor/pitalk.git
cd pitalk

# 构建 (开发模式)
cargo build

# 构建 (生产模式)
cargo build --release

2. 配置

创建配置文件和环境变量:

# 方式一:使用 YAML 配置文件
cp configs/pitalk.yaml ~/.config/pitalk.yaml

# 方式二:使用环境变量
export OPENAI_API_KEY=sk-xxx
export DEEPSEEK_API_KEY=sk-xxx
export QWEN_API_KEY=sk-xxx

配置文件示例 (~/.config/pitalk.yaml):

# LLM 配置
llm:
  default_model: qwen
  models:
    - name: qwen
      api_url: ${QWEN_API_URL}
      api_key: ${QWEN_API_KEY}
      model: qwen-turbo

# 音频配置
audio:
  sample_rate: 16000
  mic_gain: 2.0

# 对话配置
dialog:
  continuous_timeout: 8
  wake_reply: "我在"
  system_prompt: |
    你是一个智能语音助手,名字叫 PiTalk。

3. 运行

# 运行演示程序 (使用 Mock Provider,无需配置)
cargo run

# 启用真实音频设备 (需要音频硬件)
cargo run --features cpal

# 启用 Sherpa 本地语音处理 (需要模型文件)
cargo run --features sherpa

# 组合使用
cargo run --features cpal,sherpa

4. 运行测试

# 运行所有测试
cargo test

# 运行特定测试
cargo test speaker
cargo test integration

# 显示测试输出
cargo test -- --nocapture

使用示例

LLM - 大语言模型

use pitalk::llm::{LLMConfig, OpenAIProvider, LLMProvider, LLMProviderExt, Message};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 使用 DeepSeek (国内友好)
    let config = LLMConfig::deepseek("your-api-key")
        .with_system_prompt("你是一个智能音箱助手,回答要简洁友好");
    
    let provider = OpenAIProvider::new(config)?;
    
    // 简单问答
    let response = provider.ask("今天天气怎么样?").await?;
    println!("回复: {}", response);
    
    // 多轮对话
    let mut conversation = provider.new_conversation();
    conversation.user("我叫小明");
    let reply = provider.chat(&conversation).await?;
    println!("助手: {}", reply);
    
    conversation.user("你还记得我的名字吗?");
    let reply = provider.chat(&conversation).await?;
    println!("助手: {}", reply);
    
    Ok(())
}

TTS - 语音合成

use pitalk::tts::{TTSProvider, TTSProviderExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 使用 Mock TTS (开发和测试)
    let tts = MockTTSProvider::new();
    
    // 合成语音
    let text = "你好,我是 PiTalk 智能音箱";
    let audio_data = tts.synthesize_all(text).await?;
    
    println!("音频大小: {} bytes", audio_data.len());
    
    // 保存到文件
    tokio::fs::write("output.mp3", &audio_data).await?;
    println!("已保存到 output.mp3");
    
    // 列出可用语音
    let voices = tts.list_voices().await?;
    for voice in voices.iter().take(5) {
        println!("- {} ({})", voice.name, voice.language);
    }
    
    Ok(())
}

腾讯云 TTS (推荐)

use pitalk::tts::{TencentTTSProvider, TencentTTSConfig, TTSProvider, TTSProviderExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 从环境变量创建 (推荐)
    // 需要设置: TENCENT_SECRET_ID 和 TENCENT_SECRET_KEY
    let tts = TencentTTSProvider::from_env()?;
    
    // 或者直接指定密钥
    let config = TencentTTSConfig::new("your-secret-id", "your-secret-key")
        .with_voice_type(1001)  // 标准女声
        .with_speed(0)          // 正常语速
        .with_volume(5);        // 中等音量
    
    let tts = TencentTTSProvider::with_config(config)?;
    
    let audio = tts.synthesize_all("你好世界").await?;
    tokio::fs::write("tencent_output.mp3", &audio).await?;
    
    Ok(())
}

获取密钥: 前往 腾讯云控制台 获取 SecretId 和 SecretKey

Edge TTS (实验性)

use pitalk::tts::{EdgeTTSProvider, TTSProvider, TTSProviderExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 使用 Edge TTS (免费、高质量)
    let tts = EdgeTTSProvider::new()
        .with_voice("zh-CN-XiaoxiaoNeural");  // 中文女声
    
    let audio = tts.synthesize_all("你好世界").await?;
    tokio::fs::write("edge_output.mp3", &audio).await?;
    
    Ok(())
}

注意: Edge TTS 目前为实验性功能,微软 API 可能随时变更。

ASR - 语音识别

use pitalk::asr::{ASRConfig, WhisperAPIProvider, ASRProvider, ASRProviderExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 使用 OpenAI Whisper API
    let config = ASRConfig::new("your-openai-api-key")
        .with_language("zh")  // 中文
        .with_model("whisper-1");
    
    let asr = WhisperAPIProvider::new(config)?;
    
    // 从文件转录
    let text = asr.transcribe_file("recording.wav").await?;
    println!("识别结果: {}", text);
    
    // 从内存数据转录
    let audio_data = tokio::fs::read("another.wav").await?;
    let text = asr.transcribe(&audio_data).await?;
    println!("识别结果: {}", text);
    
    // 查看支持的语言
    let languages = asr.supported_languages();
    println!("支持的语言: {:?}", languages);
    
    Ok(())
}

Mock ASR (测试用)

use pitalk::asr::{MockASRProvider, ASRProvider, ASRProviderExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Mock ASR 返回固定文本,用于测试
    let asr = MockASRProvider::new()
        .with_response("这是一段模拟的识别结果");
    
    let text = asr.transcribe_file("test.wav").await?;
    println!("模拟结果: {}", text);
    
    Ok(())
}

Wake - 唤醒词检测

use pitalk::wake::{MockWakeProvider, WakeProvider};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 使用 Mock Wake Provider (测试用)
    let mut wake = MockWakeProvider::new()
        .with_keyword("hey pitalk")
        .with_auto_detect_after_frames(3);
    
    // 模拟音频帧 (16kHz, 16-bit, mono, 512 samples)
    let audio_frame = [0i16; 512];
    
    // 处理音频流
    for _ in 0..3 {
        wake.process(&audio_frame).await?;
    }
    
    // 第 4 次检测到唤醒词
    if let Some(event) = wake.process(&audio_frame).await? {
        println!("检测到: {}", event.keyword);
    }
    
    Ok(())
}

Porcupine (可选)

// 需要添加 pv_porcupine 依赖并启用 feature
use pitalk::wake::{PorcupineProvider, WakeProvider, BuiltinKeyword};

let mut wake = PorcupineProvider::new_with_builtin(
    "your-access-key",  //  console.picovoice.ai 获取
    BuiltinKeyword::Jarvis,
)?;

wake.set_sensitivity(0.5);

if let Some(event) = wake.process(&audio_frame).await? {
    println!("唤醒词: {}", event.keyword);
}

架构设计

所有模块均采用 Trait 抽象,支持自定义实现:

// LLM Provider
pub trait LLMProvider: Send + Sync {
    async fn chat(&self, messages: &[Message]) -> Result<String, LLMError>;
    async fn chat_stream(&self, messages: &[Message]) -> Result<MessageStream, LLMError>;
}

// TTS Provider
pub trait TTSProvider: Send + Sync {
    async fn synthesize(&self, text: &str) -> Result<AudioStream, TTSError>;
    async fn list_voices(&self) -> Result<Vec<Voice>, TTSError>;
}

// ASR Provider
pub trait ASRProvider: Send + Sync {
    async fn transcribe(&self, audio: &[u8]) -> Result<String, ASRError>;
}

Feature Flags

Flag 说明 依赖
(default) 基础功能,使用 Mock Provider
cpal 真实音频设备 I/O cpal, 音频硬件
sherpa 本地语音处理 (ASR/TTS/VAD/KWS/声纹) sherpa-rs, 模型文件
porcupine Porcupine 唤醒词检测 pv_porcupine

模块状态

模块 状态 说明
LLM ✅ 完成 OpenAI、DeepSeek、Qwen、Mock
TTS ✅ 完成 Edge TTS、Sherpa、Mock
ASR ✅ 完成 Whisper API、Sherpa、Mock
Wake ✅ 完成 Porcupine、Sherpa、Mock
Audio ✅ 完成 cpal、Mock、VAD
Speaker ✅ 完成 声纹提取、说话人识别
Pipeline ✅ 完成 语音交互流程编排
Tools ✅ 完成 定时器、音乐、天气、智能家居
Database ✅ 完成 SQLite 存储 (对话、声纹、设置)

硬件推荐

配置 最低 推荐
树莓派 4B (4GB) 5 (8GB)
麦克风 USB 麦克风 ReSpeaker 2/4-Mics HAT
音箱 3.5mm/蓝牙 带功放的小音箱

许可证

MIT License

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages