audio: Move tinyALSA-specific code to Module/StreamAlsa

Extract code interacting with tinyALSA which is not
specific to USB into "abstract" module and stream
implementations ModuleAlsa and StreamAlsa. Also, move
utility code which does not need module or stream
context into Utils.

This facilitates implementation of the CF core HAL which
also uses tinyALSA, allowing to share common code.

Bug: 264712385
Test: atest VtsHalAudioCoreTargetTest

Change-Id: I2134b15e970c78e8a48b254e15199b8207a8ab34
(cherry picked from commit c337a8799b)
Merged-In: I2134b15e970c78e8a48b254e15199b8207a8ab34
This commit is contained in:
Mikhail Naganov 2023-07-07 12:01:17 -07:00
parent 222e2a6c34
commit a4513dcf05
17 changed files with 923 additions and 667 deletions

View file

@ -77,6 +77,10 @@ cc_library {
"Stream.cpp",
"StreamStub.cpp",
"Telephony.cpp",
"alsa/Mixer.cpp",
"alsa/ModuleAlsa.cpp",
"alsa/StreamAlsa.cpp",
"alsa/Utils.cpp",
"r_submix/ModuleRemoteSubmix.cpp",
"r_submix/RemoteSubmixUtils.cpp",
"r_submix/SubmixRoute.cpp",
@ -84,7 +88,6 @@ cc_library {
"usb/ModuleUsb.cpp",
"usb/StreamUsb.cpp",
"usb/UsbAlsaMixerControl.cpp",
"usb/UsbAlsaUtils.cpp",
],
generated_sources: [
"audio_policy_configuration_aidl_default",

View file

@ -0,0 +1,154 @@
/*
* 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_AlsaMixer"
#include <android-base/logging.h>
#include <cmath>
#include <android/binder_status.h>
#include "Mixer.h"
namespace aidl::android::hardware::audio::core::alsa {
//-----------------------------------------------------------------------------
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<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}}}};
// static
std::map<Mixer::Control, std::shared_ptr<MixerControl>> Mixer::initializeMixerControls(
struct mixer* mixer) {
std::map<Mixer::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;
}
Mixer::Mixer(struct mixer* mixer)
: mMixer(mixer), mMixerControls(initializeMixerControls(mMixer)) {}
Mixer::~Mixer() {
mixer_close(mMixer);
}
namespace {
int volumeFloatToInteger(float fValue, int maxValue, int minValue) {
return minValue + std::ceil((maxValue - minValue) * fValue);
}
} // namespace
ndk::ScopedAStatus Mixer::setMasterMute(bool muted) {
auto it = mMixerControls.find(Mixer::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 Mixer::setMasterVolume(float volume) {
auto it = mMixerControls.find(Mixer::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 Mixer::setVolumes(const std::vector<float>& volumes) {
auto it = mMixerControls.find(Mixer::HW_VOLUME);
if (it == mMixerControls.end()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
const int numValues = it->second->getNumValues();
if (numValues < 0) {
LOG(FATAL) << __func__ << ": negative number of values: " << numValues;
}
const int maxValue = it->second->getMaxValue();
const int minValue = it->second->getMinValue();
std::vector<int> values;
size_t i = 0;
for (; i < static_cast<size_t>(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();
}
} // namespace aidl::android::hardware::audio::core::alsa

View file

@ -0,0 +1,82 @@
/*
* 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 <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::alsa {
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 Mixer {
public:
explicit Mixer(struct mixer* mixer);
~Mixer();
bool isValid() const { return mMixer != nullptr; }
ndk::ScopedAStatus setMasterMute(bool muted);
ndk::ScopedAStatus setMasterVolume(float volume);
ndk::ScopedAStatus setVolumes(const 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;
};
} // namespace aidl::android::hardware::audio::core::alsa

View file

@ -0,0 +1,67 @@
/*
* 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_ModuleAlsa"
#include <vector>
#include <android-base/logging.h>
#include "Utils.h"
#include "core-impl/ModuleAlsa.h"
extern "C" {
#include "alsa_device_profile.h"
}
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioPort;
using aidl::android::media::audio::common::AudioProfile;
namespace aidl::android::hardware::audio::core {
ndk::ScopedAStatus ModuleAlsa::populateConnectedDevicePort(AudioPort* audioPort) {
auto deviceProfile = alsa::getDeviceProfile(*audioPort);
if (!deviceProfile.has_value()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
auto profile = alsa::readAlsaDeviceInfo(*deviceProfile);
if (!profile.has_value()) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
std::vector<AudioChannelLayout> channels = alsa::getChannelMasksFromProfile(&profile.value());
std::vector<int> sampleRates = alsa::getSampleRatesFromProfile(&profile.value());
for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
profile->formats[i] != PCM_FORMAT_INVALID;
++i) {
auto audioFormatDescription =
alsa::c2aidl_pcm_format_AudioFormatDescription(profile->formats[i]);
if (audioFormatDescription.type == AudioFormatType::DEFAULT) {
LOG(WARNING) << __func__ << ": unknown pcm type=" << profile->formats[i];
continue;
}
AudioProfile audioProfile = {.format = audioFormatDescription,
.channelMasks = channels,
.sampleRates = sampleRates};
audioPort->profiles.push_back(std::move(audioProfile));
}
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::audio::core

View file

@ -0,0 +1,103 @@
/*
* 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 <limits>
#define LOG_TAG "AHAL_StreamAlsa"
#include <android-base/logging.h>
#include <Utils.h>
#include <error/expected_utils.h>
#include "core-impl/StreamAlsa.h"
namespace aidl::android::hardware::audio::core {
StreamAlsa::StreamAlsa(const Metadata& metadata, StreamContext&& context)
: StreamCommonImpl(metadata, std::move(context)),
mFrameSizeBytes(getContext().getFrameSize()),
mIsInput(isInput(metadata)),
mConfig(alsa::getPcmConfig(getContext(), mIsInput)) {}
::android::status_t StreamAlsa::init() {
return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
}
::android::status_t StreamAlsa::standby() {
mAlsaDeviceProxies.clear();
return ::android::OK;
}
::android::status_t StreamAlsa::start() {
decltype(mAlsaDeviceProxies) alsaDeviceProxies;
for (const auto& device : getDeviceProfiles()) {
auto profile = alsa::readAlsaDeviceInfo(device);
if (!profile.has_value()) {
LOG(ERROR) << __func__ << ": unable to read device info, device address=" << device;
return ::android::UNKNOWN_ERROR;
}
auto proxy = alsa::makeDeviceProxy();
// Always ask for alsa configure as required since the configuration should be supported
// by the connected device. That is guaranteed by `setAudioPortConfig` and `setAudioPatch`.
if (int err = proxy_prepare(proxy.get(), &profile.value(),
const_cast<struct pcm_config*>(&mConfig.value()),
true /*require_exact_match*/);
err != 0) {
LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device
<< " error=" << err;
return ::android::UNKNOWN_ERROR;
}
if (int err = proxy_open(proxy.get()); err != 0) {
LOG(ERROR) << __func__ << ": failed to open device, address=" << device
<< " error=" << err;
return ::android::UNKNOWN_ERROR;
}
alsaDeviceProxies.push_back(std::move(proxy));
}
mAlsaDeviceProxies = std::move(alsaDeviceProxies);
return ::android::OK;
}
::android::status_t StreamAlsa::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) {
const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
unsigned maxLatency = 0;
if (mIsInput) {
if (mAlsaDeviceProxies.empty()) {
LOG(FATAL) << __func__ << ": no input devices";
return ::android::NO_INIT;
}
// For input case, only support single device.
proxy_read(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer);
maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
} else {
for (auto& proxy : mAlsaDeviceProxies) {
proxy_write(proxy.get(), buffer, bytesToTransfer);
maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
}
}
*actualFrameCount = frameCount;
maxLatency = std::min(maxLatency, static_cast<unsigned>(std::numeric_limits<int32_t>::max()));
*latencyMs = maxLatency;
return ::android::OK;
}
void StreamAlsa::shutdown() {
mAlsaDeviceProxies.clear();
}
} // namespace aidl::android::hardware::audio::core

