fuchsia_bluetooth/assigned_numbers/
ltv.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use bitflags::bitflags;

/// See Assigned Numbers section 6.12.5
/// Codec_Specific_Configuration LTV structures.
#[derive(Default)]
pub struct CodecSpecificConfigLTV {
    pub sampling_frequency: Option<SamplingFrequency>,
    pub frame_duration: Option<FrameDuration>,
    pub audio_channel_alloc: Option<AudioLocation>,
    pub octets_per_codec_frame: Option<u16>,
    pub codec_frame_blocks_per_sdu: Option<u8>,
}

bitflags! {
    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
    pub struct AudioLocation: u32 {
        const FRONT_LEFT = 0x00000001;
        const FRONT_RIGHT = 0x00000002;
        const FRONT_CENTER = 0x00000004;
        const LOW_FREQUENCY_EFFECTS_1 = 0x00000008;
        const BACK_LEFT = 0x00000010;
        const BACK_RIGHT = 0x00000020;
        const FRONT_LEFT_OF_CENTER = 0x00000040;
        const FRONT_RIGHT_OF_CENTER = 0x00000080;
        const BACK_CENTER = 0x00000100;
        const LOW_FREQUENCY_EFFECTS_2 = 0x00000200;
        const SIDE_LEFT = 0x00000400;
        const SIDE_RIGHT = 0x00000800;
        const TOP_FRONT_LEFT = 0x00001000;
        const TOP_FRONT_RIGHT = 0x00002000;
        const TOP_FRONT_CENTER = 0x00004000;
        const TOP_CENTER = 0x00008000;
        const TOP_BACK_LEFT = 0x00010000;
        const TOP_BACK_RIGHT = 0x00020000;
        const TOP_SIDE_LEFT = 0x00040000;
        const TOP_SIDE_RIGHT = 0x00080000;
        const TOP_BACK_CENTER = 0x00100000;
        const BOTTOM_FRONT_CENTER = 0x00200000;
        const BOTTOM_FRONT_LEFT = 0x00400000;
        const BOTTOM_FRONT_RIGHT = 0x00800000;
        const FRONT_LEFT_WIDE = 0x01000000;
        const FRONT_RIGHT_WIDE = 0x02000000;
        const LEFT_SURROUND = 0x04000000;
        const RIGHT_SURROUND = 0x08000000;
    }
}

#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum SamplingFrequency {
    F8000Hz = 0x01,
    F11025Hz = 0x02,
    F16000Hz = 0x03,
    F22050Hz = 0x04,
    F24000Hz = 0x05,
    F32000Hz = 0x06,
    F44100Hz = 0x07,
    F48000Hz = 0x08,
    F88200Hz = 0x09,
    F96000Hz = 0x0A,
    F176400Hz = 0x0B,
    F192000Hz = 0x0C,
    F384000Hz = 0x0D,
}

#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum FrameDuration {
    D7p5Ms = 0x00,
    D10Ms = 0x01,
}

impl CodecSpecificConfigLTV {
    fn to_bytes(&self, big_endian: bool) -> Vec<u8> {
        let mut bytes = Vec::new();
        if let Some(sf) = &self.sampling_frequency {
            bytes.extend_from_slice(&[0x02, 0x01, *sf as u8]);
        }
        if let Some(fd) = &self.frame_duration {
            bytes.extend_from_slice(&[0x02, 0x02, *fd as u8]);
        }
        if let Some(alloc) = &self.audio_channel_alloc {
            bytes.extend_from_slice(&[0x05, 0x03]);
            let alloc_bytes =
                if big_endian { alloc.bits().to_be_bytes() } else { alloc.bits().to_le_bytes() };
            bytes.extend_from_slice(&alloc_bytes);
        }
        if let Some(o) = &self.octets_per_codec_frame {
            bytes.extend_from_slice(&[0x03, 0x04]);
            let octet_bytes = if big_endian { o.to_be_bytes() } else { o.to_le_bytes() };
            bytes.extend_from_slice(&octet_bytes);
        }
        if let Some(fb) = &self.codec_frame_blocks_per_sdu {
            bytes.extend_from_slice(&[0x02, 0x05, *fb]);
        }
        return bytes;
    }

    pub fn to_le_bytes(&self) -> Vec<u8> {
        self.to_bytes(false)
    }

    pub fn to_be_bytes(&self) -> Vec<u8> {
        self.to_bytes(true)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_codec_specific_config_ltv() {
        let config = CodecSpecificConfigLTV {
            sampling_frequency: Some(SamplingFrequency::F32000Hz),
            frame_duration: Some(FrameDuration::D7p5Ms),
            audio_channel_alloc: Some(AudioLocation::FRONT_LEFT | AudioLocation::FRONT_RIGHT),
            octets_per_codec_frame: Some(58),
            codec_frame_blocks_per_sdu: Some(40),
        };
        assert_eq!(
            config.to_le_bytes(),
            vec![
                0x02, 0x01, 0x06, // 32kHz sampling freq.
                0x02, 0x02, 0x00, // 7.5ms frame duration.
                0x05, 0x03, 0x03, 0x00, 0x00, 0x00, // LF and RF.
                0x03, 0x04, 0x3A, 0x00, // 58 octets per codec frame.
                0x02, 0x05, 0x28,
            ]
        );
        assert_eq!(
            config.to_be_bytes(),
            vec![
                0x02, 0x01, 0x06, // 32kHz sampling freq.
                0x02, 0x02, 0x00, // 7.5ms frame duration.
                0x05, 0x03, 0x00, 0x00, 0x00, 0x03, // LF and RF.
                0x03, 0x04, 0x00, 0x3A, // 58 octets per codec frame.
                0x02, 0x05, 0x28, // 40 frame block.
            ]
        );

        let config = CodecSpecificConfigLTV {
            sampling_frequency: Some(SamplingFrequency::F32000Hz),
            frame_duration: Some(FrameDuration::D10Ms),
            audio_channel_alloc: None,
            octets_per_codec_frame: Some(40),
            codec_frame_blocks_per_sdu: None,
        };
        assert_eq!(
            config.to_le_bytes(),
            vec![
                0x02, 0x01, 0x06, // 32kHz sampling freq.
                0x02, 0x02, 0x01, // 10ms frame duration.
                0x03, 0x04, 0x28, 0x00, // 40 octets per codec frame.
            ]
        );
        assert_eq!(
            config.to_be_bytes(),
            vec![
                0x02, 0x01, 0x06, // 32kHz sampling freq.
                0x02, 0x02, 0x01, // 10ms frame duration.
                0x03, 0x04, 0x00, 0x28, // 40 octets per codec frame.
            ]
        );
    }
}