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

Android AudioFlinger(五)—— 揭开AudioMixer面纱

前言:

在 Android 音频系统中,AudioMixer 是音频框架中一个关键的组件,用于处理多路音频流的混音操作。它主要存在于音频回放路径中,是 AudioFlinger 服务的一部分。

上一节我们讲threadloop的时候,提到了一个函数prepareTracks_l,在这个函数的最后就调用了 mAudioMixer->create、mAudioMixer->setParameter去设置参数,channel、format、volume等等。

AudioMixer继承自 AudioMixerBase,当我们去看AudioMixer的构造函数的时候发现并没有做任何操作
在这里插入图片描述

那他的初始化代码在哪里呢?

走进AudioMixer:

我们看prepareTracks_l内关于mAudioMixer的调用流程就可以发现,他首先调用了create函数,然而Audiomixer内部却没有实现create接口,我们追溯到它的父类,发现在AudioMixerBase对象种定义了create接口并且实现了。

我们粗略的看下create里主要做了什么,代码多我做了删减。

status_t AudioMixerBase::create(
        int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
{
    LOG_ALWAYS_FATAL_IF(exists(name), "name %d already exists", name);

    if (!isValidChannelMask(channelMask)) {
        ALOGE("%s invalid channelMask: %#x", __func__, channelMask);
        return BAD_VALUE;
    }
    if (!isValidFormat(format)) {
        ALOGE("%s invalid format: %#x", __func__, format);
        return BAD_VALUE;
    }

    auto t = preCreateTrack();
    {
        t->needs = 0;
        t->volume[0] = 0;
        ...
        t->channelCount = audio_channel_count_from_out_mask(channelMask);
        t->enabled = false;
        t->channelMask = channelMask;
        t->sessionId = sessionId;
        t->hook = NULL;
        ...
        // setBufferProvider(name, AudioBufferProvider *) is required before enable(name)
        t->sampleRate = mSampleRate;
        t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
        t->mFormat = format;
        t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
        t->mInputFrameSize = audio_bytes_per_frame(t->channelCount, t->mFormat);
        status_t status = postCreateTrack(t.get());
        if (status != OK) return status;
        mTracks[name] = t;
        return OK;
    }
}

可以看到除了一开始做了channel和format的判断,后面基本上就是对track的初始化,像volume、channel、format、sampleRate还有Hook的初始化。

初始化完成后就开始调用AudioMixer内部的接口了,我们依次往下看发现还有getUnreleasedFrames、setParameter、setBufferProvider、process等。
我们先看下setParameter,当属性变化的时候就会调用到这里。


void AudioMixer::setParameter(int name, int target, int param, void *value)
{
    LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
    const std::shared_ptr<Track> &track = getTrack(name);

    int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
    int32_t *valueBuf = reinterpret_cast<int32_t*>(value);

    switch (target) {

    case TRACK:
        switch (param) {
        case CHANNEL_MASK: {
            const audio_channel_mask_t trackChannelMask =
                static_cast<audio_channel_mask_t>(valueInt);
            if (setChannelMasks(name, trackChannelMask,
                    static_cast<audio_channel_mask_t>(
                            track->mMixerChannelMask | track->mMixerHapticChannelMask))) {
                ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask);
                invalidate();
            }
            } break;
        case MAIN_BUFFER:
            if (track->mainBuffer != valueBuf) {
                track->mainBuffer = valueBuf;
                ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
                if (track->mKeepContractedChannels) {
                    track->prepareForAdjustChannels(mFrameCount);
                }
                invalidate();
            }
            break;
        case AUX_BUFFER:
            AudioMixerBase::setParameter(name, target, param, value);
            break;
        case FORMAT: {
            audio_format_t format = static_cast<audio_format_t>(valueInt);
            if (track->mFormat != format) {
                ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
                track->mFormat = format;
                ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
                track->prepareForReformat();
                invalidate();
            }
            } break;
        case MIXER_FORMAT: {
            audio_format_t format = static_cast<audio_format_t>(valueInt);
            if (track->mMixerFormat != format) {
                track->mMixerFormat = format;
                ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format);
                if (track->mKeepContractedChannels) {
                    track->prepareForAdjustChannels(mFrameCount);
                }
            }
            } break;
        case MIXER_CHANNEL_MASK: {
            const audio_channel_mask_t mixerChannelMask =
                    static_cast<audio_channel_mask_t>(valueInt);
            if (setChannelMasks(name, static_cast<audio_channel_mask_t>(
                                    track->channelMask | track->mHapticChannelMask),
                    mixerChannelMask)) {
                ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask);
                invalidate();
            }
            } break;
...
        default:
            LOG_ALWAYS_FATAL("setParameter track: bad param %d", param);
        }
        break;

    case RESAMPLE:
    case RAMP_VOLUME:
    case VOLUME:
        AudioMixerBase::setParameter(name, target, param, value);
        break;
    case TIMESTRETCH:
        switch (param) {
        case PLAYBACK_RATE: {
            const AudioPlaybackRate *playbackRate =
                    reinterpret_cast<AudioPlaybackRate*>(value);
...
        } break;
        default:
            LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
        }
        break;

    default:
        LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
    }
}

