当前位置: 首页 > news >正文

Android Audio基础——音频混合器介绍(十二)

        在 Android 平台上,音频混合器 AudioMixer 主要用在 AudioFlinger 里,将多路音频源数据混音(包括音频叠加、音量控制、音频效果添加及处理声道等),以方便送进音频设备播放出来。位于 framework 的音频处理模库 libaudioprocessing(frameworks/av/media/libaudioprocessing)中。

一、音频混合器

        混音器(AudioMixer)是在混音回放线程类(MixerThread)中的构造函数内创建。

1、混合器创建

源码位置:/frameworks/av/services/audioflinger/Threads.cpp

AudioFlinger::MixerThread::MixerThread(……) 
{……mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);……
}

        这说明了一个 MixerThread 对应一个 AudioMixer,而且 MixerThread 传了两个参数给AudioMixer:

  • mNormalFrameCount:一次输送数据的长度,把源 buffer 的音频数据写入目的 buffer。
  • mSampleRate:音频数据输出的采样率。

2、功能接口

AudioMixer

源码位置:/frameworks/av/media/libaudioprocessing/AudioMixer.cpp

void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider)

        此方法允许用户根据给定的索引设置一个音频缓冲区提供者,name 作为索引,track 是从 mActiveTracks 取出来的 Track 对象。

void AudioMixer::setParameter(int name, int target, int param, void *value)

        该函数主要用于设置特定参数值,涉及到了音频处理中多种参数的设置,包括通道掩码、缓冲区指针、格式、重采样、音量衰减、时间拉伸等。

        这里我们主要看一下 target 和 param 的不通所对应实现的相关功能:

targetparam功能
TRACKCHANNEL_MASK设置通道掩码,并重新计算混合后的通道掩码
MAIN_BUFFER更换主要缓冲区指针,并准备通道调整
AUX_BUFFER音频处理中的辅助缓冲区管理(基类)
FORMAT修改音频格式,并准备重新格式化
MIXER_FORMAT指定音频数据的格式,如 PCM、AAC 等
MIXER_CHANNEL_MASK指定音频通道的掩码,指示哪些声道有效
HAPTIC_ENABLED控制是否启用触觉反馈
HAPTIC_INTENSITY控制触觉反馈的强度
RESAMPLE/调整音频数据的采样率(基类)
RAMP_VOLUME逐渐改变音频的音量,实现平滑过渡(基类)
VOLUME直接设置音频的音量(基类)
TIMESTRETCH处理播放速率变化等时间拉伸相关设置

         标注基类是直接透传调用 AudioMixerBase.cpp 中的对应函数。AudioMixer 继承自 AudioMixerBase,同时基类中也有几个比较重要的函数。

AudioMixerBase

源码位置:/frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp

// 启用指定的音频轨道
void AudioMixerBase::enable(int name)
// 禁用指定的音频轨道
void AudioMixerBase::disable(int name)
// 销毁指定的音频轨道
void AudioMixerBase::destroy(int name)

        这几个函数主要是用于音频轨道的控制功能,确保了轨道能够在不同状态下正确工作。

刷新函数

        上面的接口中大量调用 invalidate() 函数,该函数的主要作用就是刷新 Track。

源码位置:/frameworks/av/media/libaudioprocessing/include/media/AudioMixerBase.h

// 当轨道信息改变,需要确定一个新的进程钩子时调用
void invalidate() {mHook = &AudioMixerBase::process__validate;
}

        可以看到该函数用于当轨道信息发生变化时,重新确定一个新的进程钩子。这个钩子通常用于验证和处理轨道信息的变化,调用 AudioMixerBase 中的 process__validate() 函数。