View file

@ -0,0 +1,293 @@
/*
* 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 <map>
#include <set>
#define LOG_TAG "AHAL_AlsaUtils"
#include <Utils.h>
#include <aidl/android/media/audio/common/AudioFormatType.h>
#include <aidl/android/media/audio/common/PcmType.h>
#include <android-base/logging.h>
#include "Utils.h"
#include "core-impl/utils.h"
using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::PcmType;
namespace aidl::android::hardware::audio::core::alsa {
namespace {
using AudioChannelCountToMaskMap = std::map<unsigned int, AudioChannelLayout>;
using AudioFormatDescToPcmFormatMap = std::map<AudioFormatDescription, enum pcm_format>;
using PcmFormatToAudioFormatDescMap = std::map<enum pcm_format, AudioFormatDescription>;
AudioChannelLayout getInvalidChannelLayout() {
static const AudioChannelLayout invalidChannelLayout =
AudioChannelLayout::make<AudioChannelLayout::Tag::invalid>(0);
return invalidChannelLayout;
}
static AudioChannelCountToMaskMap make_ChannelCountToMaskMap(
const std::set<AudioChannelLayout>& channelMasks) {
AudioChannelCountToMaskMap channelMaskToCountMap;
for (const auto& channelMask : channelMasks) {
channelMaskToCountMap.emplace(getChannelCount(channelMask), channelMask);
}
return channelMaskToCountMap;
}
#define DEFINE_CHANNEL_LAYOUT_MASK(n) \
AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(AudioChannelLayout::LAYOUT_##n)
const AudioChannelCountToMaskMap& getSupportedChannelOutLayoutMap() {
static const std::set<AudioChannelLayout> supportedOutChannelLayouts = {
DEFINE_CHANNEL_LAYOUT_MASK(MONO), DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
DEFINE_CHANNEL_LAYOUT_MASK(2POINT1), DEFINE_CHANNEL_LAYOUT_MASK(QUAD),
DEFINE_CHANNEL_LAYOUT_MASK(PENTA), DEFINE_CHANNEL_LAYOUT_MASK(5POINT1),
DEFINE_CHANNEL_LAYOUT_MASK(6POINT1), DEFINE_CHANNEL_LAYOUT_MASK(7POINT1),
DEFINE_CHANNEL_LAYOUT_MASK(7POINT1POINT4), DEFINE_CHANNEL_LAYOUT_MASK(22POINT2),
};
static const AudioChannelCountToMaskMap outLayouts =
make_ChannelCountToMaskMap(supportedOutChannelLayouts);
return outLayouts;
}
const AudioChannelCountToMaskMap& getSupportedChannelInLayoutMap() {
static const std::set<AudioChannelLayout> supportedInChannelLayouts = {
DEFINE_CHANNEL_LAYOUT_MASK(MONO),
DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
};
static const AudioChannelCountToMaskMap inLayouts =
make_ChannelCountToMaskMap(supportedInChannelLayouts);
return inLayouts;
}
#undef DEFINE_CHANNEL_LAYOUT_MASK
#define DEFINE_CHANNEL_INDEX_MASK(n) \
AudioChannelLayout::make<AudioChannelLayout::Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##n)
const AudioChannelCountToMaskMap& getSupportedChannelIndexLayoutMap() {
static const std::set<AudioChannelLayout> supportedIndexChannelLayouts = {
DEFINE_CHANNEL_INDEX_MASK(1), DEFINE_CHANNEL_INDEX_MASK(2),
DEFINE_CHANNEL_INDEX_MASK(3), DEFINE_CHANNEL_INDEX_MASK(4),
DEFINE_CHANNEL_INDEX_MASK(5), DEFINE_CHANNEL_INDEX_MASK(6),
DEFINE_CHANNEL_INDEX_MASK(7), DEFINE_CHANNEL_INDEX_MASK(8),
DEFINE_CHANNEL_INDEX_MASK(9), DEFINE_CHANNEL_INDEX_MASK(10),
DEFINE_CHANNEL_INDEX_MASK(11), DEFINE_CHANNEL_INDEX_MASK(12),
DEFINE_CHANNEL_INDEX_MASK(13), DEFINE_CHANNEL_INDEX_MASK(14),
DEFINE_CHANNEL_INDEX_MASK(15), DEFINE_CHANNEL_INDEX_MASK(16),
DEFINE_CHANNEL_INDEX_MASK(17), DEFINE_CHANNEL_INDEX_MASK(18),
DEFINE_CHANNEL_INDEX_MASK(19), DEFINE_CHANNEL_INDEX_MASK(20),
DEFINE_CHANNEL_INDEX_MASK(21), DEFINE_CHANNEL_INDEX_MASK(22),
DEFINE_CHANNEL_INDEX_MASK(23), DEFINE_CHANNEL_INDEX_MASK(24),
};
static const AudioChannelCountToMaskMap indexLayouts =
make_ChannelCountToMaskMap(supportedIndexChannelLayouts);
return indexLayouts;
}
#undef DEFINE_CHANNEL_INDEX_MASK
AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
AudioFormatDescription result;
result.type = type;
return result;
}
AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
auto result = make_AudioFormatDescription(AudioFormatType::PCM);
result.pcm = pcm;
return result;
}
const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() {
static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = {
{make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8},
{make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE},
{make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE},
{make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE},
{make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE},
{make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE},
};
return formatDescToPcmFormatMap;
}
static PcmFormatToAudioFormatDescMap make_PcmFormatToAudioFormatDescMap(
const AudioFormatDescToPcmFormatMap& formatDescToPcmFormatMap) {
PcmFormatToAudioFormatDescMap result;
for (const auto& formatPair : formatDescToPcmFormatMap) {
result.emplace(formatPair.second, formatPair.first);
}
return result;
}
const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() {
static const PcmFormatToAudioFormatDescMap pcmFormatToFormatDescMap =
make_PcmFormatToAudioFormatDescMap(getAudioFormatDescriptorToPcmFormatMap());
return pcmFormatToFormatDescMap;
}
} // namespace
std::ostream& operator<<(std::ostream& os, const DeviceProfile& device) {
return os << "<" << device.card << "," << device.device << ">";
}
AudioChannelLayout getChannelLayoutMaskFromChannelCount(unsigned int channelCount, int isInput) {
return findValueOrDefault(
isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
channelCount, getInvalidChannelLayout());
}
AudioChannelLayout getChannelIndexMaskFromChannelCount(unsigned int channelCount) {
return findValueOrDefault(getSupportedChannelIndexLayoutMap(), channelCount,
getInvalidChannelLayout());
}
unsigned int getChannelCountFromChannelMask(const AudioChannelLayout& channelMask, bool isInput) {
switch (channelMask.getTag()) {
case AudioChannelLayout::Tag::layoutMask: {
return findKeyOrDefault(
isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
static_cast<unsigned>(getChannelCount(channelMask)), 0u /*defaultValue*/);
}
case AudioChannelLayout::Tag::indexMask: {
return findKeyOrDefault(getSupportedChannelIndexLayoutMap(),
static_cast<unsigned>(getChannelCount(channelMask)),
0u /*defaultValue*/);
}
case AudioChannelLayout::Tag::none:
case AudioChannelLayout::Tag::invalid:
case AudioChannelLayout::Tag::voiceMask:
default:
return 0;
}
}
std::vector<AudioChannelLayout> getChannelMasksFromProfile(const alsa_device_profile* profile) {
const bool isInput = profile->direction == PCM_IN;
std::vector<AudioChannelLayout> channels;
for (size_t i = 0; i < AUDIO_PORT_MAX_CHANNEL_MASKS && profile->channel_counts[i] != 0; ++i) {
auto layoutMask =
alsa::getChannelLayoutMaskFromChannelCount(profile->channel_counts[i], isInput);
if (layoutMask.getTag() == AudioChannelLayout::Tag::layoutMask) {
channels.push_back(layoutMask);
}
auto indexMask = alsa::getChannelIndexMaskFromChannelCount(profile->channel_counts[i]);
if (indexMask.getTag() == AudioChannelLayout::Tag::indexMask) {
channels.push_back(indexMask);
}
}
return channels;
}
std::optional<DeviceProfile> getDeviceProfile(
const ::aidl::android::media::audio::common::AudioDevice& audioDevice, bool isInput) {
if (audioDevice.address.getTag() != AudioDeviceAddress::Tag::alsa) {
LOG(ERROR) << __func__ << ": not alsa address: " << audioDevice.toString();
return std::nullopt;
}
auto& alsaAddress = audioDevice.address.get<AudioDeviceAddress::Tag::alsa>();
if (alsaAddress.size() != 2 || alsaAddress[0] < 0 || alsaAddress[1] < 0) {
LOG(ERROR) << __func__
<< ": malformed alsa address: " << ::android::internal::ToString(alsaAddress);
return std::nullopt;
}
return DeviceProfile{.card = alsaAddress[0],
.device = alsaAddress[1],
.direction = isInput ? PCM_IN : PCM_OUT};
}
std::optional<DeviceProfile> getDeviceProfile(
const ::aidl::android::media::audio::common::AudioPort& audioPort) {
if (audioPort.ext.getTag() != AudioPortExt::Tag::device) {
LOG(ERROR) << __func__ << ": port id " << audioPort.id << " is not a device port";
return std::nullopt;
}
auto& devicePort = audioPort.ext.get<AudioPortExt::Tag::device>();
return getDeviceProfile(devicePort.device, audioPort.flags.getTag() == AudioIoFlags::input);
}
std::optional<struct pcm_config> getPcmConfig(const StreamContext& context, bool isInput) {
struct pcm_config config;
config.channels = alsa::getChannelCountFromChannelMask(context.getChannelLayout(), isInput);
if (config.channels == 0) {
LOG(ERROR) << __func__ << ": invalid channel=" << context.getChannelLayout().toString();
return std::nullopt;
}
config.format = alsa::aidl2c_AudioFormatDescription_pcm_format(context.getFormat());
if (config.format == PCM_FORMAT_INVALID) {
LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString();
return std::nullopt;
}
config.rate = context.getSampleRate();
if (config.rate == 0) {
LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate;
return std::nullopt;
}
return config;
}
std::vector<int> getSampleRatesFromProfile(const alsa_device_profile* profile) {
std::vector<int> sampleRates;
for (int i = 0; i < std::min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) &&
profile->sample_rates[i] != 0;
i++) {
sampleRates.push_back(profile->sample_rates[i]);
}
return sampleRates;
}
DeviceProxy makeDeviceProxy() {
return DeviceProxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) {
if (proxy != nullptr) {
proxy_close(proxy);
delete proxy;
}
});
}
std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile) {
alsa_device_profile profile;
profile_init(&profile, deviceProfile.direction);
profile.card = deviceProfile.card;
profile.device = deviceProfile.device;
if (!profile_read_device_info(&profile)) {
LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card
<< ", device=" << profile.device;
return std::nullopt;
}
return profile;
}
AudioFormatDescription c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) {
return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription());
}
pcm_format aidl2c_AudioFormatDescription_pcm_format(const AudioFormatDescription& aidl) {
return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID);
}
} // namespace aidl::android::hardware::audio::core::alsa

