/* * 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 "Mixer.h" 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::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) { 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); } 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