小智机器人关键函数解析,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_ 在使用完毕后正确释放资源。
相关推荐
IT_陈寒3 分钟前
SpringBoot高并发优化:这5个被忽视的配置让你的QPS提升300%
前端·人工智能·后端
索迪迈科技4 分钟前
机器学习投票分类
人工智能·机器学习·分类
君名余曰正则8 分钟前
机器学习08——集成学习(Boosting、Bagging、结合策略)
人工智能·机器学习·集成学习
小鑫同学14 分钟前
M4 MacBook Pro + Qwen 模型:企业问答机器人原型微调实战方案
人工智能·llm
搬砖的小码农_Sky24 分钟前
机器人商业化落地需要突破的关键性技术
人工智能·ai·机器人
xwz小王子26 分钟前
Science Robotics 封面论文:RoboBallet利用图神经网络和强化学习规划多机器人协作
人工智能·神经网络·机器人
Deepoch29 分钟前
当按摩机器人“活了”:Deepoc具身智能如何重新定义人机交互体验
人工智能·科技·机器人·人机交互·具身智能
37手游后端团队31 分钟前
Cursor实战:用Cursor实现积分商城系统
人工智能·后端
九章云极AladdinEdu35 分钟前
绿色算力技术栈:AI集群功耗建模与动态调频系统
人工智能·pytorch·深度学习·unity·游戏引擎·transformer·gpu算力
sheepwjl1 小时前
《嵌入式硬件(六):ARM汇编核心内容总结》
汇编·arm开发·嵌入式硬件