diff --git a/audio/aidl/default/alsa/Mixer.cpp b/audio/aidl/default/alsa/Mixer.cpp index f0393e3a7e..126c033f66 100644 --- a/audio/aidl/default/alsa/Mixer.cpp +++ b/audio/aidl/default/alsa/Mixer.cpp @@ -14,44 +14,17 @@ * limitations under the License. */ -#define LOG_TAG "AHAL_AlsaMixer" -#include - +#include #include +#define LOG_TAG "AHAL_AlsaMixer" +#include #include #include "Mixer.h" namespace aidl::android::hardware::audio::core::alsa { -//----------------------------------------------------------------------------- - -MixerControl::MixerControl(struct mixer_ctl* ctl) - : mCtl(ctl), - mNumValues(mixer_ctl_get_num_values(ctl)), - mMinValue(mixer_ctl_get_range_min(ctl)), - mMaxValue(mixer_ctl_get_range_max(ctl)) {} - -unsigned int MixerControl::getNumValues() const { - return mNumValues; -} - -int MixerControl::getMaxValue() const { - return mMaxValue; -} - -int MixerControl::getMinValue() const { - return mMinValue; -} - -int MixerControl::setArray(const void* array, size_t count) { - const std::lock_guard guard(mLock); - return mixer_ctl_set_array(mCtl, array, count); -} - -//----------------------------------------------------------------------------- - // static const std::map> Mixer::kPossibleControls = { @@ -60,18 +33,20 @@ const std::map> Mixer::initializeMixerControls( - struct mixer* mixer) { - std::map> mixerControls; +Mixer::Controls Mixer::initializeMixerControls(struct mixer* mixer) { + if (mixer == nullptr) return {}; + Controls mixerControls; std::string mixerCtlNames; for (const auto& [control, possibleCtls] : kPossibleControls) { for (const auto& [ctlName, expectedCtlType] : possibleCtls) { struct mixer_ctl* ctl = mixer_get_ctl_by_name(mixer, ctlName.c_str()); if (ctl != nullptr && mixer_ctl_get_type(ctl) == expectedCtlType) { - mixerControls.emplace(control, std::make_unique(ctl)); + mixerControls.emplace(control, ctl); if (!mixerCtlNames.empty()) { mixerCtlNames += ","; } @@ -84,71 +59,141 @@ std::map> Mixer::initializeMixerCo return mixerControls; } -Mixer::Mixer(struct mixer* mixer) - : mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {} +std::ostream& operator<<(std::ostream& s, Mixer::Control c) { + switch (c) { + case Mixer::Control::MASTER_SWITCH: + s << "master mute"; + break; + case Mixer::Control::MASTER_VOLUME: + s << "master volume"; + break; + case Mixer::Control::HW_VOLUME: + s << "volume"; + break; + case Mixer::Control::MIC_SWITCH: + s << "mic mute"; + break; + case Mixer::Control::MIC_GAIN: + s << "mic gain"; + break; + } + return s; +} + +Mixer::Mixer(int card) : mMixer(mixer_open(card)), mMixerControls(initializeMixerControls(mMixer)) { + if (!isValid()) { + PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card; + } +} Mixer::~Mixer() { - mixer_close(mMixer); + if (isValid()) { + std::lock_guard l(mMixerAccess); + mixer_close(mMixer); + } } -namespace { - -int volumeFloatToInteger(float fValue, int maxValue, int minValue) { - return minValue + std::ceil((maxValue - minValue) * fValue); -} - -} // namespace - ndk::ScopedAStatus Mixer::setMasterMute(bool muted) { - auto it = mMixerControls.find(Mixer::MASTER_SWITCH); - if (it == mMixerControls.end()) { - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); - } - const int numValues = it->second->getNumValues(); - std::vector values(numValues, muted ? 0 : 1); - if (int err = it->second->setArray(values.data(), numValues); err != 0) { - LOG(ERROR) << __func__ << ": failed to set master mute, err=" << err; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } - return ndk::ScopedAStatus::ok(); + return setMixerControlMute(MASTER_SWITCH, muted); } ndk::ScopedAStatus Mixer::setMasterVolume(float volume) { - auto it = mMixerControls.find(Mixer::MASTER_VOLUME); - if (it == mMixerControls.end()) { - return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); - } - const int numValues = it->second->getNumValues(); - std::vector values(numValues, volumeFloatToInteger(volume, it->second->getMaxValue(), - it->second->getMinValue())); - if (int err = it->second->setArray(values.data(), numValues); err != 0) { - LOG(ERROR) << __func__ << ": failed to set master volume, err=" << err; - return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); - } - return ndk::ScopedAStatus::ok(); + return setMixerControlVolume(MASTER_VOLUME, volume); +} + +ndk::ScopedAStatus Mixer::setMicGain(float gain) { + return setMixerControlVolume(MIC_GAIN, gain); +} + +ndk::ScopedAStatus Mixer::setMicMute(bool muted) { + return setMixerControlMute(MIC_SWITCH, muted); } ndk::ScopedAStatus Mixer::setVolumes(const std::vector& volumes) { + if (!isValid()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } auto it = mMixerControls.find(Mixer::HW_VOLUME); if (it == mMixerControls.end()) { return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } - const int numValues = it->second->getNumValues(); - if (numValues < 0) { - LOG(FATAL) << __func__ << ": negative number of values: " << numValues; - } - const int maxValue = it->second->getMaxValue(); - const int minValue = it->second->getMinValue(); - std::vector values; - size_t i = 0; - for (; i < static_cast(numValues) && i < values.size(); ++i) { - values.emplace_back(volumeFloatToInteger(volumes[i], maxValue, minValue)); - } - if (int err = it->second->setArray(values.data(), values.size()); err != 0) { + std::vector percents; + std::transform( + volumes.begin(), volumes.end(), std::back_inserter(percents), + [](float volume) -> int { return std::floor(std::clamp(volume, 0.0f, 1.0f) * 100); }); + std::lock_guard l(mMixerAccess); + if (int err = setMixerControlPercent(it->second, percents); err != 0) { LOG(ERROR) << __func__ << ": failed to set volume, err=" << err; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus Mixer::setMixerControlMute(Mixer::Control ctl, bool muted) { + if (!isValid()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + auto it = mMixerControls.find(ctl); + if (it == mMixerControls.end()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + std::lock_guard l(mMixerAccess); + if (int err = setMixerControlValue(it->second, muted ? 0 : 1); err != 0) { + LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << muted << ", err=" << err; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Mixer::setMixerControlVolume(Control ctl, float volume) { + if (!isValid()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + auto it = mMixerControls.find(ctl); + if (it == mMixerControls.end()) { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); + } + volume = std::clamp(volume, 0.0f, 1.0f); + std::lock_guard l(mMixerAccess); + if (int err = setMixerControlPercent(it->second, std::floor(volume * 100)); err != 0) { + LOG(ERROR) << __func__ << ": failed to set " << ctl << " to " << volume << ", err=" << err; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } + return ndk::ScopedAStatus::ok(); +} + +int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, int percent) { + int ret = 0; + const unsigned int n = mixer_ctl_get_num_values(ctl); + for (unsigned int id = 0; id < n; id++) { + if (int error = mixer_ctl_set_percent(ctl, id, percent); error != 0) { + ret = error; + } + } + return ret; +} + +int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, const std::vector& percents) { + int ret = 0; + const unsigned int n = mixer_ctl_get_num_values(ctl); + for (unsigned int id = 0; id < n; id++) { + if (int error = mixer_ctl_set_percent(ctl, id, id < percents.size() ? percents[id] : 0); + error != 0) { + ret = error; + } + } + return ret; +} + +int Mixer::setMixerControlValue(struct mixer_ctl* ctl, int value) { + int ret = 0; + const unsigned int n = mixer_ctl_get_num_values(ctl); + for (unsigned int id = 0; id < n; id++) { + if (int error = mixer_ctl_set_value(ctl, id, value); error != 0) { + ret = error; + } + } + return ret; +} + } // namespace aidl::android::hardware::audio::core::alsa diff --git a/audio/aidl/default/alsa/Mixer.h b/audio/aidl/default/alsa/Mixer.h index de9e6f42cd..78728c296e 100644 --- a/audio/aidl/default/alsa/Mixer.h +++ b/audio/aidl/default/alsa/Mixer.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include @@ -31,34 +32,17 @@ extern "C" { namespace aidl::android::hardware::audio::core::alsa { -class MixerControl { - public: - explicit MixerControl(struct mixer_ctl* ctl); - - unsigned int getNumValues() const; - int getMaxValue() const; - int getMinValue() const; - int setArray(const void* array, size_t count); - - private: - std::mutex mLock; - // The mixer_ctl object is owned by ALSA and will be released when the mixer is closed. - struct mixer_ctl* mCtl GUARDED_BY(mLock); - const unsigned int mNumValues; - const int mMinValue; - const int mMaxValue; -}; - class Mixer { public: - explicit Mixer(struct mixer* mixer); - + explicit Mixer(int card); ~Mixer(); bool isValid() const { return mMixer != nullptr; } ndk::ScopedAStatus setMasterMute(bool muted); ndk::ScopedAStatus setMasterVolume(float volume); + ndk::ScopedAStatus setMicGain(float gain); + ndk::ScopedAStatus setMicMute(bool muted); ndk::ScopedAStatus setVolumes(const std::vector& volumes); private: @@ -66,17 +50,32 @@ class Mixer { MASTER_SWITCH, MASTER_VOLUME, HW_VOLUME, + MIC_SWITCH, + MIC_GAIN, }; using ControlNamesAndExpectedCtlType = std::pair; - static const std::map> kPossibleControls; - static std::map> initializeMixerControls( - struct mixer* mixer); + using Controls = std::map; + friend std::ostream& operator<<(std::ostream&, Control); + static const std::map> kPossibleControls; + static Controls initializeMixerControls(struct mixer* mixer); + + ndk::ScopedAStatus setMixerControlMute(Control ctl, bool muted); + ndk::ScopedAStatus setMixerControlVolume(Control ctl, float volume); + + int setMixerControlPercent(struct mixer_ctl* ctl, int percent) REQUIRES(mMixerAccess); + int setMixerControlPercent(struct mixer_ctl* ctl, const std::vector& percents) + REQUIRES(mMixerAccess); + int setMixerControlValue(struct mixer_ctl* ctl, int value) REQUIRES(mMixerAccess); + + // Since ALSA functions do not use internal locking, enforce thread safety at our level. + std::mutex mMixerAccess; // The mixer object is owned by ALSA and will be released when the mixer is closed. - struct mixer* mMixer; + struct mixer* const mMixer; // `mMixerControls` will only be initialized in constructor. After that, it wil only be - // read but not be modified. - const std::map> mMixerControls; + // read but not be modified. Each mixer_ctl object is owned by ALSA, it's life span is + // the same as of the mixer itself. + const Controls mMixerControls; }; } // namespace aidl::android::hardware::audio::core::alsa diff --git a/audio/aidl/default/usb/UsbAlsaMixerControl.cpp b/audio/aidl/default/usb/UsbAlsaMixerControl.cpp index 769d739969..0a49446e5a 100644 --- a/audio/aidl/default/usb/UsbAlsaMixerControl.cpp +++ b/audio/aidl/default/usb/UsbAlsaMixerControl.cpp @@ -33,12 +33,10 @@ void UsbAlsaMixerControl::setDeviceConnectionState(int card, bool masterMuted, f bool connected) { LOG(DEBUG) << __func__ << ": card=" << card << ", connected=" << connected; if (connected) { - struct mixer* mixer = mixer_open(card); - if (mixer == nullptr) { - PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card; + auto alsaMixer = std::make_shared(card); + if (!alsaMixer->isValid()) { return; } - auto alsaMixer = std::make_shared(mixer); alsaMixer->setMasterMute(masterMuted); alsaMixer->setMasterVolume(masterVolume); const std::lock_guard guard(mLock);