bt_common/generic_audio/
codec_configuration.rs

1// Copyright 2024 Google LLC
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::collections::HashSet;
6use std::ops::RangeBounds;
7
8use crate::core::ltv::LtValue;
9use crate::decodable_enum;
10
11use super::AudioLocation;
12
13#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
14pub enum CodecConfigurationType {
15    SamplingFrequency,
16    FrameDuration,
17    AudioChannelAllocation,
18    OctetsPerCodecFrame,
19    CodecFramesPerSdu,
20}
21
22impl From<CodecConfigurationType> for u8 {
23    fn from(value: CodecConfigurationType) -> Self {
24        match value {
25            CodecConfigurationType::SamplingFrequency => 1,
26            CodecConfigurationType::FrameDuration => 2,
27            CodecConfigurationType::AudioChannelAllocation => 3,
28            CodecConfigurationType::OctetsPerCodecFrame => 4,
29            CodecConfigurationType::CodecFramesPerSdu => 5,
30        }
31    }
32}
33
34decodable_enum! {
35    pub enum FrameDuration<u8, crate::packet_encoding::Error, OutOfRange> {
36        SevenFiveMs = 0x00,
37        TenMs = 0x01,
38    }
39}
40
41decodable_enum! {
42    pub enum SamplingFrequency<u8, crate::packet_encoding::Error, OutOfRange> {
43        F8000Hz = 0x01,
44        F11025Hz = 0x02,
45        F16000Hz = 0x03,
46        F22050Hz = 0x04,
47        F24000Hz = 0x05,
48        F32000Hz = 0x06,
49        F44100Hz = 0x07,
50        F48000Hz = 0x08,
51        F88200Hz = 0x09,
52        F96000Hz = 0x0A,
53        F176400Hz = 0x0B,
54        F192000Hz = 0x0C,
55        F384000Hz = 0x0D,
56    }
57}
58
59/// Codec Configuration LTV Structures
60///
61/// Defined in Assigned Numbers Section 6.12.5.
62#[derive(Clone, PartialEq, Eq, Debug)]
63pub enum CodecConfiguration {
64    SamplingFrequency(SamplingFrequency),
65    FrameDuration(FrameDuration),
66    AudioChannelAllocation(std::collections::HashSet<AudioLocation>),
67    OctetsPerCodecFrame(u16),
68    CodecFramesPerSdu(u8),
69}
70
71impl LtValue for CodecConfiguration {
72    type Type = CodecConfigurationType;
73
74    const NAME: &'static str = "Codec Compatability";
75
76    fn type_from_octet(x: u8) -> Option<Self::Type> {
77        match x {
78            1 => Some(CodecConfigurationType::SamplingFrequency),
79            2 => Some(CodecConfigurationType::FrameDuration),
80            3 => Some(CodecConfigurationType::AudioChannelAllocation),
81            4 => Some(CodecConfigurationType::OctetsPerCodecFrame),
82            5 => Some(CodecConfigurationType::CodecFramesPerSdu),
83            _ => None,
84        }
85    }
86
87    fn length_range_from_type(ty: Self::Type) -> std::ops::RangeInclusive<u8> {
88        match ty {
89            CodecConfigurationType::SamplingFrequency => 2..=2,
90            CodecConfigurationType::FrameDuration => 2..=2,
91            CodecConfigurationType::AudioChannelAllocation => 5..=5,
92            CodecConfigurationType::OctetsPerCodecFrame => 3..=3,
93            CodecConfigurationType::CodecFramesPerSdu => 2..=2,
94        }
95    }
96
97    fn into_type(&self) -> Self::Type {
98        match self {
99            CodecConfiguration::SamplingFrequency(_) => CodecConfigurationType::SamplingFrequency,
100            CodecConfiguration::FrameDuration(_) => CodecConfigurationType::FrameDuration,
101            CodecConfiguration::AudioChannelAllocation(_) => {
102                CodecConfigurationType::AudioChannelAllocation
103            }
104            CodecConfiguration::OctetsPerCodecFrame(_) => {
105                CodecConfigurationType::OctetsPerCodecFrame
106            }
107            CodecConfiguration::CodecFramesPerSdu(_) => CodecConfigurationType::CodecFramesPerSdu,
108        }
109    }
110
111    fn value_encoded_len(&self) -> u8 {
112        // All the CodecCapabilities are constant length. Remove the type octet.
113        let range = Self::length_range_from_type(self.into_type());
114        let std::ops::Bound::Included(len) = range.start_bound() else {
115            unreachable!();
116        };
117        (len - 1).into()
118    }
119
120    fn encode_value(&self, buf: &mut [u8]) -> Result<(), crate::packet_encoding::Error> {
121        match self {
122            CodecConfiguration::SamplingFrequency(freq) => buf[0] = u8::from(*freq),
123            CodecConfiguration::FrameDuration(fd) => buf[0] = u8::from(*fd),
124            CodecConfiguration::AudioChannelAllocation(audio_locations) => {
125                let locations: Vec<&AudioLocation> = audio_locations.into_iter().collect();
126                buf[0..4]
127                    .copy_from_slice(&AudioLocation::to_bits(locations.into_iter()).to_le_bytes());
128            }
129            CodecConfiguration::OctetsPerCodecFrame(octets) => {
130                buf[0..2].copy_from_slice(&octets.to_le_bytes())
131            }
132            CodecConfiguration::CodecFramesPerSdu(frames) => buf[0] = *frames,
133        };
134        Ok(())
135    }
136
137    fn decode_value(
138        ty: &CodecConfigurationType,
139        buf: &[u8],
140    ) -> Result<Self, crate::packet_encoding::Error> {
141        match ty {
142            CodecConfigurationType::SamplingFrequency => {
143                let freq = SamplingFrequency::try_from(buf[0])?;
144                Ok(Self::SamplingFrequency(freq))
145            }
146            CodecConfigurationType::FrameDuration => {
147                let fd = FrameDuration::try_from(buf[0])?;
148                Ok(Self::FrameDuration(fd))
149            }
150            CodecConfigurationType::AudioChannelAllocation => {
151                let raw_value = u32::from_le_bytes(buf[0..4].try_into().unwrap());
152                let locations = AudioLocation::from_bits(raw_value).collect::<HashSet<_>>();
153                Ok(Self::AudioChannelAllocation(locations))
154            }
155            CodecConfigurationType::OctetsPerCodecFrame => {
156                let value = u16::from_le_bytes(buf[0..2].try_into().unwrap());
157                Ok(Self::OctetsPerCodecFrame(value))
158            }
159            CodecConfigurationType::CodecFramesPerSdu => Ok(Self::CodecFramesPerSdu(buf[0])),
160        }
161    }
162}
163
164#[cfg(test)]
165mod tests {}