Files
hardware_interfaces/audio/aidl/default/alsa/Mixer.cpp
Mikhail Naganov d21b0e7e55 audio: Simplify and extend alsa::Mixer
Remove alsa::MixerControl. tinyALSA contains utility
functions for setting values in percents, they use
the same logic as used to be there for handling
the "volume" control. Use access serialization at
the mixer level, rather than for each control.

Move the call to 'mixer_open' to alsa::Mixer.

Add controls for capture (mic) mute and gain. They
will be used by the primary HAL.

Bug: 264712385
Test: atest VtsHalAudioCoreTargetTest
Change-Id: I0fad994153de96aceec3eb8f2fec19805ec912f8
(cherry picked from commit f12d4a1ef8)
Merged-In: I0fad994153de96aceec3eb8f2fec19805ec912f8
2023-08-01 14:31:01 -07:00

200 lines
7.0 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 "Mixer.h"
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::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) {
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<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(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<int>& 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