AHAL: support volume control for USB audio HAL.

Use mixer control to support master mute, master volume and hardware
volume for USB audio HAL.

Bug: 266216550
Test: atest VtsHalAudioCoreTargetTest
Change-Id: Iad544ba517cbfc778ebdf96dd161944886383b73
This commit is contained in:
jiabin 2023-02-28 18:28:06 +00:00
parent 960c33c599
commit 783c48b00e
11 changed files with 466 additions and 32 deletions

View file

@ -596,6 +596,7 @@ interface IModule {
* @param mute Whether the output from the module is muted.
* @throws EX_UNSUPPORTED_OPERATION If muting of combined output
* is not supported by the module.
* @throws EX_ILLEGAL_STATE If any error happens while muting of combined output.
*/
void setMasterMute(boolean mute);
@ -627,6 +628,8 @@ interface IModule {
* accepted range.
* @throws EX_UNSUPPORTED_OPERATION If attenuation of combined output
* is not supported by the module.
* @throws EX_ILLEGAL_STATE If any error happens while updating attenuation of
combined output.
*/
void setMasterVolume(float volume);

View file

@ -85,7 +85,8 @@ interface IStreamOut {
* @throws EX_ILLEGAL_ARGUMENT If the number of elements in the provided
* array does not match the channel count, or
* attenuation values are out of range.
* @throws EX_ILLEGAL_STATE If the stream is closed.
* @throws EX_ILLEGAL_STATE If the stream is closed or there is any error happens
when applying hardware volume.
* @throws EX_UNSUPPORTED_OPERATION If hardware volume control is not supported.
*/
void setHwVolume(in float[] channelVolumes);

View file

@ -75,6 +75,7 @@ cc_library_static {
"Telephony.cpp",
"usb/ModuleUsb.cpp",
"usb/StreamUsb.cpp",
"usb/UsbAlsaMixerControl.cpp",
"usb/UsbAlsaUtils.cpp",
],
generated_sources: [

View file

@ -457,6 +457,7 @@ ndk::ScopedAStatus Module::connectExternalDevice(const AudioPort& in_templateIdA
connectedPort.profiles = connectedProfilesIt->second;
}
ports.push_back(connectedPort);
onExternalDeviceConnectionChanged(connectedPort, true /*connected*/);
*_aidl_return = std::move(connectedPort);
std::vector<AudioRoute> newRoutes;
@ -510,6 +511,7 @@ ndk::ScopedAStatus Module::disconnectExternalDevice(int32_t in_portId) {
<< configIt->id;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
onExternalDeviceConnectionChanged(*portIt, false /*connected*/);
ports.erase(portIt);
mConnectedDevicePorts.erase(in_portId);
LOG(DEBUG) << __func__ << ": connected device port " << in_portId << " released";
@ -980,8 +982,17 @@ ndk::ScopedAStatus Module::getMasterMute(bool* _aidl_return) {
ndk::ScopedAStatus Module::setMasterMute(bool in_mute) {
LOG(DEBUG) << __func__ << ": " << in_mute;
mMasterMute = in_mute;
return ndk::ScopedAStatus::ok();
auto result = mDebug.simulateDeviceConnections ? ndk::ScopedAStatus::ok()
: onMasterMuteChanged(in_mute);
if (result.isOk()) {
mMasterMute = in_mute;
} else {
LOG(ERROR) << __func__ << ": failed calling onMasterMuteChanged(" << in_mute
<< "), error=" << result;
// Reset master mute if it failed.
onMasterMuteChanged(mMasterMute);
}
return std::move(result);
}
ndk::ScopedAStatus Module::getMasterVolume(float* _aidl_return) {
@ -993,8 +1004,17 @@ ndk::ScopedAStatus Module::getMasterVolume(float* _aidl_return) {
ndk::ScopedAStatus Module::setMasterVolume(float in_volume) {
LOG(DEBUG) << __func__ << ": " << in_volume;
if (in_volume >= 0.0f && in_volume <= 1.0f) {
mMasterVolume = in_volume;
return ndk::ScopedAStatus::ok();
auto result = mDebug.simulateDeviceConnections ? ndk::ScopedAStatus::ok()
: onMasterVolumeChanged(in_volume);
if (result.isOk()) {
mMasterVolume = in_volume;
} else {
// Reset master volume if it failed.
LOG(ERROR) << __func__ << ": failed calling onMasterVolumeChanged(" << in_volume
<< "), error=" << result;
onMasterVolumeChanged(mMasterVolume);
}
return std::move(result);
}
LOG(ERROR) << __func__ << ": invalid master volume value: " << in_volume;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
@ -1262,4 +1282,20 @@ ndk::ScopedAStatus Module::checkAudioPatchEndpointsMatch(
return ndk::ScopedAStatus::ok();
}
void Module::onExternalDeviceConnectionChanged(
const ::aidl::android::media::audio::common::AudioPort& audioPort __unused,
bool connected __unused) {
LOG(DEBUG) << __func__ << ": do nothing and return";
}
ndk::ScopedAStatus Module::onMasterMuteChanged(bool mute __unused) {
LOG(VERBOSE) << __func__ << ": do nothing and return ok";
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Module::onMasterVolumeChanged(float volume __unused) {
LOG(VERBOSE) << __func__ << ": do nothing and return ok";
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::audio::core

View file

@ -163,8 +163,6 @@ class Module : public BnModule {
// Maps port ids and port config ids to patch ids.
// Multimap because both ports and configs can be used by multiple patches.
std::multimap<int32_t, int32_t> mPatches;
bool mMasterMute = false;
float mMasterVolume = 1.0f;
bool mMicMute = false;
std::shared_ptr<sounddose::ISoundDose> mSoundDose;
ndk::SpAIBinder mSoundDoseBinder;
@ -180,6 +178,13 @@ class Module : public BnModule {
virtual ndk::ScopedAStatus checkAudioPatchEndpointsMatch(
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources,
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks);
virtual void onExternalDeviceConnectionChanged(
const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected);
virtual ndk::ScopedAStatus onMasterMuteChanged(bool mute);
virtual ndk::ScopedAStatus onMasterVolumeChanged(float volume);
bool mMasterMute = false;
float mMasterVolume = 1.0f;
};
} // namespace aidl::android::hardware::audio::core

View file

@ -28,10 +28,6 @@ class ModuleUsb : public Module {
// IModule interfaces
ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
ndk::ScopedAStatus getMasterMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMasterMute(bool in_mute) override;
ndk::ScopedAStatus getMasterVolume(float* _aidl_return) override;
ndk::ScopedAStatus setMasterVolume(float in_volume) override;
ndk::ScopedAStatus getMicMute(bool* _aidl_return) override;
ndk::ScopedAStatus setMicMute(bool in_mute) override;
@ -42,6 +38,11 @@ class ModuleUsb : public Module {
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sources,
const std::vector<::aidl::android::media::audio::common::AudioPortConfig*>& sinks)
override;
void onExternalDeviceConnectionChanged(
const ::aidl::android::media::audio::common::AudioPort& audioPort,
bool connected) override;
ndk::ScopedAStatus onMasterMuteChanged(bool mute) override;
ndk::ScopedAStatus onMasterVolumeChanged(float volume) override;
};
} // namespace aidl::android::hardware::audio::core

View file

@ -17,6 +17,7 @@
#pragma once
#include <mutex>
#include <vector>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
@ -93,6 +94,12 @@ class StreamOutUsb final : public StreamOut {
StreamContext&& context,
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
offloadInfo);
ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
int mChannelCount;
std::vector<float> mHwVolumes;
};
} // namespace aidl::android::hardware::audio::core

View file

@ -22,6 +22,7 @@
#include <android-base/logging.h>
#include <tinyalsa/asoundlib.h>
#include "UsbAlsaMixerControl.h"
#include "UsbAlsaUtils.h"
#include "core-impl/ModuleUsb.h"
@ -86,26 +87,6 @@ ndk::ScopedAStatus ModuleUsb::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_re
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus ModuleUsb::getMasterMute(bool* _aidl_return __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleUsb::setMasterMute(bool in_mute __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleUsb::getMasterVolume(float* _aidl_return __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleUsb::setMasterVolume(float in_volume __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus ModuleUsb::getMicMute(bool* _aidl_return __unused) {
LOG(DEBUG) << __func__ << ": is not supported";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
@ -180,4 +161,26 @@ ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch(
return ndk::ScopedAStatus::ok();
}
void ModuleUsb::onExternalDeviceConnectionChanged(
const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected) {
if (audioPort.ext.getTag() != AudioPortExt::Tag::device) {
return;
}
const auto& address = audioPort.ext.get<AudioPortExt::Tag::device>().device.address;
if (address.getTag() != AudioDeviceAddress::alsa) {
return;
}
const int card = address.get<AudioDeviceAddress::alsa>()[0];
usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(card, mMasterMute,
mMasterVolume, connected);
}
ndk::ScopedAStatus ModuleUsb::onMasterMuteChanged(bool mute) {
return usb::UsbAlsaMixerControl::getInstance().setMasterMute(mute);
}
ndk::ScopedAStatus ModuleUsb::onMasterVolumeChanged(float volume) {
return usb::UsbAlsaMixerControl::getInstance().setMasterVolume(volume);
}
} // namespace aidl::android::hardware::audio::core

View file

@ -17,6 +17,9 @@
#define LOG_TAG "AHAL_StreamUsb"
#include <android-base/logging.h>
#include <Utils.h>
#include "UsbAlsaMixerControl.h"
#include "UsbAlsaUtils.h"
#include "core-impl/Module.h"
#include "core-impl/StreamUsb.h"
@ -30,8 +33,12 @@ using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioDevice;
using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::MicrophoneDynamicInfo;
using aidl::android::media::audio::common::MicrophoneInfo;
using android::OK;
using android::status_t;
using android::hardware::audio::common::getChannelCount;
namespace aidl::android::hardware::audio::core {
@ -239,6 +246,31 @@ StreamOutUsb::StreamOutUsb(const SourceMetadata& sourceMetadata, StreamContext&&
// The default worker implementation is used.
return new StreamOutWorker(ctx, driver);
},
offloadInfo) {}
offloadInfo) {
mChannelCount = getChannelCount(mContext.getChannelLayout());
}
ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector<float>* _aidl_return) {
*_aidl_return = mHwVolumes;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus StreamOutUsb::setHwVolume(const std::vector<float>& in_channelVolumes) {
for (const auto& device : mConnectedDevices) {
if (device.address.getTag() != AudioDeviceAddress::alsa) {
LOG(DEBUG) << __func__ << ": skip as the device address is not alsa";
continue;
}
const int card = device.address.get<AudioDeviceAddress::alsa>()[0];
if (auto result =
usb::UsbAlsaMixerControl::getInstance().setVolumes(card, in_channelVolumes);
!result.isOk()) {
LOG(ERROR) << __func__ << ": failed to set volume for device, card=" << card;
return result;
}
}
mHwVolumes = in_channelVolumes;
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::audio::core

View file

@ -0,0 +1,239 @@
/*
* 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.
*/
#define LOG_TAG "AHAL_UsbAlsaMixerControl"
#include <android-base/logging.h>
#include <cmath>
#include <string>
#include <vector>
#include <android/binder_status.h>
#include "UsbAlsaMixerControl.h"
namespace aidl::android::hardware::audio::core::usb {
//-----------------------------------------------------------------------------
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<AlsaMixer::Control, std::vector<AlsaMixer::ControlNamesAndExpectedCtlType>>
AlsaMixer::kPossibleControls = {
{AlsaMixer::MASTER_SWITCH, {{"Master Playback Switch", MIXER_CTL_TYPE_BOOL}}},
{AlsaMixer::MASTER_VOLUME, {{"Master Playback Volume", MIXER_CTL_TYPE_INT}}},
{AlsaMixer::HW_VOLUME,
{{"Headphone Playback Volume", MIXER_CTL_TYPE_INT},
{"Headset Playback Volume", MIXER_CTL_TYPE_INT},
{"PCM Playback Volume", MIXER_CTL_TYPE_INT}}}};
// static
std::map<AlsaMixer::Control, std::shared_ptr<MixerControl>> AlsaMixer::initializeMixerControls(
struct mixer* mixer) {
std::map<AlsaMixer::Control, std::shared_ptr<MixerControl>> 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<MixerControl>(ctl));
if (!mixerCtlNames.empty()) {
mixerCtlNames += ",";
}
mixerCtlNames += ctlName;
break;
}
}
}
LOG(DEBUG) << __func__ << ": available mixer control names=[" << mixerCtlNames << "]";
return mixerControls;
}
AlsaMixer::AlsaMixer(struct mixer* mixer)
: mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {}
AlsaMixer::~AlsaMixer() {
mixer_close(mMixer);
}
namespace {
int volumeFloatToInteger(float fValue, int maxValue, int minValue) {
return minValue + std::ceil((maxValue - minValue) * fValue);
}
float volumeIntegerToFloat(int iValue, int maxValue, int minValue) {
if (iValue > maxValue) {
return 1.0f;
}
if (iValue < minValue) {
return 0.0f;
}
return static_cast<float>(iValue - minValue) / (maxValue - minValue);
}
} // namespace
ndk::ScopedAStatus AlsaMixer::setMasterMute(bool muted) {
auto it = mMixerControls.find(AlsaMixer::MASTER_SWITCH);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
const int numValues = it->second->getNumValues();
std::vector<int> 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();
}
ndk::ScopedAStatus AlsaMixer::setMasterVolume(float volume) {
auto it = mMixerControls.find(AlsaMixer::MASTER_VOLUME);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
const int numValues = it->second->getNumValues();
std::vector<int> 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();
}
ndk::ScopedAStatus AlsaMixer::setVolumes(std::vector<float> volumes) {
auto it = mMixerControls.find(AlsaMixer::HW_VOLUME);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
const int numValues = it->second->getNumValues();
const int maxValue = it->second->getMaxValue();
const int minValue = it->second->getMinValue();
std::vector<int> values;
size_t i = 0;
for (; i < 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) {
LOG(ERROR) << __func__ << ": failed to set volume, err=" << err;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
return ndk::ScopedAStatus::ok();
}
//-----------------------------------------------------------------------------
// static
UsbAlsaMixerControl& UsbAlsaMixerControl::getInstance() {
static UsbAlsaMixerControl gInstance;
return gInstance;
}
void UsbAlsaMixerControl::setDeviceConnectionState(int card, bool masterMuted, float masterVolume,
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;
return;
}
auto alsaMixer = std::make_shared<AlsaMixer>(mixer);
alsaMixer->setMasterMute(masterMuted);
alsaMixer->setMasterVolume(masterVolume);
const std::lock_guard guard(mLock);
mMixerControls.emplace(card, alsaMixer);
} else {
const std::lock_guard guard(mLock);
mMixerControls.erase(card);
}
}
ndk::ScopedAStatus UsbAlsaMixerControl::setMasterMute(bool mute) {
auto alsaMixers = getAlsaMixers();
for (auto it = alsaMixers.begin(); it != alsaMixers.end(); ++it) {
if (auto result = it->second->setMasterMute(mute); !result.isOk()) {
// Return illegal state if there are multiple devices connected and one of them fails
// to set master mute. Otherwise, return the error from calling `setMasterMute`.
LOG(ERROR) << __func__ << ": failed to set master mute for card=" << it->first;
return alsaMixers.size() > 1 ? ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE)
: std::move(result);
}
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus UsbAlsaMixerControl::setMasterVolume(float volume) {
auto alsaMixers = getAlsaMixers();
for (auto it = alsaMixers.begin(); it != alsaMixers.end(); ++it) {
if (auto result = it->second->setMasterVolume(volume); !result.isOk()) {
// Return illegal state if there are multiple devices connected and one of them fails
// to set master volume. Otherwise, return the error from calling `setMasterVolume`.
LOG(ERROR) << __func__ << ": failed to set master volume for card=" << it->first;
return alsaMixers.size() > 1 ? ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE)
: std::move(result);
}
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, std::vector<float> volumes) {
auto alsaMixer = getAlsaMixer(card);
if (alsaMixer == nullptr) {
LOG(ERROR) << __func__ << ": no mixer control found for card=" << card;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
return alsaMixer->setVolumes(volumes);
}
std::shared_ptr<AlsaMixer> UsbAlsaMixerControl::getAlsaMixer(int card) {
const std::lock_guard guard(mLock);
const auto it = mMixerControls.find(card);
return it == mMixerControls.end() ? nullptr : it->second;
}
std::map<int, std::shared_ptr<AlsaMixer>> UsbAlsaMixerControl::getAlsaMixers() {
const std::lock_guard guard(mLock);
return mMixerControls;
}
} // namespace aidl::android::hardware::audio::core::usb

View file

@ -0,0 +1,106 @@
/*
* 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.
*/
#pragma once
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <vector>
#include <android-base/thread_annotations.h>
#include <android/binder_auto_utils.h>
extern "C" {
#include <tinyalsa/mixer.h>
}
namespace aidl::android::hardware::audio::core::usb {
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 AlsaMixer {
public:
explicit AlsaMixer(struct mixer* mixer);
~AlsaMixer();
bool isValid() const { return mMixer != nullptr; }
ndk::ScopedAStatus setMasterMute(bool muted);
ndk::ScopedAStatus setMasterVolume(float volume);
ndk::ScopedAStatus setVolumes(std::vector<float> volumes);
private:
enum Control {
MASTER_SWITCH,
MASTER_VOLUME,
HW_VOLUME,
};
using ControlNamesAndExpectedCtlType = std::pair<std::string, enum mixer_ctl_type>;
static const std::map<Control, std::vector<ControlNamesAndExpectedCtlType>> kPossibleControls;
static std::map<Control, std::shared_ptr<MixerControl>> initializeMixerControls(
struct mixer* mixer);
// The mixer object is owned by ALSA and will be released when the mixer is closed.
struct mixer* mMixer;
// `mMixerControls` will only be initialized in constructor. After that, it wil only be
// read but not be modified.
const std::map<Control, std::shared_ptr<MixerControl>> mMixerControls;
};
class UsbAlsaMixerControl {
public:
static UsbAlsaMixerControl& getInstance();
void setDeviceConnectionState(int card, bool masterMuted, float masterVolume, bool connected);
// Master volume settings will be applied to all sound cards, it is only set by the
// USB module.
ndk::ScopedAStatus setMasterMute(bool muted);
ndk::ScopedAStatus setMasterVolume(float volume);
// The volume settings can be different on sound cards. It is controlled by streams.
ndk::ScopedAStatus setVolumes(int card, std::vector<float> volumes);
private:
std::shared_ptr<AlsaMixer> getAlsaMixer(int card);
std::map<int, std::shared_ptr<AlsaMixer>> getAlsaMixers();
std::mutex mLock;
// A map whose key is the card number and value is a shared pointer to corresponding
// AlsaMixer object.
std::map<int, std::shared_ptr<AlsaMixer>> mMixerControls GUARDED_BY(mLock);
};
} // namespace aidl::android::hardware::audio::core::usb