调用百度云语音识别服务——实现c++接口识别语音
文章目录
- C++ 调用百度云语音识别 API 全流程:从 0 到 1 实现语音转文字
- 一、准备工作:账号与环境搭建
- 1. 注册百度云账号并开通服务
- 2. 创建应用并获取密钥
- 3. 准备开发环境与依赖库
- 安装依赖(以 Linux 为例):
- 二、百度云语音识别 API 调用流程
- 三、 官方调用实例
- 1. 新建client
- 2. 语音识别
- 2.1 接口描述
- 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 为例):
# 安装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/SKstd::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 请求说明
举例:
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 编码 |
返回样例:
// 成功返回
{"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__)