小智机器人关键函数解析,Application::OutputAudio()处理音频数据的输出的函数

以下是对 Application::OutputAudio() 函数的详细解释:
源码:

cpp 复制代码
void Application::OutputAudio() { // 扬声器的输出
    auto now = std::chrono::steady_clock::now();
    auto codec = Board::GetInstance().GetAudioCodec();
    const int max_silence_seconds = 10;

    std::unique_lock<std::mutex> lock(mutex_);
    if (audio_decode_queue_.empty()) {
        // Disable the output if there is no audio data for a long time
        if (device_state_ == kDeviceStateIdle) {
            auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - last_output_time_).count();
            if (duration > max_silence_seconds) {
                codec->EnableOutput(false);
            }
        }
        return;
    }

    if (device_state_ == kDeviceStateListening) {
        audio_decode_queue_.clear();
        return;
    }

    last_output_time_ = now;
    auto opus = std::move(audio_decode_queue_.front());
    audio_decode_queue_.pop_front();
    lock.unlock();

    background_task_->Schedule([this, codec, opus = std::move(opus)]() mutable {
        if (aborted_) {
            return;
        }

        std::vector<int16_t> pcm;
        if (!opus_decoder_->Decode(std::move(opus), pcm)) {
            return;
        }

        // Resample if the sample rate is different
        if (opus_decode_sample_rate_ != codec->output_sample_rate()) {
            int target_size = output_resampler_.GetOutputSamples(pcm.size());
            std::vector<int16_t> resampled(target_size);
            output_resampler_.Process(pcm.data(), pcm.size(), resampled.data());
            pcm = std::move(resampled);
        }
        
        codec->OutputData(pcm);
    });
}

函数概述

Application::OutputAudio() 函数主要负责处理音频数据的输出,通过扬声器播放音频。它会检查音频解码队列的状态,根据不同情况决定是否输出音频、是否禁用输出设备,并且会对音频数据进行解码和重采样处理,最后将处理后的音频数据发送到音频编解码器进行输出。

代码详细解释

1. 时间获取和编解码器获取
cpp 复制代码
auto now = std::chrono::steady_clock::now();
auto codec = Board::GetInstance().GetAudioCodec();
const int max_silence_seconds = 10;
  • now:获取当前时间,用于后续计算音频数据的空闲时长。
  • codec:通过 Board::GetInstance().GetAudioCodec() 获取音频编解码器的实例,用于音频数据的输出。
  • max_silence_seconds:定义了最大允许的静音时长,单位为秒。
2. 加锁并检查音频解码队列
cpp 复制代码
std::unique_lock<std::mutex> lock(mutex_);
if (audio_decode_queue_.empty()) {
    if (device_state_ == kDeviceStateIdle) {
        auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - last_output_time_).count();
        if (duration > max_silence_seconds) {
            codec->EnableOutput(false);
        }
    }
    return;
}
  • std::unique_lock<std::mutex> lock(mutex_):对互斥锁 mutex_ 加锁,确保在访问 audio_decode_queue_ 时线程安全。
  • audio_decode_queue_.empty():检查音频解码队列是否为空。
    • 如果队列为空且设备状态为 kDeviceStateIdle,计算从上次输出音频到现在的时长 duration
    • 如果 duration 超过 max_silence_seconds,调用 codec->EnableOutput(false) 禁用音频输出设备。
    • 无论是否禁用输出设备,只要队列为空,函数直接返回。
3. 检查设备状态
cpp 复制代码
if (device_state_ == kDeviceStateListening) {
    audio_decode_queue_.clear();
    return;
}
  • 如果设备状态为 kDeviceStateListening,清空音频解码队列并返回,不进行音频输出。
