/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #define LOG_TAG "AHAL_AlsaMixer" #include #include #include #include "Mixer.h" namespace ndk { // This enables use of 'error/expected_utils' for ScopedAStatus. inline bool errorIsOk(const ScopedAStatus& s) { return s.isOk(); } inline std::string errorToString(const ScopedAStatus& s) { return s.getDescription(); } } // namespace ndk namespace aidl::android::hardware::audio::core::alsa { // static const std::map> Mixer::kPossibleControls = { {Mixer::MASTER_SWITCH, {{"Master Playback Switch", MIXER_CTL_TYPE_BOOL}}}, {Mixer::MASTER_VOLUME, {{"Master Playback Volume", MIXER_CTL_TYPE_INT}}}, {Mixer::HW_VOLUME, {{"Headphone Playback Volume", MIXER_CTL_TYPE_INT}, {"Headset Playback Volume", MIXER_CTL_TYPE_INT}, {"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}, {Mixer::MIC_SWITCH, {{"Capture Switch", MIXER_CTL_TYPE_BOOL}}}, {Mixer::MIC_GAIN, {{"Capture Volume", MIXER_CTL_TYPE_INT}}}}; // static 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, ctl); if (!mixerCtlNames.empty()) { mixerCtlNames += ","; } mixerCtlNames += ctlName; break; } } } LOG(DEBUG) << __func__ << ": available mixer control names=[" << mixerCtlNames << "]"; return mixerControls; } 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() { if (isValid()) { std::lock_guard l(mMixerAccess); mixer_close(mMixer); } } ndk::ScopedAStatus Mixer::getMasterMute(bool* muted) { return getMixerControlMute(MASTER_SWITCH, muted); } ndk::ScopedAStatus Mixer::getMasterVolume(float* volume) { return getMixerControlVolume(MASTER_VOLUME, volume); } ndk::ScopedAStatus Mixer::getMicGain(float* gain) { return getMixerControlVolume(MIC_GAIN, gain); } ndk::ScopedAStatus Mixer::getMicMute(bool* muted) { return getMixerControlMute(MIC_SWITCH, muted); } ndk::ScopedAStatus Mixer::getVolumes(std::vector* volumes) { struct mixer_ctl* mctl; RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl)); std::vector percents; std::lock_guard l(mMixerAccess); if (int err = getMixerControlPercent(mctl, &percents); err != 0) { LOG(ERROR) << __func__ << ": failed to get volume, err=" << err; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } std::transform(percents.begin(), percents.end(), std::back_inserter(*volumes), [](int percent) -> float { return std::clamp(percent / 100.0f, 0.0f, 1.0f); }); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Mixer::setMasterMute(bool muted) { return setMixerControlMute(MASTER_SWITCH, muted); } ndk::ScopedAStatus Mixer::setMasterVolume(float volume) { 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) { struct mixer_ctl* mctl; RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl)); 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(mctl, 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::findControl(Control ctl, struct mixer_ctl** result) { if (!isValid()) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } if (auto it = mMixerControls.find(ctl); it != mMixerControls.end()) { *result = it->second; return ndk::ScopedAStatus::ok(); } return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); } ndk::ScopedAStatus Mixer::getMixerControlMute(Control ctl, bool* muted) { struct mixer_ctl* mctl; RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl)); std::lock_guard l(mMixerAccess); std::vector mutedValues; if (int err = getMixerControlValues(mctl, &mutedValues); err != 0) { LOG(ERROR) << __func__ << ": failed to get " << ctl << ", err=" << err; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } if (mutedValues.empty()) { LOG(ERROR) << __func__ << ": got no values for " << ctl; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } *muted = mutedValues[0] != 0; return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Mixer::getMixerControlVolume(Control ctl, float* volume) { struct mixer_ctl* mctl; RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl)); std::lock_guard l(mMixerAccess); std::vector percents; if (int err = getMixerControlPercent(mctl, &percents); err != 0) { LOG(ERROR) << __func__ << ": failed to get " << ctl << ", err=" << err; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } if (percents.empty()) { LOG(ERROR) << __func__ << ": got no values for " << ctl; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); } *volume = std::clamp(percents[0] / 100.0f, 0.0f, 1.0f); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus Mixer::setMixerControlMute(Control ctl, bool muted) { struct mixer_ctl* mctl; RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl)); std::lock_guard l(mMixerAccess); if (int err = setMixerControlValue(mctl, 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) { struct mixer_ctl* mctl; RETURN_STATUS_IF_ERROR(findControl(ctl, &mctl)); volume = std::clamp(volume, 0.0f, 1.0f); std::lock_guard l(mMixerAccess); if (int err = setMixerControlPercent(mctl, 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::getMixerControlPercent(struct mixer_ctl* ctl, std::vector* percents) { const unsigned int n = mixer_ctl_get_num_values(ctl); percents->resize(n); for (unsigned int id = 0; id < n; id++) { if (int valueOrError = mixer_ctl_get_percent(ctl, id); valueOrError >= 0) { (*percents)[id] = valueOrError; } else { return valueOrError; } } return 0; } int Mixer::getMixerControlValues(struct mixer_ctl* ctl, std::vector* values) { const unsigned int n = mixer_ctl_get_num_values(ctl); values->resize(n); for (unsigned int id = 0; id < n; id++) { if (int valueOrError = mixer_ctl_get_value(ctl, id); valueOrError >= 0) { (*values)[id] = valueOrError; } else { return valueOrError; } } return 0; } int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, int percent) { 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) { return error; } } return 0; } int Mixer::setMixerControlPercent(struct mixer_ctl* ctl, const std::vector& percents) { 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) { return error; } } return 0; } int Mixer::setMixerControlValue(struct mixer_ctl* ctl, int value) { 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) { return error; } } return 0; } } // namespace aidl::android::hardware::audio::core::alsa