View file

@ -0,0 +1,70 @@
/*
* 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 <functional>
#include <iostream>
#include <memory>
#include <optional>
#include <vector>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
#include <aidl/android/media/audio/common/AudioPort.h>
#include "core-impl/Stream.h"
extern "C" {
#include <tinyalsa/pcm.h>
#include "alsa_device_profile.h"
#include "alsa_device_proxy.h"
}
namespace aidl::android::hardware::audio::core::alsa {
struct DeviceProfile {
int card;
int device;
int direction; /* PCM_OUT or PCM_IN */
};
std::ostream& operator<<(std::ostream& os, const DeviceProfile& device);
using DeviceProxyDeleter = std::function<void(alsa_device_proxy*)>;
using DeviceProxy = std::unique_ptr<alsa_device_proxy, DeviceProxyDeleter>;
::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
unsigned int channelCount, int isInput);
::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount(
unsigned int channelCount);
unsigned int getChannelCountFromChannelMask(
const ::aidl::android::media::audio::common::AudioChannelLayout& channelMask, bool isInput);
std::vector<::aidl::android::media::audio::common::AudioChannelLayout> getChannelMasksFromProfile(
const alsa_device_profile* profile);
std::optional<DeviceProfile> getDeviceProfile(
const ::aidl::android::media::audio::common::AudioDevice& audioDevice, bool isInput);
std::optional<DeviceProfile> getDeviceProfile(
const ::aidl::android::media::audio::common::AudioPort& audioPort);
std::optional<struct pcm_config> getPcmConfig(const StreamContext& context, bool isInput);
std::vector<int> getSampleRatesFromProfile(const alsa_device_profile* profile);
DeviceProxy makeDeviceProxy();
std::optional<alsa_device_profile> readAlsaDeviceInfo(const DeviceProfile& deviceProfile);
::aidl::android::media::audio::common::AudioFormatDescription
c2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy);
pcm_format aidl2c_AudioFormatDescription_pcm_format(
const ::aidl::android::media::audio::common::AudioFormatDescription& aidl);
} // namespace aidl::android::hardware::audio::core::alsa