4. 更新最后输出时间并取出音频数据
cpp 复制代码
last_output_time_ = now;
auto opus = std::move(audio_decode_queue_.front());
audio_decode_queue_.pop_front();
lock.unlock();
  • last_output_time_ = now:更新最后输出音频的时间。
  • auto opus = std::move(audio_decode_queue_.front()):将队列头部的音频数据(OPUS 格式)移动到 opus 变量中。
  • audio_decode_queue_.pop_front():从队列中移除头部元素。
  • lock.unlock():解锁互斥锁,允许其他线程访问 audio_decode_queue_
5. 调度后台任务处理音频数据
cpp 复制代码
background_task_->Schedule([this, codec, opus = std::move(opus)]() mutable {
    if (aborted_) {
        return;
    }

    std::vector<int16_t> pcm;
    if (!opus_decoder_->Decode(std::move(opus), pcm)) {
        return;
    }

    if (opus_decode_sample_rate_ != codec->output_sample_rate()) {
        int target_size = output_resampler_.GetOutputSamples(pcm.size());
        std::vector<int16_t> resampled(target_size);
        output_resampler_.Process(pcm.data(), pcm.size(), resampled.data());
        pcm = std::move(resampled);
    }

    codec->OutputData(pcm);
});
  • background_task_->Schedule(...):将一个 lambda 函数调度到后台任务中执行。
    • if (aborted_):检查是否已终止操作,如果是则直接返回。
    • opus_decoder_->Decode(std::move(opus), pcm):使用 opus_decoder_ 对 OPUS 格式的音频数据进行解码,解码结果存储在 pcm 向量中。如果解码失败,函数返回。
    • if (opus_decode_sample_rate_ != codec->output_sample_rate()):检查解码后的音频采样率是否与音频编解码器的输出采样率一致。如果不一致,进行重采样处理:
      • output_resampler_.GetOutputSamples(pcm.size()):获取重采样后的样本数量。
      • output_resampler_.Process(pcm.data(), pcm.size(), resampled.data()):执行重采样操作。
      • pcm = std::move(resampled):将重采样后的音频数据移动到 pcm 向量中。
    • codec->OutputData(pcm):将处理后的 PCM 音频数据发送到音频编解码器进行输出。

代码优化建议

  • 错误处理 :在 codec->OutputData(pcm) 调用后,可以添加错误处理逻辑,以处理可能的输出错误。
  • 日志记录:在关键步骤添加日志记录,方便调试和监控程序运行状态。
  • 资源管理 :确保 opus_decoder_output_resampler_ 在使用完毕后正确释放资源。
相关推荐
AI数据皮皮侠4 分钟前
中国区域10m空间分辨率楼高数据集(全国/分省/分市/免费数据)
大数据·人工智能·机器学习·分类·业界资讯
静心问道13 分钟前
大语言模型能够理解并可以通过情绪刺激进行增强
人工智能·语言模型·大模型
运器12320 分钟前
【一起来学AI大模型】算法核心:数组/哈希表/树/排序/动态规划(LeetCode精练)
开发语言·人工智能·python·算法·ai·散列表·ai编程
aneasystone本尊23 分钟前
管理 Claude Code 的工具权限
人工智能
聚客AI37 分钟前
大模型学习进阶路线图:从Prompt到预训练的四阶段全景解析
人工智能·llm·掘金·日新计划
晓131342 分钟前
第七章 OpenCV篇——角点检测与特征检测
人工智能·深度学习·计算机视觉
DeepSeek大模型官方教程1 小时前
NLP之文本纠错开源大模型:兼看语音大模型总结
大数据·人工智能·ai·自然语言处理·大模型·产品经理·大模型学习
MidJourney中文版2 小时前
深度报告:中老年AI陪伴机器人需求分析
人工智能·机器人
王上上2 小时前
【论文阅读41】-LSTM-PINN预测人口
论文阅读·人工智能·lstm
智慧化智能化数字化方案3 小时前
69页全面预算管理体系的框架与落地【附全文阅读】
大数据·人工智能·全面预算管理·智慧财务·智慧预算