Android Framework audio subsystem (13) basis for volume adjustment

This series of articles Master link: Thematic sub-directory Android Framework Class Audio Subsystem


Summary and description of key points in this chapter:

This chapter mainly focuses on ➕ the volume adjustment part on the upper left of the above mind map. Explained the basic knowledge of volume and AudioFlinger's volume adjustment process, mainly including:

  • AudioFlinger's initial setting process for master volume and stream volume
  • AudioFlinger's setMasterVolume master volume setting process
  • AudioFlinger's setStreamVolume stream volume setting process
  • Play thread Load volume setting process

1 Volume basics

@ 1 Four categories of Volume

  • master volume: setting it equals to setting all stream volume and track volume. It can be written to the sound card to control the volume of all sounds. It can also not be written into the sound card, but as a multiplier factor to affect all the volume. In other words: master volume can set all AudioTrack volume and stream volume.
  • stream volume: Set the volume of a certain stream. Android system supports 10 kinds of streams. The volume of various streams can also be set independently without affecting each other. For example, "music volume" should not affect the volume of "incoming call ringing", "alarm clock", "call".
  • stream volume alias: Set the same set of stream volume, grouped in the Android source code called "alias", namely alias. For example, in the phone, the aliases of the five streams (STREAM_SYSTEM, STREAM_RING, STREAM_NOTIFICATION, STREAM_SYSTEM_ENFORCED, STREAM_DTMF) are all STREAM_RING, then the corresponding slider can control the volume of these five streams.
  • AudioTrack volume: This is set when the volume is set by a single app, it only affects the volume of the app.

@ 2 十种 stream

There are 10 types of streams in the Android system, which are defined in system / core / include / system / audio.h, but the 10 types of streams are divided into groups, and streams belonging to the same group have the same alias. When we set the volume, a volume adjustment slider has an alias, and streams with the same alias will be affected by this slider. The relationship between stream and alias (reference) is as follows:

@ 3 Two paths for sound playback

  • MixerThread: APP's volume setting will not affect the hardware volume of the sound card, but will only affect the amplitude of the APP's audio data (become smaller or amplified). These audio data are finally mixed and passed to the sound card.
  • DirectOutputThread (such as HDMI, a single audio application uses a sound card alone): only one APP and only one AudioTrack use it at the same time, so the volume of this AudioTrack can be directly used by DirectOutputThread to set the hardware volume, this kind of sound card is not used much . If the parameter information in the configuration file contains "flags AUDIO_OUTPUT_FLAG_DIRECT", it means that this sound card can be monopolized by an App. App can use this sound card in the form of DirectOutputThread.

@ 4 The logic of mixing

  • app1: mixing data 1 = audio data 1 * master_volume * stream1_volume * AudioTrack1_volume
  • app2: mixing data 2 = audio data 2 * master_volume * stream2_volume * AudioTrack2_volume
  • app3: mixing data 3 = audio data 3 * master_volume * stream3_volume * AudioTrack3_volume

Mix together: final mix = mix data 1 + mix data 2 + mix data 3, and then write the mixed data to the hardware.

@ 5 Explanation of some key variables in the audio system:

Related members of the AudioFlinger class:

stream_type_t mStreamTypes[AUDIO_STREAM_CNT];
float mMasterVolume; //存储master volume
bool mMasterMute; //存储是否静音

In the playbackThread class:

//为DuplicatingThread的OutputTrack多出一项, DuplicatingThread可以用于在两个声卡上播放出同样的声音
stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1]; 
bool mMasterMute;
float mMasterVolume; //来源于AudioFlinger中的同名的变量

AudioTrack class (App side):

float mVolume[2]; //两项,分别表示App设置的左右声道的音量

Note: The volume in stream volume and audioTreack is only processed by the software. If the value written in masterVolue is provided by HAL, it will be written to the hardware.


2 AudioFlinger volume adjustment process

2.1 AudioFlinger volume setting process description

The volume setting is to store the volume value in the variable through logical operation, and then perform a round of logical operation again during playback, and finally write it into the sound card together with the sound data, and then play a reasonable sound.

@ 1 AudioFlinger's initial setting process for master volume and stream volume

