blob: 8f8a82a47b5e41bc59bf75748161ca6667c35284 [file]
/*
* Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
* www.ehima.com
*
* 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.
*/
/*
* This file contains definitions for Basic Audio Profile / Audio Stream Control
* and Published Audio Capabilities definitions, structures etc.
*/
#pragma once
#include <bluetooth/log.h>
#include <stdint.h>
#include <bitset>
#include <map>
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include "bluetooth/uuid.h"
#include "bta/include/bta_le_audio_uuids.h"
#include "osi/include/alarm.h"
#include "stack/include/bt_types.h"
#include "stack/include/btm_iso_api_types.h"
namespace bluetooth::le_audio {
#define UINT8_TO_VEC_UINT8(u8) \
std::vector<uint8_t> { u8 }
#define UINT16_TO_VEC_UINT8(u16) \
std::vector<uint8_t>((uint8_t*)&u16, (uint8_t*)&u16 + sizeof(uint16_t))
#define UINT32_TO_VEC_UINT8(u32) \
std::vector<uint8_t>((uint8_t*)&u32, (uint8_t*)&u32 + sizeof(uint32_t))
#define VEC_UINT8_TO_UINT8(vec) vec.data()[0]
#define VEC_UINT8_TO_UINT16(vec) ((vec.data()[1] << 8) + vec.data()[0])
#define OFF_VEC_UINT8_TO_UINT16(vec, off) \
((vec.data()[1 + off] << 8) + vec.data()[0 + off])
#define VEC_UINT8_TO_UINT32(vec) \
((vec.data()[3] << 24) + (vec.data()[2] << 16) + (vec.data()[1] << 8) + \
vec.data()[0])
enum class DsaMode { DISABLED = 0, ACL, ISO_SW, ISO_HW };
typedef std::vector<DsaMode> DsaModes;
namespace uuid {
/* CAP service
* This service is used to identify peer role (which we are not using for now)
* and to wrap CSIS service as this is required to understand the context of the
* CSIS
*/
static const bluetooth::Uuid kCapServiceUuid =
bluetooth::Uuid::From16Bit(UUID_COMMON_AUDIO_SERVICE);
/* Assigned numbers for attributes */
static const bluetooth::Uuid kPublishedAudioCapabilityServiceUuid =
bluetooth::Uuid::From16Bit(0x1850);
static const bluetooth::Uuid kAudioStreamControlServiceUuid =
bluetooth::Uuid::From16Bit(0x184E);
static const bluetooth::Uuid kTelephonyMediaAudioServiceUuid =
bluetooth::Uuid::From16Bit(0x1855);
/* Published Audio Capabilities Service Characteristics */
static const bluetooth::Uuid kSinkPublishedAudioCapabilityCharacteristicUuid =
bluetooth::Uuid::From16Bit(0x2BC9);
static const bluetooth::Uuid kSourcePublishedAudioCapabilityCharacteristicUuid =
bluetooth::Uuid::From16Bit(0x2BCB);
static const bluetooth::Uuid kSinkAudioLocationCharacteristicUuid =
bluetooth::Uuid::From16Bit(0x2BCA);
static const bluetooth::Uuid kSourceAudioLocationCharacteristicUuid =
bluetooth::Uuid::From16Bit(0x2BCC);
/* Audio Stream Control Service Characteristics */
static const bluetooth::Uuid kAudioContextAvailabilityCharacteristicUuid =
bluetooth::Uuid::From16Bit(0x2BCD);
static const bluetooth::Uuid kAudioSupportedContextCharacteristicUuid =
bluetooth::Uuid::From16Bit(0x2BCE);
/* Audio Stream Control Service Characteristics */
static const bluetooth::Uuid kSinkAudioStreamEndpointUuid =
bluetooth::Uuid::From16Bit(0x2BC4);
static const bluetooth::Uuid kSourceAudioStreamEndpointUuid =
bluetooth::Uuid::From16Bit(0x2BC5);
static const bluetooth::Uuid
kAudioStreamEndpointControlPointCharacteristicUuid =
bluetooth::Uuid::From16Bit(0x2BC6);
/* Telephony and Media Audio Service Characteristics */
static const bluetooth::Uuid kTelephonyMediaAudioProfileRoleCharacteristicUuid =
bluetooth::Uuid::From16Bit(0x2B51);
} // namespace uuid
namespace codec_spec_conf {
/* LTV Types */
constexpr uint8_t kLeAudioLtvTypeSamplingFreq = 0x01;
constexpr uint8_t kLeAudioLtvTypeFrameDuration = 0x02;
constexpr uint8_t kLeAudioLtvTypeAudioChannelAllocation = 0x03;
constexpr uint8_t kLeAudioLtvTypeOctetsPerCodecFrame = 0x04;
constexpr uint8_t kLeAudioLtvTypeCodecFrameBlocksPerSdu = 0x05;
/* Sampling Frequencies */
constexpr uint8_t kLeAudioSamplingFreq8000Hz = 0x01;
constexpr uint8_t kLeAudioSamplingFreq11025Hz = 0x02;
constexpr uint8_t kLeAudioSamplingFreq16000Hz = 0x03;
constexpr uint8_t kLeAudioSamplingFreq22050Hz = 0x04;
constexpr uint8_t kLeAudioSamplingFreq24000Hz = 0x05;
constexpr uint8_t kLeAudioSamplingFreq32000Hz = 0x06;
constexpr uint8_t kLeAudioSamplingFreq44100Hz = 0x07;
constexpr uint8_t kLeAudioSamplingFreq48000Hz = 0x08;
constexpr uint8_t kLeAudioSamplingFreq88200Hz = 0x09;
constexpr uint8_t kLeAudioSamplingFreq96000Hz = 0x0A;
constexpr uint8_t kLeAudioSamplingFreq176400Hz = 0x0B;
constexpr uint8_t kLeAudioSamplingFreq192000Hz = 0x0C;
constexpr uint8_t kLeAudioSamplingFreq384000Hz = 0x0D;
/* Frame Durations */
constexpr uint8_t kLeAudioCodecFrameDur7500us = 0x00;
constexpr uint8_t kLeAudioCodecFrameDur10000us = 0x01;
/* Audio Allocations */
constexpr uint32_t kLeAudioLocationNotAllowed = 0x00000000;
constexpr uint32_t kLeAudioLocationFrontLeft = 0x00000001;
constexpr uint32_t kLeAudioLocationFrontRight = 0x00000002;
constexpr uint32_t kLeAudioLocationFrontCenter = 0x00000004;
constexpr uint32_t kLeAudioLocationLowFreqEffects1 = 0x00000008;
constexpr uint32_t kLeAudioLocationBackLeft = 0x00000010;
constexpr uint32_t kLeAudioLocationBackRight = 0x00000020;
constexpr uint32_t kLeAudioLocationFrontLeftOfCenter = 0x00000040;
constexpr uint32_t kLeAudioLocationFrontRightOfCenter = 0x00000080;
constexpr uint32_t kLeAudioLocationBackCenter = 0x00000100;
constexpr uint32_t kLeAudioLocationLowFreqEffects2 = 0x00000200;
constexpr uint32_t kLeAudioLocationSideLeft = 0x00000400;
constexpr uint32_t kLeAudioLocationSideRight = 0x00000800;
constexpr uint32_t kLeAudioLocationTopFrontLeft = 0x00001000;
constexpr uint32_t kLeAudioLocationTopFrontRight = 0x00002000;
constexpr uint32_t kLeAudioLocationTopFrontCenter = 0x00004000;
constexpr uint32_t kLeAudioLocationTopCenter = 0x00008000;
constexpr uint32_t kLeAudioLocationTopBackLeft = 0x00010000;
constexpr uint32_t kLeAudioLocationTopBackRight = 0x00020000;
constexpr uint32_t kLeAudioLocationTopSideLeft = 0x00040000;
constexpr uint32_t kLeAudioLocationTopSideRight = 0x00080000;
constexpr uint32_t kLeAudioLocationTopBackCenter = 0x00100000;
constexpr uint32_t kLeAudioLocationBottomFrontCenter = 0x00200000;
constexpr uint32_t kLeAudioLocationBottomFrontLeft = 0x00400000;
constexpr uint32_t kLeAudioLocationBottomFrontRight = 0x00800000;
constexpr uint32_t kLeAudioLocationFrontLeftWide = 0x01000000;
constexpr uint32_t kLeAudioLocationFrontRightWide = 0x02000000;
constexpr uint32_t kLeAudioLocationLeftSurround = 0x04000000;
constexpr uint32_t kLeAudioLocationRightSurround = 0x08000000;
constexpr uint32_t kLeAudioLocationAnyLeft =
kLeAudioLocationFrontLeft | kLeAudioLocationBackLeft |
kLeAudioLocationFrontLeftOfCenter | kLeAudioLocationSideLeft |
kLeAudioLocationTopFrontLeft | kLeAudioLocationTopBackLeft |
kLeAudioLocationTopSideLeft | kLeAudioLocationBottomFrontLeft |
kLeAudioLocationFrontLeftWide | kLeAudioLocationLeftSurround;
constexpr uint32_t kLeAudioLocationAnyRight =
kLeAudioLocationFrontRight | kLeAudioLocationBackRight |
kLeAudioLocationFrontRightOfCenter | kLeAudioLocationSideRight |
kLeAudioLocationTopFrontRight | kLeAudioLocationTopBackRight |
kLeAudioLocationTopSideRight | kLeAudioLocationBottomFrontRight |
kLeAudioLocationFrontRightWide | kLeAudioLocationRightSurround;
constexpr uint32_t kLeAudioLocationStereo =
kLeAudioLocationFrontLeft | kLeAudioLocationFrontRight;
/* Octets Per Frame */
constexpr uint16_t kLeAudioCodecFrameLen30 = 30;
constexpr uint16_t kLeAudioCodecFrameLen40 = 40;
constexpr uint16_t kLeAudioCodecFrameLen60 = 60;
constexpr uint16_t kLeAudioCodecFrameLen80 = 80;
constexpr uint16_t kLeAudioCodecFrameLen100 = 100;
constexpr uint16_t kLeAudioCodecFrameLen120 = 120;
} // namespace codec_spec_conf
constexpr uint8_t kInvalidCisId = 0xFF;
namespace codec_spec_caps {
uint16_t constexpr SamplingFreqConfig2Capability(uint8_t conf) {
return (1 << (conf - 1));
}
uint8_t constexpr FrameDurationConfig2Capability(uint8_t conf) {
return (0x01 << (conf));
}
/* LTV Types - same values as in Codec Specific Configurations but 0x03 is
* named differently.
*/
constexpr uint8_t kLeAudioLtvTypeSupportedSamplingFrequencies =
codec_spec_conf::kLeAudioLtvTypeSamplingFreq;
constexpr uint8_t kLeAudioLtvTypeSupportedFrameDurations =
codec_spec_conf::kLeAudioLtvTypeFrameDuration;
constexpr uint8_t kLeAudioLtvTypeSupportedAudioChannelCounts =
codec_spec_conf::kLeAudioLtvTypeAudioChannelAllocation;
constexpr uint8_t kLeAudioLtvTypeSupportedOctetsPerCodecFrame =
codec_spec_conf::kLeAudioLtvTypeOctetsPerCodecFrame;
constexpr uint8_t kLeAudioLtvTypeSupportedMaxCodecFramesPerSdu =
codec_spec_conf::kLeAudioLtvTypeCodecFrameBlocksPerSdu;
/* Sampling Frequencies */
constexpr uint16_t kLeAudioSamplingFreq8000Hz =
SamplingFreqConfig2Capability(codec_spec_conf::kLeAudioSamplingFreq8000Hz);
constexpr uint16_t kLeAudioSamplingFreq11025Hz =
SamplingFreqConfig2Capability(codec_spec_conf::kLeAudioSamplingFreq11025Hz);
constexpr uint16_t kLeAudioSamplingFreq16000Hz =
SamplingFreqConfig2Capability(codec_spec_conf::kLeAudioSamplingFreq16000Hz);
constexpr uint16_t kLeAudioSamplingFreq22050Hz =
SamplingFreqConfig2Capability(codec_spec_conf::kLeAudioSamplingFreq22050Hz);
constexpr uint16_t kLeAudioSamplingFreq24000Hz =
SamplingFreqConfig2Capability(codec_spec_conf::kLeAudioSamplingFreq24000Hz);
constexpr uint16_t kLeAudioSamplingFreq32000Hz =
SamplingFreqConfig2Capability(codec_spec_conf::kLeAudioSamplingFreq32000Hz);
constexpr uint16_t kLeAudioSamplingFreq44100Hz =
SamplingFreqConfig2Capability(codec_spec_conf::kLeAudioSamplingFreq44100Hz);
constexpr uint16_t kLeAudioSamplingFreq48000Hz =
SamplingFreqConfig2Capability(codec_spec_conf::kLeAudioSamplingFreq48000Hz);
constexpr uint16_t kLeAudioSamplingFreq88200Hz =
SamplingFreqConfig2Capability(codec_spec_conf::kLeAudioSamplingFreq88200Hz);
constexpr uint16_t kLeAudioSamplingFreq96000Hz =
SamplingFreqConfig2Capability(codec_spec_conf::kLeAudioSamplingFreq96000Hz);
constexpr uint16_t kLeAudioSamplingFreq176400Hz = SamplingFreqConfig2Capability(
codec_spec_conf::kLeAudioSamplingFreq176400Hz);
constexpr uint16_t kLeAudioSamplingFreq192000Hz = SamplingFreqConfig2Capability(
codec_spec_conf::kLeAudioSamplingFreq192000Hz);
constexpr uint16_t kLeAudioSamplingFreq384000Hz = SamplingFreqConfig2Capability(
codec_spec_conf::kLeAudioSamplingFreq384000Hz);
/* Frame Durations */
constexpr uint8_t kLeAudioCodecFrameDur7500us = FrameDurationConfig2Capability(
codec_spec_conf::kLeAudioCodecFrameDur7500us);
constexpr uint8_t kLeAudioCodecFrameDur10000us = FrameDurationConfig2Capability(
codec_spec_conf::kLeAudioCodecFrameDur10000us);
constexpr uint8_t kLeAudioCodecFrameDurPrefer7500us = 0x10;
constexpr uint8_t kLeAudioCodecFrameDurPrefer10000us = 0x20;
/* Audio Channel Counts */
/* Each bit represents support for additional channel: bit 0 - one channel,
* bit 1 - two, bit 3 - four channels. Multiple bits can be enabled at once.
*/
constexpr uint8_t kLeAudioCodecChannelCountNone = 0x00;
constexpr uint8_t kLeAudioCodecChannelCountSingleChannel = 0x01;
constexpr uint8_t kLeAudioCodecChannelCountTwoChannel = 0x02;
constexpr uint8_t kLeAudioCodecChannelCountThreeChannel = 0x04;
constexpr uint8_t kLeAudioCodecChannelCountFourChannel = 0x08;
constexpr uint8_t kLeAudioCodecChannelCountFiveChannel = 0x10;
constexpr uint8_t kLeAudioCodecChannelCountSixChannel = 0x20;
constexpr uint8_t kLeAudioCodecChannelCountSevenChannel = 0x40;
constexpr uint8_t kLeAudioCodecChannelCountEightChannel = 0x80;
/* Octets Per Frame */
constexpr uint16_t kLeAudioCodecFrameLen30 =
codec_spec_conf::kLeAudioCodecFrameLen30;
constexpr uint16_t kLeAudioCodecFrameLen40 =
codec_spec_conf::kLeAudioCodecFrameLen40;
constexpr uint16_t kLeAudioCodecFrameLen60 =
codec_spec_conf::kLeAudioCodecFrameLen60;
constexpr uint16_t kLeAudioCodecFrameLen80 =
codec_spec_conf::kLeAudioCodecFrameLen80;
constexpr uint16_t kLeAudioCodecFrameLen120 =
codec_spec_conf::kLeAudioCodecFrameLen120;
}; // namespace codec_spec_caps
namespace types {
constexpr uint8_t kLeAudioCodingFormatLC3 = bluetooth::hci::kIsoCodingFormatLc3;
constexpr uint8_t kLeAudioCodingFormatVendorSpecific =
bluetooth::hci::kIsoCodingFormatVendorSpecific;
constexpr uint16_t kLeAudioVendorCompanyIdUndefined = 0x00;
constexpr uint16_t kLeAudioVendorCodecIdUndefined = 0x00;
constexpr uint16_t kLeAudioVendorCompanyIdGoogle = 0x00E0;
constexpr uint16_t kLeAudioVendorCodecIdHeadtracking = 0x0001;
/* Metadata types from Assigned Numbers */
constexpr uint8_t kLeAudioMetadataTypePreferredAudioContext = 0x01;
constexpr uint8_t kLeAudioMetadataTypeStreamingAudioContext = 0x02;
constexpr uint8_t kLeAudioMetadataTypeProgramInfo = 0x03;
constexpr uint8_t kLeAudioMetadataTypeLanguage = 0x04;
constexpr uint8_t kLeAudioMetadataTypeCcidList = 0x05;
constexpr uint8_t kLeAudioMetadataTypeparentalRating = 0x06;
constexpr uint8_t kLeAudioMetadataTypeProgramInfoUri = 0x07;
constexpr uint8_t kLeAudioMetadataTypeAudioActiveState = 0x08;
constexpr uint8_t kLeAudioMetadataTypeBroadcastAudioImmediateRenderingFlag =
0x09;
constexpr uint8_t kLeAudioMetadataTypeExtendedMetadata = 0xFE;
constexpr uint8_t kLeAudioMetadataTypeVendorSpecific = 0xFF;
constexpr uint8_t kLeAudioMetadataTypeLen = 1;
constexpr uint8_t kLeAudioMetadataLenLen = 1;
constexpr uint8_t kLeAudioMetadataStreamingAudioContextLen = 2;
/* Android Headtracker Codec metadata */
constexpr uint8_t kLeAudioMetadataHeadtrackerTransportLen = 1;
constexpr uint8_t kLeAudioMetadataHeadtrackerTransportVal = 1;
constexpr uint8_t kLeAudioMetadataHeadtrackerTransportLeAcl = 1;
constexpr uint8_t kLeAudioMetadataHeadtrackerTransportLeIso = 2;
/* CSIS Types */
constexpr uint8_t kDefaultScanDurationS = 5;
constexpr uint8_t kDefaultCsisSetSize = 2;
constexpr uint8_t kLeAudioDirectionSink = 0x01;
constexpr uint8_t kLeAudioDirectionSource = 0x02;
constexpr uint8_t kLeAudioDirectionBoth =
kLeAudioDirectionSink | kLeAudioDirectionSource;
/* Audio stream config types */
constexpr uint8_t kFramingUnframedPduSupported = 0x00;
constexpr uint8_t kFramingUnframedPduUnsupported = 0x01;
constexpr uint8_t kTargetLatencyUndefined = 0x00;
constexpr uint8_t kTargetLatencyLower = 0x01;
constexpr uint8_t kTargetLatencyBalancedLatencyReliability = 0x02;
constexpr uint8_t kTargetLatencyHigherReliability = 0x03;
constexpr uint8_t kTargetPhyUndefined = 0x00;
constexpr uint8_t kTargetPhy1M = 0x01;
constexpr uint8_t kTargetPhy2M = 0x02;
constexpr uint8_t kTargetPhyCoded = 0x03;
constexpr uint32_t kPresDelayNoPreference = 0x00000000;
constexpr uint16_t kMaxTransportLatencyMin = 0x0005;
constexpr uint16_t kMaxTransportLatencyMax = 0x0FA0;
enum class CigState : uint8_t { NONE, CREATING, CREATED, REMOVING, RECOVERING };
/* ASE states according to BAP defined state machine states */
enum class AseState : uint8_t {
BTA_LE_AUDIO_ASE_STATE_IDLE = 0x00,
BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED = 0x01,
BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED = 0x02,
BTA_LE_AUDIO_ASE_STATE_ENABLING = 0x03,
BTA_LE_AUDIO_ASE_STATE_STREAMING = 0x04,
BTA_LE_AUDIO_ASE_STATE_DISABLING = 0x05,
BTA_LE_AUDIO_ASE_STATE_RELEASING = 0x06,
};
enum class CisState {
IDLE,
ASSIGNED,
CONNECTING,
CONNECTED,
DISCONNECTING,
};
enum class DataPathState {
IDLE,
CONFIGURING,
CONFIGURED,
REMOVING,
};
enum class CisType {
CIS_TYPE_BIDIRECTIONAL,
CIS_TYPE_UNIDIRECTIONAL_SINK,
CIS_TYPE_UNIDIRECTIONAL_SOURCE,
};
struct cis {
uint8_t id;
CisType type;
uint16_t conn_handle;
RawAddress addr;
};
enum class CodecLocation {
HOST,
ADSP,
CONTROLLER,
};
/* Context Types */
enum class LeAudioContextType : uint16_t {
UNINITIALIZED = 0x0000,
UNSPECIFIED = 0x0001,
CONVERSATIONAL = 0x0002,
MEDIA = 0x0004,
GAME = 0x0008,
INSTRUCTIONAL = 0x0010,
VOICEASSISTANTS = 0x0020,
LIVE = 0x0040,
SOUNDEFFECTS = 0x0080,
NOTIFICATIONS = 0x0100,
RINGTONE = 0x0200,
ALERTS = 0x0400,
EMERGENCYALARM = 0x0800,
RFU = 0x1000,
};
class AudioContexts {
using T = std::underlying_type<LeAudioContextType>::type;
T mValue;
public:
explicit constexpr AudioContexts()
: mValue(static_cast<T>(LeAudioContextType::UNINITIALIZED)) {}
explicit constexpr AudioContexts(const T& v) : mValue(v) {}
explicit constexpr AudioContexts(const LeAudioContextType& v)
: mValue(static_cast<T>(v)) {}
constexpr AudioContexts(const AudioContexts& other)
: mValue(static_cast<T>(other.value())) {}
constexpr T value() const { return mValue; }
T& value_ref() { return mValue; }
bool none() const {
return mValue == static_cast<T>(LeAudioContextType::UNINITIALIZED);
}
bool any() const { return !none(); }
void set(const LeAudioContextType& v) { mValue |= static_cast<T>(v); }
void set_all(const AudioContexts& v) { mValue |= v.value(); }
void unset(const LeAudioContextType& v) { mValue &= ~static_cast<T>(v); }
void unset_all(const AudioContexts& v) { mValue &= ~v.value(); }
bool test(const LeAudioContextType& v) const {
return (mValue & static_cast<T>(v)) != 0;
}
bool test_all(const AudioContexts& v) const {
return (mValue & v.value()) == v.value();
}
bool test_any(const AudioContexts& v) const {
return (mValue & v.value()) != 0;
}
void clear() { mValue = static_cast<T>(LeAudioContextType::UNINITIALIZED); }
std::string to_string() const;
AudioContexts& operator=(AudioContexts&& other) = default;
AudioContexts& operator=(const AudioContexts&) = default;
bool operator==(const AudioContexts& other) const {
return value() == other.value();
};
bool operator!=(const AudioContexts& other) const {
return value() != other.value();
};
constexpr AudioContexts operator~() const { return AudioContexts(~value()); }
};
AudioContexts operator|(std::underlying_type<LeAudioContextType>::type lhs,
const LeAudioContextType rhs);
AudioContexts& operator|=(AudioContexts& lhs, AudioContexts const& rhs);
AudioContexts& operator&=(AudioContexts& lhs, AudioContexts const& rhs);
constexpr AudioContexts operator^(const AudioContexts& lhs,
const AudioContexts& rhs) {
return AudioContexts(lhs.value() ^ rhs.value());
}
constexpr AudioContexts operator|(const AudioContexts& lhs,
const AudioContexts& rhs) {
return AudioContexts(lhs.value() | rhs.value());
}
constexpr AudioContexts operator&(const AudioContexts& lhs,
const AudioContexts& rhs) {
return AudioContexts(lhs.value() & rhs.value());
}
constexpr AudioContexts operator|(const LeAudioContextType& lhs,
const LeAudioContextType& rhs) {
using T = std::underlying_type<LeAudioContextType>::type;
return AudioContexts(static_cast<T>(lhs) | static_cast<T>(rhs));
}
constexpr AudioContexts operator|(const LeAudioContextType& lhs,
const AudioContexts& rhs) {
return AudioContexts(lhs) | rhs;
}
constexpr AudioContexts operator|(const AudioContexts& lhs,
const LeAudioContextType& rhs) {
return lhs | AudioContexts(rhs);
}
std::string ToHexString(const types::LeAudioContextType& value);
template <typename T>
struct BidirectionalPair {
T sink;
T source;
const T& get(uint8_t direction) const;
T& get(uint8_t direction);
};
template <typename T>
T get_bidirectional(BidirectionalPair<T> p);
template <typename T>
bool operator==(const types::BidirectionalPair<T>& lhs,
const types::BidirectionalPair<T>& rhs) {
return (lhs.sink == rhs.sink) && (lhs.source == rhs.source);
}
/* Configuration strategy */
enum class LeAudioConfigurationStrategy : uint8_t {
MONO_ONE_CIS_PER_DEVICE = 0x00, /* Common true wireless speakers */
STEREO_TWO_CISES_PER_DEVICE =
0x01, /* Requires 2 ASEs and 2 Audio Allocation for left/right */
STEREO_ONE_CIS_PER_DEVICE = 0x02, /* Requires channel count 2*/
RFU = 0x03,
};
constexpr LeAudioContextType kLeAudioContextAllTypesArray[] = {
LeAudioContextType::UNSPECIFIED, LeAudioContextType::CONVERSATIONAL,
LeAudioContextType::MEDIA, LeAudioContextType::GAME,
LeAudioContextType::INSTRUCTIONAL, LeAudioContextType::VOICEASSISTANTS,
LeAudioContextType::LIVE, LeAudioContextType::SOUNDEFFECTS,
LeAudioContextType::NOTIFICATIONS, LeAudioContextType::RINGTONE,
LeAudioContextType::ALERTS, LeAudioContextType::EMERGENCYALARM,
};
constexpr AudioContexts kLeAudioContextAllTypes =
LeAudioContextType::UNSPECIFIED | LeAudioContextType::CONVERSATIONAL |
LeAudioContextType::MEDIA | LeAudioContextType::GAME |
LeAudioContextType::INSTRUCTIONAL | LeAudioContextType::VOICEASSISTANTS |
LeAudioContextType::LIVE | LeAudioContextType::SOUNDEFFECTS |
LeAudioContextType::NOTIFICATIONS | LeAudioContextType::RINGTONE |
LeAudioContextType::ALERTS | LeAudioContextType::EMERGENCYALARM;
constexpr AudioContexts kLeAudioContextAllBidir =
LeAudioContextType::GAME | LeAudioContextType::LIVE |
LeAudioContextType::CONVERSATIONAL | LeAudioContextType::VOICEASSISTANTS;
constexpr AudioContexts kLeAudioContextAllRemoteSource =
LeAudioContextType::GAME | LeAudioContextType::LIVE |
LeAudioContextType::CONVERSATIONAL | LeAudioContextType::VOICEASSISTANTS;
constexpr AudioContexts kLeAudioContextAllRemoteSinkOnly =
LeAudioContextType::MEDIA | LeAudioContextType::INSTRUCTIONAL |
LeAudioContextType::SOUNDEFFECTS | LeAudioContextType::NOTIFICATIONS |
LeAudioContextType::RINGTONE | LeAudioContextType::ALERTS |
LeAudioContextType::EMERGENCYALARM;
/* Print formaters for LTV data */
std::string CodecCapabilitiesLtvFormat(const uint8_t& type,
const std::vector<uint8_t>& value);
/* Structures */
/** LE Audio ASE codec configuration parameters, built from LTV types defined
* in the BT Assigned Numbers.
* NOTE: This base structure does not support the vendor specific parameters.
*/
struct LeAudioCoreCodecConfig {
static const std::map<uint8_t, uint32_t> sampling_freq_map;
static const std::map<uint32_t, uint8_t> sample_rate_map;
static const std::map<uint8_t, uint32_t> frame_duration_map;
static const std::map<uint32_t, uint8_t> data_interval_map;
std::optional<uint8_t> sampling_frequency;
std::optional<uint8_t> frame_duration;
std::optional<uint32_t> audio_channel_allocation;
std::optional<uint16_t> octets_per_codec_frame;
std::optional<uint8_t> codec_frames_blocks_per_sdu;
static uint32_t GetSamplingFrequencyHz(uint8_t sample_freq) {
return sampling_freq_map.count(sample_freq)
? sampling_freq_map.at(sample_freq)
: 0;
}
static uint32_t GetFrameDurationUs(uint8_t framn_dur) {
return frame_duration_map.count(framn_dur)
? frame_duration_map.at(framn_dur)
: 0;
}
uint16_t GetOctectsPerFrame() const {
return octets_per_codec_frame.value_or(0);
}
/** Returns the sampling frequency representation in Hz */
uint32_t GetSamplingFrequencyHz() const {
if (sampling_frequency)
return sampling_freq_map.count(*sampling_frequency)
? sampling_freq_map.at(*sampling_frequency)
: 0;
return 0;
}
/** Returns the frame duration representation in us */
uint32_t GetFrameDurationUs() const {
if (frame_duration)
return frame_duration_map.count(*frame_duration)
? frame_duration_map.at(*frame_duration)
: 0;
return 0;
}
};
struct LeAudioCoreCodecCapabilities {
bool HasSupportedSamplingFrequencies() const {
return supported_sampling_frequencies.has_value();
}
bool HasSupportedFrameDurations() const {
return supported_frame_durations.has_value();
}
bool HasSupportedOctetsPerCodecFrame() const {
return supported_min_octets_per_codec_frame.has_value() &&
supported_max_octets_per_codec_frame.has_value();
}
bool HasSupportedAudioChannelCounts() const {
return supported_audio_channel_counts.has_value();
}
bool HasSupportedMaxCodecFramesPerSdu() const {
return supported_max_codec_frames_per_sdu.has_value();
}
bool IsSamplingFrequencyConfigSupported(uint8_t value) const {
return supported_sampling_frequencies.value_or(0) &
codec_spec_caps::SamplingFreqConfig2Capability(value);
}
bool IsFrameDurationConfigSupported(uint8_t value) const {
return supported_frame_durations.value_or(0) &
codec_spec_caps::FrameDurationConfig2Capability(value);
}
bool IsAudioChannelCountsSupported(uint8_t value) const {
if (value > 0)
return supported_audio_channel_counts.value_or(0) & (0b1 << (value - 1));
return false;
}
bool IsOctetsPerCodecFrameConfigSupported(uint16_t value) const {
return (value >= supported_min_octets_per_codec_frame.value_or(0)) &&
(value <= supported_max_octets_per_codec_frame.value_or(0));
}
bool IsCodecFramesPerSduSupported(uint8_t value) const {
return value <= supported_max_codec_frames_per_sdu.value_or(1);
}
std::optional<uint16_t> supported_sampling_frequencies;
std::optional<uint8_t> supported_frame_durations;
std::optional<uint8_t> supported_audio_channel_counts;
std::optional<uint16_t> supported_min_octets_per_codec_frame;
std::optional<uint16_t> supported_max_octets_per_codec_frame;
std::optional<uint8_t> supported_max_codec_frames_per_sdu;
};
struct LeAudioMetadata {
std::optional<uint16_t> preferred_audio_context;
std::optional<uint16_t> streaming_audio_context;
std::optional<std::string> program_info;
std::optional<std::string> language; // ISO 639-3 (3 lowercase letter codes)
std::optional<std::vector<uint8_t>> ccid_list;
std::optional<uint8_t> parental_rating;
std::optional<std::string> program_info_uri;
std::optional<std::vector<uint8_t>> extended_metadata;
std::optional<std::vector<uint8_t>> vendor_specific;
std::optional<bool> audio_active_state;
std::optional<bool> broadcast_audio_immediate_rendering;
};
std::ostream& operator<<(std::ostream& os, const LeAudioMetadata& config);
#define LTV_ENTRY_SAMPLING_FREQUENCY(value) \
{ \
le_audio::codec_spec_conf::kLeAudioLtvTypeSamplingFreq, \
std::vector<uint8_t>({(value) & 0xFF}) \
}
#define LTV_ENTRY_FRAME_DURATION(value) \
{ \
le_audio::codec_spec_conf::kLeAudioLtvTypeFrameDuration, \
std::vector<uint8_t>({(value) & 0xFF}) \
}
#define LTV_ENTRY_AUDIO_CHANNEL_ALLOCATION(value) \
{ \
le_audio::codec_spec_conf::kLeAudioLtvTypeAudioChannelAllocation, \
std::vector<uint8_t>({(uint8_t)(value) & 0xFF, \
(uint8_t)((value) << 8) & 0xFF, \
(uint8_t)((value) << 16) & 0xFF, \
(uint8_t)((value) << 24) & 0xFF}) \
}
#define LTV_ENTRY_OCTETS_PER_CODEC_FRAME(value) \
{ \
le_audio::codec_spec_conf::kLeAudioLtvTypeOctetsPerCodecFrame, \
std::vector<uint8_t>( \
{(uint8_t)(value) & 0xFF, (uint8_t)((value) << 8) & 0xFF}) \
}
#define LTV_ENTRY_FRAME_BLOCKS_PER_SDU(value) \
{ \
le_audio::codec_spec_conf::kLeAudioLtvTypeCodecFrameBlocksPerSdu, \
std::vector<uint8_t>({(value) & 0xFF}) \
}
class LeAudioLtvMap {
public:
LeAudioLtvMap(std::map<uint8_t, std::vector<uint8_t>> values)
: values(values),
value_hash(0),
core_config(std::nullopt),
core_capabilities(std::nullopt),
metadata(std::nullopt) {}
LeAudioLtvMap() = default;
~LeAudioLtvMap() = default;
bool operator==(const LeAudioLtvMap& other) const {
return GetHash() == other.GetHash();
}
bool operator!=(const LeAudioLtvMap& other) const {
return GetHash() != other.GetHash();
}
std::optional<std::vector<uint8_t>> Find(uint8_t type) const;
const auto& At(uint8_t type) const { return values.at(type); }
LeAudioLtvMap& Add(uint8_t type, std::vector<uint8_t> value) {
values.insert_or_assign(type, std::move(value));
invalidate();
return *this;
}
// Add vendor specific data preceded with 2 octets of company ID
LeAudioLtvMap& Add(uint8_t type, uint16_t vendorCompanyId,
std::vector<uint8_t> value) {
std::vector<uint8_t> data(value.size() + 2);
auto ptr = data.data();
UINT16_TO_STREAM(ptr, vendorCompanyId);
ARRAY_TO_STREAM(ptr, value.data(), (int)value.size());
return Add(type, data);
}
LeAudioLtvMap& Add(uint8_t type, const std::string& value) {
std::vector<uint8_t> v(value.size());
auto ptr = v.data();
ARRAY_TO_STREAM(ptr, value.c_str(), (int)value.size());
values.insert_or_assign(type, v);
invalidate();
return *this;
}
LeAudioLtvMap& Add(uint8_t type, bool value) {
std::vector<uint8_t> v(1);
auto ptr = v.data();
UINT8_TO_STREAM(ptr, value ? 0x01 : 0x00);
values.insert_or_assign(type, v);
invalidate();
return *this;
}
LeAudioLtvMap& Add(uint8_t type, uint8_t value) {
std::vector<uint8_t> v(sizeof(value));
auto ptr = v.data();
UINT8_TO_STREAM(ptr, value);
values.insert_or_assign(type, v);
invalidate();
return *this;
}
LeAudioLtvMap& Add(uint8_t type, uint16_t value) {
std::vector<uint8_t> v(sizeof(value));
auto ptr = v.data();
UINT16_TO_STREAM(ptr, value);
values.insert_or_assign(type, std::move(v));
invalidate();
return *this;
}
LeAudioLtvMap& Add(uint8_t type, uint32_t value) {
std::vector<uint8_t> v(sizeof(value));
auto ptr = v.data();
UINT32_TO_STREAM(ptr, value);
values.insert_or_assign(type, std::move(v));
invalidate();
return *this;
}
void Remove(uint8_t type) {
values.erase(type);
invalidate();
}
void RemoveAllTypes(const LeAudioLtvMap& other);
bool IsEmpty() const { return values.empty(); }
void Clear() {
invalidate();
values.clear();
}
size_t Size() const { return values.size(); }
const std::map<uint8_t, std::vector<uint8_t>>& Values() const {
return values;
}
const struct LeAudioCoreCodecConfig& GetAsCoreCodecConfig() const;
const struct LeAudioCoreCodecCapabilities& GetAsCoreCodecCapabilities() const;
const struct LeAudioMetadata& GetAsLeAudioMetadata() const;
LeAudioLtvMap GetIntersection(const LeAudioLtvMap& other) const;
std::string ToString(
const std::string& indent_string,
std::string (*format)(const uint8_t&, const std::vector<uint8_t>&)) const;
size_t RawPacketSize() const;
uint8_t* RawPacket(uint8_t* p_buf) const;
std::vector<uint8_t> RawPacket() const;
static LeAudioLtvMap Parse(const uint8_t* value, uint8_t len, bool& success);
bool Parse(const uint8_t* value, uint8_t len);
void Append(const LeAudioLtvMap& other);
size_t GetHash() const {
if (value_hash == 0) RecalculateValueHash();
return value_hash;
}
private:
void invalidate() {
core_config = std::nullopt;
core_capabilities = std::nullopt;
metadata = std::nullopt;
value_hash = 0;
}
static LeAudioMetadata LtvMapToMetadata(const LeAudioLtvMap& ltvs) {
LeAudioMetadata metadata;
auto vec_opt = ltvs.Find(types::kLeAudioMetadataTypePreferredAudioContext);
if (vec_opt &&
(vec_opt->size() ==
sizeof(decltype(metadata.preferred_audio_context)::value_type))) {
auto ptr = vec_opt->data();
STREAM_TO_UINT16(metadata.preferred_audio_context, ptr);
}
vec_opt = ltvs.Find(types::kLeAudioMetadataTypeStreamingAudioContext);
if (vec_opt &&
(vec_opt->size() ==
sizeof(decltype(metadata.streaming_audio_context)::value_type))) {
auto ptr = vec_opt->data();
STREAM_TO_UINT16(metadata.streaming_audio_context, ptr);
}
vec_opt = ltvs.Find(types::kLeAudioMetadataTypeProgramInfo);
if (vec_opt) {
metadata.program_info = std::string(
reinterpret_cast<const char*>(vec_opt->data()), vec_opt->size());
}
vec_opt = ltvs.Find(types::kLeAudioMetadataTypeLanguage);
if (vec_opt && (vec_opt->size() == 3)) { // it is always 3 in ISO 639-3
metadata.language = std::string(
reinterpret_cast<const char*>(vec_opt->data()), vec_opt->size());
}
metadata.ccid_list = ltvs.Find(types::kLeAudioMetadataTypeCcidList);
vec_opt = ltvs.Find(types::kLeAudioMetadataTypeparentalRating);
if (vec_opt && (vec_opt->size() ==
sizeof(decltype(metadata.parental_rating)::value_type))) {
auto ptr = vec_opt->data();
STREAM_TO_UINT8(metadata.parental_rating, ptr);
}
vec_opt = ltvs.Find(types::kLeAudioMetadataTypeProgramInfoUri);
if (vec_opt) {
metadata.program_info_uri = std::string(
reinterpret_cast<const char*>(vec_opt->data()), vec_opt->size());
}
vec_opt = ltvs.Find(types::kLeAudioMetadataTypeAudioActiveState);
if (vec_opt &&
(vec_opt->size() ==
sizeof(decltype(metadata.audio_active_state)::value_type))) {
auto ptr = vec_opt->data();
uint8_t val;
STREAM_TO_UINT8(val, ptr);
metadata.audio_active_state = val ? true : false;
}
vec_opt = ltvs.Find(
types::kLeAudioMetadataTypeBroadcastAudioImmediateRenderingFlag);
if (vec_opt) {
metadata.broadcast_audio_immediate_rendering = true;
}
metadata.extended_metadata =
ltvs.Find(types::kLeAudioMetadataTypeExtendedMetadata);
metadata.vendor_specific =
ltvs.Find(types::kLeAudioMetadataTypeVendorSpecific);
return metadata;
}
static LeAudioCoreCodecConfig LtvMapToCoreCodecConfig(
const LeAudioLtvMap& ltvs) {
LeAudioCoreCodecConfig core;
auto vec_opt = ltvs.Find(codec_spec_conf::kLeAudioLtvTypeSamplingFreq);
if (vec_opt && (vec_opt->size() ==
sizeof(decltype(core.sampling_frequency)::value_type))) {
auto ptr = vec_opt->data();
STREAM_TO_UINT8(core.sampling_frequency, ptr);
}
vec_opt = ltvs.Find(codec_spec_conf::kLeAudioLtvTypeFrameDuration);
if (vec_opt && (vec_opt->size() ==
sizeof(decltype(core.frame_duration)::value_type))) {
auto ptr = vec_opt->data();
STREAM_TO_UINT8(core.frame_duration, ptr);
}
vec_opt = ltvs.Find(codec_spec_conf::kLeAudioLtvTypeAudioChannelAllocation);
if (vec_opt &&
(vec_opt->size() ==
sizeof(decltype(core.audio_channel_allocation)::value_type))) {
auto ptr = vec_opt->data();
STREAM_TO_UINT32(core.audio_channel_allocation, ptr);
}
vec_opt = ltvs.Find(codec_spec_conf::kLeAudioLtvTypeOctetsPerCodecFrame);
if (vec_opt &&
(vec_opt->size() ==
sizeof(decltype(core.octets_per_codec_frame)::value_type))) {
auto ptr = vec_opt->data();
STREAM_TO_UINT16(core.octets_per_codec_frame, ptr);
}
vec_opt = ltvs.Find(codec_spec_conf::kLeAudioLtvTypeCodecFrameBlocksPerSdu);
if (vec_opt &&
(vec_opt->size() ==
sizeof(decltype(core.codec_frames_blocks_per_sdu)::value_type))) {
auto ptr = vec_opt->data();
STREAM_TO_UINT8(core.codec_frames_blocks_per_sdu, ptr);
}
return core;
}
static LeAudioCoreCodecCapabilities LtvMapToCoreCodecCapabilities(
const LeAudioLtvMap& pacs) {
LeAudioCoreCodecCapabilities core;
auto pac =
pacs.Find(codec_spec_caps::kLeAudioLtvTypeSupportedSamplingFrequencies);
if (pac &&
(pac.value().size() ==
sizeof(decltype(core.supported_sampling_frequencies)::value_type))) {
core.supported_sampling_frequencies = VEC_UINT8_TO_UINT16(pac.value());
}
pac = pacs.Find(codec_spec_caps::kLeAudioLtvTypeSupportedFrameDurations);
if (pac && (pac.value().size() ==
sizeof(decltype(core.supported_frame_durations)::value_type))) {
core.supported_frame_durations = VEC_UINT8_TO_UINT8(pac.value());
}
pac =
pacs.Find(codec_spec_caps::kLeAudioLtvTypeSupportedOctetsPerCodecFrame);
if (pac &&
(pac.value().size() ==
(sizeof(
decltype(core.supported_min_octets_per_codec_frame)::value_type) +
sizeof(decltype(core.supported_max_octets_per_codec_frame)::
value_type)))) {
core.supported_min_octets_per_codec_frame =
VEC_UINT8_TO_UINT16(pac.value());
core.supported_max_octets_per_codec_frame = OFF_VEC_UINT8_TO_UINT16(
pac.value(),
sizeof(
decltype(core.supported_min_octets_per_codec_frame)::value_type));
}
/*
* BAP_1.0.1 4.3.1 Codec_Specific_Capabilities LTV requirements:
* The absence of the Supported_Audio_Channel_Counts LTV structure shall be
* interpreted as equivalent to a Supported_Audio_Channel_Counts value of
* 0x01 (one Audio Channel supported).
*/
pac =
pacs.Find(codec_spec_caps::kLeAudioLtvTypeSupportedAudioChannelCounts);
if (pac &&
(pac.value().size() ==
sizeof(decltype(core.supported_audio_channel_counts)::value_type))) {
core.supported_audio_channel_counts = VEC_UINT8_TO_UINT8(pac.value());
} else {
core.supported_audio_channel_counts = 0b1;
}
/*
* BAP_1.0.1 4.3.1 Codec_Specific_Capabilities LTV requirements:
* The absence of the Supported_Max_Codec_Frames_Per_SDU LTV structure shall
* be interpreted as equivalent to a Supported_Max_Codec_Frames_Per_SDU
* value of 1 codec frame per Audio Channel per SDU maximum.
*/
pac = pacs.Find(
codec_spec_caps::kLeAudioLtvTypeSupportedMaxCodecFramesPerSdu);
if (pac &&
(pac.value().size() ==
sizeof(
decltype(core.supported_max_codec_frames_per_sdu)::value_type))) {
core.supported_max_codec_frames_per_sdu = VEC_UINT8_TO_UINT8(pac.value());
} else {
core.supported_max_codec_frames_per_sdu = 1;
}
return core;
}
void RecalculateValueHash() const {
if (IsEmpty()) {
value_hash = 0;
return;
}
auto value_vec = RawPacket();
value_hash = std::hash<std::string_view>{}(
{reinterpret_cast<const char*>(value_vec.data()), value_vec.size()});
}
std::map<uint8_t, std::vector<uint8_t>> values = {};
mutable size_t value_hash = 0;
// Lazy-constructed views of the LTV data
mutable std::optional<struct LeAudioCoreCodecConfig> core_config =
std::nullopt;
mutable std::optional<struct LeAudioCoreCodecCapabilities> core_capabilities =
std::nullopt;
mutable std::optional<struct LeAudioMetadata> metadata = std::nullopt;
};
struct LeAudioCodecId {
uint8_t coding_format;
uint16_t vendor_company_id;
uint16_t vendor_codec_id;
friend bool operator==(const LeAudioCodecId& lhs, const LeAudioCodecId& rhs) {
if (lhs.coding_format != rhs.coding_format) return false;
if (lhs.coding_format == kLeAudioCodingFormatVendorSpecific &&
(lhs.vendor_company_id != rhs.vendor_company_id ||
lhs.vendor_codec_id != rhs.vendor_codec_id))
return false;
return true;
}
friend bool operator!=(const LeAudioCodecId& lhs, const LeAudioCodecId& rhs) {
return !(lhs == rhs);
}
};
/* Google vendor specific codec for Headtracking */
constexpr LeAudioCodecId kLeAudioCodecHeadtracking = {
kLeAudioCodingFormatVendorSpecific, kLeAudioVendorCompanyIdGoogle,
kLeAudioVendorCodecIdHeadtracking};
struct IsoDataPathConfiguration {
types::LeAudioCodecId codecId = {0, 0, 0};
bool isTransparent = true;
uint32_t controllerDelayUs = 0;
std::vector<uint8_t> configuration = {};
bool operator==(const IsoDataPathConfiguration& other) const {
if (codecId != other.codecId) return false;
if (isTransparent != other.isTransparent) return false;
if (controllerDelayUs != other.controllerDelayUs) return false;
if (configuration.size() != other.configuration.size()) return false;
if ((!other.configuration.empty()) &&
memcmp(configuration.data(), other.configuration.data(),
other.configuration.size())) {
return false;
}
return true;
}
bool operator!=(const IsoDataPathConfiguration& other) const {
return !(*this == other);
}
};
std::ostream& operator<<(
std::ostream& os, const le_audio::types::IsoDataPathConfiguration& config);
struct DataPathConfiguration {
uint8_t dataPathId = 0;
std::vector<uint8_t> dataPathConfig = {};
IsoDataPathConfiguration isoDataPathConfig;
bool operator==(const DataPathConfiguration& other) const {
if (dataPathId != other.dataPathId) return false;
if (isoDataPathConfig != other.isoDataPathConfig) return false;
if (dataPathConfig.size() != other.dataPathConfig.size()) return false;
if ((!other.dataPathConfig.empty()) &&
memcmp(dataPathConfig.data(), other.dataPathConfig.data(),
other.dataPathConfig.size())) {
return false;
}
return true;
}
bool operator!=(const DataPathConfiguration& other) const {
return !(*this == other);
}
};
std::ostream& operator<<(std::ostream& os,
const le_audio::types::DataPathConfiguration& config);
struct hdl_pair {
hdl_pair() = default;
hdl_pair(uint16_t val_hdl, uint16_t ccc_hdl)
: val_hdl(val_hdl), ccc_hdl(ccc_hdl) {}
uint16_t val_hdl = 0;
uint16_t ccc_hdl = 0;
};
struct AseQosConfiguration {
uint32_t presentation_delay = 0;
uint32_t sdu_interval = 0;
uint16_t max_transport_latency = 0;
uint16_t max_sdu_size = 0;
uint8_t retrans_nb = 0;
uint8_t framing = 0;
uint8_t phy = 0;
};
struct AseQosPreferences {
uint8_t supported_framing = 0;
uint8_t preferred_phy = 0;
uint8_t preferred_retrans_nb = 0;
uint32_t pres_delay_min = 0;
uint32_t pres_delay_max = 0;
uint32_t preferred_pres_delay_min = 0;
uint32_t preferred_pres_delay_max = 0;
};
struct ase {
static constexpr uint8_t kAseIdInvalid = 0x00;
ase(uint16_t val_hdl, uint16_t ccc_hdl, uint8_t direction,
uint8_t initial_id = kAseIdInvalid)
: hdls(val_hdl, ccc_hdl),
id(initial_id),
cis_id(kInvalidCisId),
direction(direction),
target_latency(types::kTargetLatencyBalancedLatencyReliability),
active(false),
reconfigure(false),
cis_state(CisState::IDLE),
data_path_state(DataPathState::IDLE),
configured_for_context_type(LeAudioContextType::UNINITIALIZED),
autonomous_operation_timer_(nullptr),
autonomous_target_state_(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE),
state(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {}
struct hdl_pair hdls;
uint8_t id;
uint8_t cis_id;
const uint8_t direction;
uint8_t target_latency;
uint16_t cis_conn_hdl = 0;
bool active;
bool reconfigure;
CisState cis_state;
DataPathState data_path_state;
LeAudioContextType configured_for_context_type;
/* Codec configuration */
LeAudioCodecId codec_id;
LeAudioLtvMap codec_config;
std::vector<uint8_t> vendor_codec_config;
uint8_t channel_count;
/* Data path configuration */
DataPathConfiguration data_path_configuration;
/* Qos configuration */
AseQosConfiguration qos_config;
/* QoS requirements in Codec Configured state */
AseQosPreferences qos_preferences;
std::vector<uint8_t> metadata;
/* Autonomous change data */
alarm_t* autonomous_operation_timer_;
types::AseState autonomous_target_state_;
AseState state;
};
struct acs_ac_record {
LeAudioCodecId codec_id;
LeAudioLtvMap codec_spec_caps;
std::vector<uint8_t> codec_spec_caps_raw;
std::vector<uint8_t> metadata;
};
using PublishedAudioCapabilities =
std::vector<std::tuple<hdl_pair, std::vector<acs_ac_record>>>;
using AudioLocations = std::bitset<32>;
std::ostream& operator<<(std::ostream& os, const AseState& state);
std::ostream& operator<<(std::ostream& os, const CigState& state);
std::ostream& operator<<(std::ostream& os, const LeAudioCodecId& codec_id);
std::ostream& operator<<(std::ostream& os,
const LeAudioCoreCodecConfig& config);
std::string contextTypeToStr(const LeAudioContextType& context);
std::ostream& operator<<(std::ostream& os, const LeAudioContextType& context);
std::ostream& operator<<(std::ostream& os, const DataPathState& state);
std::ostream& operator<<(std::ostream& os, const CisState& state);
std::ostream& operator<<(std::ostream& os, const AudioContexts& contexts);
} // namespace types
namespace set_configurations {
struct CodecConfigSetting {
/* Codec identifier */
types::LeAudioCodecId id;
/* Codec Specific Configuration */
types::LeAudioLtvMap params;
/* Vendor Specific Configuration */
std::vector<uint8_t> vendor_params;
/* Channel count per device */
uint8_t channel_count_per_iso_stream;
/* Octects per fram for codec */
uint16_t GetOctectsPerFrame() const;
/* Sampling freqency requested for codec */
uint32_t GetSamplingFrequencyHz() const;
/* Data fetch/feed interval for codec in microseconds */
uint32_t GetDataIntervalUs() const;
/* Audio bit depth required for codec */
uint8_t GetBitsPerSample() const;
/* Audio channels number for a device */
uint8_t GetChannelCountPerIsoStream() const {
return channel_count_per_iso_stream;
}
bool operator==(const CodecConfigSetting& other) const {
return (id == other.id) &&
(channel_count_per_iso_stream ==
other.channel_count_per_iso_stream) &&
(vendor_params == other.vendor_params) && (params == other.params);
}
bool operator!=(const CodecConfigSetting& other) const {
return !(*this == other);
}
/* TODO: Add vendor parameter or Ltv map viewers for
* vendor specific LTV types.
*/
};
std::ostream& operator<<(std::ostream& os, const CodecConfigSetting& config);
struct QosConfigSetting {
uint8_t target_latency;
uint8_t retransmission_number;
uint16_t max_transport_latency;
int sduIntervalUs;
int maxSdu;
bool operator!=(const QosConfigSetting& other) { return !(*this == other); }
bool operator==(const QosConfigSetting& other) const {
return ((target_latency == other.target_latency) &&
(retransmission_number == other.retransmission_number) &&
(max_transport_latency == other.max_transport_latency));
}
};
std::ostream& operator<<(std::ostream& os, const QosConfigSetting& config);
struct AseConfiguration {
AseConfiguration(CodecConfigSetting codec,
QosConfigSetting qos = {.target_latency = 0,
.retransmission_number = 0,
.max_transport_latency = 0})
: codec(codec), qos(qos) {}
types::DataPathConfiguration data_path_configuration;
CodecConfigSetting codec;
QosConfigSetting qos;
bool operator!=(const AseConfiguration& other) { return !(*this == other); }
bool operator==(const AseConfiguration& other) const {
return ((data_path_configuration == other.data_path_configuration) &&
(codec == other.codec) && (qos == other.qos));
}
};
std::ostream& operator<<(std::ostream& os, const AseConfiguration& config);
/* Defined audio scenarios */
struct AudioSetConfiguration {
std::string name = "";
/* ISO data packing within the CIG */
uint8_t packing = bluetooth::hci::kIsoCigPackingSequential;
types::BidirectionalPair<std::vector<struct AseConfiguration>> confs;
bool operator!=(const AudioSetConfiguration& other) {
return !(*this == other);
}
bool operator==(const AudioSetConfiguration& other) const {
return ((packing == other.packing) && (confs == other.confs));
}
};
std::ostream& operator<<(std::ostream& os, const AudioSetConfiguration& config);
using AudioSetConfigurations = std::vector<const AudioSetConfiguration*>;
const types::LeAudioCodecId LeAudioCodecIdLc3 = {
.coding_format = types::kLeAudioCodingFormatLC3,
.vendor_company_id = types::kLeAudioVendorCompanyIdUndefined,
.vendor_codec_id = types::kLeAudioVendorCodecIdUndefined};
static constexpr uint32_t kChannelAllocationStereo =
codec_spec_conf::kLeAudioLocationFrontLeft |
codec_spec_conf::kLeAudioLocationFrontRight;
/* Declarations */
void get_cis_count(types::LeAudioContextType context_type,
int expected_device_cnt,
types::LeAudioConfigurationStrategy strategy,
int group_ase_snk_cnt, int group_ase_src_count,
uint8_t& cis_count_bidir, uint8_t& cis_count_unidir_sink,
uint8_t& cis_count_unidir_source);
} // namespace set_configurations
struct stream_parameters {
/* For now we have always same frequency for all the channels */
uint32_t sample_frequency_hz;
uint32_t frame_duration_us;
uint16_t octets_per_codec_frame;
uint32_t audio_channel_allocation;
uint8_t codec_frames_blocks_per_sdu;
/* Total number of channels we request from the audio framework */
uint8_t num_of_channels;
int num_of_devices;
/* cis_handle, audio location*/
std::vector<std::pair<uint16_t, uint32_t>> stream_locations;
void clear() {
sample_frequency_hz = 0;
frame_duration_us = 0;
octets_per_codec_frame = 0;
audio_channel_allocation = 0;
codec_frames_blocks_per_sdu = 0;
num_of_channels = 0;
num_of_devices = 0;
stream_locations.clear();
}
};
struct stream_configuration {
/* Whether the group should be reconfigured once the streaming stops */
bool pending_configuration;
/* Currently selected remote device set configuration */
std::shared_ptr<
const bluetooth::le_audio::set_configurations::AudioSetConfiguration>
conf;
/* Currently selected local audio codec */
types::LeAudioCodecId codec_id;
/* Currently selected local Sink & Source configuration */
types::BidirectionalPair<stream_parameters> stream_params;
};
void AppendMetadataLtvEntryForCcidList(std::vector<uint8_t>& metadata,
const std::vector<uint8_t>& ccid_list);
void AppendMetadataLtvEntryForStreamingContext(
std::vector<uint8_t>& metadata, types::AudioContexts context_type);
uint8_t GetMaxCodecFramesPerSduFromPac(const types::acs_ac_record* pac_record);
} // namespace bluetooth::le_audio
namespace fmt {
template <>
struct formatter<bluetooth::le_audio::DsaMode>
: enum_formatter<bluetooth::le_audio::DsaMode> {};
template <>
struct formatter<bluetooth::le_audio::types::CisType>
: enum_formatter<bluetooth::le_audio::types::CisType> {};
template <>
struct formatter<bluetooth::le_audio::types::LeAudioConfigurationStrategy>
: enum_formatter<bluetooth::le_audio::types::LeAudioConfigurationStrategy> {
};
} // namespace fmt