小智机器人关键函数解析,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_ 在使用完毕后正确释放资源。
相关推荐
羑悻的小杀马特1 小时前
OpenCV 引擎:驱动实时应用开发的科技狂飙
人工智能·科技·opencv·计算机视觉
guanshiyishi4 小时前
ABeam 德硕 | 中国汽车市场(2)——新能源车的崛起与中国汽车市场机遇与挑战
人工智能
极客天成ScaleFlash5 小时前
极客天成NVFile:无缓存直击存储性能天花板,重新定义AI时代并行存储新范式
人工智能·缓存
澳鹏Appen6 小时前
AI安全:构建负责任且可靠的系统
人工智能·安全
蹦蹦跳跳真可爱5896 小时前
Python----机器学习(KNN:使用数学方法实现KNN)
人工智能·python·机器学习
视界宝藏库7 小时前
多元 AI 配音软件,打造独特音频体验
人工智能
xinxiyinhe7 小时前
GitHub上英语学习工具的精选分类汇总
人工智能·deepseek·学习英语精选
ZStack开发者社区8 小时前
全球化2.0 | ZStack举办香港Partner Day,推动AIOS智塔+DeepSeek海外实践
人工智能·云计算
可待电子单片机设计定制(论文)8 小时前
【STM32设计】基于STM32的智能门禁管理系统(指纹+密码+刷卡+蜂鸣器报警)(代码+资料+论文)
stm32·单片机·嵌入式硬件
Spcarrydoinb9 小时前
基于yolo11的BGA图像目标检测
人工智能·目标检测·计算机视觉