android 音量调整

1 流程图

android 音量调整_第1张图片

2 audio_policy_volumes.xml

阐述了 流跟device 的音量范围
【Android Audio】5、EngineBase加载音量曲线和策略 【基于Android Q 】

1.按键处理

1.1 从输入子系统到音频子系统的处理

按键会从inputReader的getEvent到 inputDisptacher最后到ViewRootimpl中的ViewPostImeInputStage::processKeyEvent方法,这里的processKeyEvent代码如下所示:

private int processKeyEvent(QueuedInputEvent q) {
    final KeyEvent event = (KeyEvent)q.mEvent;
    //...
    // Apply the fallback event policy.
    if (mFallbackEventHandler.dispatchKeyEvent(event)) {
        return FINISH_HANDLED;
    }
    //...
    // Handle automatic focus changes...
    //...
    return FORWARD;
}

这里的mFallbackEventHandler<=等价=>PhoneFallbackEventHandler,即音量键的处理将交给PhoneFallbackEventHandler,它的dispatchKeyEvent代码实现如下:

public boolean dispatchKeyEvent(KeyEvent event) {
 
    final int action = event.getAction();
    final int keyCode = event.getKeyCode();
 
    if (action == KeyEvent.ACTION_DOWN) {
        return onKeyDown(keyCode, event);
    } else {
        return onKeyUp(keyCode, event);
    }
}

onKeyDown和onKeyUp的代码实现如下:

boolean onKeyDown(int keyCode, KeyEvent event) {
    final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();
    switch (keyCode) {
        case KeyEvent.KEYCODE_VOLUME_UP:
        case KeyEvent.KEYCODE_VOLUME_DOWN:
        case KeyEvent.KEYCODE_VOLUME_MUTE: {
            MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false);
            return true;
        }
        //...
    }
    //...
}
 
boolean onKeyUp(int keyCode, KeyEvent event) {
    final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();
    if (dispatcher != null) {
        dispatcher.handleUpEvent(event);
    }
 
    switch (keyCode) {
        case KeyEvent.KEYCODE_VOLUME_UP:
        case KeyEvent.KEYCODE_VOLUME_DOWN:
        case KeyEvent.KEYCODE_VOLUME_MUTE: {
            if (!event.isCanceled()) {
                //MediaSessionLegacyHelper.getHelper(mContext)<=等价=>MediaSessionLegacyHelper
                MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, false);
            }
            return true;
        }
        //...
    }
    //...
}

这里会调用MediaSessionLegacyHelper的sendVolumeKeyEvent方法,代码实现如下:

public void sendVolumeKeyEvent(KeyEvent keyEvent, boolean musicOnly) {
    //...
    if (down || up) {
        //...
        if (direction != 0) {
            // If this is action up we want to send a beep for non-music events
            if (up) {
                direction = 0;
            }
            mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
                    direction, flags);
        } else if (isMute) {
            if (down) {
                mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
                        MediaSessionManager.DIRECTION_MUTE, flags);
            }
            mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
                    0 /* direction, causes UI to show on down */, flags);
        }
    }
}

这里继续分析mSessionManager.dispatchAdjustVolume的实现,代码如下:

public void dispatchAdjustVolume(int suggestedStream, int direction, int flags) {
    try {
        mService.dispatchAdjustVolume(suggestedStream, direction, flags);
    } catch (RemoteException e) {
        Log.e(TAG, "Failed to send adjust volume.", e);
    }
}

这里的service是SessionManagerImpl类型,对应的dispatchAdjustVolume代码实现如下:

public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
    final int pid = Binder.getCallingPid();
    final int uid = Binder.getCallingUid();
    final long token = Binder.clearCallingIdentity();
    try {
        synchronized (mLock) {
            MediaSessionRecord session = mPriorityStack
                    .getDefaultVolumeSession(mCurrentUserId);
            dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
        }
    } finally {
        Binder.restoreCallingIdentity(token);
    }
}

继续分析dispatchAdjustVolumeLocked,代码实现如下:

