mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 22:04:26 +00:00
Implement volume control on default audio HAL
Implemented volume control based on setting audio port config. Bug: 336370745 Test: atest VtsHalAudioCoreTargetTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:122597a5e96c873239540a53523caf67fe806d90) Merged-In: Ia590974e61aa3a1c3f70afdb54ce87c85e9a1b3c Change-Id: Ia590974e61aa3a1c3f70afdb54ce87c85e9a1b3c
This commit is contained in:
committed by
Android Build Cherrypicker Worker
parent
ad6288c8bc
commit
a33bb5eaf5
@@ -47,6 +47,7 @@ using aidl::android::media::audio::common::AudioDevice;
|
||||
using aidl::android::media::audio::common::AudioDeviceType;
|
||||
using aidl::android::media::audio::common::AudioFormatDescription;
|
||||
using aidl::android::media::audio::common::AudioFormatType;
|
||||
using aidl::android::media::audio::common::AudioGainConfig;
|
||||
using aidl::android::media::audio::common::AudioInputFlags;
|
||||
using aidl::android::media::audio::common::AudioIoFlags;
|
||||
using aidl::android::media::audio::common::AudioMMapPolicy;
|
||||
@@ -1200,7 +1201,9 @@ ndk::ScopedAStatus Module::setAudioPortConfigImpl(
|
||||
}
|
||||
|
||||
if (in_requested.gain.has_value()) {
|
||||
// Let's pretend that gain can always be applied.
|
||||
if (!setAudioPortConfigGain(*portIt, in_requested.gain.value())) {
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
|
||||
}
|
||||
out_suggested->gain = in_requested.gain.value();
|
||||
}
|
||||
|
||||
@@ -1242,6 +1245,52 @@ ndk::ScopedAStatus Module::setAudioPortConfigImpl(
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
bool Module::setAudioPortConfigGain(const AudioPort& port, const AudioGainConfig& gainRequested) {
|
||||
auto& ports = getConfig().ports;
|
||||
if (gainRequested.index < 0 || gainRequested.index >= (int)port.gains.size()) {
|
||||
LOG(ERROR) << __func__ << ": gains for port " << port.id << " is undefined";
|
||||
return false;
|
||||
}
|
||||
int stepValue = port.gains[gainRequested.index].stepValue;
|
||||
if (stepValue == 0) {
|
||||
LOG(ERROR) << __func__ << ": port gain step value is 0";
|
||||
return false;
|
||||
}
|
||||
int minValue = port.gains[gainRequested.index].minValue;
|
||||
int maxValue = port.gains[gainRequested.index].maxValue;
|
||||
if (gainRequested.values[0] > maxValue || gainRequested.values[0] < minValue) {
|
||||
LOG(ERROR) << __func__ << ": gain value " << gainRequested.values[0]
|
||||
<< " out of range of min and max gain config";
|
||||
return false;
|
||||
}
|
||||
int gainIndex = (gainRequested.values[0] - minValue) / stepValue;
|
||||
int totalSteps = (maxValue - minValue) / stepValue;
|
||||
if (totalSteps == 0) {
|
||||
LOG(ERROR) << __func__ << ": difference between port gain min value " << minValue
|
||||
<< " and max value " << maxValue << " is less than step value " << stepValue;
|
||||
return false;
|
||||
}
|
||||
// Root-power quantities are used in curve:
|
||||
// 10^((minMb / 100 + (maxMb / 100 - minMb / 100) * gainIndex / totalSteps) / (10 * 2))
|
||||
// where 100 is the conversion from mB to dB, 10 comes from the log 10 conversion from power
|
||||
// ratios, and 2 means are the square of amplitude.
|
||||
float gain =
|
||||
pow(10, (minValue + (maxValue - minValue) * (gainIndex / (float)totalSteps)) / 2000);
|
||||
if (gain < 0) {
|
||||
LOG(ERROR) << __func__ << ": gain " << gain << " is less than 0";
|
||||
return false;
|
||||
}
|
||||
for (const auto& route : getConfig().routes) {
|
||||
if (route.sinkPortId != port.id) {
|
||||
continue;
|
||||
}
|
||||
for (const auto sourcePortId : route.sourcePortIds) {
|
||||
mStreams.setGain(sourcePortId, gain);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::resetAudioPatch(int32_t in_patchId) {
|
||||
auto& patches = getConfig().patches;
|
||||
auto patchIt = findById<AudioPatch>(patches, in_patchId);
|
||||
|
||||
@@ -855,6 +855,11 @@ ndk::ScopedAStatus StreamCommonImpl::setConnectedDevices(
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus StreamCommonImpl::setGain(float gain) {
|
||||
LOG(DEBUG) << __func__ << ": gain " << gain;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus StreamCommonImpl::bluetoothParametersUpdated() {
|
||||
LOG(DEBUG) << __func__;
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
|
||||
@@ -260,4 +260,12 @@ ndk::ScopedAStatus StreamSwitcher::bluetoothParametersUpdated() {
|
||||
return mStream->bluetoothParametersUpdated();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus StreamSwitcher::setGain(float gain) {
|
||||
if (mStream == nullptr) {
|
||||
LOG(ERROR) << __func__ << ": stream was closed";
|
||||
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
|
||||
}
|
||||
return mStream->setGain(gain);
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
||||
@@ -110,6 +110,7 @@ StreamAlsa::~StreamAlsa() {
|
||||
mReadWriteRetries);
|
||||
maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
|
||||
} else {
|
||||
alsa::applyGain(buffer, mGain, bytesToTransfer, mConfig.value().format, mConfig->channels);
|
||||
for (auto& proxy : mAlsaDeviceProxies) {
|
||||
proxy_write_with_retries(proxy.get(), buffer, bytesToTransfer, mReadWriteRetries);
|
||||
maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
|
||||
@@ -159,4 +160,9 @@ void StreamAlsa::shutdown() {
|
||||
mAlsaDeviceProxies.clear();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus StreamAlsa::setGain(float gain) {
|
||||
mGain = gain;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include <aidl/android/media/audio/common/AudioFormatType.h>
|
||||
#include <aidl/android/media/audio/common/PcmType.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <audio_utils/primitives.h>
|
||||
#include <cutils/compiler.h>
|
||||
|
||||
#include "Utils.h"
|
||||
#include "core-impl/utils.h"
|
||||
@@ -343,4 +345,68 @@ pcm_format aidl2c_AudioFormatDescription_pcm_format(const AudioFormatDescription
|
||||
return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID);
|
||||
}
|
||||
|
||||
void applyGain(void* buffer, float gain, size_t bytesToTransfer, enum pcm_format pcmFormat,
|
||||
int channelCount) {
|
||||
if (channelCount != 1 && channelCount != 2) {
|
||||
LOG(WARNING) << __func__ << ": unsupported channel count " << channelCount;
|
||||
return;
|
||||
}
|
||||
if (!getPcmFormatToAudioFormatDescMap().contains(pcmFormat)) {
|
||||
LOG(WARNING) << __func__ << ": unsupported pcm format " << pcmFormat;
|
||||
return;
|
||||
}
|
||||
const float unityGainFloat = 1.0f;
|
||||
if (std::abs(gain - unityGainFloat) < 1e-6) {
|
||||
return;
|
||||
}
|
||||
int numFrames;
|
||||
switch (pcmFormat) {
|
||||
case PCM_FORMAT_S16_LE: {
|
||||
const uint16_t unityGainQ4_12 = u4_12_from_float(unityGainFloat);
|
||||
const uint16_t vl = u4_12_from_float(gain);
|
||||
const uint32_t vrl = (vl << 16) | vl;
|
||||
if (channelCount == 2) {
|
||||
numFrames = bytesToTransfer / sizeof(uint32_t);
|
||||
uint32_t* intBuffer = (uint32_t*)buffer;
|
||||
if (CC_UNLIKELY(vl > unityGainQ4_12)) {
|
||||
// volume is boosted, so we might need to clamp even though
|
||||
// we process only one track.
|
||||
do {
|
||||
int32_t l = mulRL(1, *intBuffer, vrl) >> 12;
|
||||
int32_t r = mulRL(0, *intBuffer, vrl) >> 12;
|
||||
l = clamp16(l);
|
||||
r = clamp16(r);
|
||||
*intBuffer++ = (r << 16) | (l & 0xFFFF);
|
||||
} while (--numFrames);
|
||||
} else {
|
||||
do {
|
||||
int32_t l = mulRL(1, *intBuffer, vrl) >> 12;
|
||||
int32_t r = mulRL(0, *intBuffer, vrl) >> 12;
|
||||
*intBuffer++ = (r << 16) | (l & 0xFFFF);
|
||||
} while (--numFrames);
|
||||
}
|
||||
} else {
|
||||
numFrames = bytesToTransfer / sizeof(uint16_t);
|
||||
int16_t* intBuffer = (int16_t*)buffer;
|
||||
if (CC_UNLIKELY(vl > unityGainQ4_12)) {
|
||||
// volume is boosted, so we might need to clamp even though
|
||||
// we process only one track.
|
||||
do {
|
||||
int32_t mono = mulRL(1, *intBuffer, vrl) >> 12;
|
||||
*intBuffer++ = clamp16(mono);
|
||||
} while (--numFrames);
|
||||
} else {
|
||||
do {
|
||||
int32_t mono = mulRL(1, *intBuffer, vrl) >> 12;
|
||||
*intBuffer++ = static_cast<int16_t>(mono & 0xFFFF);
|
||||
} while (--numFrames);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
// TODO(336370745): Implement gain for other supported formats
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core::alsa
|
||||
|
||||
@@ -59,6 +59,8 @@ class DeviceProxy {
|
||||
AlsaProxy mProxy;
|
||||
};
|
||||
|
||||
void applyGain(void* buffer, float gain, size_t bytesToTransfer, enum pcm_format pcmFormat,
|
||||
int channelCount);
|
||||
::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
|
||||
unsigned int channelCount, int isInput);
|
||||
::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount(
|
||||
|
||||
@@ -263,6 +263,9 @@ class Module : public BnModule {
|
||||
::aidl::android::media::audio::common::AudioPortConfig* out_suggested, bool* applied);
|
||||
ndk::ScopedAStatus updateStreamsConnectedState(const AudioPatch& oldPatch,
|
||||
const AudioPatch& newPatch);
|
||||
bool setAudioPortConfigGain(
|
||||
const ::aidl::android::media::audio::common::AudioPort& port,
|
||||
const ::aidl::android::media::audio::common::AudioGainConfig& gainRequested);
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Module::Type t);
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <aidl/android/media/audio/common/AudioIoFlags.h>
|
||||
#include <aidl/android/media/audio/common/AudioOffloadInfo.h>
|
||||
#include <aidl/android/media/audio/common/MicrophoneInfo.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <error/expected_utils.h>
|
||||
#include <fmq/AidlMessageQueue.h>
|
||||
#include <system/thread_defs.h>
|
||||
@@ -342,6 +343,7 @@ struct StreamCommonInterface {
|
||||
virtual ndk::ScopedAStatus setConnectedDevices(
|
||||
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) = 0;
|
||||
virtual ndk::ScopedAStatus bluetoothParametersUpdated() = 0;
|
||||
virtual ndk::ScopedAStatus setGain(float gain) = 0;
|
||||
};
|
||||
|
||||
// This is equivalent to automatically generated 'IStreamCommonDelegator' but uses
|
||||
@@ -443,6 +445,7 @@ class StreamCommonImpl : virtual public StreamCommonInterface, virtual public Dr
|
||||
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
|
||||
override;
|
||||
ndk::ScopedAStatus bluetoothParametersUpdated() override;
|
||||
ndk::ScopedAStatus setGain(float gain) override;
|
||||
|
||||
protected:
|
||||
static StreamWorkerInterface::CreateInstance getDefaultInWorkerCreator() {
|
||||
@@ -609,6 +612,12 @@ class StreamWrapper {
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus setGain(float gain) {
|
||||
auto s = mStream.lock();
|
||||
if (s) return s->setGain(gain);
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
private:
|
||||
std::weak_ptr<StreamCommonInterface> mStream;
|
||||
ndk::SpAIBinder mStreamBinder;
|
||||
@@ -644,6 +653,12 @@ class Streams {
|
||||
return isOk ? ndk::ScopedAStatus::ok()
|
||||
: ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
|
||||
}
|
||||
ndk::ScopedAStatus setGain(int32_t portId, float gain) {
|
||||
if (auto it = mStreams.find(portId); it != mStreams.end()) {
|
||||
return it->second.setGain(gain);
|
||||
}
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
private:
|
||||
// Maps port ids and port config ids to streams. Multimap because a port
|
||||
|
||||
@@ -45,6 +45,7 @@ class StreamAlsa : public StreamCommonImpl {
|
||||
int32_t* latencyMs) override;
|
||||
::android::status_t refinePosition(StreamDescriptor::Position* position) override;
|
||||
void shutdown() override;
|
||||
ndk::ScopedAStatus setGain(float gain) override;
|
||||
|
||||
protected:
|
||||
// Called from 'start' to initialize 'mAlsaDeviceProxies', the vector must be non-empty.
|
||||
@@ -58,6 +59,9 @@ class StreamAlsa : public StreamCommonImpl {
|
||||
const int mReadWriteRetries;
|
||||
// All fields below are only used on the worker thread.
|
||||
std::vector<alsa::DeviceProxy> mAlsaDeviceProxies;
|
||||
|
||||
private:
|
||||
std::atomic<float> mGain = 1.0;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
||||
@@ -130,6 +130,7 @@ class StreamSwitcher : virtual public StreamCommonInterface {
|
||||
const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices)
|
||||
override;
|
||||
ndk::ScopedAStatus bluetoothParametersUpdated() override;
|
||||
ndk::ScopedAStatus setGain(float gain) override;
|
||||
|
||||
protected:
|
||||
// Since switching a stream requires closing down the current stream, StreamSwitcher
|
||||
|
||||
Reference in New Issue
Block a user