Initially, MasterVolume, MasterMute, StreamVolume, and StreamMute are initialized when the AudioFlinger object is created. MasterVolume, and MasterMute are initialized directly in the constructor. The code is as follows:

AudioFlinger::AudioFlinger()
    : BnAudioFlinger(),
      mPrimaryHardwareDev(NULL),
      mAudioHwDevs(NULL),
      mHardwareStatus(AUDIO_HW_IDLE),
      mMasterVolume(1.0f),//初值1.0f
      mMasterMute(false),//静音初值
      mNextUniqueId(1),
      mMode(AUDIO_MODE_INVALID),
      mBtNrecIsOff(false),
      mIsLowRamDevice(true),
      mIsDeviceTypeKnown(false),
      mGlobalEffectEnableTime(0),
      mPrimaryOutputSampleRate(0)
{
//...
}

The initialization of StreamVolume and StreamMute is initialized in the member variable structure, the code is as follows:

//在创建结构体的时候 直接初始化
struct  stream_type_t {
    stream_type_t()
        :   volume(1.0f),
            mute(false)
    {
    }
    float       volume;
    bool        mute;
};

After this stage is initialized, loadHwModule is executed when the audio library is loaded, and the code is as follows:

audio_module_handle_t AudioFlinger::loadHwModule(const char *name)
{
    if (name == NULL) {
        return 0;
    }
    if (!settingsAllowed()) {
        return 0;
    }
    Mutex::Autolock _l(mLock);
    return loadHwModule_l(name);
}

Continue to analyze loadHwModule_l, the code implementation is as follows:

// loadHwModule_l() must be called with AudioFlinger::mLock held
audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
{
    for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
        if (strncmp(mAudioHwDevs.valueAt(i)->moduleName(), name, strlen(name)) == 0) {
            ALOGW("loadHwModule() module %s already loaded", name);
            return mAudioHwDevs.keyAt(i);
        }
    }

    audio_hw_device_t *dev;
    //获取audio_hw_device_t类型设备dev,可以直接操作HAL层
    int rc = load_audio_interface(name, &dev);
    mHardwareStatus = AUDIO_HW_INIT;
    rc = dev->init_check(dev);
    mHardwareStatus = AUDIO_HW_IDLE;

    AudioHwDevice::Flags flags = static_cast<AudioHwDevice::Flags>(0);
    {  // scope for auto-lock pattern
        AutoMutex lock(mHardwareLock);

        if (0 == mAudioHwDevs.size()) {
            mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME;
            //只要dev中含有get_master_volume,表明可以从库中获取master_volume的初值
            if (NULL != dev->get_master_volume) {
                float mv;
                if (OK == dev->get_master_volume(dev, &mv)) {
                    mMasterVolume = mv;
                }
            }

            mHardwareStatus = AUDIO_HW_GET_MASTER_MUTE;
            //只要dev中含有get_master_mute,表明可以从库中获取master_mute的初值
            if (NULL != dev->get_master_mute) {
                bool mm;
                if (OK == dev->get_master_mute(dev, &mm)) {
                    mMasterMute = mm;
                }
            }
        }

        mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
        //设置master_volume的初值 到硬件中
        if ((NULL != dev->set_master_volume) &&
            (OK == dev->set_master_volume(dev, mMasterVolume))) {
            flags = static_cast<AudioHwDevice::Flags>(flags |
                    AudioHwDevice::AHWD_CAN_SET_MASTER_VOLUME);
        }
        mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE;
        //设置master_mute的初值 到硬件中
        if ((NULL != dev->set_master_mute) &&
            (OK == dev->set_master_mute(dev, mMasterMute))) {
            flags = static_cast<AudioHwDevice::Flags>(flags |
                    AudioHwDevice::AHWD_CAN_SET_MASTER_MUTE);
        }
        mHardwareStatus = AUDIO_HW_IDLE;
    }

    audio_module_handle_t handle = nextUniqueId();
    mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
    return handle;
}

Here, MasterVolume, and MasterMute are initialized a second time, that is, if the audio library supports the initial value setting, the value in the audio library is the main, otherwise it is the initial value when the AudioFlinger is created.