private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
        MediaSessionRecord session) {
    boolean preferSuggestedStream = false;
    if (isValidLocalStreamType(suggestedStream)
            && AudioSystem.isStreamActive(suggestedStream, 0)) {
        preferSuggestedStream = true;
    }
    if (session == null || preferSuggestedStream) {
        if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
                && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
            return;
        }
        try {
            String packageName = getContext().getOpPackageName();
            if (mUseMasterVolume) {
                boolean isMasterMute = mAudioService.isMasterMute();
                if (direction == MediaSessionManager.DIRECTION_MUTE) {
                    mAudioService.setMasterMute(!isMasterMute, flags, packageName, mICallback);
                } else {
                    mAudioService.adjustMasterVolume(direction, flags, packageName);
                    if (isMasterMute && direction != 0) {
                        mAudioService.setMasterMute(false, flags, packageName, mICallback);
                    }
                }
            } else {
                boolean isStreamMute = mAudioService.isStreamMute(suggestedStream);
                if (direction == MediaSessionManager.DIRECTION_MUTE) {
                    mAudioManager.setStreamMute(suggestedStream, !isStreamMute);
                } else {
                    mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
                            flags, packageName);
                    // Do not call setStreamMute when direction = 0 which is used just to
                    // show the UI.
                    if (isStreamMute && direction != 0) {
                        mAudioManager.setStreamMute(suggestedStream, false);
                    }
                }
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Error adjusting default volume.", e);
        }
    } else {
        session.adjustVolume(direction, flags, getContext().getPackageName(),
                UserHandle.myUserId(), true);
    }
}

这里最后调用到了AudioService.adjustSuggestedStreamVolume。从输入子系统进入到音频子系统。

1.2 音频处理流程

从AudioService.adjustSuggestedStreamVolume到发送消息MSG_SET_DEVICE_VOLUME。

private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
        String callingPackage, int uid) {
    int streamType;
    if (mVolumeControlStream != -1) {
        streamType = mVolumeControlStream;
    } else {
        //关键点1:获取推荐的streamType,这里根据建议的流类型与AudioService的实际情况,返回一个值
        streamType = getActiveStreamType(suggestedStreamType);
    }
    //...
    //关键点2
    adjustStreamVolume(streamType, direction, flags, callingPackage, uid);
}

分析getActiveStreamType,代码实现如下:

private int getActiveStreamType(int suggestedStreamType) {
    switch (mPlatformType) {
    case PLATFORM_VOICE://针对Phone
        if (isInCommunication()) {
            //通话中,如果用蓝牙耳机就设置STREAM_BLUETOOTH_SCO,否则设置STREAM_VOICE_CALL
            if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
                    == AudioSystem.FORCE_BT_SCO) {
                return AudioSystem.STREAM_BLUETOOTH_SCO;
            } else {
                return AudioSystem.STREAM_VOICE_CALL;
            }
        } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
            if (isAfMusicActiveRecently(StreamOverride.sDelayMs)) {
                return AudioSystem.STREAM_MUSIC;
                } else :
                    return AudioSystem.STREAM_RING;
            }
        } else if (isAfMusicActiveRecently(0)) {
            return AudioSystem.STREAM_MUSIC;
        }
        break;
    case PLATFORM_TELEVISION://电视
        if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
                // TV always defaults to STREAM_MUSIC
                return AudioSystem.STREAM_MUSIC;
        }
        break;
    default://平板或者其他设备。
        if (isInCommunication()) {
            if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
                    == AudioSystem.FORCE_BT_SCO) {
                return AudioSystem.STREAM_BLUETOOTH_SCO;
            } else {
                return AudioSystem.STREAM_VOICE_CALL;
            }
        } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
                StreamOverride.sDelayMs) ||
                AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
                        StreamOverride.sDelayMs)) {
            return AudioSystem.STREAM_NOTIFICATION;
        } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
            if (isAfMusicActiveRecently(StreamOverride.sDelayMs)) {
                return AudioSystem.STREAM_MUSIC;
            } else {
                return AudioSystem.STREAM_NOTIFICATION;
            }
        }
        break;
    }
    return suggestedStreamType;
}

分析adjustStreamVolume,代码实现如下:

private void adjustStreamVolume(int streamType, int direction, int flags,
        String callingPackage, int uid) {
    //...
    ensureValidDirection(direction);//确认 调整的音量方向
    ensureValidStreamType(streamType);//确认 调整的stream类型  
    
    int streamTypeAlias = mStreamVolumeAlias[streamType];
    //VolumeStreamState类,保存与一个流类型所有音量相关的信息
    VolumeStreamState streamState = mStreamStates[streamTypeAlias];
    final int device = getDeviceForStream(streamTypeAlias);
    //...
    flags &= ~AudioManager.FLAG_FIXED_VOLUME;
    if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
           ((device & mFixedVolumeDevices) != 0)) {
        flags |= AudioManager.FLAG_FIXED_VOLUME;
        if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
                (device & mSafeMediaVolumeDevices) != 0) {
            step = mSafeMediaVolumeIndex;
        } else {
            step = streamState.getMaxIndex();
        }
        if (aliasIndex != 0) {
            aliasIndex = step;
        }
    } else {
        // convert one UI step (+/-1) into a number of internal units on the stream alias
        //这里将音量值的变化量从源 stream类型变换到目标stream类型,
        //由于不同的流类型的音量调节范围不同,所以这个转换是必需的
        step = rescaleIndex(10, streamType, streamTypeAlias);
    }
 
    if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
            (streamTypeAlias == getMasterStreamType())) {
        int ringerMode = getRingerModeInternal();
        if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
            flags &= ~AudioManager.FLAG_VIBRATE;
        }
        final int result = checkForRingerModeChange(aliasIndex, direction, step);
        //布尔变量,用来表示是否有必要继续设置音量值
        adjustVolume = (result & FLAG_ADJUST_VOLUME) != 0;
        if ((result & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
            flags |= AudioManager.FLAG_SHOW_SILENT_HINT;
        }
        if ((result & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
            flags |= AudioManager.FLAG_SHOW_VIBRATE_HINT;
        }
    }
    //取出调整前的音量值。这个值会在sendVolumeUpdate()调用
    int oldIndex = mStreamStates[streamType].getIndex(device);
 
    if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
        //...
        /*判断streamState.adjustIndex 返回值,如果音量值在调整之后并没有发生变化
         *比如到了最大值,就不需要继续后面的操作了
         */
        if ((direction == AudioManager.ADJUST_RAISE) &&
                !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
            mVolumeController.postDisplaySafeVolumeWarning(flags);
        } else if (streamState.adjustIndex(direction * step, device)) {
            //通过sendMsg()发送消息MSG_SET_DEVICE_VOLUME到mAudioHandler
            sendMsg(mAudioHandler,
                    MSG_SET_DEVICE_VOLUME,
                    SENDMSG_QUEUE,
                    device,
                    0,
                    streamState,
                    0);
        }
        //...
    }
    //...
    int index = mStreamStates[streamType].getIndex(device);
    //通知外界音量值发生了变化
    sendVolumeUpdate(streamType, oldIndex, index, flags);
}

这里经过一系列处理后,最终发送了MSG_SET_DEVICE_VOLUME给mAudioHandler去处理。

1.3 处理消息MSG_SET_DEVICE_VOLUME

这里开始分析处理消息MSG_SET_DEVICE_VOLUME的流程mAudioHandler的handleMessage处理如下:

@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
 
        case MSG_SET_DEVICE_VOLUME:
            setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
            break;
            //...
        case MSG_PERSIST_MICROPHONE_MUTE:
            Settings.System.putIntForUser(mContentResolver,
                                         Settings.System.MICROPHONE_MUTE,
                                         msg.arg1,
                                         msg.arg2);
            break;
    }
}

这里继续分析setDeviceVolume的实现, 代码如下:

private void setDeviceVolume(VolumeStreamState streamState, int device) {
 
    synchronized (VolumeStreamState.class) {
        // Apply volume
        streamState.applyDeviceVolume_syncVSS(device);
 
        // Apply change to all streams using this one as alias
        int numStreamTypes = AudioSystem.getNumStreamTypes();
        for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
            if (streamType != streamState.mStreamType &&
                    mStreamVolumeAlias[streamType] == streamState.mStreamType) {
                int streamDevice = getDeviceForStream(streamType);
                if ((device != streamDevice) && mAvrcpAbsVolSupported &&
                        ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0)) {
                    mStreamStates[streamType].applyDeviceVolume_syncVSS(device);
                }
                mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
            }
        }
    }
    //...
}

这里applyDeviceVolume_syncVSS代码实现如下:

// must be called while synchronized VolumeStreamState.class
public void applyDeviceVolume_syncVSS(int device) {
    int index;
    if (isMuted_syncVSS()) {
        index = 0;
    } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported)
            || ((device & mFullVolumeDevices) != 0)) {
        index = (mIndexMax + 5)/10;
    } else {
        index = (getIndex(device) + 5)/10;
    }
    AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
}

这里继续分析setStreamVolumeIndex的实现,setStreamVolumeIndex是JNI函数,映射关系为:

    {"setStreamVolumeIndex","(III)I",   (void *)android_media_AudioSystem_setStreamVolumeIndex},

