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 为例):
# 安装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:

    #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 请求说明

举例:

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);
}

接口函数说明:

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

dev_pid 参数列表

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

返回数据参数详情

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

返回样例:

// 成功返回
{
    "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
}

四、 封装调用

#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

#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__)
Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