void AudioMixerBase::process__validate()
{……// 遍历轨道for (const auto &pair : mTracks) {const int name = pair.first;const std::shared_ptr<TrackBase> &t = pair.second;// 判断该track是否需要混音if (!t->enabled) continue;// 将启用的轨道添加到 mEnabled 和 mGroups 中mEnabled.emplace_back(name);mGroups[t->mainBuffer].emplace_back(name);// 计算每个轨道的需求 (needs) 并设置相应的处理钩子 (hook)uint32_t n = 0;// 可以溢出(掩码只有3位)n |= NEEDS_CHANNEL_1 + t->channelCount - 1;// 设置重采样 NEEDS_RESAMPLE 标志位if (t->doesResample()) {n |= NEEDS_RESAMPLE;}// 设置辅助通道 NEEDS_AUX 标志位if (t->auxLevel != 0 && t->auxBuffer != NULL) {n |= NEEDS_AUX;}if (t->volumeInc[0]|t->volumeInc[1]) {// 增益正在变化volumeRamp = true;} else if (!t->doesResample() && t->volumeRL == 0) {// 不需要重采样且增益为零n |= NEEDS_MUTE;}// 将所有设置好的标志位保存到 t->needs 中t->needs = n;if (n & NEEDS_MUTE) { // 需要静音t->hook = &TrackBase::track__nop;} else {if (n & NEEDS_AUX) { // 处理辅助通道all16BitsStereoNoResample = false;}if (n & NEEDS_RESAMPLE) { // 处理重采样all16BitsStereoNoResample = false;resampling = true;if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1&& t->channelMask == AUDIO_CHANNEL_OUT_MONO // MONO_HACK&& isAudioChannelPositionMask(t->mMixerChannelMask)) { // 重采样单声道t->hook = TrackBase::getTrackHook(TRACKTYPE_RESAMPLEMONO, t->mMixerChannelCount,t->mMixerInFormat, t->mMixerFormat);} else if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2&& t->useStereoVolume()) { // 重采样立体声t->hook = TrackBase::getTrackHook(TRACKTYPE_RESAMPLESTEREO, t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);} else { // 其他情况t->hook = TrackBase::getTrackHook(TRACKTYPE_RESAMPLE, t->mMixerChannelCount,t->mMixerInFormat, t->mMixerFormat);}ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,"Track %d needs downmix + resample", name);} else { // 处理无需重采样的情况……}}}// 根据条件选择合适的处理钩子mHook = &AudioMixerBase::process__nop;if (mEnabled.size() > 0) {if (resampling) {if (mOutputTemp.get() == nullptr) {mOutputTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);}if (mResampleTemp.get() == nullptr) {mResampleTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);}mHook = &AudioMixerBase::process__genericResampling;} else {mHook = &AudioMixerBase::process__genericNoResampling;if (all16BitsStereoNoResample && !volumeRamp) {if (mEnabled.size() == 1) {const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];if ((t->needs & NEEDS_MUTE) == 0) {mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK, t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat, t->useStereoVolume());}}}}}……process();// 处理音量渐变,设置最优状态和轨道处理钩子if (mEnabled.size() > 0) {bool allMuted = true;for (const int name : mEnabled) {const std::shared_ptr<TrackBase> &t = mTracks[name];if (!t->doesResample() && t->volumeRL == 0) {t->needs |= NEEDS_MUTE;t->hook = &TrackBase::track__nop;} else {allMuted = false;}}if (allMuted) {mHook = &AudioMixerBase::process__nop;} else if (all16BitsStereoNoResample) {if (mEnabled.size() == 1) {const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK, t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat, t->useStereoVolume());}}}
}

        这些步骤确保了音频轨道信息变化时能够正确处理各种情况,包括重采样、音量渐变等。 

二、AudioMixer混音

        关于混音,我们已经知道:混音以 track 为源,mainBuffer 为目标,frameCount 为一次混音长度。AudioMixer 最多能维护 32 个 track。track 可以对应不同 mainBuffer,尽管一般情况下他们的 mainBuffer 都是同一个。

        调用 AudioMixer 的 process 方法进行混音的,实际上混音的方法是调用 AudioMixerBase 内的 process_xxx 方法,各个 process 方法大同小异。下面来分析 process__genericResampling 这个方法。 