所映射的是android_media_AudioSystem_setStreamVolumeIndex方法,代码如下:

static jint
android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env,
                                               jobject thiz,
                                               jint stream,
                                               jint index,
                                               jint device)
{
    return (jint) check_AudioSystem_Command(
            AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream),
                                              index,
                                              (audio_devices_t)device));
}

继续分析这里AudioSystem::setStreamVolumeIndex的实现,代码如下:

status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,
                                           int index,
                                           audio_devices_t device)
{
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    if (aps == 0) return PERMISSION_DENIED;
    return aps->setStreamVolumeIndex(stream, index, device);
}

这里调用的是AudioPolicyManager的setStreamVolumeIndex方法,代码实现如下:

status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
                                                      int index,
                                                      audio_devices_t device)
{
    //...
    // Force max volume if stream cannot be muted
    if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax;
    if (device == AUDIO_DEVICE_OUT_DEFAULT) {
        mStreams[stream].mIndexCur.clear();
    }
    mStreams[stream].mIndexCur.add(device, index);
 
    // update volume on all outputs whose current device is also selected by the same
    // strategy as the device specified by the caller
    audio_devices_t strategyDevice = getDeviceForStrategy(getStrategy(stream), true /*fromCache*/);
 
 
    //FIXME: AUDIO_STREAM_ACCESSIBILITY volume follows AUDIO_STREAM_MUSIC for now
    audio_devices_t accessibilityDevice = AUDIO_DEVICE_NONE;
    if (stream == AUDIO_STREAM_MUSIC) {
        mStreams[AUDIO_STREAM_ACCESSIBILITY].mIndexCur.add(device, index);
        accessibilityDevice = getDeviceForStrategy(STRATEGY_ACCESSIBILITY, true /*fromCache*/);
    }
    if ((device != AUDIO_DEVICE_OUT_DEFAULT) &&
            (device & (strategyDevice | accessibilityDevice)) == 0) {
        return NO_ERROR;
    }
    status_t status = NO_ERROR;
    for (size_t i = 0; i < mOutputs.size(); i++) {
        audio_devices_t curDevice =
                getDeviceForVolume(mOutputs.valueAt(i)->device());
        if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & strategyDevice) != 0)) {
            status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice);
            if (volStatus != NO_ERROR) {
                status = volStatus;
            }
        }
        if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & accessibilityDevice) != 0)) {
            status_t volStatus = checkAndSetVolume(AUDIO_STREAM_ACCESSIBILITY,
                                                   index, mOutputs.keyAt(i), curDevice);
        }
    }
    return status;
}

这里会调用checkAndSetVolume方法,代码实现如下:

status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
                                                   int index,
                                                   audio_io_handle_t output,
                                                   audio_devices_t device,
                                                   int delayMs,
                                                   bool force)
{
    //...
    // We actually change the volume if:
    // - the float value returned by computeVolume() changed
    // - the force flag is set
    if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
            force) {
        mOutputs.valueFor(output)->mCurVolume[stream] = volume;
        if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
            mpClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volume, output, delayMs);
        }
        mpClientInterface->setStreamVolume(stream, volume, output, delayMs);
    }
 
    if (stream == AUDIO_STREAM_VOICE_CALL ||
        stream == AUDIO_STREAM_BLUETOOTH_SCO) {
        float voiceVolume;
        if (stream == AUDIO_STREAM_VOICE_CALL) {
            voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
        } else {
            voiceVolume = 1.0;
        }
 
        if (voiceVolume != mLastVoiceVolume && output == mPrimaryOutput) {
            mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
            mLastVoiceVolume = voiceVolume;
        }
    }
 
    return NO_ERROR;
}

这里最终一定会调用AudioPolicyClient的setStreamVolume(方法,代码实现如下:

status_t AudioPolicyService::AudioPolicyClient::setStreamVolume(audio_stream_type_t stream,
                     float volume, audio_io_handle_t output,
                     int delay_ms)
{
    return mAudioPolicyService->setStreamVolume(stream, volume, output,
                                               delay_ms);
}

这里调用到了AudioPolicyService的setStreamVolume方法,代码实现如下:

int AudioPolicyService::setStreamVolume(audio_stream_type_t stream,
                                        float volume,
                                        audio_io_handle_t output,
                                        int delayMs)
{
    return (int)mAudioCommandThread->volumeCommand(stream, volume,
                                                   output, delayMs);
}

这里 AudioCommandThread的volumeCommand的代码实现如下

status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream,
                                                               float volume,
                                                               audio_io_handle_t output,
                                                               int delayMs)
{
    sp<AudioCommand> command = new AudioCommand();
    command->mCommand = SET_VOLUME;
    sp<VolumeData> data = new VolumeData();
    data->mStream = stream;
    data->mVolume = volume;
    data->mIO = output;
    command->mParam = data;
    command->mWaitStatus = true;
    return sendCommand(command, delayMs);
}

