Article directory
Native's Process Processing
I just mentioned the logic in Audio Service, which is finally set to the native layer through Audio System. setStream Volume Index (mStream Type, index, device). So let's see how native handles it.
## Audio device
SetStream VolumeIndex is set according to device and stream type. In other words, the volume of each stream type of each device is separate and can be different. For example, a Bluetooth headset can have a Music volume of 15 and a Speaker volume of 5. So the question arises. There's no logic at all at the top? How did this work out? Let's first look at how to get the equipment!
* frameworks/base/services/core/java/com/android/server/audio/AudioService.java::VolumeStreamState public int observeDevicesForStream_syncVSS(boolean checkOthers) { final int devices = AudioSystem.getDevicesForStream(mStreamType); if (devices == mObservedDevices) { return devices; } final int prevDevices = mObservedDevices; mObservedDevices = devices; if (checkOthers) { // one stream's devices have changed, check the others observeDevicesForStreams(mStreamType); } // log base stream changes to the event log if (mStreamVolumeAlias[mStreamType] == mStreamType) { EventLogTags.writeStreamDevicesChanged(mStreamType, prevDevices, devices); } sendBroadcastToAll(mStreamDevicesChanged .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, prevDevices) .putExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, devices)); return devices; }
device is obtained through AudioSystem. getDevices ForStream (mStreamType), which is a stream type, not an alias for mapping.
native's getDevices ForStream function is as follows:
audio_devices_t AudioSystem::getDevicesForStream(audio_stream_type_t stream) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return AUDIO_DEVICE_NONE; return aps->getDevicesForStream(stream); }
The final decision is made through AudioPolicy.
* frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) { if (uint32_t(stream) >= AUDIO_STREAM_PUBLIC_CNT) { return AUDIO_DEVICE_NONE; } if (mAudioPolicyManager == NULL) { return AUDIO_DEVICE_NONE; } Mutex::Autolock _l(mLock); return mAudioPolicyManager->getDevicesForStream(stream); }
MAudioPolicy Managers are the core of AudioPolicy. When AudioPolicy Service was established, mAudioPolicy Managers and corresponding threads were created.
frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp void AudioPolicyService::onFirstRef() { { Mutex::Autolock _l(mLock); // start tone playback thread mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this); // start audio commands thread mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this); // start output activity command thread mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this); mAudioPolicyClient = new AudioPolicyClient(this); mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient); } // load audio processing modules sp<AudioPolicyEffects>audioPolicyEffects = new AudioPolicyEffects(); { Mutex::Autolock _l(mLock); mAudioPolicyEffects = audioPolicyEffects; } }
MAudioPolicy Manager is created through the createAudioPolicy Manager function. AOSP's createAudioPolicy Manager is as follows:
* frameworks/av/services/audiopolicy/manager/AudioPolicyFactory.cpp extern "C" AudioPolicyInterface* createAudioPolicyManager( AudioPolicyClientInterface *clientInterface) { return new AudioPolicyManager(clientInterface); }
Vendor can also implement its own Audio Policy Manager, such as Qualcomm's:
* hardware/qcom/audio/policy_hal/AudioPolicyManager.cpp extern "C" AudioPolicyInterface* createAudioPolicyManager( AudioPolicyClientInterface *clientInterface) { return new AudioPolicyManagerCustom(clientInterface); }
Let's move on to the getDevicesForStream function:
* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stream) { // By checking the range of stream before calling getStrategy, we avoid // getStrategy's behavior for invalid streams. getStrategy would do a ALOGE // and then return STRATEGY_MEDIA, but we want to return the empty set. if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_PUBLIC_CNT) { return AUDIO_DEVICE_NONE; } audio_devices_t devices = AUDIO_DEVICE_NONE; for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { continue; } routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream); audio_devices_t curDevices = getDeviceForStrategy((routing_strategy)curStrategy, false /*fromCache*/); SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(curDevices, mOutputs); for (size_t i = 0; i < outputs.size(); i++) { sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]); if (outputDesc->isStreamActive((audio_stream_type_t)curStream)) { curDevices |= outputDesc->device(); } } devices |= curDevices; } /*Filter SPEAKER_SAFE out of results, as AudioService doesn't know about it and doesn't really need to.*/ if (devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) { devices |= AUDIO_DEVICE_OUT_SPEAKER; devices &= ~AUDIO_DEVICE_OUT_SPEAKER_SAFE; } return devices; }
- Streams Match Forvolume is to determine whether two are equal.
- getStrategy function, which converts stream to Strategy
- GettDeviceForStrategy gets the device based on Strategy
- getOutputsForDevice gets device-related output
- Output-related device, output Desc - > device ()
## Mapping Audio Stream to Audio Policy
In native, streams are converted into policies, devices are selected according to policies, etc.
* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp routing_strategy AudioPolicyManager::getStrategy(audio_stream_type_t stream) const { ALOG_ASSERT(stream != AUDIO_STREAM_PATCH,"getStrategy() called for AUDIO_STREAM_PATCH"); return mEngine->getStrategyForStream(stream); }
mEngine is generated in the constructor of AudioPolicy Manager.
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface) : ... ... { ... ... // Once policy config has been parsed, retrieve an instance of the engine and initialize it. audio_policy::EngineInstance *engineInstance = audio_policy::EngineInstance::getInstance(); if (!engineInstance) { ALOGE("%s: Could not get an instance of policy engine", __FUNCTION__); return; } // Retrieve the Policy Manager Interface mEngine = engineInstance->queryInterface<AudioPolicyManagerInterface>(); if (mEngine == NULL) { ALOGE("%s: Failed to get Policy Engine Interface", __FUNCTION__); return; } mEngine->setObserver(this); status_t status = mEngine->initCheck();
Engine can be configured. According to the definition of USE_CONFIGURABLE_AUDIO_POLICY, we analyze Default's enginedefault here.
* frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp routing_strategy Engine::getStrategyForStream(audio_stream_type_t stream) { // stream to strategy mapping switch (stream) { case AUDIO_STREAM_VOICE_CALL: case AUDIO_STREAM_BLUETOOTH_SCO: return STRATEGY_PHONE; case AUDIO_STREAM_RING: case AUDIO_STREAM_ALARM: return STRATEGY_SONIFICATION; case AUDIO_STREAM_NOTIFICATION: return STRATEGY_SONIFICATION_RESPECTFUL; case AUDIO_STREAM_DTMF: return STRATEGY_DTMF; default: ALOGE("unknown stream type %d", stream); case AUDIO_STREAM_SYSTEM: // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs // while key clicks are played produces a poor result case AUDIO_STREAM_MUSIC: return STRATEGY_MEDIA; case AUDIO_STREAM_ENFORCED_AUDIBLE: return STRATEGY_ENFORCED_AUDIBLE; case AUDIO_STREAM_TTS: return STRATEGY_TRANSMITTED_THROUGH_SPEAKER; case AUDIO_STREAM_ACCESSIBILITY: return STRATEGY_ACCESSIBILITY; case AUDIO_STREAM_REROUTING: return STRATEGY_REROUTING; } }
Definition of native Sound Flow:
* system/media/audio/include/system/audio-base.h typedef enum { AUDIO_STREAM_DEFAULT = -1, // (-1) AUDIO_STREAM_MIN = 0, AUDIO_STREAM_VOICE_CALL = 0, AUDIO_STREAM_SYSTEM = 1, AUDIO_STREAM_RING = 2, AUDIO_STREAM_MUSIC = 3, AUDIO_STREAM_ALARM = 4, AUDIO_STREAM_NOTIFICATION = 5, AUDIO_STREAM_BLUETOOTH_SCO = 6, AUDIO_STREAM_ENFORCED_AUDIBLE = 7, AUDIO_STREAM_DTMF = 8, AUDIO_STREAM_TTS = 9, AUDIO_STREAM_ACCESSIBILITY = 10, AUDIO_STREAM_REROUTING = 11, AUDIO_STREAM_PATCH = 12, AUDIO_STREAM_PUBLIC_CNT = 11, // (ACCESSIBILITY + 1) AUDIO_STREAM_FOR_POLICY_CNT = 12, // PATCH AUDIO_STREAM_CNT = 13, // (PATCH + 1) } audio_stream_type_t;
Policy Definition:
* frameworks/av/services/audiopolicy/common/include/RoutingStrategy.h enum routing_strategy { STRATEGY_MEDIA, STRATEGY_PHONE, STRATEGY_SONIFICATION, STRATEGY_SONIFICATION_RESPECTFUL, STRATEGY_DTMF, STRATEGY_ENFORCED_AUDIBLE, STRATEGY_TRANSMITTED_THROUGH_SPEAKER, STRATEGY_ACCESSIBILITY, STRATEGY_REROUTING, NUM_STRATEGIES };
The corresponding relations between them are as follows:
Flow code | Flow type | Strategy Mapping | describe |
---|---|---|---|
0 | STREAM_VOICE_CALL | STRATEGY_PHONE | Voice related audio |
6 | AUDIO_STREAM_BLUETOOTH_SCO | STRATEGY_PHONE | Bluetooth SCO Call Related Audio |
2 | AUDIO_STREAM_RING | STRATEGY_SONIFICATION | Ring tone |
4 | AUDIO_STREAM_ALARM | STRATEGY_SONIFICATION | alarm clock |
5 | AUDIO_STREAM_NOTIFICATION | STRATEGY_SONIFICATION_RESPECTFUL | Notification tone |
8 | AUDIO_STREAM_DTMF | STRATEGY_DTMF | DTMF tone |
1 | AUDIO_STREAM_SYSTEM | STRATEGY_MEDIA | System sound |
3 | AUDIO_STREAM_MUSIC | STRATEGY_MEDIA | Media voice |
7 | AUDIO_STREAM_ENFORCED_AUDIBLE | STRATEGY_ENFORCED_AUDIBLE | Force Speaker to speak |
9 | AUDIO_STREAM_TTS | STRATEGY_TRANSMITTED_THROUGH_SPEAKER | TTS broadcast |
10 | AUDIO_STREAM_ACCESSIBILITY | STRATEGY_ACCESSIBILITY | Auxiliary tone |
11 | AUDIO_STREAM_REROUTING | STRATEGY_REROUTING | Dynamic output mixing |
AUDIO_STREAM_ENFORCED_AUDIBLE is a stream that does not silence users and forces Speaker to speak. For example, photographic sound, photographic sound must be Speaker voice, anti-camera.
Back to the getDevices ForStream function ~
Acquiring equipment
According to the flow type, after the policy is acquired, the related devices are acquired according to the policy.
* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy, bool fromCache) { // Routing // see if we have an explicit route // scan the whole RouteMap, for each entry, convert the stream type to a strategy // (getStrategy(stream)). // if the strategy from the stream type in the RouteMap is the same as the argument above, // and activity count is non-zero and the device in the route descriptor is available // then select this device. for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) { sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex); routing_strategy routeStrategy = getStrategy(route->mStreamType); if ((routeStrategy == strategy) && route->isActiveOrChanged() && (mAvailableOutputDevices.indexOf(route->mDeviceDescriptor) >= 0)) { return route->mDeviceDescriptor->type(); } } if (fromCache) { ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]); return mDeviceForStrategy[strategy]; } return mEngine->getDeviceForStrategy(strategy); }
Finally, it is realized by mEngine. STRATEGY_TRANSMITTED_THROUGH_SPEAKER
* frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const { DeviceVector availableOutputDevices = mApmObserver->getAvailableOutputDevices(); DeviceVector availableInputDevices = mApmObserver->getAvailableInputDevices(); const SwAudioOutputCollection &outputs = mApmObserver->getOutputs(); return getDeviceForStrategyInt(strategy, availableOutputDevices, availableInputDevices, outputs); }
It is mainly implemented in getDeviceForStrategyInt function. The getDeviceForStrategyInt function is very long. There is no full code to paste here. The main decisions are as follows:
- Is it on the phone?
- What is the current flow out of Active state
- Is there ForceUse?
Let's look at the STRATEGY_SONIFICATION_RESPECTFUL, AUDIO_STREAM_NOTIFICATION scenario:
case STRATEGY_SONIFICATION_RESPECTFUL: if (isInCall()) { device = getDeviceForStrategyInt( STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs); } else if (outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { // while media is playing on a remote device, use the the sonification behavior. // Note that we test this usecase before testing if media is playing because // the isStreamActive() method only informs about the activity of a stream, not // if it's for local playback. Note also that we use the same delay between both tests device = getDeviceForStrategyInt( STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs); //user "safe" speaker if available instead of normal speaker to avoid triggering //other acoustic safety mechanisms for notification if ((device & AUDIO_DEVICE_OUT_SPEAKER) && (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) { device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE; device &= ~AUDIO_DEVICE_OUT_SPEAKER; } } else if (outputs.isStreamActive( AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) || outputs.isStreamActive( AUDIO_STREAM_ACCESSIBILITY, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { // while media/a11y is playing (or has recently played), use the same device device = getDeviceForStrategyInt( STRATEGY_MEDIA, availableOutputDevices, availableInputDevices, outputs); } else { // when media is not playing anymore, fall back on the sonification behavior device = getDeviceForStrategyInt( STRATEGY_SONIFICATION, availableOutputDevices, availableInputDevices, outputs); //user "safe" speaker if available instead of normal speaker to avoid triggering //other acoustic safety mechanisms for notification if ((device & AUDIO_DEVICE_OUT_SPEAKER) && (availableOutputDevicesType & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) { device |= AUDIO_DEVICE_OUT_SPEAKER_SAFE; device &= ~AUDIO_DEVICE_OUT_SPEAKER; } } break;
- Convert to STRATEGY_SONIFICATION in case of a call
- If Remote Music Active, convert to STRATEGY_SONIFICATION, but if SPEAKER_SAFE is supported, SPEAKER_SAFE is preferred.
- If AUDIO_STREAM_MUSIC or AUDIO_STREAM_ACCESSIBILITY, convert to STRATEGY_MEDIA
- In other cases, STRATEGY_SONIFICATION is used, but if SPEAKER_SAFE is supported, SPEAKER_SAFE is preferred.
Specific circumstances, in particular, are more complex. Anyway, so far, our device has been acquired.
Native sets volume
Volume is set to native through Audio System's setStream VolumeIndex function:
public void applyDeviceVolume_syncVSS(int device) { int index; if (mIsMuted) { index = 0; } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported) { index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10); } else if ((device & mFullVolumeDevices) != 0) { index = (mIndexMax + 5)/10; } else { index = (getIndex(device) + 5)/10; } AudioSystem.setStreamVolumeIndex(mStreamType, index, device); }
The intermediate process is omitted. We implement AudioPolicy Manager directly:
* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, int index, audio_devices_t device) { ... ... // Force max volume if stream cannot be muted if (!mVolumeCurves->canBeMuted(stream)) index = mVolumeCurves->getVolumeIndexMax(stream); ALOGV("setStreamVolumeIndex() stream %d, device %08x, index %d", stream, device, index); // update other private stream volumes which follow this one for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { continue; } mVolumeCurves->addCurrentVolumeIndex((audio_stream_type_t)curStream, device, index); } status_t status = NO_ERROR; for (size_t i = 0; i < mOutputs.size(); i++) { sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i); audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device()); for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) { if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) { continue; } if (!(desc->isStreamActive((audio_stream_type_t)curStream) || (isInCall() && (curStream == AUDIO_STREAM_VOICE_CALL)))) { continue; } routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream); audio_devices_t curStreamDevice = Volume::getDeviceForVolume(getDeviceForStrategy( curStrategy, false /*fromCache*/)); if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) && ((curStreamDevice & device) == 0)) { continue; } bool applyVolume; if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) { curStreamDevice |= device; applyVolume = (curDevice & curStreamDevice) != 0; } else { applyVolume = !mVolumeCurves->hasVolumeIndexForDevice( stream, curStreamDevice); } if (applyVolume) { //FIXME: workaround for truncated touch sounds // delayed volume change for system stream to be removed when the problem is // handled by system UI status_t volStatus = checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice, (stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0); if (volStatus != NO_ERROR) { status = volStatus; } } } } return status; }
- mVolumeCurves, the description of streams, is a collection, represented by Stream Descriptor Collection. Specific flows are described by Stream Descriptor. The previous flows are described by an int type, and now Stream Descriptor is used to describe them.
frameworks/av/services/audiopolicy/common/managerdefinitions/src/StreamDescriptor.cpp
- The index value of Volume is saved in Stream Descriptor by addCurrent Volume Index:
* frameworks/av/services/audiopolicy/common/managerdefinitions/src/StreamDescriptor.cpp void StreamDescriptor::addCurrentVolumeIndex(audio_devices_t device, int index) { mIndexCur.add(device, index); }
Volume is saved in mIndexCur according to device:
KeyedVector<audio_devices_t, int> mIndexCur;
- mOutputs Current Output Devices
Each output device is described in SwAudio Output Descriptor:
frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
- Get the equipment, confirm again
Volume::getDeviceForVolume static audio_devices_t getDeviceForVolume(audio_devices_t device) { if (device == AUDIO_DEVICE_NONE) { device = AUDIO_DEVICE_OUT_SPEAKER; } else if (popcount(device) > 1) { if (device & AUDIO_DEVICE_OUT_SPEAKER) { device = AUDIO_DEVICE_OUT_SPEAKER; } else if (device & AUDIO_DEVICE_OUT_SPEAKER_SAFE) { device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; } else if (device & AUDIO_DEVICE_OUT_HDMI_ARC) { device = AUDIO_DEVICE_OUT_HDMI_ARC; } else if (device & AUDIO_DEVICE_OUT_AUX_LINE) { device = AUDIO_DEVICE_OUT_AUX_LINE; } else if (device & AUDIO_DEVICE_OUT_SPDIF) { device = AUDIO_DEVICE_OUT_SPDIF; } else { device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP); } } /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/ if (device == AUDIO_DEVICE_OUT_SPEAKER_SAFE) device = AUDIO_DEVICE_OUT_SPEAKER; return device; }
- According to the flow acquisition strategy, the device is acquired according to the test, and then the volume is judged according to the device whether the volume needs to be applied. The volume is validated by checkAndSetVolume.
If no stream is currently active, the volume settings are not valid, depending on the previous applyVolume. If it does not take effect at this time, AndSetVolume will also be checked at startSource later.
* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, int index, const sp<AudioOutputDescriptor>& outputDesc, audio_devices_t device, int delayMs, bool force) { // Quiet, no volume changes if (outputDesc->mMuteCount[stream] != 0) { ALOGVV("checkAndSetVolume() stream %d muted count %d", stream, outputDesc->mMuteCount[stream]); return NO_ERROR; } audio_policy_forced_cfg_t forceUseForComm = mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION); // If it's a Bluetooth call, don't change the volume value, and vice versa if ((stream == AUDIO_STREAM_VOICE_CALL && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) || (stream == AUDIO_STREAM_BLUETOOTH_SCO && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO)) { ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm", stream, forceUseForComm); return INVALID_OPERATION; } if (device == AUDIO_DEVICE_NONE) { device = outputDesc->device(); } // The key code computeVolume calculates the volume value and converts index value to Db value. float volumeDb = computeVolume(stream, index, device); if (outputDesc->isFixedVolume(device)) { volumeDb = 0.0f; } // set volume outputDesc->setVolume(volumeDb, stream, device, delayMs, force); // Call volume, SCO set to maximum, headset managed by itself. if (stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) { float voiceVolume; if (stream == AUDIO_STREAM_VOICE_CALL) { voiceVolume = (float)index/(float)mVolumeCurves->getVolumeIndexMax(stream); } else { voiceVolume = 1.0; } if (voiceVolume != mLastVoiceVolume) { mpClientInterface->setVoiceVolume(voiceVolume, delayMs); mLastVoiceVolume = voiceVolume; } } return NO_ERROR; }
Calculate volume Db value
* frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp float AudioPolicyManager::computeVolume(audio_stream_type_t stream, int index, audio_devices_t device) { float volumeDB = mVolumeCurves->volIndexToDb(stream, Volume::getDeviceCategory(device), index); // Handle scenes where auxiliary functions are turned on and the bell rings if ((stream == AUDIO_STREAM_ACCESSIBILITY) && (AUDIO_MODE_RINGTONE == mEngine->getPhoneState()) && isStreamActive(AUDIO_STREAM_RING, 0)) { const float ringVolumeDB = computeVolume(AUDIO_STREAM_RING, index, device); return ringVolumeDB - 4 > volumeDB ? ringVolumeDB - 4 : volumeDB; } // Scene in the Call if ((stream != AUDIO_STREAM_VOICE_CALL) && (device & AUDIO_DEVICE_OUT_EARPIECE) && isInCall()) { switch (stream) { case AUDIO_STREAM_SYSTEM: case AUDIO_STREAM_RING: case AUDIO_STREAM_MUSIC: case AUDIO_STREAM_ALARM: case AUDIO_STREAM_NOTIFICATION: case AUDIO_STREAM_ENFORCED_AUDIBLE: case AUDIO_STREAM_DTMF: case AUDIO_STREAM_ACCESSIBILITY: { const float maxVoiceVolDb = computeVolume(AUDIO_STREAM_VOICE_CALL, index, device) + IN_CALL_EARPIECE_HEADROOM_DB; if (volumeDB > maxVoiceVolDb) { ALOGV("computeVolume() stream %d at vol=%f overriden by stream %d at vol=%f", stream, volumeDB, AUDIO_STREAM_VOICE_CALL, maxVoiceVolDb); volumeDB = maxVoiceVolDb; } } break; default: break; } } // The scene of inserting headphones to prevent loud ringing and other sounds const routing_strategy stream_strategy = getStrategy(stream); if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE | AUDIO_DEVICE_OUT_USB_HEADSET)) && ((stream_strategy == STRATEGY_SONIFICATION) || (stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL) || (stream == AUDIO_STREAM_SYSTEM) || ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) && (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) && mVolumeCurves->canBeMuted(stream)) { // when the phone is ringing we must consider that music could have been paused just before // by the music application and behave as if music was active if the last music track was // just stopped if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) || mLimitRingtoneVolume) { volumeDB += SONIFICATION_HEADSET_VOLUME_FACTOR_DB; audio_devices_t musicDevice = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/); float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC, mVolumeCurves->getVolumeIndex(AUDIO_STREAM_MUSIC, musicDevice), musicDevice); float minVolDB = (musicVolDB > SONIFICATION_HEADSET_VOLUME_MIN_DB) ? musicVolDB : SONIFICATION_HEADSET_VOLUME_MIN_DB; if (volumeDB > minVolDB) { volumeDB = minVolDB; ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDB, musicVolDB); } if (device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES)) { // on A2DP, also ensure notification volume is not too low compared to media when // intended to be played if ((volumeDB > -96.0f) && (musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB > volumeDB)) { ALOGV("computeVolume increasing volume for stream=%d device=0x%X from %f to %f", stream, device, volumeDB, musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB); volumeDB = musicVolDB - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB; } } } else if ((Volume::getDeviceForVolume(device) != AUDIO_DEVICE_OUT_SPEAKER) || stream_strategy != STRATEGY_SONIFICATION) { volumeDB += SONIFICATION_HEADSET_VOLUME_FACTOR_DB; } } return volumeDB; }
The Db value is converted through mVolumeCurves - > volIndexToDb, and then adjusted according to the actual scene.
Let's take a look at Volume::getDeviceCategory, Audio's side of the mess, all kinds of concepts. device_category classifies devices.
* frameworks/av/services/audiopolicy/common/include/Volume.h static device_category getDeviceCategory(audio_devices_t device) { switch(getDeviceForVolume(device)) { case AUDIO_DEVICE_OUT_EARPIECE: return DEVICE_CATEGORY_EARPIECE; case AUDIO_DEVICE_OUT_WIRED_HEADSET: case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: case AUDIO_DEVICE_OUT_USB_HEADSET: return DEVICE_CATEGORY_HEADSET; case AUDIO_DEVICE_OUT_LINE: case AUDIO_DEVICE_OUT_AUX_DIGITAL: case AUDIO_DEVICE_OUT_USB_DEVICE: return DEVICE_CATEGORY_EXT_MEDIA; case AUDIO_DEVICE_OUT_SPEAKER: case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: case AUDIO_DEVICE_OUT_USB_ACCESSORY: case AUDIO_DEVICE_OUT_REMOTE_SUBMIX: default: return DEVICE_CATEGORY_SPEAKER; } }
The volIndexToDb function is as follows:
float StreamDescriptorCollection::volIndexToDb(audio_stream_type_t stream, device_category category, int indexInUi) const { const StreamDescriptor &streamDesc = valueAt(stream); return Gains::volIndexToDb(streamDesc.getVolumeCurvePoint(category), streamDesc.getVolumeIndexMin(), streamDesc.getVolumeIndexMax(), indexInUi); }
float Gains::volIndexToDb(const VolumeCurvePoint *curve, int indexMin, int indexMax, int indexInUi) { // the volume index in the UI is relative to the min and max volume indices for this stream type int nbSteps = 1 + curve[Volume::VOLMAX].mIndex - curve[Volume::VOLMIN].mIndex; int volIdx = (nbSteps * (indexInUi - indexMin)) / (indexMax - indexMin); // find what part of the curve this index volume belongs to, or if it's out of bounds int segment = 0; if (volIdx < curve[Volume::VOLMIN].mIndex) { // out of bounds return VOLUME_MIN_DB; } else if (volIdx < curve[Volume::VOLKNEE1].mIndex) { segment = 0; } else if (volIdx < curve[Volume::VOLKNEE2].mIndex) { segment = 1; } else if (volIdx <= curve[Volume::VOLMAX].mIndex) { segment = 2; } else { // out of bounds return 0.0f; } // 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)) ); ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]", curve[segment].mIndex, volIdx, curve[segment+1].mIndex, curve[segment].mDBAttenuation, decibels, curve[segment+1].mDBAttenuation); return decibels; }
The Db values are all configured in audio_policy_volumes.xml. AOSP
frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml
Installation streams and device classifications to distinguish, for example, the volume of a headset call:
<volume stream="AUDIO_STREAM_VOICE_CALL" deviceCategory="DEVICE_CATEGORY_HEADSET"> <point>0,-4200</point> <point>33,-2800</point> <point>66,-1400</point> <point>100,0</point> </volume>
In addition to directly defining values, you can also refer to other things, such as the media volume of headphones, DEFAULT_MEDIA_VOLUME_CURVE.
<volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_HEADSET" ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
DEFAULT_MEDIA_VOLUME_CURVE is given in another table:
* frameworks/av/services/audiopolicy/config/default_volume_tables.xml <reference name="DEFAULT_MEDIA_VOLUME_CURVE"> <!-- Default Media reference Volume Curve --> <point>1,-5800</point> <point>20,-4000</point> <point>60,-1700</point> <point>100,0</point> </reference>
So when we adjust the volume, we change it to the corresponding DB according to the volume index, and the final effect is db. Many users feedback that the volume above is very high, but the actual voice is still low, maybe adjust the DB configuration here.
Volume effective
The calculated Db value is set to output through the setVolume function
outputDesc->setVolume(volumeDb, stream, device, delayMs, force);
The functions are as follows:
bool SwAudioOutputDescriptor::setVolume(float volume, audio_stream_type_t stream, audio_devices_t device, uint32_t delayMs, bool force) { bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force); if (changed) { // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is // enabled float volume = Volume::DbToAmpl(mCurVolume[stream]); if (stream == AUDIO_STREAM_BLUETOOTH_SCO) { mClientInterface->setStreamVolume( AUDIO_STREAM_VOICE_CALL, volume, mIoHandle, delayMs); } mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs); } return changed; }
- Save the volume value in Descriptor first
bool AudioOutputDescriptor::setVolume(float volume, audio_stream_type_t stream, audio_devices_t device __unused, uint32_t delayMs, bool force) { // We actually change the volume if: // - the float value returned by computeVolume() changed // - the force flag is set if (volume != mCurVolume[stream] || force) { ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volume, delayMs); mCurVolume[stream] = volume; return true; } return false; }
Each output is an mCurVolume[stream] saved by the installation stream type
- SetStream Volume Interface Through mClientInterface
* frameworks/av/services/audiopolicy/service/AudioPolicyClientImpl.cpp 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); }
Audio Policy Service is passed to Audio Manager through Audio Command Thread
* frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp 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); }
The corresponding command: SET_VOLUME
bool AudioPolicyService::AudioCommandThread::threadLoop() { nsecs_t waitTime = -1; mLock.lock(); while (!exitPending()) { sp<AudioPolicyService> svc; while (!mAudioCommands.isEmpty() && !exitPending()) { ... ... case SET_VOLUME: { VolumeData *data = (VolumeData *)command->mParam.get(); ALOGV("AudioCommandThread() processing set volume stream %d, \ volume %f, output %d", data->mStream, data->mVolume, data->mIO); command->mStatus = AudioSystem::setStreamVolume(data->mStream, data->mVolume, data->mIO); }break;
Ultimately through the Audio System interface to complete:
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's setStream Volume function is as follows:
status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output) { ALOGI("setStreamVolume: stream %d, value %f, output %d", stream, value, output); // Jurisdiction if (!settingsAllowed()) { return PERMISSION_DENIED; } // Flow type status_t status = checkStreamType(stream); if (status != NO_ERROR) { return status; } ALOG_ASSERT(stream != AUDIO_STREAM_PATCH, "attempt to change AUDIO_STREAM_PATCH volume"); AutoMutex lock(mLock); Vector<VolumeInterface *> volumeInterfaces; //Get volume interface if (output != AUDIO_IO_HANDLE_NONE) { VolumeInterface *volumeInterface = getVolumeInterface_l(output); if (volumeInterface == NULL) { return BAD_VALUE; } volumeInterfaces.add(volumeInterface); } // Save volume Db value mStreamTypes[stream].volume = value; if (volumeInterfaces.size() == 0) { volumeInterfaces = getAllVolumeInterfaces_l(); } // Setting Volume Value for (size_t i = 0; i < volumeInterfaces.size(); i++) { volumeInterfaces[i]->setStreamVolume(stream, value); } return NO_ERROR; }
Volume Interface is mainly used to set the volume. Playback Thread and Map Playback Thread implement the specific interface of Volume Interface:
class VolumeInterface { public: virtual ~VolumeInterface() {} virtual void setMasterVolume(float value) = 0; virtual void setMasterMute(bool muted) = 0; virtual void setStreamVolume(audio_stream_type_t stream, float value) = 0; virtual void setStreamMute(audio_stream_type_t stream, bool muted) = 0; virtual float streamVolume(audio_stream_type_t stream) const = 0; };
Let's look at PlaybackThread's setStream Volume function:
void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value) { Mutex::Autolock _l(mLock); size_t size = mEffectChains.size(); mStreamTypes[stream].volume = value; for (size_t i = 0; i < size; i++) { mEffectChains[i]->setStreamVolume_l(stream, value); } broadcast_l(); }
PlaybackThread saves the volume value and sets it to the sound effect.
OK, so far, it's all about setting the volume, so where does it work?
For MixerThread, it takes effect here, and the volume value is eventually mixed into the data stream.
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l( Vector< sp<Track> > *tracksToRemove) { ... ... // 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 // read original volumes with volume control float typeVolume = mStreamTypes[track->streamType()].volume; float v = masterVolume * typeVolume; if (track->isPausing() || mStreamTypes[track->streamType()].mute) { vl = vr = 0; vlf = vrf = vaf = 0.; if (track->isPausing()) { track->setPaused(); } } else { sp<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; } const float vh = track->getVolumeHandler()->getVolume( track->mAudioTrackServerProxy->framesReleased()).first; // now apply the master volume and stream type volume and shaper volume vlf *= v * vh; vrf *= v * vh; // 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 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); }
We can dump the volume of each stream of thread s through the adb command:
$ adb shell dumpsys media.audio_flinger | grep "Stream volumes in dB" Stream volumes in dB: 0:-24, 1:-27, 2:-25, 3:-52, 4:0, 5:-27, 6:0, 7:-6, 8:-27, 9:0, 10:-47, 11:0, 12:0 Stream volumes in dB: 0:-24, 1:-27, 2:-25, 3:-52, 4:0, 5:-27, 6:0, 7:-6, 8:-27, 9:0, 10:-47, 11:0, 12:0 Stream volumes in dB: 0:-24, 1:-27, 2:-25, 3:-52, 4:0, 5:-27, 6:0, 7:-6, 8:-27, 9:0, 10:-47, 11:0, 12:0