1、AudioMixerBase.cpp

process__genericResampling

void AudioMixerBase::process__genericResampling()
{ALOGVV("process__genericResampling\n");// 初始化 outTemp 指针int32_t * const outTemp = mOutputTemp.get(); // 获取当前帧数 numFramessize_t numFrames = mFrameCount;// 遍历每个音频组 mGroupsfor (const auto &pair : mGroups) {const auto &group = pair.second;// 获取第一个轨道 t1const std::shared_ptr<TrackBase> &t1 = mTracks[group[0]];// 清除了临时缓冲区 outTempmemset(outTemp, 0, sizeof(*outTemp) * t1->mMixerChannelCount * mFrameCount);// 处理每个轨道for (const int name : group) {const std::shared_ptr<TrackBase> &t = mTracks[name];int32_t *aux = NULL;if (CC_UNLIKELY(t->needs & NEEDS_AUX)) {aux = t->auxBuffer;}// 如果轨道需要重采样,则直接调用重采样钩子if (t->needs & NEEDS_RESAMPLE) {(t.get()->*t->hook)(outTemp, numFrames, mResampleTemp.get() /* naked ptr */, aux);} else { // 逐帧获取数据并调用处理钩子// 清除临时缓冲区size_t outFrames = 0;// 获取每个轨道的数据并调用相应的处理钩子while (outFrames < numFrames) {t->buffer.frameCount = numFrames - outFrames;t->bufferProvider->getNextBuffer(&t->buffer);t->mIn = t->buffer.raw;// t->mIn == nullptr:启用混音后刚刚刷新音轨if (t->mIn == nullptr) break;(t.get()->*t->hook)(outTemp + outFrames * t->mMixerChannelCount, t->buffer.frameCount,mResampleTemp.get() /* naked ptr */, aux != nullptr ? aux + outFrames : nullptr);outFrames += t->buffer.frameCount;t->bufferProvider->releaseBuffer(&t->buffer);}}}// 将处理后的数据转换为混音器所需的格式convertMixerFormat(t1->mainBuffer, t1->mMixerFormat,outTemp, t1->mMixerInFormat, numFrames * t1->mMixerChannelCount);}
}

        这些步骤确保了在需要重采样的情况下,能够正确处理每个轨道的数据,并将其转换为所需的格式。


http://www.mrgr.cn/news/56658.html

相关文章:

  • 基于单片机优先级的信号状态机设计
  • Eclipse Java 构建路径
  • Maven - Assembly实战
  • Unity学习记录-API
  • 算法:KMP算法详解
  • SQL Server 2019数据库“正常,已自动关闭”
  • 深入解析 FarmHash 算法C++ 实现与性能优化
  • 【源码+文档】基于SpringBoot+Vue城市智慧社区综合服务平台【提供源码+答辩PPT+参考文档+项目部署】
  • 什么是感知与计算融合?
  • java中double强制转换int引发的OOM问题
  • 大厂物联网(IoT)高频面试题及参考答案
  • AIGC文本生成视频
  • Python中的isinstance和hasattr
  • 【使用Flask构建RESTful API】从零开始开发简单的Web服务!
  • 追寻数组的轨迹,解开算法的情愫
  • Python语法基础:复数
  • 【在Linux世界中追寻伟大的One Piece】Socket编程UDP
  • 道可云人工智能元宇宙每日资讯|上海市互联网业联合会人工智能专业委员会成立
  • 上海亚商投顾:沪指缩量震荡 风电、传媒股集体走强
  • 大语言模型及其应用场景
  • 数据集笔记:北京市摩拜数据(摩拜杯算法挑战赛)
  • 【2024工业图像异常检测文献】GLASS: 基于全局和局部异常共合成策略的异常检测方法
  • 配置linux网络的操作步骤(grub,nmcli)
  • ARM架构流派
  • 【日志】Unity3D模型导入基本问题以及浅谈游戏框架
  • 吃透高并发模型与RPC框架,拿下大厂offer!!!