@ 2 AudioFlinger :: setMasterVolume master volume setting process

The code implementation of AudioFlinger :: setMasterVolume is as follows:

status_t AudioFlinger::setMasterVolume(float value)
{
    status_t ret = initCheck();
    //...
    Mutex::Autolock _l(mLock);
    mMasterVolume = value;
    for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
        AutoMutex lock(mHardwareLock);
        AudioHwDevice *dev = mAudioHwDevs.valueAt(i);

        mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;
        //直接将master_volume的值设置到硬件中
        if (dev->canSetMasterVolume()) {
            dev->hwDevice()->set_master_volume(dev->hwDevice(), value);
        }
        mHardwareStatus = AUDIO_HW_IDLE;
    }
    //将master_volume的值设置到各个播放线程中
    for (size_t i = 0; i < mPlaybackThreads.size(); i++)
        mPlaybackThreads.valueAt(i)->setMasterVolume(value);

    return NO_ERROR;
}

Here the code of PlaybackThread :: setMasterVolume is implemented as follows:

void AudioFlinger::PlaybackThread::setMasterVolume(float value)
{
    Mutex::Autolock _l(mLock);
    // Don't apply master volume in SW if our HAL can do it for us.
    if (mOutput && mOutput->audioHwDev &&
        mOutput->audioHwDev->canSetMasterVolume()) {
        mMasterVolume = 1.0;
    } else {
        mMasterVolume = value;
    }
}

It can be seen that the parameters are set by directly operating the interface of the HAL layer.

@ 3 AudioFlinger :: setStreamVolume stream volume setting process

 The code implementation of AudioFlinger :: setStreamVolume is as follows:

status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
        audio_io_handle_t output)
{
    status_t status = checkStreamType(stream);
    AutoMutex lock(mLock);
    PlaybackThread *thread = NULL;
    if (output != AUDIO_IO_HANDLE_NONE) {
        thread = checkPlaybackThread_l(output);
        if (thread == NULL) {
            return BAD_VALUE;
        }
    }

    mStreamTypes[stream].volume = value;
    if (thread == NULL) {
        //未指定线程则全部播放线程 均设置
        for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
            mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
        }
    } else {
        //指定线程则直接设置
        thread->setStreamVolume(stream, value);
    }
    return NO_ERROR;
}

Continue to analyze the setStreamVolume method of the playback thread. The code implementation is as follows:

void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
    Mutex::Autolock _l(mLock);
    mStreamTypes[stream].volume = value;//赋值
    broadcast_l();
}

In fact, each playback thread has mStreamTypes [stream] .volume, which is consistent with AudioFlinger's mStreamTypes [stream] .volume.

@ 4 AudioTrack volume settings

 The code implementation of AudioTrack :: setVolume is as follows:

status_t AudioTrack::setVolume(float volume)
{
    return setVolume(volume, volume);
}

status_t AudioTrack::setVolume(float left, float right)
{
    //...
    AutoMutex lock(mLock);
    mVolume[AUDIO_INTERLEAVE_LEFT] = left;
    mVolume[AUDIO_INTERLEAVE_RIGHT] = right;
    //这里会通过ClientProxy将音量参数设置到共享内存中
    //这里的mProxy = 
    //new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
    mProxy->setVolumeLR(gain_minifloat_pack(gain_from_float(left), gain_from_float(right)));

    if (isOffloaded_l()) {
        mAudioTrack->signal();
    }
    return NO_ERROR;
}

Here is to record this data in the mVolumeLR field. The Cblk parameter passed when creating the Proxy is the head of the shared memory.

2.2 Playing thread Loading volume setting process

@ 1 Source code analysis

The analysis here mainly focuses on the relevant code of the volume part. The code implementation is as follows:

// prepareTracks_l() must be called with ThreadBase::mLock held
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
        Vector< sp<Track> > *tracksToRemove)
{
    //...
    float masterVolume = mMasterVolume;
    bool masterMute = mMasterMute;
    if (masterMute) {//如果静音条件为真,则设置masterVolume=0
        masterVolume = 0;
    }
    //...
    mMixerBufferValid = false;  // mMixerBuffer has no valid data until appropriate tracks found.
    mEffectBufferValid = false; // mEffectBuffer has no valid data until tracks found.

    for (size_t i=0 ; i<count ; i++) {
        const sp<Track> t = mActiveTracks[i].promote();
        if (t == 0) {
            continue;
        }

        // this const just means the local variable doesn't change
        Track* const track = t.get();
        //...
        {   // local variable scope to avoid goto warning
        audio_track_cblk_t* cblk = track->cblk();
        int name = track->name();
        size_t desiredFrames;
        uint32_t sr = track->sampleRate();
        if (sr == mSampleRate) {
            desiredFrames = mNormalFrameCount;
        } else {
            // +1 for rounding and +1 for additional sample needed for interpolation
            desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1;
            desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
        }
        uint32_t minFrames = 1;
        if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
                (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
            minFrames = desiredFrames;
        }

        size_t framesReady = track->framesReady();
        if ((framesReady >= minFrames) && track->isReady() &&
                !track->isPaused() && !track->isTerminated())
        {
            mixedTracks++;
            //...
            // compute volume for this track
            uint32_t vl, vr;       // in U8.24 integer format
            float vlf, vrf, vaf;   // in [0.0, 1.0] float format
            if (track->isPausing() || mStreamTypes[track->streamType()].mute) {
                vl = vr = 0;
                vlf = vrf = vaf = 0.;
                if (track->isPausing()) {
                    track->setPaused();
                }
            } else {
                // read original volumes with volume control
                //获取 StreamType Volume
                float typeVolume = mStreamTypes[track->streamType()].volume;
                float v = masterVolume * typeVolume;
                //获取共享内存代理
                AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
                //从共享内存中获得左右声道
                gain_minifloat_packed_t vlr = proxy->getVolumeLR();
                vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
                vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
                // track volumes come from shared memory, so can't be trusted and must be clamped
                //边界判断
                if (vlf > GAIN_FLOAT_UNITY) {
                    ALOGV("Track left volume out of range: %.3g", vlf);
                    vlf = GAIN_FLOAT_UNITY;
                }
                if (vrf > GAIN_FLOAT_UNITY) {
                    ALOGV("Track right volume out of range: %.3g", vrf);
                    vrf = GAIN_FLOAT_UNITY;
                }
                // now apply the master volume and stream type volume
                //放大系数:master_volume * stream_volume * AudioTrack_volume
                vlf *= v;
                vrf *= v;
                // assuming master volume and stream type volume each go up to 1.0,
                // then derive vl and vr as U8.24 versions for the effect chain
                //下面主要是左右声道转换成AUX单声道的一些逻辑运算
                const float scaleto8_24 = MAX_GAIN_INT * MAX_GAIN_INT;
                vl = (uint32_t) (scaleto8_24 * vlf);
                vr = (uint32_t) (scaleto8_24 * vrf);
                // vl and vr are now in U8.24 format
                uint16_t sendLevel = proxy->getSendLevel_U4_12();
                // send level comes from shared memory and so may be corrupt
                if (sendLevel > MAX_GAIN_INT) {
                    ALOGV("Track send level out of range: %04X", sendLevel);
                    sendLevel = MAX_GAIN_INT;
                }
                // vaf is represented as [0.0, 1.0] float by rescaling sendLevel
                vaf = v * sendLevel * (1. / MAX_GAIN_INT);
            }

            // Delegate volume control to effect in track effect chain if needed
            if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
                // Do not ramp volume if volume is controlled by effect
                param = AudioMixer::VOLUME;
                // Update remaining floating point volume levels
                vlf = (float)vl / (1 << 24);
                vrf = (float)vr / (1 << 24);
                track->mHasVolumeController = true;
            } else {
                // force no volume ramp when volume controller was just disabled or removed
                // from effect chain to avoid volume spike
                if (track->mHasVolumeController) {
                    param = AudioMixer::VOLUME;
                }
                track->mHasVolumeController = false;
            }

            // XXX: these things DON'T need to be done each time
            mAudioMixer->setBufferProvider(name, track);
            mAudioMixer->enable(name);
            //关键点:通过参数设置将音量信息传递出去
            mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);
            mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf);
            mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, &vaf);
            //设置其他参数
            //...
            // reset retry count
            track->mRetryCount = kMaxTrackRetries;
            if (mMixerStatusIgnoringFastTracks != MIXER_TRACKS_READY ||
                    mixerStatus != MIXER_TRACKS_ENABLED) {
                mixerStatus = MIXER_TRACKS_READY;
            }
        } else {
            //...
        }

        }   // local variable scope to avoid goto warning