函数的主要结构就是一个switch,首先通过trackId找到对应的track对象,然后去设置对应track的parameter参数,例如 CHANNEL_MASK、FORMAT、MAIN_BUFFER等。

这只是设置参数,那混音在哪里呢?我们继续往下看process

void process() {
    preProcess();
    (this->*mHook)();
    postProcess();
}

这里主要就是调用mHook,mHook是一个函数指针,他会根据不同的场景分别调用不同的函数。

  • process__nop:初始值
  • process__genericResampling:对两路以上的track进行重采样操作
  • process__genericNoResampling:对两路以上的track不进行重采样操作
  • process__validate:这个函数就是根据当前的不同情况将mHook指向不同的函数
  • process__oneTrack16BitsStereoNoResampling:只有一路track,16bit,立体声的时候不进行重采样
process_hook_t mHook = &AudioMixerBase::process__nop;

mHook初始化的时候指向的是process__nop

void invalidate() {
        mHook = &AudioMixerBase::process__validate;
    }

process__validate是在invalidate函数里幅值给了mHook 指针。

void AudioMixerBase::process__validate()
{

    // select the processing hooks
    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 {
            // we keep temp arrays around.
            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) {
                        // The check prevents a muted track from acquiring a process hook.
                        //
                        // This is dangerous if the track is MONO as that requires
                        // special case handling due to implicit channel duplication.
                        // Stereo or Multichannel should actually be fine here.
                        mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
                                t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,
                                t->useStereoVolume());
                    }
                }
            }
        }
    }
}

这个函数首先使用while循环来遍历每一个track,然后通过 NEEDS_RESAMPLE、NEEDS_AUX、NEEDS_CHANNEL_1、NEEDS_MUTE等判断,最终得到resampling、all16BitsStereoNoResample、volumeRamp的值,然后基于这几个值来决定调用,mHook来指向哪一个函数。

至于音频流数据是如何混到一起的,我们后面章节再来进一步分析。

相关文章:

  • 前端安全面试题汇总及参考答案
  • 豆包大模型 MarsCode AI 刷题专栏 002
  • Android 平台架构系统启动流程详解
  • 正则表达式,idea,插件anyrule
  • 003-Boost文件锁
  • ADB 和 Monkey 进行 Android 应用的测试和调试
  • 【Java代码审计 | 第九篇】文件写入漏洞成因及防范
  • Manus是由中国创业公司Monica研发的通用型AI智能体
  • 何有效预防神经性头抖
  • 使用Node.js从零搭建DeepSeek本地部署(Express框架、Ollama)
  • 【C#】async与await介绍
  • JAVASE(一)
  • 计算机视觉|3D卷积网络VoxelNet:点云检测的革新力量
  • Unity组件TrailRenderer屏幕滑动拖尾
  • 策略模式详解:实现灵活多样的支付方式
  • 数据上新 | 专业领域多语种对话语音数据集
  • P63 C++当中的计时
  • 不小心更改了/etc权限为777导致sudo,ssh等软件都无法使用
  • “此电脑”中删除WPS云盘方法(百度网盘通用)
  • 10 【HarmonyOS NEXT】 仿uv-ui组件开发之Avatar头像组件开发教程(一)
  • 建网站要服务器/app开发平台
  • 在小说网站做责编/seo推广外包
  • 网站优化要从哪些方面做/站长统计网站大全
  • 网页简单模板下载/seo外包是什么
  • 高校网站群建设的公司有哪些/线上宣传推广方式
  • 网站设计建设公司/网络营销的策略有哪些