调用百度云语音识别服务——实现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__)
相关推荐
K 旺仔小馒头7 小时前
优选算法:01 双指针巧解移动零问题
c++·算法·刷题
AlexMercer10127 小时前
Ubuntu从零开始配置Git
c++·git·ubuntu·gitee
无敌最俊朗@9 小时前
C++ STL Deque 高频面试题与答案
开发语言·c++
和编程干到底9 小时前
C++基础
开发语言·c++
John.Lewis10 小时前
C++初阶(14)list
开发语言·c++·笔记
hsjkdhs11 小时前
C++文件操作
开发语言·c++
沐怡旸11 小时前
【穿越Effective C++】条款4:确定对象使用前已先被初始化——C++资源管理的基石
c++·面试
淀粉肠kk11 小时前
【C++】多态
c++
ceclar12312 小时前
C++Lambda表达式
开发语言·c++·算法