From d9cfeb447356cb6334379eaf5da1e49424eb5979 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Mon, 22 Sep 2014 16:51:34 -0700 Subject: Support speaker routing for FLAG_BEACON Add support for routing audio stream to the speaker when audio attributes carry the FLAG_BEACON value: - associate streams with FLAG_BEACON to the up-to-now unused STREAM_TTS ("Transmitted Through Speaker"). The remapping happens in AudioTrack. - AudioPolicyManager: define new strategy for handling the Beacon behavior, that only goes to speaker. Define new volume curves for STREAM_TTS that are not silence only for the speaker device category. Keep ref count of other streams and never play Beacon streams when anything else is playing: either don't start playback if something else is playing, or unmute STREAM_TTS when it's allowed to play and mute when it's done. - Take into account muting latency when starting an output. Bug 15415971 Change-Id: I26539c7ec1a486accd85bbeb1623e9c7d3a1192f --- services/audiopolicy/AudioPolicyManager.cpp | 135 ++++++++++++++++++++++++++-- services/audiopolicy/AudioPolicyManager.h | 22 +++++ 2 files changed, 148 insertions(+), 9 deletions(-) (limited to 'services/audiopolicy') diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp index 5567800..584e170 100644 --- a/services/audiopolicy/AudioPolicyManager.cpp +++ b/services/audiopolicy/AudioPolicyManager.cpp @@ -1127,6 +1127,20 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, return BAD_VALUE; } + // cannot start playback of STREAM_TTS if any other output is being used + uint32_t beaconMuteLatency = 0; + if (stream == AUDIO_STREAM_TTS) { + ALOGV("\t found BEACON stream"); + if (isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) { + return INVALID_OPERATION; + } else { + beaconMuteLatency = handleEventForBeacon(STARTING_BEACON); + } + } else { + // some playback other than beacon starts + beaconMuteLatency = handleEventForBeacon(STARTING_OUTPUT); + } + sp outputDesc = mOutputs.valueAt(index); // increment usage count for this stream on the requested output: @@ -1138,8 +1152,9 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/); routing_strategy strategy = getStrategy(stream); bool shouldWait = (strategy == STRATEGY_SONIFICATION) || - (strategy == STRATEGY_SONIFICATION_RESPECTFUL); - uint32_t waitMs = 0; + (strategy == STRATEGY_SONIFICATION_RESPECTFUL) || + (beaconMuteLatency > 0); + uint32_t waitMs = beaconMuteLatency; bool force = false; for (size_t i = 0; i < mOutputs.size(); i++) { sp desc = mOutputs.valueAt(i); @@ -1153,7 +1168,8 @@ status_t AudioPolicyManager::startOutput(audio_io_handle_t output, force = true; } // wait for audio on other active outputs to be presented when starting - // a notification so that audio focus effect can propagate. + // a notification so that audio focus effect can propagate, or that a mute/unmute + // event occurred for beacon uint32_t latency = desc->latency(); if (shouldWait && desc->isActive(latency * 2) && (waitMs < latency)) { waitMs = latency; @@ -1197,6 +1213,9 @@ status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, sp outputDesc = mOutputs.valueAt(index); + // always handle stream stop, check which stream type is stopping + handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT); + // handle special case for sonification while in call if (isInCall()) { handleIncallSonification(stream, false, false); @@ -2669,7 +2688,10 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0), mA2dpSuspended(false), mSpeakerDrcEnabled(false), mNextUniqueId(1), - mAudioPortGeneration(1) + mAudioPortGeneration(1), + mBeaconMuteRefCount(0), + mBeaconPlayingRefCount(0), + mBeaconMuted(false) { mUidCached = getuid(); mpClientInterface = clientInterface; @@ -3840,6 +3862,8 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, // use device for strategy media // 7: the strategy DTMF is active on the output: // use device for strategy DTMF + // 8: the strategy for beacon, a.k.a. "transmitted through speaker" is active on the output: + // use device for strategy t-t-s if (outputDesc->isStrategyActive(STRATEGY_ENFORCED_AUDIBLE) && mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); @@ -3856,6 +3880,8 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache); } else if (outputDesc->isStrategyActive(STRATEGY_DTMF)) { device = getDeviceForStrategy(STRATEGY_DTMF, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_TRANSMITTED_THROUGH_SPEAKER)) { + device = getDeviceForStrategy(STRATEGY_TRANSMITTED_THROUGH_SPEAKER, fromCache); } ALOGV("getNewOutputDevice() selected device %x", device); @@ -3934,16 +3960,20 @@ AudioPolicyManager::routing_strategy AudioPolicyManager::getStrategy( 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_TTS: 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; } } uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) { // flags to strategy mapping + if ((attr->flags & AUDIO_FLAG_BEACON) == AUDIO_FLAG_BEACON) { + return (uint32_t) STRATEGY_TRANSMITTED_THROUGH_SPEAKER; + } if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) { return (uint32_t) STRATEGY_ENFORCED_AUDIBLE; } @@ -3991,6 +4021,74 @@ void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t } } +bool AudioPolicyManager::isAnyOutputActive(audio_stream_type_t streamToIgnore) { + for (size_t s = 0 ; s < AUDIO_STREAM_CNT ; s++) { + if (s == (size_t) streamToIgnore) { + continue; + } + for (size_t i = 0; i < mOutputs.size(); i++) { + const sp outputDesc = mOutputs.valueAt(i); + if (outputDesc->mRefCount[s] != 0) { + return true; + } + } + } + return false; +} + +uint32_t AudioPolicyManager::handleEventForBeacon(int event) { + switch(event) { + case STARTING_OUTPUT: + mBeaconMuteRefCount++; + break; + case STOPPING_OUTPUT: + if (mBeaconMuteRefCount > 0) { + mBeaconMuteRefCount--; + } + break; + case STARTING_BEACON: + mBeaconPlayingRefCount++; + break; + case STOPPING_BEACON: + if (mBeaconPlayingRefCount > 0) { + mBeaconPlayingRefCount--; + } + break; + } + + if (mBeaconMuteRefCount > 0) { + // any playback causes beacon to be muted + return setBeaconMute(true); + } else { + // no other playback: unmute when beacon starts playing, mute when it stops + return setBeaconMute(mBeaconPlayingRefCount == 0); + } +} + +uint32_t AudioPolicyManager::setBeaconMute(bool mute) { + ALOGV("setBeaconMute(%d) mBeaconMuteRefCount=%d mBeaconPlayingRefCount=%d", + mute, mBeaconMuteRefCount, mBeaconPlayingRefCount); + // keep track of muted state to avoid repeating mute/unmute operations + if (mBeaconMuted != mute) { + // mute/unmute AUDIO_STREAM_TTS on all outputs + ALOGV("\t muting %d", mute); + uint32_t maxLatency = 0; + for (size_t i = 0; i < mOutputs.size(); i++) { + sp desc = mOutputs.valueAt(i); + setStreamMute(AUDIO_STREAM_TTS, mute/*on*/, + desc->mIoHandle, + 0 /*delay*/, AUDIO_DEVICE_NONE); + const uint32_t latency = desc->latency() * 2; + if (latency > maxLatency) { + maxLatency = latency; + } + } + mBeaconMuted = mute; + return maxLatency; + } + return 0; +} + audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy, bool fromCache) { @@ -4004,6 +4102,14 @@ audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strate audio_devices_t availableOutputDeviceTypes = mAvailableOutputDevices.types(); switch (strategy) { + case STRATEGY_TRANSMITTED_THROUGH_SPEAKER: + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; + if (!device) { + ALOGE("getDeviceForStrategy() no device found for "\ + "STRATEGY_TRANSMITTED_THROUGH_SPEAKER"); + } + break; + case STRATEGY_SONIFICATION_RESPECTFUL: if (isInCall()) { device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); @@ -4929,6 +5035,16 @@ const AudioPolicyManager::VolumeCurvePoint }; const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sLinearVolumeCurve[AudioPolicyManager::VOLCNT] = { + {0, -96.0f}, {33, -68.0f}, {66, -34.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSilentVolumeCurve[AudioPolicyManager::VOLCNT] = { + {0, -96.0f}, {1, -96.0f}, {2, -96.0f}, {100, -96.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint *AudioPolicyManager::sVolumeProfiles[AUDIO_STREAM_CNT] [AudioPolicyManager::DEVICE_CATEGORY_CNT] = { { // AUDIO_STREAM_VOICE_CALL @@ -4986,10 +5102,11 @@ const AudioPolicyManager::VolumeCurvePoint sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA }, { // AUDIO_STREAM_TTS - sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET - sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER - sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE - sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + // "Transmitted Through Speaker": always silent except on DEVICE_CATEGORY_SPEAKER + sSilentVolumeCurve, // DEVICE_CATEGORY_HEADSET + sLinearVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sSilentVolumeCurve, // DEVICE_CATEGORY_EARPIECE + sSilentVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA }, }; diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h index 7dbd73f..50d7831 100644 --- a/services/audiopolicy/AudioPolicyManager.h +++ b/services/audiopolicy/AudioPolicyManager.h @@ -187,6 +187,7 @@ protected: STRATEGY_SONIFICATION_RESPECTFUL, STRATEGY_DTMF, STRATEGY_ENFORCED_AUDIBLE, + STRATEGY_TRANSMITTED_THROUGH_SPEAKER, NUM_STRATEGIES }; @@ -434,6 +435,8 @@ protected: static const VolumeCurvePoint sHeadsetSystemVolumeCurve[AudioPolicyManager::VOLCNT]; static const VolumeCurvePoint sDefaultVoiceVolumeCurve[AudioPolicyManager::VOLCNT]; static const VolumeCurvePoint sSpeakerVoiceVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sLinearVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sSilentVolumeCurve[AudioPolicyManager::VOLCNT]; // default volume curves per stream and device category. See initializeVolumeCurves() static const VolumeCurvePoint *sVolumeProfiles[AUDIO_STREAM_CNT][DEVICE_CATEGORY_CNT]; @@ -808,6 +811,18 @@ protected: sp mCallTxPatch; sp mCallRxPatch; + // for supporting "beacon" streams, i.e. streams that only play on speaker, and never + // when something other than STREAM_TTS (a.k.a. "Transmitted Through Speaker") is playing + enum { + STARTING_OUTPUT, + STARTING_BEACON, + STOPPING_OUTPUT, + STOPPING_BEACON + }; + uint32_t mBeaconMuteRefCount; // ref count for stream that would mute beacon + uint32_t mBeaconPlayingRefCount;// ref count for the playing beacon streams + bool mBeaconMuted; // has STREAM_TTS been muted + #ifdef AUDIO_POLICY_TEST Mutex mLock; Condition mWaitWorkCV; @@ -852,6 +867,13 @@ private: const audio_offload_info_t *offloadInfo); // internal function to derive a stream type value from audio attributes audio_stream_type_t streamTypefromAttributesInt(const audio_attributes_t *attr); + // return true if any output is playing anything besides the stream to ignore + bool isAnyOutputActive(audio_stream_type_t streamToIgnore); + // event is one of STARTING_OUTPUT, STARTING_BEACON, STOPPING_OUTPUT, STOPPING_BEACON + // returns 0 if no mute/unmute event happened, the largest latency of the device where + // the mute/unmute happened + uint32_t handleEventForBeacon(int event); + uint32_t setBeaconMute(bool mute); }; }; -- cgit v1.1