文章目录
- [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 Key 和Secret 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_KEY
与SECRET_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__)