调用百度云语音识别服务——实现c++接口识别语音

文章目录

  • [C++ 调用百度云语音识别 API 全流程:从 0 到 1 实现语音转文字](#C++ 调用百度云语音识别 API 全流程:从 0 到 1 实现语音转文字)
    • 一、准备工作:账号与环境搭建
      • [1. 注册百度云账号并开通服务](#1. 注册百度云账号并开通服务)
      • [2. 创建应用并获取密钥](#2. 创建应用并获取密钥)
      • [3. 准备开发环境与依赖库](#3. 准备开发环境与依赖库)
        • [安装依赖(以 Linux 为例):](#安装依赖(以 Linux 为例):)
    • [二、百度云语音识别 API 调用流程](#二、百度云语音识别 API 调用流程)
    • [三、 官方调用实例](#三、 官方调用实例)
      • [1. 新建client](#1. 新建client)
      • [2. 语音识别](#2. 语音识别)
        • [2.1 接口描述](#2.1 接口描述)
        • [2.2 请求说明](#2.2 请求说明)
    • [四、 封装调用](#四、 封装调用)

C++ 调用百度云语音识别 API 全流程:从 0 到 1 实现语音转文字

在即时通讯、智能助手等场景中,语音转文字是一项高频需求。百度云语音识别 API 提供了稳定、高效的语音转文字能力,本文将以短语音识别为例,手把手教你用 C++ 实现调用,从前期准备到代码落地,每一步都清晰可操作。

一、准备工作:账号与环境搭建

在写代码前,我们需要完成百度云账号配置和开发环境准备,这是调用 API 的基础。

1. 注册百度云账号并开通服务

2. 创建应用并获取密钥

  • 在语音技术控制台左侧导航栏选择 "应用列表",点击 "创建应用"。
  • 填写应用名称(如 "C++ 语音识别测试"),选择需要的接口(至少勾选 "语音识别 - 短语音"),其他项默认即可。
  • 创建成功后,在应用列表中可看到API KeySecret Key ,这两个密钥是获取访问令牌的关键,后续会用到。

3. 准备开发环境与依赖库

C++ 调用 API 需要处理 HTTP 请求、JSON 解析和 Base64 编码,推荐以下库:

  • libcurl:处理 HTTP/HTTPS 请求(跨平台,支持 Windows、Linux、Mac)。
  • nlohmann/json:轻量级 JSON 解析库(仅需一个头文件,无需编译)。
  • Base64 编码库 :可使用libb64或自定义实现(处理音频文件转 Base64)。
安装依赖(以 Linux 为例):
bash 复制代码
# 安装libcurl
sudo apt-get install libcurl4-openssl-dev

# 下载nlohmann/json(直接放在项目目录)
wget https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp

# 下载libb64(Base64编码库)可选
git clone https://github.com/libb64/libb64.git
cd libb64
make  # 编译生成libb64.a

二、百度云语音识别 API 调用流程

建议使用简单的SDK调用,再百度云官方下载即可。c++建议选择如下模块下载即可。解压缩即可使用。

三、 官方调用实例

1. 新建client

client是语音识别的C++客户端,为使用语音识别的开发人员提供了一系列的交互方法。当您引入了相应头文件后就可以新建一个client对象

用户可以参考如下代码新建一个client:

cpp 复制代码
    #include "speech.h"

    // 设置APPID/AK/SK
    std::string app_id = "你的 App ID";
    std::string api_key = "你的 Api key";
    std::string secret_key = "你的 Secret Key";

    aip::Speech client(app_id, api_key, secret_key);

在上面代码中,常量APP_ID在百度云控制台中创建,常量API_KEYSECRET_KEY是在创建完毕应用后,系统分配给用户的,均为字符串,用于标识用户,为访问做签名验证,可在AI服务控制台中的应用列表中查看。

2. 语音识别

2.1 接口描述

向远程服务上传整段语音进行识别

2.2 请求说明

举例:

cpp 复制代码
void asr(aip::Speech client)
{
    // 无可选参数调用接口
    std::string file_content;
    aip::get_file_content("./assets/voice/16k_test.pcm", &file_content);
    Json::Value result = client.recognize(file_content, "pcm", 16000, aip::null);

    // 极速版调用函数
    // Json::Value result = client.recognize_pro(file_content, "pcm", 16000, aip::null);

    // 如果需要覆盖或者加入参数
    std::map<std::string, std::string> options;
    options["dev_pid"] = "1537";
    Json::Value result = client.recognize(file_content, "pcm", 16000, options);
}

接口函数说明:

参数 类型 描述 是否必须
data byte[] 语音二进制数据, 语音文件的格式,pcm 或者 wav 或者 amr。不区分大小写
format String 语音文件的格式,pcm 或者 wav 或者 amr。不区分大小写。推荐pcm文件
rate int 采样率,16000,固定值
cuid String 用户唯一标识,用来区分用户,填写机器 MAC 地址或 IMEI 码,长度为60以内
dev_pid int 不填写lan参数生效,都不填写,默认1537(普通话 输入法模型),dev_pid参数见下面的表格
lm_id int 自训练平台模型id,填dev_pid = 8001 或 8002生效 选填

dev_pid 参数列表

dev_pid 语言 模型 是否有标点 备注
1537 普通话(纯中文识别) 语音近场识别模型 有标点 支持自定义词库
1737 英语 英语模型 无标点 不支持自定义词库
1637 粤语 粤语模型 有标点 不支持自定义词库
1837 四川话 四川话模型 有标点 不支持自定义词库

返回数据参数详情

参数 类型 是否一定输出 描述
err_no int 错误码
err_msg int 错误码描述
sn int 语音数据唯一标识,系统内部产生,用于 debug
result int 识别结果数组,提供1-5 个候选结果,string 类型为识别的字符串, utf-8 编码

返回样例:

json 复制代码
// 成功返回
{
    "err_no": 0,
    "err_msg": "success.",
    "corpus_no": "15984125203285346378",
    "sn": "481D633F-73BA-726F-49EF-8659ACCC2F3D",
    "result": ["北京天气"]
}

// 失败返回
{
    "err_no": 2000,
    "err_msg": "data empty.",
    "sn": null
}

四、 封装调用

cpp 复制代码
#include "LogTool.hpp"
#include <aip-cpp-sdk/base/http.h>
#include <aip-cpp-sdk/speech.h>
#include <curl/curl.h>
#include <jsoncpp/json/json.h>
#include <memory>
#include <stdexcept>
#include <string>


namespace ASRTool   
{
    class SpeechToText
    {
    public:
        SpeechToText(const std::string& app_id
            , const std::string& api_key
            , const std::string& secret_key )
        {
            LogModule::Log::Init();
            app_id_ = app_id;
            api_key_ = api_key;
            secret_key_ = secret_key;
            client_ = std::make_unique<aip::Speech>(app_id_, api_key_, secret_key_); // 若失败直接抛异常
            if (!IsValid())
            {
                throw std::runtime_error("Speech client initialize fail!");
            }
        }

        std::string Recognize(const std::string& file_content)
        {
            if (!IsValid())
            {
                throw std::runtime_error("Speech client not initialized");
            }

            Json::Value result;
            TryCatch([&](){
                result = client_->recognize(file_content, "pcm", 16000, aip::null);
            });
            if(result["err_no"].asInt() != 0)
            {
                //throw std::runtime_error(result["err_msg"].asString());
                LOG_ERROR("解析语音失败:{}", result["err_msg"].asString());
            }
            else
            {
                return result["result"][0].asString();
            }
            return std::string();
        }
        
        // 检查客户端是否初始化成功
        bool IsValid() const 
        {
            return client_ != nullptr;
        }

        ~SpeechToText()
        {}
    private:
        // 改进 TryCatch:增加参数控制是否传播异常
        template<typename T = std::exception>
        bool TryCatch(std::function<void ()> func, bool propagate = false)
        {
            try
            {
                func();
                return true; // 执行成功
            }
            catch (const T& e)
            {
                LOG_ERROR("Error Get:{}", e.what());
                if (propagate) {
                    throw e; // 向上传播异常
                }
                return false; // 执行失败
            }
        }
    private:
        std::string app_id_;
        std::string api_key_;
        std::string secret_key_;
        std::unique_ptr<aip::Speech> client_;
    };
};

LogTool.hpp

cpp 复制代码
#pragma once
#include <atomic>
#include <memory>
#include <string>
#include <vector>

#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>

namespace LogModule 
{
    // 简化输出模式:只保留常用的 3 种,语义更清晰
    enum class OutputMode
    {
        ConsoleOnly,  // 仅控制台
        FileOnly,     // 仅文件
        Both          // 控制台+文件
    };

    // 日志配置结构体:简化默认值,移除冗余参数
    struct LogConfig
    {
        OutputMode output_mode = OutputMode::ConsoleOnly;  // 默认仅控制台
        bool enable_debug = true;                          // 默认开启调试级日志
        std::string log_file = "app.log";                  // 默认日志文件名(更规范)
        bool use_async = false;                            // 默认同步日志(简化使用)
        size_t async_queue_size = 4096;                    // 异步队列默认值(1<<12 简化为 4096)
        size_t async_thread_num = 1;                       // 异步线程默认 1 个(足够日常使用)
    };

    class Log
    {
    public:
        // 1. 简化初始化:默认配置可直接调用,无需传参
        static void Init(const LogConfig& config = LogConfig())
        {
            if (is_init_.load(std::memory_order_acquire))  // 原子操作保证线程安全
                return;

            log_config_ = config;
            if (!create_logger())  // 创建失败直接返回,避免后续崩溃
                return;

            is_init_.store(true, std::memory_order_release);
            logger_->info("日志模块初始化完成");  // 简化日志内容
        }

        // 2. 单例获取:保持简洁,确保全局唯一
        static Log& GetInstance()
        {
            static Log instance;  // C++11 后静态局部变量线程安全
            return instance;
        }

        // 3. 日志接口:移除重复判断,统一前置检查
        template<typename... Args>
        void trace(const char* fmt, const Args&... args)
        {
            if (!check_init()) return;
            logger_->trace(fmt, args...);
        }

        template<typename... Args>
        void info(const char* fmt, const Args&... args)
        {
            if (!check_init()) return;
            logger_->info(fmt, args...);
        }

        template<typename... Args>
        void debug(const char* fmt, const Args&... args)
        {
            if (!check_init()) return;
            logger_->debug(fmt, args...);
        }

        template<typename... Args>
        void warn(const char* fmt, const Args&... args)
        {
            if (!check_init()) return;
            logger_->warn(fmt, args...);
        }

        template<typename... Args>
        void error(const char* fmt, const Args&... args)
        {
            if (!check_init()) return;
            logger_->error(fmt, args...);
        }

        template<typename... Args>
        void critical(const char* fmt, const Args&... args)
        {
            if (!check_init()) return;
            logger_->critical(fmt, args...);
        }

        // 4. 简化关闭接口:直接调用 spdlog 关闭,无需额外逻辑
        static void Shutdown()
        {
            if (is_init_.load(std::memory_order_acquire))
            {
                logger_->info("日志模块关闭");
                spdlog::shutdown();
                is_init_.store(false, std::memory_order_release);
            }
        }

    private:
        // 私有构造/析构:禁止外部创建
        Log() = default;
        ~Log() = default;
        // 禁止拷贝赋值:确保单例唯一性
        Log(const Log&) = delete;
        Log& operator=(const Log&) = delete;

        // 5. 统一初始化检查:避免每个接口重复写判断
        bool check_init()
        {
            if (!is_init_.load(std::memory_order_acquire))
            {
                // 未初始化时用标准输出提示(避免 logger_ 空指针崩溃)
                fprintf(stderr, "[日志错误] 未初始化,请先调用 Log::Init()\n");
                return false;
            }
            return true;
        }

        // 6. 简化日志器创建:移除冗余逻辑,增加错误处理
        static bool create_logger()
        {
            try
            {
                std::vector<spdlog::sink_ptr> sinks;

                // 根据输出模式添加 sink(逻辑更清晰)
                if (log_config_.output_mode == OutputMode::ConsoleOnly || 
                    log_config_.output_mode == OutputMode::Both)
                {
                    // 控制台 sink:多线程安全版本(mt 后缀)
                    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
                    sinks.push_back(console_sink);
                }

                if (log_config_.output_mode == OutputMode::FileOnly || 
                    log_config_.output_mode == OutputMode::Both)
                {
                    // 文件 sink:多线程安全+覆盖写入(默认不追加,避免日志膨胀)
                    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(
                        log_config_.log_file, false);
                    sinks.push_back(file_sink);
                }

                // 7. 简化日志级别设置:直接映射,无需额外变量
                auto log_level = log_config_.enable_debug ? 
                    spdlog::level::trace : spdlog::level::info;

                // 创建同步/异步日志器(逻辑拆分,更易读)
                if (log_config_.use_async)
                {
                    // 初始化异步线程池(仅当启用异步时创建)
                    spdlog::init_thread_pool(
                        log_config_.async_queue_size, 
                        log_config_.async_thread_num);
                    // 创建异步日志器
                    logger_ = std::make_shared<spdlog::async_logger>(
                        "AppLogger",  // 日志器名称:更规范
                        sinks.begin(), sinks.end(),
                        spdlog::thread_pool(),
                        spdlog::async_overflow_policy::block);  // 溢出时阻塞(避免丢日志)
                }
                else
                {
                    // 创建同步日志器
                    logger_ = std::make_shared<spdlog::logger>(
                        "AppLogger", sinks.begin(), sinks.end());
                }

                // 8. 简化日志格式:保留关键信息,去掉冗余(如 %-8l 简化为 %l)
                logger_->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%t][%l] %v");
                logger_->set_level(log_level);
                spdlog::set_default_logger(logger_);  // 设置为默认日志器,兼容 spdlog 原生接口
                return true;
            }
            catch (const std::exception& e)
            {
                // 异常时用标准输出提示(避免未初始化的 logger_ 崩溃)
                fprintf(stderr, "[日志错误] 创建日志器失败:%s\n", e.what());
                return false;
            }
        }

    private:
        static inline std::shared_ptr<spdlog::logger> logger_;  // 日志器实例
        static inline LogConfig log_config_;                    // 日志配置
        static inline std::atomic<bool> is_init_ = false;       // 初始化状态(原子变量保证线程安全)
    };
}

// 9. 简化宏定义:保留文件行号(调试关键),格式更简洁
#define LOG_TRACE(fmt, ...) LogModule::Log::GetInstance().trace("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_INFO(fmt, ...)  LogModule::Log::GetInstance().info("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_DEBUG(fmt, ...) LogModule::Log::GetInstance().debug("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_WARN(fmt, ...)  LogModule::Log::GetInstance().warn("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_ERROR(fmt, ...) LogModule::Log::GetInstance().error("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_CRIT(fmt, ...)  LogModule::Log::GetInstance().critical("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
相关推荐
郝学胜-神的一滴17 小时前
Qt QPushButton 样式完全指南:从基础到高级实现
linux·开发语言·c++·qt·程序人生
⠀One0ne17 小时前
【C++ 面试题】内存对齐
c++
Elias不吃糖17 小时前
NebulaChat 框架学习笔记:原子变量与左值引用的工程应用
c++·学习
Theliars17 小时前
Ubuntu 上使用 VSCode 调试 C++ (CMake 项目) 指南
c++·vscode·ubuntu·cmake
mjhcsp17 小时前
C++ map 容器:有序关联容器的深度解析与实战
开发语言·c++·map
将编程培养成爱好17 小时前
C++ 设计模式《账本事故:当备份被删光那天》
开发语言·c++·设计模式·备忘录模式
小欣加油18 小时前
leetcode 474 一和零
c++·算法·leetcode·职场和发展·动态规划
LexieLexie18 小时前
从“Hello, World!”说起:理解程序的基本结构
c++
旭意19 小时前
数据结构顺序表
数据结构·c++·蓝桥杯
煤球王子19 小时前
学而时习之:C++中的结构体
c++