以下是对 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_
在使用完毕后正确释放资源。