mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:50:18 +00:00
Merge changes I392933f8,I5e4da0c3,I2134b15e into main am: f0ad731bb9
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2657259 Change-Id: Ia0cc734587bc6a69cc7f132b5ba846b9ab81d2db Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -73,18 +73,23 @@ cc_library {
|
||||
"Configuration.cpp",
|
||||
"EngineConfigXmlConverter.cpp",
|
||||
"Module.cpp",
|
||||
"ModulePrimary.cpp",
|
||||
"SoundDose.cpp",
|
||||
"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",
|
||||
"r_submix/StreamRemoteSubmix.cpp",
|
||||
"stub/ModuleStub.cpp",
|
||||
"stub/StreamStub.cpp",
|
||||
"usb/ModuleUsb.cpp",
|
||||
"usb/StreamUsb.cpp",
|
||||
"usb/UsbAlsaMixerControl.cpp",
|
||||
"usb/UsbAlsaUtils.cpp",
|
||||
],
|
||||
generated_sources: [
|
||||
"audio_policy_configuration_aidl_default",
|
||||
|
||||
@@ -144,10 +144,6 @@ static AudioRoute createRoute(const std::vector<AudioPort>& sources, const Audio
|
||||
// - no profiles specified
|
||||
// * "FM Tuner", IN_FM_TUNER
|
||||
// - no profiles specified
|
||||
// * "USB Out", OUT_DEVICE, CONNECTION_USB
|
||||
// - no profiles specified
|
||||
// * "USB In", IN_DEVICE, CONNECTION_USB
|
||||
// - no profiles specified
|
||||
//
|
||||
// Mix ports:
|
||||
// * "primary output", PRIMARY, 1 max open, 1 max active stream
|
||||
@@ -172,8 +168,7 @@ static AudioRoute createRoute(const std::vector<AudioPort>& sources, const Audio
|
||||
//
|
||||
// Routes:
|
||||
// "primary out", "compressed offload" -> "Speaker"
|
||||
// "primary out", "compressed offload" -> "USB Out"
|
||||
// "Built-in Mic", "USB In" -> "primary input"
|
||||
// "Built-in Mic" -> "primary input"
|
||||
// "telephony_tx" -> "Telephony Tx"
|
||||
// "Telephony Rx" -> "telephony_rx"
|
||||
// "FM Tuner" -> "fm_tuner"
|
||||
@@ -185,14 +180,6 @@ static AudioRoute createRoute(const std::vector<AudioPort>& sources, const Audio
|
||||
// * "Telephony Rx" device port: PCM 24-bit; MONO; 48000
|
||||
// * "FM Tuner" device port: PCM 24-bit; STEREO; 48000
|
||||
//
|
||||
// Profiles for device port connected state:
|
||||
// * USB Out":
|
||||
// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
|
||||
// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
|
||||
// * USB In":
|
||||
// - profile PCM 16-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
|
||||
// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
|
||||
//
|
||||
std::unique_ptr<Configuration> getPrimaryConfiguration() {
|
||||
static const Configuration configuration = []() {
|
||||
const std::vector<AudioProfile> standardPcmAudioProfiles = {
|
||||
@@ -252,19 +239,6 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
|
||||
AudioChannelLayout::LAYOUT_STEREO, 48000, 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_FM_TUNER, 0)));
|
||||
|
||||
AudioPort usbOutDevice =
|
||||
createPort(c.nextPortId++, "USB Out", 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
|
||||
AudioDeviceDescription::CONNECTION_USB));
|
||||
c.ports.push_back(usbOutDevice);
|
||||
c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles;
|
||||
|
||||
AudioPort usbInDevice = createPort(c.nextPortId++, "USB In", 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_DEVICE, 0,
|
||||
AudioDeviceDescription::CONNECTION_USB));
|
||||
c.ports.push_back(usbInDevice);
|
||||
c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles;
|
||||
|
||||
// Mix ports
|
||||
|
||||
AudioPort primaryOutMix = createPort(c.nextPortId++, "primary output",
|
||||
@@ -323,8 +297,7 @@ std::unique_ptr<Configuration> getPrimaryConfiguration() {
|
||||
c.ports.push_back(fmTunerInMix);
|
||||
|
||||
c.routes.push_back(createRoute({primaryOutMix, compressedOffloadOutMix}, speakerOutDevice));
|
||||
c.routes.push_back(createRoute({primaryOutMix, compressedOffloadOutMix}, usbOutDevice));
|
||||
c.routes.push_back(createRoute({micInDevice, usbInDevice}, primaryInMix));
|
||||
c.routes.push_back(createRoute({micInDevice}, primaryInMix));
|
||||
c.routes.push_back(createRoute({telephonyTxOutMix}, telephonyTxOutDevice));
|
||||
c.routes.push_back(createRoute({telephonyRxInDevice}, telephonyRxInMix));
|
||||
c.routes.push_back(createRoute({fmTunerInDevice}, fmTunerInMix));
|
||||
@@ -406,22 +379,31 @@ std::unique_ptr<Configuration> getRSubmixConfiguration() {
|
||||
// Usb configuration:
|
||||
//
|
||||
// Device ports:
|
||||
// * "USB Device Out", OUT_DEVICE, CONNECTION_USB
|
||||
// - no profiles specified
|
||||
// * "USB Headset Out", OUT_HEADSET, CONNECTION_USB
|
||||
// - no profiles specified
|
||||
// * "USB Device In", IN_DEVICE, CONNECTION_USB
|
||||
// - no profiles specified
|
||||
// * "USB Headset In", IN_HEADSET, CONNECTION_USB
|
||||
// - no profiles specified
|
||||
//
|
||||
// Mix ports:
|
||||
// * "usb_headset output", 1 max open, 1 max active stream
|
||||
// * "usb_device output", 1 max open, 1 max active stream
|
||||
// - no profiles specified
|
||||
// * "usb_headset input", 1 max open, 1 max active stream
|
||||
// * "usb_device input", 1 max open, 1 max active stream
|
||||
// - no profiles specified
|
||||
//
|
||||
// Routes:
|
||||
// * "usb_device output" -> "USB Device Out"
|
||||
// * "usb_device output" -> "USB Headset Out"
|
||||
// * "USB Device In", "USB Headset In" -> "usb_device input"
|
||||
//
|
||||
// Profiles for device port connected state:
|
||||
// * USB Headset Out":
|
||||
// * "USB Device Out", "USB Headset Out":
|
||||
// - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
|
||||
// - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
|
||||
// * USB Headset In":
|
||||
// * "USB Device In", "USB Headset In":
|
||||
// - profile PCM 16-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
|
||||
// - profile PCM 24-bit; MONO, STEREO, INDEX_MASK_1, INDEX_MASK_2; 44100, 48000
|
||||
//
|
||||
@@ -440,6 +422,13 @@ std::unique_ptr<Configuration> getUsbConfiguration() {
|
||||
|
||||
// Device ports
|
||||
|
||||
AudioPort usbOutDevice =
|
||||
createPort(c.nextPortId++, "USB Device Out", 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
|
||||
AudioDeviceDescription::CONNECTION_USB));
|
||||
c.ports.push_back(usbOutDevice);
|
||||
c.connectedProfiles[usbOutDevice.id] = standardPcmAudioProfiles;
|
||||
|
||||
AudioPort usbOutHeadset =
|
||||
createPort(c.nextPortId++, "USB Headset Out", 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_HEADSET, 0,
|
||||
@@ -447,6 +436,12 @@ std::unique_ptr<Configuration> getUsbConfiguration() {
|
||||
c.ports.push_back(usbOutHeadset);
|
||||
c.connectedProfiles[usbOutHeadset.id] = standardPcmAudioProfiles;
|
||||
|
||||
AudioPort usbInDevice = createPort(c.nextPortId++, "USB Device In", 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_DEVICE, 0,
|
||||
AudioDeviceDescription::CONNECTION_USB));
|
||||
c.ports.push_back(usbInDevice);
|
||||
c.connectedProfiles[usbInDevice.id] = standardPcmAudioProfiles;
|
||||
|
||||
AudioPort usbInHeadset =
|
||||
createPort(c.nextPortId++, "USB Headset In", 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_HEADSET, 0,
|
||||
@@ -456,16 +451,110 @@ std::unique_ptr<Configuration> getUsbConfiguration() {
|
||||
|
||||
// Mix ports
|
||||
|
||||
AudioPort usbHeadsetOutMix =
|
||||
createPort(c.nextPortId++, "usb_headset output", 0, false, createPortMixExt(1, 1));
|
||||
c.ports.push_back(usbHeadsetOutMix);
|
||||
AudioPort usbDeviceOutMix =
|
||||
createPort(c.nextPortId++, "usb_device output", 0, false, createPortMixExt(1, 1));
|
||||
c.ports.push_back(usbDeviceOutMix);
|
||||
|
||||
AudioPort usbHeadsetInMix =
|
||||
createPort(c.nextPortId++, "usb_headset input", 0, true, createPortMixExt(1, 1));
|
||||
c.ports.push_back(usbHeadsetInMix);
|
||||
AudioPort usbDeviceInMix =
|
||||
createPort(c.nextPortId++, "usb_device input", 0, true, createPortMixExt(1, 1));
|
||||
c.ports.push_back(usbDeviceInMix);
|
||||
|
||||
c.routes.push_back(createRoute({usbHeadsetOutMix}, usbOutHeadset));
|
||||
c.routes.push_back(createRoute({usbInHeadset}, usbHeadsetInMix));
|
||||
c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutDevice));
|
||||
c.routes.push_back(createRoute({usbDeviceOutMix}, usbOutHeadset));
|
||||
c.routes.push_back(createRoute({usbInDevice, usbInHeadset}, usbDeviceInMix));
|
||||
|
||||
return c;
|
||||
}();
|
||||
return std::make_unique<Configuration>(configuration);
|
||||
}
|
||||
|
||||
// Stub configuration:
|
||||
//
|
||||
// Device ports:
|
||||
// * "Test Out", OUT_AFE_PROXY
|
||||
// - no profiles specified
|
||||
// * "Test In", IN_AFE_PROXY
|
||||
// - no profiles specified
|
||||
//
|
||||
// Mix ports:
|
||||
// * "test output", 1 max open, 1 max active stream
|
||||
// - profile PCM 24-bit; MONO, STEREO; 8000, 11025, 16000, 32000, 44100, 48000
|
||||
// * "compressed offload", DIRECT|COMPRESS_OFFLOAD|NON_BLOCKING, 1 max open, 1 max active stream
|
||||
// - profile MP3; MONO, STEREO; 44100, 48000
|
||||
// * "test input", 2 max open, 2 max active streams
|
||||
// - profile PCM 24-bit; MONO, STEREO, FRONT_BACK;
|
||||
// 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
|
||||
//
|
||||
// Routes:
|
||||
// "test output", "compressed offload" -> "Test Out"
|
||||
// "Test In" -> "test input"
|
||||
//
|
||||
// Initial port configs:
|
||||
// * "Test Out" device port: PCM 24-bit; STEREO; 48000
|
||||
// * "Test In" device port: PCM 24-bit; MONO; 48000
|
||||
//
|
||||
std::unique_ptr<Configuration> getStubConfiguration() {
|
||||
static const Configuration configuration = []() {
|
||||
Configuration c;
|
||||
|
||||
// Device ports
|
||||
|
||||
AudioPort testOutDevice = createPort(c.nextPortId++, "Test Out", 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_AFE_PROXY, 0));
|
||||
c.ports.push_back(testOutDevice);
|
||||
c.initialConfigs.push_back(
|
||||
createPortConfig(testOutDevice.id, testOutDevice.id, PcmType::INT_24_BIT,
|
||||
AudioChannelLayout::LAYOUT_STEREO, 48000, 0, false,
|
||||
createDeviceExt(AudioDeviceType::OUT_AFE_PROXY, 0)));
|
||||
|
||||
AudioPort testInDevice = createPort(c.nextPortId++, "Test In", 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0));
|
||||
c.ports.push_back(testInDevice);
|
||||
c.initialConfigs.push_back(
|
||||
createPortConfig(testInDevice.id, testInDevice.id, PcmType::INT_24_BIT,
|
||||
AudioChannelLayout::LAYOUT_MONO, 48000, 0, true,
|
||||
createDeviceExt(AudioDeviceType::IN_AFE_PROXY, 0)));
|
||||
|
||||
// Mix ports
|
||||
|
||||
AudioPort testOutMix =
|
||||
createPort(c.nextPortId++, "test output", 0, false, createPortMixExt(1, 1));
|
||||
testOutMix.profiles.push_back(
|
||||
createProfile(PcmType::INT_24_BIT,
|
||||
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
|
||||
{8000, 11025, 16000, 32000, 44100, 48000}));
|
||||
c.ports.push_back(testOutMix);
|
||||
|
||||
AudioPort compressedOffloadOutMix =
|
||||
createPort(c.nextPortId++, "compressed offload",
|
||||
makeBitPositionFlagMask({AudioOutputFlags::DIRECT,
|
||||
AudioOutputFlags::COMPRESS_OFFLOAD,
|
||||
AudioOutputFlags::NON_BLOCKING}),
|
||||
false, createPortMixExt(1, 1));
|
||||
compressedOffloadOutMix.profiles.push_back(
|
||||
createProfile(::android::MEDIA_MIMETYPE_AUDIO_MPEG,
|
||||
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO},
|
||||
{44100, 48000}));
|
||||
c.ports.push_back(compressedOffloadOutMix);
|
||||
|
||||
AudioPort testInMIx =
|
||||
createPort(c.nextPortId++, "test input", 0, true, createPortMixExt(2, 2));
|
||||
testInMIx.profiles.push_back(
|
||||
createProfile(PcmType::INT_16_BIT,
|
||||
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
|
||||
AudioChannelLayout::LAYOUT_FRONT_BACK},
|
||||
{8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
|
||||
testInMIx.profiles.push_back(
|
||||
createProfile(PcmType::INT_24_BIT,
|
||||
{AudioChannelLayout::LAYOUT_MONO, AudioChannelLayout::LAYOUT_STEREO,
|
||||
AudioChannelLayout::LAYOUT_FRONT_BACK},
|
||||
{8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}));
|
||||
c.ports.push_back(testInMIx);
|
||||
|
||||
c.routes.push_back(createRoute({testOutMix, compressedOffloadOutMix}, testOutDevice));
|
||||
c.routes.push_back(createRoute({testInDevice}, testInMIx));
|
||||
|
||||
c.portConfigs.insert(c.portConfigs.end(), c.initialConfigs.begin(), c.initialConfigs.end());
|
||||
|
||||
return c;
|
||||
}();
|
||||
|
||||
@@ -25,13 +25,12 @@
|
||||
#include <android/binder_ibinder_platform.h>
|
||||
#include <error/expected_utils.h>
|
||||
|
||||
#include "core-impl/Bluetooth.h"
|
||||
#include "core-impl/Module.h"
|
||||
#include "core-impl/ModulePrimary.h"
|
||||
#include "core-impl/ModuleRemoteSubmix.h"
|
||||
#include "core-impl/ModuleStub.h"
|
||||
#include "core-impl/ModuleUsb.h"
|
||||
#include "core-impl/SoundDose.h"
|
||||
#include "core-impl/StreamStub.h"
|
||||
#include "core-impl/Telephony.h"
|
||||
#include "core-impl/utils.h"
|
||||
|
||||
using aidl::android::hardware::audio::common::getFrameSizeInBytes;
|
||||
@@ -110,13 +109,14 @@ bool findAudioProfile(const AudioPort& port, const AudioFormatDescription& forma
|
||||
// static
|
||||
std::shared_ptr<Module> Module::createInstance(Type type) {
|
||||
switch (type) {
|
||||
case Module::Type::USB:
|
||||
return ndk::SharedRefBase::make<ModuleUsb>(type);
|
||||
case Type::R_SUBMIX:
|
||||
return ndk::SharedRefBase::make<ModuleRemoteSubmix>(type);
|
||||
case Type::DEFAULT:
|
||||
default:
|
||||
return ndk::SharedRefBase::make<Module>(type);
|
||||
return ndk::SharedRefBase::make<ModulePrimary>();
|
||||
case Type::R_SUBMIX:
|
||||
return ndk::SharedRefBase::make<ModuleRemoteSubmix>();
|
||||
case Type::STUB:
|
||||
return ndk::SharedRefBase::make<ModuleStub>();
|
||||
case Type::USB:
|
||||
return ndk::SharedRefBase::make<ModuleUsb>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +128,9 @@ std::ostream& operator<<(std::ostream& os, Module::Type t) {
|
||||
case Module::Type::R_SUBMIX:
|
||||
os << "r_submix";
|
||||
break;
|
||||
case Module::Type::STUB:
|
||||
os << "stub";
|
||||
break;
|
||||
case Module::Type::USB:
|
||||
os << "usb";
|
||||
break;
|
||||
@@ -292,6 +295,9 @@ internal::Configuration& Module::getConfig() {
|
||||
case Type::R_SUBMIX:
|
||||
mConfig = std::move(internal::getRSubmixConfiguration());
|
||||
break;
|
||||
case Type::STUB:
|
||||
mConfig = std::move(internal::getStubConfiguration());
|
||||
break;
|
||||
case Type::USB:
|
||||
mConfig = std::move(internal::getUsbConfiguration());
|
||||
break;
|
||||
@@ -395,38 +401,26 @@ ndk::ScopedAStatus Module::setModuleDebug(
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
|
||||
if (!mTelephony) {
|
||||
mTelephony = ndk::SharedRefBase::make<Telephony>();
|
||||
}
|
||||
*_aidl_return = mTelephony.getPtr();
|
||||
LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get();
|
||||
*_aidl_return = nullptr;
|
||||
LOG(DEBUG) << __func__ << ": returning null";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
|
||||
if (!mBluetooth) {
|
||||
mBluetooth = ndk::SharedRefBase::make<Bluetooth>();
|
||||
}
|
||||
*_aidl_return = mBluetooth.getPtr();
|
||||
LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get();
|
||||
*_aidl_return = nullptr;
|
||||
LOG(DEBUG) << __func__ << ": returning null";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
|
||||
if (!mBluetoothA2dp) {
|
||||
mBluetoothA2dp = ndk::SharedRefBase::make<BluetoothA2dp>();
|
||||
}
|
||||
*_aidl_return = mBluetoothA2dp.getPtr();
|
||||
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
|
||||
*_aidl_return = nullptr;
|
||||
LOG(DEBUG) << __func__ << ": returning null";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
|
||||
if (!mBluetoothLe) {
|
||||
mBluetoothLe = ndk::SharedRefBase::make<BluetoothLe>();
|
||||
}
|
||||
*_aidl_return = mBluetoothLe.getPtr();
|
||||
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
|
||||
*_aidl_return = nullptr;
|
||||
LOG(DEBUG) << __func__ << ": returning null";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
@@ -1334,22 +1328,6 @@ bool Module::isMmapSupported() {
|
||||
return mIsMmapSupported.value();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::createInputStream(const SinkMetadata& sinkMetadata,
|
||||
StreamContext&& context,
|
||||
const std::vector<MicrophoneInfo>& microphones,
|
||||
std::shared_ptr<StreamIn>* result) {
|
||||
return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
|
||||
microphones);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::createOutputStream(const SourceMetadata& sourceMetadata,
|
||||
StreamContext&& context,
|
||||
const std::optional<AudioOffloadInfo>& offloadInfo,
|
||||
std::shared_ptr<StreamOut>* result) {
|
||||
return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
|
||||
offloadInfo);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus Module::populateConnectedDevicePort(AudioPort* audioPort __unused) {
|
||||
LOG(VERBOSE) << __func__ << ": do nothing and return ok";
|
||||
return ndk::ScopedAStatus::ok();
|
||||
|
||||
60
audio/aidl/default/ModulePrimary.cpp
Normal file
60
audio/aidl/default/ModulePrimary.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 <vector>
|
||||
|
||||
#define LOG_TAG "AHAL_ModulePrimary"
|
||||
#include <Utils.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "core-impl/ModulePrimary.h"
|
||||
#include "core-impl/StreamStub.h"
|
||||
#include "core-impl/Telephony.h"
|
||||
|
||||
using aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using aidl::android::hardware::audio::common::SourceMetadata;
|
||||
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::MicrophoneInfo;
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
ndk::ScopedAStatus ModulePrimary::getTelephony(std::shared_ptr<ITelephony>* _aidl_return) {
|
||||
if (!mTelephony) {
|
||||
mTelephony = ndk::SharedRefBase::make<Telephony>();
|
||||
}
|
||||
*_aidl_return = mTelephony.getPtr();
|
||||
LOG(DEBUG) << __func__ << ": returning instance of ITelephony: " << _aidl_return->get();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModulePrimary::createInputStream(const SinkMetadata& sinkMetadata,
|
||||
StreamContext&& context,
|
||||
const std::vector<MicrophoneInfo>& microphones,
|
||||
std::shared_ptr<StreamIn>* result) {
|
||||
return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
|
||||
microphones);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModulePrimary::createOutputStream(
|
||||
const SourceMetadata& sourceMetadata, StreamContext&& context,
|
||||
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
|
||||
return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
|
||||
offloadInfo);
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
@@ -110,10 +110,12 @@ void StreamWorkerCommonLogic::populateReply(StreamDescriptor::Reply* reply,
|
||||
if (isConnected) {
|
||||
reply->observable.frames = mFrameCount;
|
||||
reply->observable.timeNs = ::android::elapsedRealtimeNano();
|
||||
} else {
|
||||
reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
|
||||
reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
|
||||
if (auto status = mDriver->getPosition(&reply->observable); status == ::android::OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
reply->observable.frames = StreamDescriptor::Position::UNKNOWN;
|
||||
reply->observable.timeNs = StreamDescriptor::Position::UNKNOWN;
|
||||
}
|
||||
|
||||
void StreamWorkerCommonLogic::populateReplyWrongState(
|
||||
|
||||
154
audio/aidl/default/alsa/Mixer.cpp
Normal file
154
audio/aidl/default/alsa/Mixer.cpp
Normal 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
|
||||
82
audio/aidl/default/alsa/Mixer.h
Normal file
82
audio/aidl/default/alsa/Mixer.h
Normal 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
|
||||
67
audio/aidl/default/alsa/ModuleAlsa.cpp
Normal file
67
audio/aidl/default/alsa/ModuleAlsa.cpp
Normal 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
|
||||
135
audio/aidl/default/alsa/StreamAlsa.cpp
Normal file
135
audio/aidl/default/alsa/StreamAlsa.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 <audio_utils/clock.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;
|
||||
}
|
||||
|
||||
::android::status_t StreamAlsa::getPosition(StreamDescriptor::Position* position) {
|
||||
if (mAlsaDeviceProxies.empty()) {
|
||||
LOG(FATAL) << __func__ << ": no input devices";
|
||||
return ::android::NO_INIT;
|
||||
}
|
||||
if (mIsInput) {
|
||||
if (int ret = proxy_get_capture_position(mAlsaDeviceProxies[0].get(), &position->frames,
|
||||
&position->timeNs);
|
||||
ret != 0) {
|
||||
LOG(WARNING) << __func__ << ": failed to retrieve capture position: " << ret;
|
||||
return ::android::INVALID_OPERATION;
|
||||
}
|
||||
} else {
|
||||
uint64_t hwFrames;
|
||||
struct timespec timestamp;
|
||||
if (int ret = proxy_get_presentation_position(mAlsaDeviceProxies[0].get(), &hwFrames,
|
||||
×tamp);
|
||||
ret == 0) {
|
||||
if (hwFrames > std::numeric_limits<int64_t>::max()) {
|
||||
hwFrames -= std::numeric_limits<int64_t>::max();
|
||||
}
|
||||
position->frames = static_cast<int64_t>(hwFrames);
|
||||
position->timeNs = audio_utils_ns_from_timespec(×tamp);
|
||||
} else {
|
||||
LOG(WARNING) << __func__ << ": failed to retrieve presentation position: " << ret;
|
||||
return ::android::INVALID_OPERATION;
|
||||
}
|
||||
}
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
void StreamAlsa::shutdown() {
|
||||
mAlsaDeviceProxies.clear();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
293
audio/aidl/default/alsa/Utils.cpp
Normal file
293
audio/aidl/default/alsa/Utils.cpp
Normal 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
|
||||
70
audio/aidl/default/alsa/Utils.h
Normal file
70
audio/aidl/default/alsa/Utils.h
Normal 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
|
||||
@@ -9,6 +9,11 @@
|
||||
<version>1</version>
|
||||
<fqname>IModule/r_submix</fqname>
|
||||
</hal>
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.audio.core</name>
|
||||
<version>1</version>
|
||||
<fqname>IModule/stub</fqname>
|
||||
</hal>
|
||||
<hal format="aidl">
|
||||
<name>android.hardware.audio.core</name>
|
||||
<version>1</version>
|
||||
|
||||
48
audio/aidl/default/include/core-impl/ChildInterface.h
Normal file
48
audio/aidl/default/include/core-impl/ChildInterface.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <android/binder_auto_utils.h>
|
||||
#include <android/binder_ibinder_platform.h>
|
||||
#include <system/thread_defs.h>
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
// Helper used for interfaces that require a persistent instance. We hold them via a strong
|
||||
// pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'.
|
||||
template <class C>
|
||||
struct ChildInterface : private std::pair<std::shared_ptr<C>, ndk::SpAIBinder> {
|
||||
ChildInterface() = default;
|
||||
ChildInterface& operator=(const std::shared_ptr<C>& c) {
|
||||
return operator=(std::shared_ptr<C>(c));
|
||||
}
|
||||
ChildInterface& operator=(std::shared_ptr<C>&& c) {
|
||||
this->first = std::move(c);
|
||||
this->second = this->first->asBinder();
|
||||
AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
|
||||
return *this;
|
||||
}
|
||||
explicit operator bool() const { return !!this->first; }
|
||||
C& operator*() const { return *(this->first); }
|
||||
C* operator->() const { return this->first; }
|
||||
std::shared_ptr<C> getPtr() const { return this->first; }
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
@@ -45,6 +45,7 @@ struct Configuration {
|
||||
|
||||
std::unique_ptr<Configuration> getPrimaryConfiguration();
|
||||
std::unique_ptr<Configuration> getRSubmixConfiguration();
|
||||
std::unique_ptr<Configuration> getStubConfiguration();
|
||||
std::unique_ptr<Configuration> getUsbConfiguration();
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core::internal
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include <aidl/android/hardware/audio/core/BnModule.h>
|
||||
|
||||
#include "core-impl/ChildInterface.h"
|
||||
#include "core-impl/Configuration.h"
|
||||
#include "core-impl/Stream.h"
|
||||
|
||||
@@ -31,7 +33,7 @@ class Module : public BnModule {
|
||||
public:
|
||||
// This value is used for all AudioPatches and reported by all streams.
|
||||
static constexpr int32_t kLatencyMs = 10;
|
||||
enum Type : int { DEFAULT, R_SUBMIX, USB };
|
||||
enum Type : int { DEFAULT, R_SUBMIX, STUB, USB };
|
||||
|
||||
static std::shared_ptr<Module> createInstance(Type type);
|
||||
|
||||
@@ -132,26 +134,6 @@ class Module : public BnModule {
|
||||
bool forceTransientBurst = false;
|
||||
bool forceSynchronousDrain = false;
|
||||
};
|
||||
// Helper used for interfaces that require a persistent instance. We hold them via a strong
|
||||
// pointer. The binder token is retained for a call to 'setMinSchedulerPolicy'.
|
||||
template <class C>
|
||||
struct ChildInterface : private std::pair<std::shared_ptr<C>, ndk::SpAIBinder> {
|
||||
ChildInterface() {}
|
||||
ChildInterface& operator=(const std::shared_ptr<C>& c) {
|
||||
return operator=(std::shared_ptr<C>(c));
|
||||
}
|
||||
ChildInterface& operator=(std::shared_ptr<C>&& c) {
|
||||
this->first = std::move(c);
|
||||
this->second = this->first->asBinder();
|
||||
AIBinder_setMinSchedulerPolicy(this->second.get(), SCHED_NORMAL,
|
||||
ANDROID_PRIORITY_AUDIO);
|
||||
return *this;
|
||||
}
|
||||
explicit operator bool() const { return !!this->first; }
|
||||
C& operator*() const { return *(this->first); }
|
||||
C* operator->() const { return this->first; }
|
||||
std::shared_ptr<C> getPtr() const { return this->first; }
|
||||
};
|
||||
// ids of device ports created at runtime via 'connectExternalDevice'.
|
||||
// Also stores a list of ids of mix ports with dynamic profiles that were populated from
|
||||
// the connected port. This list can be empty, thus an int->int multimap can't be used.
|
||||
@@ -164,10 +146,6 @@ class Module : public BnModule {
|
||||
std::unique_ptr<internal::Configuration> mConfig;
|
||||
ModuleDebug mDebug;
|
||||
VendorDebug mVendorDebug;
|
||||
ChildInterface<ITelephony> mTelephony;
|
||||
ChildInterface<IBluetooth> mBluetooth;
|
||||
ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
|
||||
ChildInterface<IBluetoothLe> mBluetoothLe;
|
||||
ConnectedDevicePorts mConnectedDevicePorts;
|
||||
Streams mStreams;
|
||||
Patches mPatches;
|
||||
@@ -184,13 +162,13 @@ class Module : public BnModule {
|
||||
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
|
||||
StreamContext&& context,
|
||||
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
|
||||
std::shared_ptr<StreamIn>* result);
|
||||
std::shared_ptr<StreamIn>* result) = 0;
|
||||
virtual ndk::ScopedAStatus createOutputStream(
|
||||
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
|
||||
StreamContext&& context,
|
||||
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
|
||||
offloadInfo,
|
||||
std::shared_ptr<StreamOut>* result);
|
||||
std::shared_ptr<StreamOut>* result) = 0;
|
||||
// If the module is unable to populate the connected device port correctly, the returned error
|
||||
// code must correspond to the errors of `IModule.connectedExternalDevice` method.
|
||||
virtual ndk::ScopedAStatus populateConnectedDevicePort(
|
||||
@@ -232,4 +210,6 @@ class Module : public BnModule {
|
||||
const AudioPatch& newPatch);
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, Module::Type t);
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
|
||||
38
audio/aidl/default/include/core-impl/ModuleAlsa.h
Normal file
38
audio/aidl/default/include/core-impl/ModuleAlsa.h
Normal 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
|
||||
46
audio/aidl/default/include/core-impl/ModulePrimary.h
Normal file
46
audio/aidl/default/include/core-impl/ModulePrimary.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
class ModulePrimary final : public Module {
|
||||
public:
|
||||
ModulePrimary() : Module(Type::DEFAULT) {}
|
||||
|
||||
protected:
|
||||
ndk::ScopedAStatus getTelephony(std::shared_ptr<ITelephony>* _aidl_return) override;
|
||||
|
||||
ndk::ScopedAStatus createInputStream(
|
||||
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
|
||||
StreamContext&& context,
|
||||
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
|
||||
std::shared_ptr<StreamIn>* result) override;
|
||||
ndk::ScopedAStatus createOutputStream(
|
||||
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
|
||||
StreamContext&& context,
|
||||
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
|
||||
offloadInfo,
|
||||
std::shared_ptr<StreamOut>* result) override;
|
||||
|
||||
private:
|
||||
ChildInterface<ITelephony> mTelephony;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
@@ -22,7 +22,7 @@ namespace aidl::android::hardware::audio::core {
|
||||
|
||||
class ModuleRemoteSubmix : public Module {
|
||||
public:
|
||||
explicit ModuleRemoteSubmix(Module::Type type) : Module(type) {}
|
||||
ModuleRemoteSubmix() : Module(Type::R_SUBMIX) {}
|
||||
|
||||
private:
|
||||
// IModule interfaces
|
||||
|
||||
50
audio/aidl/default/include/core-impl/ModuleStub.h
Normal file
50
audio/aidl/default/include/core-impl/ModuleStub.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
class ModuleStub final : public Module {
|
||||
public:
|
||||
ModuleStub() : Module(Type::STUB) {}
|
||||
|
||||
protected:
|
||||
ndk::ScopedAStatus getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) override;
|
||||
ndk::ScopedAStatus getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) override;
|
||||
ndk::ScopedAStatus getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) override;
|
||||
|
||||
ndk::ScopedAStatus createInputStream(
|
||||
const ::aidl::android::hardware::audio::common::SinkMetadata& sinkMetadata,
|
||||
StreamContext&& context,
|
||||
const std::vector<::aidl::android::media::audio::common::MicrophoneInfo>& microphones,
|
||||
std::shared_ptr<StreamIn>* result) override;
|
||||
ndk::ScopedAStatus createOutputStream(
|
||||
const ::aidl::android::hardware::audio::common::SourceMetadata& sourceMetadata,
|
||||
StreamContext&& context,
|
||||
const std::optional<::aidl::android::media::audio::common::AudioOffloadInfo>&
|
||||
offloadInfo,
|
||||
std::shared_ptr<StreamOut>* result) override;
|
||||
|
||||
private:
|
||||
ChildInterface<IBluetooth> mBluetooth;
|
||||
ChildInterface<IBluetoothA2dp> mBluetoothA2dp;
|
||||
ChildInterface<IBluetoothLe> mBluetoothLe;
|
||||
};
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
@@ -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) {}
|
||||
ModuleUsb() : ModuleAlsa(Type::USB) {}
|
||||
|
||||
private:
|
||||
// IModule interfaces
|
||||
|
||||
@@ -184,6 +184,12 @@ struct DriverInterface {
|
||||
virtual ::android::status_t start() = 0;
|
||||
virtual ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
|
||||
int32_t* latencyMs) = 0;
|
||||
// No need to implement 'getPosition' unless the driver can provide more precise
|
||||
// data than just total frame count. For example, the driver may correctly account
|
||||
// for any intermediate buffers.
|
||||
virtual ::android::status_t getPosition(StreamDescriptor::Position* /*position*/) {
|
||||
return ::android::OK;
|
||||
}
|
||||
virtual void shutdown() = 0; // This function is only called once.
|
||||
};
|
||||
|
||||
|
||||
55
audio/aidl/default/include/core-impl/StreamAlsa.h
Normal file
55
audio/aidl/default/include/core-impl/StreamAlsa.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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;
|
||||
::android::status_t getPosition(StreamDescriptor::Position* position) 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
|
||||
@@ -39,6 +39,7 @@ class StreamRemoteSubmix : public StreamCommonImpl {
|
||||
::android::status_t start() override;
|
||||
::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount,
|
||||
int32_t* latencyMs) override;
|
||||
::android::status_t getPosition(StreamDescriptor::Position* position) override;
|
||||
void shutdown() override;
|
||||
|
||||
// Overridden methods of 'StreamCommonImpl', called on a Binder thread.
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -16,20 +16,21 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android/binder_ibinder_platform.h>
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/binder_process.h>
|
||||
|
||||
#include "core-impl/Config.h"
|
||||
#include "core-impl/Module.h"
|
||||
#include "core-impl/ModuleUsb.h"
|
||||
|
||||
using aidl::android::hardware::audio::core::Config;
|
||||
using aidl::android::hardware::audio::core::Module;
|
||||
using aidl::android::hardware::audio::core::ModuleUsb;
|
||||
|
||||
int main() {
|
||||
// Random values are used in the implementation.
|
||||
@@ -52,18 +53,19 @@ int main() {
|
||||
CHECK_EQ(STATUS_OK, status);
|
||||
|
||||
// Make modules
|
||||
auto createModule = [](Module::Type type, const std::string& instance) {
|
||||
auto createModule = [](Module::Type type) {
|
||||
auto module = Module::createInstance(type);
|
||||
ndk::SpAIBinder moduleBinder = module->asBinder();
|
||||
const std::string moduleName = std::string(Module::descriptor).append("/").append(instance);
|
||||
std::stringstream moduleName;
|
||||
moduleName << Module::descriptor << "/" << type;
|
||||
AIBinder_setMinSchedulerPolicy(moduleBinder.get(), SCHED_NORMAL, ANDROID_PRIORITY_AUDIO);
|
||||
binder_status_t status = AServiceManager_addService(moduleBinder.get(), moduleName.c_str());
|
||||
binder_status_t status =
|
||||
AServiceManager_addService(moduleBinder.get(), moduleName.str().c_str());
|
||||
CHECK_EQ(STATUS_OK, status);
|
||||
return std::make_pair(module, moduleBinder);
|
||||
};
|
||||
auto modules = {createModule(Module::Type::DEFAULT, "default"),
|
||||
createModule(Module::Type::R_SUBMIX, "r_submix"),
|
||||
createModule(Module::Type::USB, "usb")};
|
||||
auto modules = {createModule(Module::Type::DEFAULT), createModule(Module::Type::R_SUBMIX),
|
||||
createModule(Module::Type::USB), createModule(Module::Type::STUB)};
|
||||
(void)modules;
|
||||
|
||||
ABinderProcess_joinThreadPool();
|
||||
|
||||
@@ -179,6 +179,27 @@ void StreamRemoteSubmix::shutdown() {
|
||||
: outWrite(buffer, frameCount, actualFrameCount));
|
||||
}
|
||||
|
||||
::android::status_t StreamRemoteSubmix::getPosition(StreamDescriptor::Position* position) {
|
||||
sp<MonoPipeReader> source = mCurrentRoute->getSource();
|
||||
if (source == nullptr) {
|
||||
return ::android::NO_INIT;
|
||||
}
|
||||
const ssize_t framesInPipe = source->availableToRead();
|
||||
if (framesInPipe < 0) {
|
||||
return ::android::INVALID_OPERATION;
|
||||
}
|
||||
if (mIsInput) {
|
||||
position->frames += framesInPipe;
|
||||
} else {
|
||||
if (position->frames > framesInPipe) {
|
||||
position->frames -= framesInPipe;
|
||||
} else {
|
||||
position->frames = 0;
|
||||
}
|
||||
}
|
||||
return ::android::OK;
|
||||
}
|
||||
|
||||
// Calculate the maximum size of the pipe buffer in frames for the specified stream.
|
||||
size_t StreamRemoteSubmix::getStreamPipeSizeInFrames() {
|
||||
auto pipeConfig = mCurrentRoute->mPipeConfig;
|
||||
|
||||
78
audio/aidl/default/stub/ModuleStub.cpp
Normal file
78
audio/aidl/default/stub/ModuleStub.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 <vector>
|
||||
|
||||
#define LOG_TAG "AHAL_ModuleStub"
|
||||
#include <Utils.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include "core-impl/Bluetooth.h"
|
||||
#include "core-impl/ModuleStub.h"
|
||||
#include "core-impl/StreamStub.h"
|
||||
|
||||
using aidl::android::hardware::audio::common::SinkMetadata;
|
||||
using aidl::android::hardware::audio::common::SourceMetadata;
|
||||
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::MicrophoneInfo;
|
||||
|
||||
namespace aidl::android::hardware::audio::core {
|
||||
|
||||
ndk::ScopedAStatus ModuleStub::getBluetooth(std::shared_ptr<IBluetooth>* _aidl_return) {
|
||||
if (!mBluetooth) {
|
||||
mBluetooth = ndk::SharedRefBase::make<Bluetooth>();
|
||||
}
|
||||
*_aidl_return = mBluetooth.getPtr();
|
||||
LOG(DEBUG) << __func__ << ": returning instance of IBluetooth: " << _aidl_return->get();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleStub::getBluetoothA2dp(std::shared_ptr<IBluetoothA2dp>* _aidl_return) {
|
||||
if (!mBluetoothA2dp) {
|
||||
mBluetoothA2dp = ndk::SharedRefBase::make<BluetoothA2dp>();
|
||||
}
|
||||
*_aidl_return = mBluetoothA2dp.getPtr();
|
||||
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothA2dp: " << _aidl_return->get();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleStub::getBluetoothLe(std::shared_ptr<IBluetoothLe>* _aidl_return) {
|
||||
if (!mBluetoothLe) {
|
||||
mBluetoothLe = ndk::SharedRefBase::make<BluetoothLe>();
|
||||
}
|
||||
*_aidl_return = mBluetoothLe.getPtr();
|
||||
LOG(DEBUG) << __func__ << ": returning instance of IBluetoothLe: " << _aidl_return->get();
|
||||
return ndk::ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleStub::createInputStream(const SinkMetadata& sinkMetadata,
|
||||
StreamContext&& context,
|
||||
const std::vector<MicrophoneInfo>& microphones,
|
||||
std::shared_ptr<StreamIn>* result) {
|
||||
return createStreamInstance<StreamInStub>(result, sinkMetadata, std::move(context),
|
||||
microphones);
|
||||
}
|
||||
|
||||
ndk::ScopedAStatus ModuleStub::createOutputStream(
|
||||
const SourceMetadata& sourceMetadata, StreamContext&& context,
|
||||
const std::optional<AudioOffloadInfo>& offloadInfo, std::shared_ptr<StreamOut>* result) {
|
||||
return createStreamInstance<StreamOutStub>(result, sourceMetadata, std::move(context),
|
||||
offloadInfo);
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::audio::core
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user