298 lines
10 KiB
C++
298 lines
10 KiB
C++
/*
|
|
* 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 <algorithm>
|
|
#include <cmath>
|
|
|
|
#define LOG_TAG "AHAL_AlsaMixer"
|
|
#include <android-base/logging.h>
|
|
#include <android/binder_status.h>
|
|
#include <error/expected_utils.h>
|
|
|
|
#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::Control, std::vector<Mixer::ControlNamesAndExpectedCtlType>>
|
|
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<float>* volumes) {
|
|
struct mixer_ctl* mctl;
|
|
RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl));
|
|
std::vector<int> 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<float>& volumes) {
|
|
struct mixer_ctl* mctl;
|
|
RETURN_STATUS_IF_ERROR(findControl(Mixer::HW_VOLUME, &mctl));
|
|
std::vector<int> 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<int> 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<int> 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<int>* 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<int>* 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<int>& 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
|