View file

@ -0,0 +1,38 @@
/*
* 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 "core-impl/Module.h"
namespace aidl::android::hardware::audio::core {
// This class is intended to be used as a base class for implementations
// that use TinyAlsa. This can be either a primary module or a USB Audio
// module. This class does not define a complete module implementation,
// and should never be used on its own. Derived classes are expected to
// provide necessary overrides for all interface methods omitted here.
class ModuleAlsa : public Module {
public:
explicit ModuleAlsa(Module::Type type) : Module(type) {}
protected:
// Extension methods of 'Module'.
ndk::ScopedAStatus populateConnectedDevicePort(
::aidl::android::media::audio::common::AudioPort* audioPort) override;
};
} // namespace aidl::android::hardware::audio::core

View file

@ -16,13 +16,13 @@
#pragma once
#include "core-impl/Module.h"
#include "core-impl/ModuleAlsa.h"
namespace aidl::android::hardware::audio::core {
class ModuleUsb : public Module {
class ModuleUsb final : public ModuleAlsa {
public:
explicit ModuleUsb(Module::Type type) : Module(type) {}
explicit ModuleUsb(Module::Type type) : ModuleAlsa(type) {}
private:
// IModule interfaces

View file

@ -0,0 +1,54 @@
/*
* 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 <optional>
#include <vector>
#include "Stream.h"
#include "alsa/Utils.h"
namespace aidl::android::hardware::audio::core {
// This class is intended to be used as a base class for implementations
// that use TinyAlsa.
// This class does not define a complete stream implementation,
// and should never be used on its own. Derived classes are expected to
// provide necessary overrides for all interface methods omitted here.
class StreamAlsa : public StreamCommonImpl {
public:
StreamAlsa(const Metadata& metadata, StreamContext&& context);
// Methods of 'DriverInterface'.
::android::status_t init() override;
::android::status_t standby() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
void shutdown() override;
protected:
// Called from 'start' to initialize 'mAlsaDeviceProxies', the vector must be non-empty.
virtual std::vector<alsa::DeviceProfile> getDeviceProfiles() = 0;
const size_t mFrameSizeBytes;
const bool mIsInput;
const std::optional<struct pcm_config> mConfig;
// All fields below are only used on the worker thread.
std::vector<alsa::DeviceProxy> mAlsaDeviceProxies;
};
} // namespace aidl::android::hardware::audio::core

View file

@ -17,55 +17,34 @@
#pragma once
#include <atomic>
#include <functional>
#include <mutex>
#include <optional>
#include <vector>
#include <aidl/android/media/audio/common/AudioChannelLayout.h>
#include "core-impl/Stream.h"
extern "C" {
#include <tinyalsa/pcm.h>
#include "alsa_device_proxy.h"
}
#include "StreamAlsa.h"
namespace aidl::android::hardware::audio::core {
class StreamUsb : public StreamCommonImpl {
class StreamUsb : public StreamAlsa {
public:
StreamUsb(const Metadata& metadata, StreamContext&& context);
// Methods of 'DriverInterface'.
::android::status_t init() override;
::android::status_t drain(StreamDescriptor::DrainMode) override;
::android::status_t flush() override;
::android::status_t pause() override;
::android::status_t standby() override;
::android::status_t start() override;
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) override;
void shutdown() override;
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.
const ConnectedDevices& getConnectedDevices() const override;
ndk::ScopedAStatus setConnectedDevices(const ConnectedDevices& devices) override;
private:
using AlsaDeviceProxyDeleter = std::function<void(alsa_device_proxy*)>;
using AlsaDeviceProxy = std::unique_ptr<alsa_device_proxy, AlsaDeviceProxyDeleter>;
static std::optional<struct pcm_config> maybePopulateConfig(const StreamContext& context,
bool isInput);
protected:
std::vector<alsa::DeviceProfile> getDeviceProfiles() override;
mutable std::mutex mLock;
const size_t mFrameSizeBytes;
const bool mIsInput;
const std::optional<struct pcm_config> mConfig;
std::vector<alsa::DeviceProfile> mConnectedDeviceProfiles GUARDED_BY(mLock);
std::atomic<bool> mConnectedDevicesUpdated = false;
// All fields below are only used on the worker thread.
std::vector<AlsaDeviceProxy> mAlsaDeviceProxies;
};
class StreamInUsb final : public StreamUsb, public StreamIn {
@ -94,7 +73,7 @@ class StreamOutUsb final : public StreamUsb, public StreamOut {
ndk::ScopedAStatus getHwVolume(std::vector<float>* _aidl_return) override;
ndk::ScopedAStatus setHwVolume(const std::vector<float>& in_channelVolumes) override;
int mChannelCount;
const int mChannelCount;
std::vector<float> mHwVolumes;
};

View file

@ -14,68 +14,34 @@
* limitations under the License.
*/
#define LOG_TAG "AHAL_ModuleUsb"
#include <vector>
#define LOG_TAG "AHAL_ModuleUsb"
#include <Utils.h>
#include <android-base/logging.h>
#include <tinyalsa/asoundlib.h>
#include "UsbAlsaMixerControl.h"
#include "UsbAlsaUtils.h"
#include "alsa/Utils.h"
#include "core-impl/ModuleUsb.h"
#include "core-impl/StreamUsb.h"
extern "C" {
#include "alsa_device_profile.h"
}
using aidl::android::hardware::audio::common::SinkMetadata;
using aidl::android::hardware::audio::common::SourceMetadata;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioOffloadInfo;
using aidl::android::media::audio::common::AudioPort;
using aidl::android::media::audio::common::AudioPortConfig;
using aidl::android::media::audio::common::AudioPortExt;
using aidl::android::media::audio::common::AudioProfile;
using aidl::android::media::audio::common::MicrophoneInfo;
namespace aidl::android::hardware::audio::core {
namespace {
std::vector<AudioChannelLayout> populateChannelMasksFromProfile(const alsa_device_profile* profile,
bool isInput) {
std::vector<AudioChannelLayout> channels;
for (size_t i = 0; i < AUDIO_PORT_MAX_CHANNEL_MASKS && profile->channel_counts[i] != 0; ++i) {
auto layoutMask =
usb::getChannelLayoutMaskFromChannelCount(profile->channel_counts[i], isInput);
if (layoutMask.getTag() == AudioChannelLayout::Tag::layoutMask) {
channels.push_back(layoutMask);
}
auto indexMask = usb::getChannelIndexMaskFromChannelCount(profile->channel_counts[i]);
if (indexMask.getTag() == AudioChannelLayout::Tag::indexMask) {
channels.push_back(indexMask);
}
}
return channels;
}
std::vector<int> populateSampleRatesFromProfile(const alsa_device_profile* profile) {
std::vector<int> sampleRates;
for (int i = 0; i < std::min(MAX_PROFILE_SAMPLE_RATES, AUDIO_PORT_MAX_SAMPLING_RATES) &&
profile->sample_rates[i] != 0;
i++) {
sampleRates.push_back(profile->sample_rates[i]);
}
return sampleRates;
bool isUsbDevicePort(const AudioPort& audioPort) {
return audioPort.ext.getTag() == AudioPortExt::Tag::device &&
audioPort.ext.get<AudioPortExt::Tag::device>().device.type.connection ==
AudioDeviceDescription::CONNECTION_USB;
}
} // namespace
@ -122,55 +88,11 @@ ndk::ScopedAStatus ModuleUsb::createOutputStream(const SourceMetadata& sourceMet
}
ndk::ScopedAStatus ModuleUsb::populateConnectedDevicePort(AudioPort* audioPort) {
if (audioPort->ext.getTag() != AudioPortExt::Tag::device) {
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a device port";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
auto& devicePort = audioPort->ext.get<AudioPortExt::Tag::device>();
if (devicePort.device.type.connection != AudioDeviceDescription::CONNECTION_USB) {
if (!isUsbDevicePort(*audioPort)) {
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not a usb device port";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
if (devicePort.device.address.getTag() != AudioDeviceAddress::Tag::alsa) {
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " is not using alsa address";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
auto& alsaAddress = devicePort.device.address.get<AudioDeviceAddress::Tag::alsa>();
if (alsaAddress.size() != 2 || alsaAddress[0] < 0 || alsaAddress[1] < 0) {
LOG(ERROR) << __func__ << ": port id " << audioPort->id << " invalid alsa address";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
const bool isInput = audioPort->flags.getTag() == AudioIoFlags::input;
alsa_device_profile profile;
profile_init(&profile, isInput ? PCM_IN : PCM_OUT);
profile.card = alsaAddress[0];
profile.device = alsaAddress[1];
if (!profile_read_device_info(&profile)) {
LOG(ERROR) << __func__ << ": failed to read device info, card=" << profile.card
<< ", device=" << profile.device;
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
std::vector<AudioChannelLayout> channels = populateChannelMasksFromProfile(&profile, isInput);
std::vector<int> sampleRates = populateSampleRatesFromProfile(&profile);
for (size_t i = 0; i < std::min(MAX_PROFILE_FORMATS, AUDIO_PORT_MAX_AUDIO_PROFILES) &&
profile.formats[i] != PCM_FORMAT_INVALID;
++i) {
auto audioFormatDescription =
usb::legacy2aidl_pcm_format_AudioFormatDescription(profile.formats[i]);
if (audioFormatDescription.type == AudioFormatType::DEFAULT) {
LOG(WARNING) << __func__ << ": unknown pcm type=" << profile.formats[i];
continue;
}
AudioProfile audioProfile = {.format = audioFormatDescription,
.channelMasks = channels,
.sampleRates = sampleRates};
audioPort->profiles.push_back(std::move(audioProfile));
}
return ndk::ScopedAStatus::ok();
return ModuleAlsa::populateConnectedDevicePort(audioPort);
}
ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch(
@ -191,15 +113,14 @@ ndk::ScopedAStatus ModuleUsb::checkAudioPatchEndpointsMatch(
void ModuleUsb::onExternalDeviceConnectionChanged(
const ::aidl::android::media::audio::common::AudioPort& audioPort, bool connected) {
if (audioPort.ext.getTag() != AudioPortExt::Tag::device) {
if (!isUsbDevicePort(audioPort)) {
return;
}
const auto& address = audioPort.ext.get<AudioPortExt::Tag::device>().device.address;
if (address.getTag() != AudioDeviceAddress::alsa) {
auto profile = alsa::getDeviceProfile(audioPort);
if (!profile.has_value()) {
return;
}
const int card = address.get<AudioDeviceAddress::alsa>()[0];
usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(card, getMasterMute(),
usb::UsbAlsaMixerControl::getInstance().setDeviceConnectionState(profile->card, getMasterMute(),
getMasterVolume(), connected);
}

View file

@ -23,64 +23,20 @@
#include <error/expected_utils.h>
#include "UsbAlsaMixerControl.h"
#include "UsbAlsaUtils.h"
#include "core-impl/Module.h"
#include "core-impl/StreamUsb.h"
extern "C" {
#include "alsa_device_profile.h"
}
using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::hardware::audio::common::SinkMetadata;
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;
namespace aidl::android::hardware::audio::core {
StreamUsb::StreamUsb(const Metadata& metadata, StreamContext&& context)
: StreamCommonImpl(metadata, std::move(context)),
mFrameSizeBytes(getContext().getFrameSize()),
mIsInput(isInput(metadata)),
mConfig(maybePopulateConfig(getContext(), mIsInput)) {}
// static
std::optional<struct pcm_config> StreamUsb::maybePopulateConfig(const StreamContext& context,
bool isInput) {
struct pcm_config config;
config.channels = usb::getChannelCountFromChannelMask(context.getChannelLayout(), isInput);
if (config.channels == 0) {
LOG(ERROR) << __func__ << ": invalid channel=" << context.getChannelLayout().toString();
return std::nullopt;
}
config.format = usb::aidl2legacy_AudioFormatDescription_pcm_format(context.getFormat());
if (config.format == PCM_FORMAT_INVALID) {
LOG(ERROR) << __func__ << ": invalid format=" << context.getFormat().toString();
return std::nullopt;
}
config.rate = context.getSampleRate();
if (config.rate == 0) {
LOG(ERROR) << __func__ << ": invalid sample rate=" << config.rate;
return std::nullopt;
}
return config;
}
::android::status_t StreamUsb::init() {
return mConfig.has_value() ? ::android::OK : ::android::NO_INIT;
}
const StreamCommonInterface::ConnectedDevices& StreamUsb::getConnectedDevices() const {
std::lock_guard guard(mLock);
return mConnectedDevices;
}
: StreamAlsa(metadata, std::move(context)) {}
ndk::ScopedAStatus StreamUsb::setConnectedDevices(
const std::vector<AudioDevice>& connectedDevices) {
@ -89,14 +45,19 @@ ndk::ScopedAStatus StreamUsb::setConnectedDevices(
<< ") for input stream";
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
std::vector<alsa::DeviceProfile> connectedDeviceProfiles;
for (const auto& connectedDevice : connectedDevices) {
if (connectedDevice.address.getTag() != AudioDeviceAddress::alsa) {
LOG(ERROR) << __func__ << ": bad device address" << connectedDevice.address.toString();
auto profile = alsa::getDeviceProfile(connectedDevice, mIsInput);
if (!profile.has_value()) {
LOG(ERROR) << __func__
<< ": unsupported device address=" << connectedDevice.address.toString();
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
connectedDeviceProfiles.push_back(*profile);
}
std::lock_guard guard(mLock);
RETURN_STATUS_IF_ERROR(StreamCommonImpl::setConnectedDevices(connectedDevices));
std::lock_guard guard(mLock);
mConnectedDeviceProfiles = std::move(connectedDeviceProfiles);
mConnectedDevicesUpdated.store(true, std::memory_order_release);
return ndk::ScopedAStatus::ok();
}
@ -119,87 +80,22 @@ ndk::ScopedAStatus StreamUsb::setConnectedDevices(
::android::status_t StreamUsb::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
int32_t* latencyMs) {
if (mConnectedDevicesUpdated.load(std::memory_order_acquire)) {
// 'setConnectedDevices' has been called. I/O will be restarted.
// 'setConnectedDevices' was called. I/O will be restarted.
*actualFrameCount = 0;
*latencyMs = StreamDescriptor::LATENCY_UNKNOWN;
return ::android::OK;
}
const size_t bytesToTransfer = frameCount * mFrameSizeBytes;
unsigned maxLatency = 0;
if (mIsInput) {
if (mAlsaDeviceProxies.empty()) {
LOG(FATAL) << __func__ << ": no input devices";
return ::android::NO_INIT;
}
// For input case, only support single device.
proxy_read(mAlsaDeviceProxies[0].get(), buffer, bytesToTransfer);
maxLatency = proxy_get_latency(mAlsaDeviceProxies[0].get());
} else {
for (auto& proxy : mAlsaDeviceProxies) {
proxy_write(proxy.get(), buffer, bytesToTransfer);
maxLatency = std::max(maxLatency, proxy_get_latency(proxy.get()));
}
}
*actualFrameCount = frameCount;
maxLatency = std::min(maxLatency, static_cast<unsigned>(std::numeric_limits<int32_t>::max()));
*latencyMs = maxLatency;
return ::android::OK;
return StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs);
}
::android::status_t StreamUsb::standby() {
mAlsaDeviceProxies.clear();
return ::android::OK;
}
void StreamUsb::shutdown() {
mAlsaDeviceProxies.clear();
}
::android::status_t StreamUsb::start() {
std::vector<AudioDeviceAddress> connectedDevices;
std::vector<alsa::DeviceProfile> StreamUsb::getDeviceProfiles() {
std::vector<alsa::DeviceProfile> connectedDevices;
{
std::lock_guard guard(mLock);
std::transform(mConnectedDevices.begin(), mConnectedDevices.end(),
std::back_inserter(connectedDevices),
[](const auto& device) { return device.address; });
connectedDevices = mConnectedDeviceProfiles;
mConnectedDevicesUpdated.store(false, std::memory_order_release);
}
decltype(mAlsaDeviceProxies) alsaDeviceProxies;
for (const auto& device : connectedDevices) {
alsa_device_profile profile;
profile_init(&profile, mIsInput ? PCM_IN : PCM_OUT);
profile.card = device.get<AudioDeviceAddress::alsa>()[0];
profile.device = device.get<AudioDeviceAddress::alsa>()[1];
if (!profile_read_device_info(&profile)) {
LOG(ERROR) << __func__
<< ": unable to read device info, device address=" << device.toString();
return ::android::UNKNOWN_ERROR;
}
AlsaDeviceProxy proxy(new alsa_device_proxy, [](alsa_device_proxy* proxy) {
proxy_close(proxy);
free(proxy);
});
// Always ask for alsa configure as required since the configuration should be supported
// by the connected device. That is guaranteed by `setAudioPortConfig` and
// `setAudioPatch`.
if (int err = proxy_prepare(proxy.get(), &profile,
const_cast<struct pcm_config*>(&mConfig.value()),
true /*is_bit_perfect*/);
err != 0) {
LOG(ERROR) << __func__ << ": fail to prepare for device address=" << device.toString()
<< " error=" << err;
return ::android::UNKNOWN_ERROR;
}
if (int err = proxy_open(proxy.get()); err != 0) {
LOG(ERROR) << __func__ << ": failed to open device, address=" << device.toString()
<< " error=" << err;
return ::android::UNKNOWN_ERROR;
}
alsaDeviceProxies.push_back(std::move(proxy));
}
mAlsaDeviceProxies = std::move(alsaDeviceProxies);
return ::android::OK;
return connectedDevices;
}
StreamInUsb::StreamInUsb(const SinkMetadata& sinkMetadata, StreamContext&& context,
@ -214,9 +110,9 @@ ndk::ScopedAStatus StreamInUsb::getActiveMicrophones(
StreamOutUsb::StreamOutUsb(const SourceMetadata& sourceMetadata, StreamContext&& context,
const std::optional<AudioOffloadInfo>& offloadInfo)
: StreamUsb(sourceMetadata, std::move(context)), StreamOut(offloadInfo) {
mChannelCount = getChannelCount(getContext().getChannelLayout());
}
: StreamUsb(sourceMetadata, std::move(context)),
StreamOut(offloadInfo),
mChannelCount(getChannelCount(getContext().getChannelLayout())) {}
ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector<float>* _aidl_return) {
*_aidl_return = mHwVolumes;
@ -224,17 +120,17 @@ ndk::ScopedAStatus StreamOutUsb::getHwVolume(std::vector<float>* _aidl_return) {
}
ndk::ScopedAStatus StreamOutUsb::setHwVolume(const std::vector<float>& in_channelVolumes) {
// Avoid using mConnectedDeviceProfiles because it requires a lock.
for (const auto& device : getConnectedDevices()) {
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;
if (auto deviceProfile = alsa::getDeviceProfile(device, mIsInput);
deviceProfile.has_value()) {
if (auto result = usb::UsbAlsaMixerControl::getInstance().setVolumes(
deviceProfile->card, in_channelVolumes);
!result.isOk()) {
LOG(ERROR) << __func__
<< ": failed to set volume for device address=" << *deviceProfile;
return result;
}
}
}
mHwVolumes = in_channelVolumes;

View file

@ -17,144 +17,12 @@
#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);
}
} // 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();
if (numValues < 0) {
LOG(FATAL) << __func__ << ": negative number of values: " << numValues;
}
const int maxValue = it->second->getMaxValue();
const int minValue = it->second->getMinValue();
std::vector<int> values;
size_t i = 0;
for (; i < static_cast<size_t>(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;
@ -170,7 +38,7 @@ void UsbAlsaMixerControl::setDeviceConnectionState(int card, bool masterMuted, f
PLOG(ERROR) << __func__ << ": failed to open mixer for card=" << card;
return;
}
auto alsaMixer = std::make_shared<AlsaMixer>(mixer);
auto alsaMixer = std::make_shared<alsa::Mixer>(mixer);
alsaMixer->setMasterMute(masterMuted);
alsaMixer->setMasterVolume(masterVolume);
const std::lock_guard guard(mLock);
@ -209,7 +77,7 @@ ndk::ScopedAStatus UsbAlsaMixerControl::setMasterVolume(float volume) {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, std::vector<float> volumes) {
ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, const std::vector<float>& volumes) {
auto alsaMixer = getAlsaMixer(card);
if (alsaMixer == nullptr) {
LOG(ERROR) << __func__ << ": no mixer control found for card=" << card;
@ -218,13 +86,13 @@ ndk::ScopedAStatus UsbAlsaMixerControl::setVolumes(int card, std::vector<float>
return alsaMixer->setVolumes(volumes);
}
std::shared_ptr<AlsaMixer> UsbAlsaMixerControl::getAlsaMixer(int card) {
std::shared_ptr<alsa::Mixer> 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() {
std::map<int, std::shared_ptr<alsa::Mixer>> UsbAlsaMixerControl::getAlsaMixers() {
const std::lock_guard guard(mLock);
return mMixerControls;
}

View file

@ -19,67 +19,15 @@
#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>
}
#include "alsa/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();
@ -91,16 +39,16 @@ class UsbAlsaMixerControl {
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);
ndk::ScopedAStatus setVolumes(int card, const std::vector<float>& volumes);
private:
std::shared_ptr<AlsaMixer> getAlsaMixer(int card);
std::map<int, std::shared_ptr<AlsaMixer>> getAlsaMixers();
std::shared_ptr<alsa::Mixer> getAlsaMixer(int card);
std::map<int, std::shared_ptr<alsa::Mixer>> 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);
std::map<int, std::shared_ptr<alsa::Mixer>> mMixerControls GUARDED_BY(mLock);
};
} // namespace aidl::android::hardware::audio::core::usb

View file

@ -1,181 +0,0 @@
/*
* 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 <map>
#include <set>
#include <Utils.h>
#include <aidl/android/media/audio/common/AudioFormatType.h>
#include <aidl/android/media/audio/common/PcmType.h>
#include "UsbAlsaUtils.h"
#include "core-impl/utils.h"
using aidl::android::hardware::audio::common::getChannelCount;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioFormatDescription;
using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::PcmType;
namespace aidl::android::hardware::audio::core::usb {
namespace {
using AudioChannelCountToMaskMap = std::map<unsigned int, AudioChannelLayout>;
using AudioFormatDescToPcmFormatMap = std::map<AudioFormatDescription, enum pcm_format>;
using PcmFormatToAudioFormatDescMap = std::map<enum pcm_format, AudioFormatDescription>;
static const AudioChannelLayout INVALID_CHANNEL_LAYOUT =
AudioChannelLayout::make<AudioChannelLayout::Tag::invalid>(0);
#define DEFINE_CHANNEL_LAYOUT_MASK(n) \
AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(AudioChannelLayout::LAYOUT_##n)
static const std::set<AudioChannelLayout> SUPPORTED_OUT_CHANNEL_LAYOUTS = {
DEFINE_CHANNEL_LAYOUT_MASK(MONO), DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
DEFINE_CHANNEL_LAYOUT_MASK(2POINT1), DEFINE_CHANNEL_LAYOUT_MASK(QUAD),
DEFINE_CHANNEL_LAYOUT_MASK(PENTA), DEFINE_CHANNEL_LAYOUT_MASK(5POINT1),
DEFINE_CHANNEL_LAYOUT_MASK(6POINT1), DEFINE_CHANNEL_LAYOUT_MASK(7POINT1),
DEFINE_CHANNEL_LAYOUT_MASK(7POINT1POINT4), DEFINE_CHANNEL_LAYOUT_MASK(22POINT2),
};
static const std::set<AudioChannelLayout> SUPPORTED_IN_CHANNEL_LAYOUTS = {
DEFINE_CHANNEL_LAYOUT_MASK(MONO),
DEFINE_CHANNEL_LAYOUT_MASK(STEREO),
};
#define DEFINE_CHANNEL_INDEX_MASK(n) \
AudioChannelLayout::make<AudioChannelLayout::Tag::indexMask>(AudioChannelLayout::INDEX_MASK_##n)
static const std::set<AudioChannelLayout> SUPPORTED_INDEX_CHANNEL_LAYOUTS = {
DEFINE_CHANNEL_INDEX_MASK(1), DEFINE_CHANNEL_INDEX_MASK(2), DEFINE_CHANNEL_INDEX_MASK(3),
DEFINE_CHANNEL_INDEX_MASK(4), DEFINE_CHANNEL_INDEX_MASK(5), DEFINE_CHANNEL_INDEX_MASK(6),
DEFINE_CHANNEL_INDEX_MASK(7), DEFINE_CHANNEL_INDEX_MASK(8), DEFINE_CHANNEL_INDEX_MASK(9),
DEFINE_CHANNEL_INDEX_MASK(10), DEFINE_CHANNEL_INDEX_MASK(11), DEFINE_CHANNEL_INDEX_MASK(12),
DEFINE_CHANNEL_INDEX_MASK(13), DEFINE_CHANNEL_INDEX_MASK(14), DEFINE_CHANNEL_INDEX_MASK(15),
DEFINE_CHANNEL_INDEX_MASK(16), DEFINE_CHANNEL_INDEX_MASK(17), DEFINE_CHANNEL_INDEX_MASK(18),
DEFINE_CHANNEL_INDEX_MASK(19), DEFINE_CHANNEL_INDEX_MASK(20), DEFINE_CHANNEL_INDEX_MASK(21),
DEFINE_CHANNEL_INDEX_MASK(22), DEFINE_CHANNEL_INDEX_MASK(23), DEFINE_CHANNEL_INDEX_MASK(24),
};
static AudioChannelCountToMaskMap make_ChannelCountToMaskMap(
const std::set<AudioChannelLayout>& channelMasks) {
AudioChannelCountToMaskMap channelMaskToCountMap;
for (const auto& channelMask : channelMasks) {
channelMaskToCountMap.emplace(getChannelCount(channelMask), channelMask);
}
return channelMaskToCountMap;
}
const AudioChannelCountToMaskMap& getSupportedChannelOutLayoutMap() {
static const AudioChannelCountToMaskMap outLayouts =
make_ChannelCountToMaskMap(SUPPORTED_OUT_CHANNEL_LAYOUTS);
return outLayouts;
}
const AudioChannelCountToMaskMap& getSupportedChannelInLayoutMap() {
static const AudioChannelCountToMaskMap inLayouts =
make_ChannelCountToMaskMap(SUPPORTED_IN_CHANNEL_LAYOUTS);
return inLayouts;
}
const AudioChannelCountToMaskMap& getSupportedChannelIndexLayoutMap() {
static const AudioChannelCountToMaskMap indexLayouts =
make_ChannelCountToMaskMap(SUPPORTED_INDEX_CHANNEL_LAYOUTS);
return indexLayouts;
}
AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
AudioFormatDescription result;
result.type = type;
return result;
}
AudioFormatDescription make_AudioFormatDescription(PcmType pcm) {
auto result = make_AudioFormatDescription(AudioFormatType::PCM);
result.pcm = pcm;
return result;
}
const AudioFormatDescToPcmFormatMap& getAudioFormatDescriptorToPcmFormatMap() {
static const AudioFormatDescToPcmFormatMap formatDescToPcmFormatMap = {
{make_AudioFormatDescription(PcmType::UINT_8_BIT), PCM_FORMAT_S8},
{make_AudioFormatDescription(PcmType::INT_16_BIT), PCM_FORMAT_S16_LE},
{make_AudioFormatDescription(PcmType::FIXED_Q_8_24), PCM_FORMAT_S24_LE},
{make_AudioFormatDescription(PcmType::INT_24_BIT), PCM_FORMAT_S24_3LE},
{make_AudioFormatDescription(PcmType::INT_32_BIT), PCM_FORMAT_S32_LE},
{make_AudioFormatDescription(PcmType::FLOAT_32_BIT), PCM_FORMAT_FLOAT_LE},
};
return formatDescToPcmFormatMap;
}
static PcmFormatToAudioFormatDescMap make_PcmFormatToAudioFormatDescMap(
const AudioFormatDescToPcmFormatMap& formatDescToPcmFormatMap) {
PcmFormatToAudioFormatDescMap result;
for (const auto& formatPair : formatDescToPcmFormatMap) {
result.emplace(formatPair.second, formatPair.first);
}
return result;
}
const PcmFormatToAudioFormatDescMap& getPcmFormatToAudioFormatDescMap() {
static const PcmFormatToAudioFormatDescMap pcmFormatToFormatDescMap =
make_PcmFormatToAudioFormatDescMap(getAudioFormatDescriptorToPcmFormatMap());
return pcmFormatToFormatDescMap;
}
} // namespace
AudioChannelLayout getChannelLayoutMaskFromChannelCount(unsigned int channelCount, int isInput) {
return findValueOrDefault(
isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
channelCount, INVALID_CHANNEL_LAYOUT);
}
AudioChannelLayout getChannelIndexMaskFromChannelCount(unsigned int channelCount) {
return findValueOrDefault(getSupportedChannelIndexLayoutMap(), channelCount,
INVALID_CHANNEL_LAYOUT);
}
unsigned int getChannelCountFromChannelMask(const AudioChannelLayout& channelMask, bool isInput) {
switch (channelMask.getTag()) {
case AudioChannelLayout::Tag::layoutMask: {
return findKeyOrDefault(
isInput ? getSupportedChannelInLayoutMap() : getSupportedChannelOutLayoutMap(),
(unsigned int)getChannelCount(channelMask), 0u /*defaultValue*/);
}
case AudioChannelLayout::Tag::indexMask: {
return findKeyOrDefault(getSupportedChannelIndexLayoutMap(),
(unsigned int)getChannelCount(channelMask),
0u /*defaultValue*/);
}
case AudioChannelLayout::Tag::none:
case AudioChannelLayout::Tag::invalid:
case AudioChannelLayout::Tag::voiceMask:
default:
return 0;
}
}
AudioFormatDescription legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy) {
return findValueOrDefault(getPcmFormatToAudioFormatDescMap(), legacy, AudioFormatDescription());
}
pcm_format aidl2legacy_AudioFormatDescription_pcm_format(const AudioFormatDescription& aidl) {
return findValueOrDefault(getAudioFormatDescriptorToPcmFormatMap(), aidl, PCM_FORMAT_INVALID);
}
} // namespace aidl::android::hardware::audio::core::usb

View file

@ -1,39 +0,0 @@
/*
* 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 <aidl/android/media/audio/common/AudioChannelLayout.h>
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
extern "C" {
#include <tinyalsa/pcm.h>
}
namespace aidl::android::hardware::audio::core::usb {
::aidl::android::media::audio::common::AudioChannelLayout getChannelLayoutMaskFromChannelCount(
unsigned int channelCount, int isInput);
::aidl::android::media::audio::common::AudioChannelLayout getChannelIndexMaskFromChannelCount(
unsigned int channelCount);
unsigned int getChannelCountFromChannelMask(
const ::aidl::android::media::audio::common::AudioChannelLayout& channelMask, bool isInput);
::aidl::android::media::audio::common::AudioFormatDescription
legacy2aidl_pcm_format_AudioFormatDescription(enum pcm_format legacy);
pcm_format aidl2legacy_AudioFormatDescription_pcm_format(
const ::aidl::android::media::audio::common::AudioFormatDescription& aidl);
} // namespace aidl::android::hardware::audio::core::usb