Chat
Ithy Logo

本地部署FunASR实现流式自动语音识别的完整指南

在Windows平台下使用C++监控麦克风音频并集成FunASR

C++ coding on Windows with microphone and server

关键要点

  • 环境准备与依赖安装:确保系统和开发环境符合要求,并安装必要的库和工具。
  • 麦克风音频配置与数据预处理:使用适当的音频捕获库获取并处理实时音频数据。
  • FunASR集成与优化:将预处理后的音频数据传入FunASR,进行流式识别并优化系统性能。

一、环境准备

1. 系统与工具要求

在开始部署FunASR之前,需确保您的Windows系统满足以下要求,并安装相关开发工具:

系统要求

  • Windows 10/11操作系统
  • 启用虚拟机平台和适用于Linux的Windows子系统(WSL)

开发工具

  • Visual Studio 2022(安装C++桌面开发组件)
  • Git工具
  • Docker Desktop(用于容器化部署)
  • Microsoft Build Tools

2. 下载与安装FunASR模型

从ModelScope或FunASR的GitHub仓库下载所需的模型文件,并将其放置在指定目录。例如:


git clone https://www.modelscope.cn/damo/speech_fsmn_vad_zh-cn-16k-common-onnx.git
git clone https://www.modelscope.cn/damo/speech_paraformer-large-vad-punc_asr_nat-zh-cn-16k-common-vocab8404-onnx.git
git clone https://www.modelscope.cn/damo/speech_ngram_lm_zh-cn-ai-wesp-fst.git
git clone https://www.modelscope.cn/damo/punc_ct-transformer_cn-en-common-vocab471067-large-onnx.git
  

3. 启动FunASR服务端

使用Docker启动FunASR服务端,并挂载模型目录:


docker run -p 10095:10095 -it --privileged=true \
-v d:/FunASR/model:/workspace/models \
registry.cn-hangzhou.aliyuncs.com/funasr_repo/funasr:funasr-runtime-sdk-online-cpu-0.1.9
  

二、安装依赖

1. 安装PortAudio库

PortAudio是一个跨平台的音频I/O库,用于捕获麦克风音频。可通过以下步骤进行安装:


git clone https://git.assembla.com/portaudio.git
cd portaudio
./configure
make
make install
  

2. 安装Boost库

Boost库提供了丰富的C++库支持,特别适用于网络通信。使用vcpkg进行安装:


vcpkg install boost:x86-windows
  

3. 安装其他必要库

根据FunASR的需求,可能需要安装以下库:

  • libsamplerate(用于采样率转换)
  • WebRTC(用于音频捕获和处理)
  • その他相關的C++库,如cURL、JSON解析库等

三、配置麦克风输入

1. 使用PortAudio进行音频捕获

编写C++代码,使用PortAudio库实时捕获麦克风音频数据。以下是基本的音频捕获设置:


// 初始化PortAudio
PaError err = Pa_Initialize();
if (err != paNoError) {
    std::cerr << "PortAudio初始化失败: " << Pa_GetErrorText(err) << std::endl;
    return -1;
}

// 打开默认输入流
PaStream* stream;
err = Pa_OpenDefaultStream(&stream, 1, 0, paInt16, 16000, 256, nullptr, nullptr);
if (err != paNoError) {
    std::cerr << "打开音频流失败: " << Pa_GetErrorText(err) << std::endl;
    return -1;
}

// 启动流
err = Pa_StartStream(stream);
if (err != paNoError) {
    std::cerr << "启动音频流失败: " << Pa_GetErrorText(err) << std::endl;
    return -1;
}
  

2. 音频数据预处理

捕获到的音频数据需要进行预处理,以符合FunASR模型的输入要求。这包括:

  • 采样率转换(如果需要)
  • 数据归一化
  • 分帧处理:将连续的音频数据按时间窗口进行分帧处理

四、集成FunASR

1. 建立WebSocket连接

使用Boost.Beast库建立与FunASR服务端的WebSocket连接:


#include <boost/beast/websocket.hpp>
#include <boost/asio.hpp>

boost::asio::io_context ioc;
boost::asio::ip::tcp::resolver resolver(ioc);
auto const results = resolver.resolve("127.0.0.1", "10095");

boost::beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ioc);
boost::asio::connect(ws.next_layer(), results.begin(), results.end());
ws.handshake("127.0.0.1", "/");
  

2. 发送音频数据到FunASR

将预处理后的音频数据通过WebSocket实时发送到FunASR服务端:


// 读取音频数据
short buffer[256];
while (true) {
    Pa_ReadStream(stream, buffer, 256);
    // 发送数据
    ws.write(boost::asio::buffer(buffer, sizeof(buffer)));
    
    // 接收识别结果
    boost::beast::flat_buffer res;
    ws.read(res);
    std::string result = boost::beast::buffers_to_string(res.data());
    std::cout << "识别结果: " << result << std::endl;
}
  

3. 处理识别结果

FunASR会返回识别结果,可以实时显示或进行后续处理:


// 处理返回的识别结果
ws.on_message([](const std::string& result) {
    std::cout << "识别结果: " << result << std::endl;
});
  

五、调试与优化

1. 测试与验证

在开始实时识别之前,建议先使用预先录制的音频文件进行离线测试,确保预处理和识别流程的正确性。

2. 优化音频延迟

实时识别的延迟需要尽量控制,可以通过调整音频缓冲区大小、帧长度等参数来优化性能。

3. 使用多线程设计