关注sendCommand的实现,代码如下:

status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
{
    {
        Mutex::Autolock _l(mLock);
        insertCommand_l(command, delayMs);
        mWaitWorkCV.signal();
    }
    Mutex::Autolock _l(command->mLock);
    while (command->mWaitStatus) {
        nsecs_t timeOutNs = kAudioCommandTimeoutNs + milliseconds(delayMs);
        if (command->mCond.waitRelative(command->mLock, timeOutNs) != NO_ERROR) {
            command->mStatus = TIMED_OUT;
            command->mWaitStatus = false;
        }
    }
    return command->mStatus;
}

这里唤醒了AudioCommandThread对应的线程,看它的threadloop实现,代码如下:

bool AudioPolicyService::AudioCommandThread::threadLoop()
{
    nsecs_t waitTime = INT64_MAX;
    mLock.lock();
    while (!exitPending())
    {
        sp<AudioPolicyService> svc;
        while (!mAudioCommands.isEmpty() && !exitPending()) {
            nsecs_t curTime = systemTime();
            // commands are sorted by increasing time stamp: execute them from index 0 and up
            if (mAudioCommands[0]->mTime <= curTime) {
                sp<AudioCommand> command = mAudioCommands[0];
                mAudioCommands.removeAt(0);
                mLastCommand = command;
 
                switch (command->mCommand) {
                //...
                //唤醒后开始处理SET_VOLUME命令
                case SET_VOLUME: {
                    VolumeData *data = (VolumeData *)command->mParam.get();
                    command->mStatus = AudioSystem::setStreamVolume(data->mStream,
                                                                    data->mVolume,
                                                                    data->mIO);
                    }break;
                //...
                default:
                    ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
                }
                {
                    Mutex::Autolock _l(command->mLock);
                    if (command->mWaitStatus) {
                        command->mWaitStatus = false;
                        command->mCond.signal();
                    }
                }
                waitTime = INT64_MAX;
            } else {
                waitTime = mAudioCommands[0]->mTime - curTime;
                break;
            }
        }
        mLock.unlock();
        svc.clear();
        mLock.lock();
        if (!exitPending() && mAudioCommands.isEmpty()) {
            // release delayed commands wake lock
            release_wake_lock(mName.string());
            //在这里等待,也在这里被唤醒
            mWaitWorkCV.waitRelative(mLock, waitTime);
        }
    }
    // release delayed commands wake lock before quitting
    if (!mAudioCommands.isEmpty()) {
        release_wake_lock(mName.string());
    }
    mLock.unlock();
    return false;
}

唤醒线程后这里调用了AudioSystem的setStreamVolume方法,代码实现如下:

status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value,
        audio_io_handle_t output)
{
    if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
    if (af == 0) return PERMISSION_DENIED;
    af->setStreamVolume(stream, value, output);
    return NO_ERROR;
}

这里调用到了AudioFlinger的setStreamVolume

2.音量控制曲线

由于人耳对声音的听感具指数曲线型,也就是对小音量时比较敏感,随着声音的加大其听感随之变的不敏感,其变化近似指数函数曲线的形式。为了使听感变的近似直线的变化,人们在实践中就采用了音量变化近似对数式曲线型的电位器来实现这个目的。对比法产生音量控制曲线与最终扬声器输出的声压有关.

volIndexToAmpl函数是真正计算音量的地方,我们一起来分析这个函数。