track_is_ready: ;
    }
    //...
    return mixerStatus;
}

Here focus on analysis of AudioMixer's parameter setting setParameter method, the code implementation is as follows:

void AudioMixer::setParameter(int name, int target, int param, void *value)
{
    name -= TRACK0;
    track_t& track = mState.tracks[name];
    int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
    int32_t *valueBuf = reinterpret_cast<int32_t*>(value);

    switch (target) {
    //...
    case RAMP_VOLUME:
    case VOLUME:
        switch (param) {
        case AUXLEVEL:
            if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
                    target == RAMP_VOLUME ? mState.frameCount : 0,
                    &track.auxLevel, &track.prevAuxLevel, &track.auxInc,
                    &track.mAuxLevel, &track.mPrevAuxLevel, &track.mAuxInc)) {
                invalidateState(1 << name);
            }
            break;
        default:
            if ((unsigned)param >= VOLUME0 && (unsigned)param < VOLUME0 + MAX_NUM_VOLUMES) {
                //setVolumeRampVariables主要是 float和int类型之间的转换的一些逻辑操作
                if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
                        target == RAMP_VOLUME ? mState.frameCount : 0,
                        &track.volume[param - VOLUME0], &track.prevVolume[param - VOLUME0],
                        &track.volumeInc[param - VOLUME0],
                        &track.mVolume[param - VOLUME0], &track.mPrevVolume[param - VOLUME0],
                        &track.mVolumeInc[param - VOLUME0])) {
                    invalidateState(1 << name);
                }
            } else {
                LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param);
            }
        }
        break;
    default:
        LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
    }
}

Here focus on the implementation of invalidateState, the code is as follows:

void AudioMixer::invalidateState(uint32_t mask)
{
    if (mask != 0) {
        mState.needsChanged |= mask;
        mState.hook = process__validate;
    }
}

The implementation of process__validate is as follows:

void AudioMixer::process__validate(state_t* state, int64_t pts)
{
    //...
    // compute everything we need...
    while (en) {
        //...
        if (n & NEEDS_MUTE) {
            t.hook = track__nop;
        } else {
            if (n & NEEDS_AUX) {
                all16BitsStereoNoResample = false;
            }
            if (n & NEEDS_RESAMPLE) {
                all16BitsStereoNoResample = false;
                resampling = true;
                t.hook = getTrackHook(TRACKTYPE_RESAMPLE, t.mMixerChannelCount,
                        t.mMixerInFormat, t.mMixerFormat);
            } else {
                if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
                    t.hook = getTrackHook(
                            t.mMixerChannelCount == 2 // TODO: MONO_HACK.
                                ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
                            t.mMixerChannelCount,
                            t.mMixerInFormat, t.mMixerFormat);
                    all16BitsStereoNoResample = false;
                }
                if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
                    t.hook = getTrackHook(TRACKTYPE_NORESAMPLE, t.mMixerChannelCount,
                            t.mMixerInFormat, t.mMixerFormat);
                }
            }
        }
    }
    //...
}

Here mainly focus on the getTrackHook function, the code implementation is as follows:

AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, uint32_t channelCount,
        audio_format_t mixerInFormat, audio_format_t mixerOutFormat __unused)
{
    if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
        switch (trackType) {
        //...
        case TRACKTYPE_NORESAMPLE:
            return track__16BitsStereo;
        default:
            break;
        }
    }
    //...
    return NULL;
}

Taking TRACKTYPE_NORESAMPLE as an example here, track__16BitsStereo will eventually be called, and the code implementation is as follows:

void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount,
        int32_t* temp __unused, int32_t* aux)
{
    ALOGVV("track__16BitsStereo\n");
    const int16_t *in = static_cast<const int16_t *>(t->in);

    if (CC_UNLIKELY(aux != NULL)) {
        //忽略AUX相关处理
    } else {
        // ramp gain
        if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
            int32_t vl = t->prevVolume[0];
            int32_t vr = t->prevVolume[1];
            const int32_t vlInc = t->volumeInc[0];左声道音量
            const int32_t vrInc = t->volumeInc[1];右声道音量

            do {
                *out++ += (vl >> 16) * (int32_t) *in++;
                *out++ += (vr >> 16) * (int32_t) *in++;
                vl += vlInc;
                vr += vrInc;
            } while (--frameCount);

            t->prevVolume[0] = vl;
            t->prevVolume[1] = vr;
            t->adjustVolumeRamp(false);
        }

        // constant gain
        else {
            const uint32_t vrl = t->volumeRL;
            do {
                uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
                in += 2;
                out[0] = mulAddRL(1, rl, vrl, out[0]);
                out[1] = mulAddRL(0, rl, vrl, out[1]);
                out += 2;
            } while (--frameCount);
        }
    }
    t->in = in;
}

Finally, we store the data in out. There are three mulAddRL here. Here we take the following implementation as an example. The code is as follows:

//另外2种实现模式类似
static inline
int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
{
#if USE_INLINE_ASSEMBLY
    int32_t out;
    if (left) {
        asm( "smlawb %[out], %[v], %[inRL], %[a] \n"
             : [out]"=r"(out)
             : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
             : );
    } else {
        asm( "smlawt %[out], %[v], %[inRL], %[a] \n"
             : [out]"=r"(out)
             : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a)
             : );
    }
    return out;
#else
    int16_t s = left ? int16_t(inRL) : int16_t(inRL>>16);
    return a + int32_t((int64_t(v) * s) >> 16);
#endif
}

Although it is possible to use assembly statements to optimize, the logic is actually consistent (outL is used here for the left channel, and outR is the right channel):

  • Left channel: outL = (inRL & 0xffff * v) + outL // first 16 bits
  • Right channel: outR = (inRL >> 16 * v) + outR // last 16 bits

Finally, outL and outR are combined into one value (lower 16bit is left channel data, higher 16bit is right channel data) and returned. This is actually an operation in tracks [x] .hook in MixerThread :: prepareTracks_l in playing audio. Through this operation, the parameters set by prepareTrack_l are used to mix in threadLoop_mix. Finally, it is used to output the mixed audio through threadLoop_write, and finally write the filled Buffer to the hardware.

@ 2 Explanation of the track_t structure involved in MixerThread :: prepareTracks_l

The code is implemented as follows:

struct track_t {
	//...
    // TODO: Eventually remove legacy integer volume settings
	//int类型 普通声音
    union {
    int16_t     volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero)
    int32_t     volumeRL;
    };
    int32_t     prevVolume[MAX_NUM_VOLUMES];
    int32_t     volumeInc[MAX_NUM_VOLUMES];
	//...
	//int类型 aux声音
    int32_t     auxInc;
    int32_t     prevAuxLevel;
	int16_t     auxLevel;       // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performance
	//...
	//float类型 普通声音
    float          mVolume[MAX_NUM_VOLUMES];     // floating point set volume
    float          mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume
    float          mVolumeInc[MAX_NUM_VOLUMES];  // floating point volume increment
	//...
	//float类型 aux声音
    float          mAuxLevel;                     // floating point set aux level
    float          mPrevAuxLevel;                 // floating point prev aux level
    float          mAuxInc;                       // floating point aux increment
	//...
};

The data of aux here is actually the way that the left and right channels are superimposed together and converted into a specific channel after specific processing.
Here we can find that there are 4 sets of volume variables in the track_t structure. They are all PreVolume, VolumeInc, Volume this mode, the meaning of these three variables are shown below:

The interpretation is as follows:

  • PreVolume: the initial value of the previous volume
  • VolumeInc: indicates the step size of each adjustment
  • Volume: current volume = master_volume * stream_volume * AudioTrack_volume
 
Published 290 original articles · praised 47 · 30,000+ views

Guess you like

Origin blog.csdn.net/vviccc/article/details/105437166