VolumeShaper: Enable for offload and direct tracks
Test: Play Music in offload mode
Bug: 31015569
Change-Id: I00bb59e3e6809d4682f42057b1cc083f4fa9b9d1
diff --git a/include/media/VolumeShaper.h b/include/media/VolumeShaper.h
index acb22ab..1421ae7 100644
--- a/include/media/VolumeShaper.h
+++ b/include/media/VolumeShaper.h
@@ -94,6 +94,14 @@
, mId(-1) {
}
+ Configuration(const Configuration &configuration)
+ : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
+ , mType(configuration.mType)
+ , mOptionFlags(configuration.mOptionFlags)
+ , mDurationMs(configuration.mDurationMs)
+ , mId(configuration.mId) {
+ }
+
Type getType() const {
return mType;
}
@@ -493,13 +501,8 @@
return new VolumeShaper::State(mLastVolume, mXOffset);
}
- std::pair<T, bool> getVolume(int64_t trackFrameCount, double trackSampleRate) {
- if (mConfiguration.get() == nullptr || mConfiguration->empty()) {
- ALOGE("nonexistent VolumeShaper, removing");
- mLastVolume = T(1);
- mXOffset = 0.f;
- return std::make_pair(T(1), true);
- }
+ std::pair<T /* volume */, bool /* active */> getVolume(
+ int64_t trackFrameCount, double trackSampleRate) {
if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
VS_LOG("delayed VolumeShaper, ignoring");
mLastVolume = T(1);
@@ -534,13 +537,13 @@
if (x > mConfiguration->last().first) {
mXOffset = 0.f;
mLastVolume = 1.f;
- return std::make_pair(T(1), false); // too early
+ return std::make_pair(T(1), true); // too early
}
} else {
if (x < mConfiguration->first().first) {
mXOffset = 0.f;
mLastVolume = 1.f;
- return std::make_pair(T(1), false); // too early
+ return std::make_pair(T(1), true); // too early
}
if (x > mConfiguration->last().first) {
mXOffset = 1.f;
@@ -558,7 +561,7 @@
const T volume = mConfiguration->adjustVolume(volumeChange);
VS_LOG("volume: %f unscaled: %f", volume, unscaledVolume);
mLastVolume = volume;
- return std::make_pair(volume, false);
+ return std::make_pair(volume, true);
}
std::string toString() const {
@@ -597,6 +600,8 @@
VolumeShaper::Status applyVolumeShaper(
const sp<VolumeShaper::Configuration> &configuration,
const sp<VolumeShaper::Operation> &operation) {
+ VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
+ VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
AutoMutex _l(mLock);
if (configuration == nullptr) {
ALOGE("null configuration");
@@ -614,34 +619,6 @@
VS_LOG("applyVolumeShaper id: %d", id);
switch (configuration->getType()) {
- case VolumeShaper::Configuration::TYPE_ID: {
- VS_LOG("trying to find id: %d", id);
- auto it = findId_l(id);
- if (it == mVolumeShapers.end()) {
- VS_LOG("couldn't find id: %d\n%s", id, this->toString().c_str());
- return VolumeShaper::Status(INVALID_OPERATION);
- }
- if ((it->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
- VS_LOG("terminate id: %d", id);
- mVolumeShapers.erase(it);
- break;
- }
- if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
- (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
- const S x = it->mXTranslate((T)mLastFrame);
- VS_LOG("translation: %f", x);
- // reflect position
- S target = 1.f - x;
- if (target < it->mConfiguration->first().first) {
- VS_LOG("clamp to start - begin immediately");
- target = 0.;
- }
- VS_LOG("target: %f", target);
- it->mXTranslate.setOffset(it->mXTranslate.getOffset()
- + (x - target) / it->mXTranslate.getScale());
- }
- it->mOperation = operation; // replace the operation
- } break;
case VolumeShaper::Configuration::TYPE_SCALE: {
const int replaceId = operation->getReplaceId();
if (replaceId >= 0) {
@@ -670,6 +647,38 @@
}
// create new VolumeShaper
mVolumeShapers.emplace_back(configuration, operation);
+ }
+ // fall through to handle the operation
+ case VolumeShaper::Configuration::TYPE_ID: {
+ VS_LOG("trying to find id: %d", id);
+ auto it = findId_l(id);
+ if (it == mVolumeShapers.end()) {
+ VS_LOG("couldn't find id: %d", id);
+ return VolumeShaper::Status(INVALID_OPERATION);
+ }
+ if ((it->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
+ VS_LOG("terminate id: %d", id);
+ mVolumeShapers.erase(it);
+ break;
+ }
+ const bool clockTime = (it->mConfiguration->getOptionFlags()
+ & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
+ if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
+ (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
+ const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
+ const S x = it->mXTranslate((T)frameCount);
+ VS_LOG("reverse translation: %f", x);
+ // reflect position
+ S target = 1.f - x;
+ if (target < it->mConfiguration->first().first) {
+ VS_LOG("clamp to start - begin immediately");
+ target = 0.;
+ }
+ VS_LOG("target reverse: %f", target);
+ it->mXTranslate.setOffset(it->mXTranslate.getOffset()
+ + (x - target) / it->mXTranslate.getScale());
+ }
+ it->mOperation = operation; // replace the operation
} break;
}
return VolumeShaper::Status(id);
@@ -684,21 +693,19 @@
return it->getState();
}
- T getVolume(int64_t trackFrameCount) {
+ std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
AutoMutex _l(mLock);
mLastFrame = trackFrameCount;
T volume(1);
+ size_t activeCount = 0;
for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
std::pair<T, bool> shaperVolume =
it->getVolume(trackFrameCount, mSampleRate);
volume *= shaperVolume.first;
- if (shaperVolume.second) {
- it = mVolumeShapers.erase(it);
- continue;
- }
+ activeCount += shaperVolume.second;
++it;
}
- return volume;
+ return std::make_pair(volume, activeCount != 0);
}
std::string toString() const {
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index f33c710..f84ba08 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -59,6 +59,10 @@
bool isOffloaded() const
{ return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; }
bool isDirect() const { return (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0; }
+ bool isOffloadedOrDirect() const { return (mFlags
+ & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD
+ | AUDIO_OUTPUT_FLAG_DIRECT)) != 0; }
+
status_t setParameters(const String8& keyValuePairs);
status_t attachAuxEffect(int EffectId);
void setAuxBuffer(int EffectId, int32_t *buffer);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 8bf1688..b10e42c 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3063,8 +3063,13 @@
releaseWakeLock_l();
released = true;
}
- ALOGV("wait async completion");
- mWaitWorkCV.wait(mLock);
+
+ const int64_t waitNs = computeWaitTimeNs_l();
+ ALOGV("wait async completion (wait time: %lld)", (long long)waitNs);
+ status_t status = mWaitWorkCV.waitRelative(mLock, waitNs);
+ if (status == TIMED_OUT) {
+ mSignalPending = true; // if timeout recheck everything
+ }
ALOGV("async completion/wake");
if (released) {
acquireWakeLock_l();
@@ -4131,7 +4136,7 @@
// cache the combined master volume and stream type volume for fast mixer; this
// lacks any synchronization or barrier so VolumeProvider may read a stale value
const float vh = track->getVolumeHandler()->getVolume(
- track->mAudioTrackServerProxy->framesReleased());
+ track->mAudioTrackServerProxy->framesReleased()).first;
track->mCachedVolume = masterVolume
* mStreamTypes[track->streamType()].volume
* vh;
@@ -4277,7 +4282,7 @@
vrf = GAIN_FLOAT_UNITY;
}
const float vh = track->getVolumeHandler()->getVolume(
- track->mAudioTrackServerProxy->framesReleased());
+ track->mAudioTrackServerProxy->framesReleased()).first;
// now apply the master volume and stream type volume and shaper volume
vlf *= v * vh;
vrf *= v * vh;
@@ -4756,6 +4761,7 @@
ThreadBase::type_t type, bool systemReady)
: PlaybackThread(audioFlinger, output, id, device, type, systemReady)
// mLeftVolFloat, mRightVolFloat
+ , mVolumeShaperActive(false)
{
}
@@ -4774,13 +4780,12 @@
float v = mMasterVolume * typeVolume;
sp<AudioTrackServerProxy> proxy = track->mAudioTrackServerProxy;
- if (audio_is_linear_pcm(mFormat) && !usesHwAvSync()) {
- const float vh = track->getVolumeHandler()->getVolume(
+ // Get volumeshaper scaling
+ std::pair<float /* volume */, bool /* active */>
+ vh = track->getVolumeHandler()->getVolume(
track->mAudioTrackServerProxy->framesReleased());
- v *= vh;
- } else {
- // TODO: implement volume scaling in HW
- }
+ v *= vh.first;
+ mVolumeShaperActive = vh.second;
gain_minifloat_packed_t vlr = proxy->getVolumeLR();
left = float_from_gain(gain_minifloat_unpack_left(vlr));
@@ -5238,6 +5243,13 @@
mFlushPending = false;
}
+int64_t AudioFlinger::DirectOutputThread::computeWaitTimeNs_l() const {
+ // If a VolumeShaper is active, we must wake up periodically to update volume.
+ const int64_t NS_PER_MS = 1000000;
+ return mVolumeShaperActive ?
+ kMinNormalSinkBufferSizeMs * NS_PER_MS : PlaybackThread::computeWaitTimeNs_l();
+}
+
// ----------------------------------------------------------------------------
AudioFlinger::AsyncCallbackThread::AsyncCallbackThread(
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 2a6652d..0a17a8e 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -757,6 +757,9 @@
virtual void getAudioPortConfig(struct audio_port_config *config);
+ // Return the asynchronous signal wait time.
+ virtual int64_t computeWaitTimeNs_l() const { return INT64_MAX; }
+
protected:
// updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
@@ -1174,6 +1177,7 @@
// volumes last sent to audio HAL with stream->set_volume()
float mLeftVolFloat;
float mRightVolFloat;
+ bool mVolumeShaperActive;
DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
audio_io_handle_t id, uint32_t device, ThreadBase::type_t type,
@@ -1187,6 +1191,8 @@
public:
virtual bool hasFastMixer() const { return false; }
+
+ virtual int64_t computeWaitTimeNs_l() const override;
};
class OffloadThread : public DirectOutputThread {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index edf41fd..5e07e3b 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -905,10 +905,33 @@
const sp<VolumeShaper::Configuration>& configuration,
const sp<VolumeShaper::Operation>& operation)
{
- // Note: We don't check if Thread exists.
+ sp<VolumeShaper::Configuration> newConfiguration;
- // mVolumeHandler is thread-safe.
- return mVolumeHandler->applyVolumeShaper(configuration, operation);
+ if (isOffloadedOrDirect()) {
+ const VolumeShaper::Configuration::OptionFlag optionFlag
+ = configuration->getOptionFlags();
+ if ((optionFlag & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) == 0) {
+ ALOGW("%s tracks do not support frame counted VolumeShaper,"
+ " using clock time instead", isOffloaded() ? "Offload" : "Direct");
+ newConfiguration = new VolumeShaper::Configuration(*configuration);
+ newConfiguration->setOptionFlags(
+ VolumeShaper::Configuration::OptionFlag(optionFlag
+ | VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME));
+ }
+ }
+
+ VolumeShaper::Status status = mVolumeHandler->applyVolumeShaper(
+ (newConfiguration.get() != nullptr ? newConfiguration : configuration), operation);
+
+ if (isOffloadedOrDirect()) {
+ // Signal thread to fetch new volume.
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ thread->broadcast_l();
+ }
+ }
+ return status;
}
sp<VolumeShaper::State> AudioFlinger::PlaybackThread::Track::getVolumeShaperState(int id)