float AudioPolicyManagerBase::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc,
        int indexInUi)
{
    device_category deviceCategory = getDeviceCategory(device);
    const VolumeCurvePoint *curve = streamDesc.mVolumeCurve[deviceCategory];

    // the volume index in the UI is relative to the min and max volume indices for this stream type
    int nbSteps = 1 + curve[VOLMAX].mIndex -
            curve[VOLMIN].mIndex;//计算预置的曲线区间的范围,这里是(1-100)
    ALOGI("VOLUME vol indexInUi=%d, nbSteps=%d, mIndexMin=%d, mIndexMax=%d",indexInUi,nbSteps,streamDesc.mIndexMin,streamDesc.mIndexMax);
    int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) /
            (streamDesc.mIndexMax - streamDesc.mIndexMin);//(由传进来的UIIndex计算百分比的index,比如现在是第一级 100*(1-0)/(15-0)=6)

    // find what part of the curve this index volume belongs to, or if it's out of bounds
    int segment = 0;
    if (volIdx < curve[VOLMIN].mIndex) {         // out of bounds
        return 0.0f;
    } else if (volIdx < curve[VOLKNEE1].mIndex) {
        segment = 0;
    } else if (volIdx < curve[VOLKNEE2].mIndex) {
        segment = 1;
    } else if (volIdx <= curve[VOLMAX].mIndex) {
        segment = 2;
    } else {                                                               // out of bounds
        return 1.0f;
    }
//第一极6是在区间VOLKNEE1之间,其区间表是在AudioPolicyManager初始化的时候就已经加载,因此它对应的segment为0
    // linear interpolation in the attenuation table in dB
    float decibels = curve[segment].mDBAttenuation +
            ((float)(volIdx - curve[segment].mIndex)) *
                ( (curve[segment+1].mDBAttenuation -
                        curve[segment].mDBAttenuation) /
                    ((float)(curve[segment+1].mIndex -
                            curve[segment].mIndex)) );
//计算衰减分贝数 curve[0].db + 该区间每一级index对应的db*index数  
    float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
//由指数公式计算出音量amplification db = 20log(V/Vmax)  linearToLog Vmax是一个参考值
    ALOGI("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f",
            curve[segment].mIndex, volIdx,
            curve[segment+1].mIndex,
            curve[segment].mDBAttenuation,
            decibels,
            curve[segment+1].mDBAttenuation,
            amplification);

    return amplification;
}
1. 音量索引映射公式

将UI上的音量级别 (indexInUi) 映射到预定义曲线的索引 (volIdx):

volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin)

其中:

nbSteps = curve[VOLMAX].mIndex - curve[VOLMIN].mIndex + 1

示例:

  • 若UI音量范围为 0~15,曲线范围为 1~100,用户选择 indexInUi=1 :
    nbSteps = 100 - 1 + 1 = 100
    volIdx = (100 * (1 - 0)) / (15 - 0) = 6.67 → 取整为6
    
2. 分段插值公式

根据 volIdx 所在曲线分段,计算分贝衰减值:

decibels = curve[segment].mDBAttenuation + 
           (volIdx - curve[segment].mIndex) * 
           (curve[segment+1].mDBAttenuation - curve[segment].mDBAttenuation) / 
           (curve[segment+1].mIndex - curve[segment].mIndex)

示例:

  • 分段0的曲线点:
    • curve[0].mIndex=1curve[0].mDBAttenuation=-49.5
    • curve[1].mIndex=33curve[1].mDBAttenuation=-33.5
  • 当 volIdx=6 时:
    decibels = -49.5 + (6-1) * (-33.5 - (-49.5)) / (33-1)
             = -49.5 + 5 * 16 / 32 
             = -49.5 + 2.5 
             = -47.0 dB
    
3. 分贝转放大倍数公式

将分贝值转换为音频放大倍数:

amplification = exp(decibels * 0.115129)

其中 0.115129 = ln(10)/20,对应声学公式:

dB = 20 * log10(V/V_max) → V = V_max * 10^(dB/20) = exp(dB * 0.115129)

示例:

  • 当 decibels = -47.0 dB 时:
    amplification = exp(-47.0 * 0.115129) 
                 = exp(-5.411) 
                 ≈ 0.0045
    
4. 分段规则

根据 volIdx 确定曲线分段:

  1. 分段0:volIdx ∈ [VOLMIN, VOLKNEE1)
  2. 分段1:volIdx ∈ [VOLKNEE1, VOLKNEE2)
  3. 分段2:volIdx ∈ [VOLKNEE2, VOLMAX]
5. 边界处理
  • 静音:若 volIdx < curve[VOLMIN].mIndex → 返回 amplification = 0.0
  • 最大音量:若 volIdx > curve[VOLMAX].mIndex → 返回 amplification = 1.0
6. 流程图
用户操作 → UI音量索引 (indexInUi)
           ↓
映射到曲线索引 (volIdx)
           ↓
确定分段 (segment 0/1/2)
           ↓
插值计算分贝值 (decibels)
           ↓
转换为放大倍数 (amplification)
           ↓
输出到音频硬件

你可能感兴趣的:(安卓,audio,音视频)