为了确保音频捕获与识别过程互不干扰,建议采用多线程设计:

  • 主线程负责音频捕获并将数据写入共享队列。
  • 工作线程从队列中读取数据并调用FunASR进行识别。

4. 利用GPU加速

如果识别速度较慢,可以考虑通过GPU加速或使用TensorRT等工具对FunASR模型进行优化。


六、代码示例

完整C++客户端示例

以下是一个简化的C++客户端示例,展示了如何捕获麦克风音频并与FunASR进行实时通信:


#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <atomic>
#include <portaudio.h>
#include <boost/beast/websocket.hpp>
#include <boost/asio.hpp>

// 共享队列与互斥锁
std::queue<std::vector<short>> audioQueue;
std::mutex queueMutex;
std::atomic<bool> capturing(true);

// PortAudio 回调函数——采集音频数据
static int audioCallback(const void *inputBuffer, void *outputBuffer,
                         unsigned long framesPerBuffer,
                         const PaStreamCallbackTimeInfo* timeInfo,
                         PaStreamCallbackFlags statusFlags,
                         void *userData)
{
    const short* in = reinterpret_cast<const short*>(inputBuffer);
    std::vector<short> frame(in, in + framesPerBuffer);
    {
        std::lock_guard<std::mutex> lock(queueMutex);
        audioQueue.push(frame);
    }
    return paContinue;
}

// 模型识别线程函数
void recognitionThread(boost::beast::websocket::stream<boost::asio::ip::tcp::socket>& ws)
{
    while (capturing || !audioQueue.empty())
    {
        std::vector<short> audioFrame;
        {
            std::lock_guard<std::mutex> lock(queueMutex);
            if (!audioQueue.empty())
            {
                audioFrame = std::move(audioQueue.front());
                audioQueue.pop();
            }
        }
        if (!audioFrame.empty()) {
            ws.write(boost::asio::buffer(audioFrame.data(), audioFrame.size() * sizeof(short)));
            
            // 接收识别结果
            boost::beast::flat_buffer res;
            ws.read(res);
            std::string result = boost::beast::buffers_to_string(res.data());
            if (!result.empty()) {
                std::cout << "识别结果: " << result << std::endl;
            }
        }
        else {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
}

int main()
{
    // 初始化PortAudio
    PaError err = Pa_Initialize();
    if (err != paNoError) {
        std::cerr << "PortAudio初始化失败: " << Pa_GetErrorText(err) << std::endl;
        return -1;
    }

    // 打开默认输入流
    PaStream* stream;
    err = Pa_OpenDefaultStream(&stream, 1, 0, paInt16, 16000, 256, audioCallback, nullptr);
    if (err != paNoError) {
        std::cerr << "打开音频流失败: " << Pa_GetErrorText(err) << std::endl;
        return -1;
    }

    // 启动音频流
    err = Pa_StartStream(stream);
    if (err != paNoError) {
        std::cerr << "启动音频流失败: " << Pa_GetErrorText(err) << std::endl;
        return -1;
    }

    // 初始化Boost.Asio和WebSocket
    boost::asio::io_context ioc;
    boost::asio::ip::tcp::resolver resolver(ioc);
    auto const results = resolver.resolve("127.0.0.1", "10095");
    boost::beast::websocket::stream<boost::asio::ip::tcp::socket> ws(ioc);
    boost::asio::connect(ws.next_layer(), results.begin(), results.end());
    ws.handshake("127.0.0.1", "/");

    // 启动识别线程
    std::thread recogThread(recognitionThread, std::ref(ws));

    // 主循环,等待用户输入退出
    std::cout << "按回车键停止采集并结束识别..." << std::endl;
    std::cin.get();
    capturing = false;

    // 停止PortAudio流
    Pa_StopStream(stream);
    Pa_CloseStream(stream);
    Pa_Terminate();

    // 等待识别线程结束
    recogThread.join();

    return 0;
}
  

七、常见问题及解决方案

1. 音频延迟过高

解决方案:

  • 调整缓冲区大小,减少每次读取的音频帧数。
  • 优化线程同步,避免不必要的等待。
  • 使用高性能的音频捕获库,如WebRTC。

2. 识别准确率不高

解决方案:

  • 确保音频采样率和格式与模型要求一致。
  • 使用噪声抑制和回声消除等音频预处理技术。
  • 更新FunASR模型至最新版本,提升识别性能。

3. WebSocket连接失败

解决方案:

  • 检查FunASR服务端是否正确启动并监听对应端口。
  • 确保防火墙未阻止WebSocket连接。
  • 验证WebSocket客户端的连接参数是否正确。

八、性能优化

1. 多线程处理

将音频捕获和识别处理分离到不同线程,确保实时性和响应性。

2. 音频缓冲区优化

合理设置音频缓冲区大小,平衡延迟和系统负载。

3. GPU加速与TensorRT优化

使用GPU进行模型推理,或通过TensorRT进行模型优化,提升识别速度。

4. 内存管理与资源优化

优化内存使用,避免内存泄漏和资源浪费,确保系统长时间稳定运行。


九、总结

在Windows平台上使用C++本地部署FunASR,实现流式自动语音识别,需要经过环境准备、依赖安装、麦克风音频配置、FunASR集成以及调试优化等多个步骤。通过合理的设计和优化,可以实现高效、准确的实时语音识别系统。

十、参考文献


Last updated February 7, 2025
Ask Ithy AI
Export article
Delete article