bt_common/
generic_audio.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub mod codec_capabilities;
6pub mod codec_configuration;
7pub mod metadata_ltv;
8
9use crate::{codable_as_bitmask, decodable_enum};
10
11// Source:
12// https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/profiles_and_services/generic_audio/context_type.yaml
13decodable_enum! {
14    pub enum ContextType<u16, crate::packet_encoding::Error, OutOfRange> {
15        Unspecified = 0x0001,
16        Conversational = 0x0002,
17        Media = 0x0004,
18        Game = 0x0008,
19        Instructional = 0x0010,
20        VoiceAssistants = 0x0020,
21        Live = 0x0040,
22        SoundEffects = 0x0080,
23        Notifications = 0x0100,
24        Ringtone = 0x0200,
25        Alerts = 0x0400,
26        EmergencyAlarm = 0x0800,
27    }
28}
29
30codable_as_bitmask!(ContextType, u16);
31
32// Source:
33// https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/profiles_and_services/generic_audio/audio_location_definitions.yaml
34// Regexp magic for quick variants:
35// %s/ - value: \(\S\+\)\n   audio_location: \(.*\)\n/\2 = \1\r,/g
36// with subsequent removal of Spaces
37decodable_enum! {
38    pub enum AudioLocation<u32, crate::packet_encoding::Error, OutOfRange> {
39        FrontLeft = 0x00000001,
40        FrontRight = 0x00000002,
41        FrontCenter = 0x00000004,
42        LowFrequencyEffects1 = 0x00000008,
43        BackLeft = 0x00000010,
44        BackRight = 0x00000020,
45        FrontLeftOfCenter = 0x00000040,
46        FrontRightOfCenter = 0x00000080,
47        BackCenter = 0x00000100,
48        LowFrequencyEffects2 = 0x00000200,
49        SideLeft = 0x00000400,
50        SideRight = 0x00000800,
51        TopFrontLeft = 0x00001000,
52        TopFrontRight = 0x00002000,
53        TopFrontCenter = 0x00004000,
54        TopCenter = 0x00008000,
55        TopBackLeft = 0x00010000,
56        TopBackRight = 0x00020000,
57        TopSideLeft = 0x00040000,
58        TopSideRight = 0x00080000,
59        TopBackCenter = 0x00100000,
60        BottomFrontCenter = 0x00200000,
61        BottomFrontLeft = 0x00400000,
62        BottomFrontRight = 0x00800000,
63        FrontLeftWide = 0x01000000,
64        FrontRightWide = 0x02000000,
65        LeftSurround = 0x04000000,
66        RightSurround = 0x08000000,
67    }
68}
69
70codable_as_bitmask!(AudioLocation, u32);
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn locations_decodable() {
78        let five_point_one = 0b111111;
79
80        let locations: std::collections::HashSet<AudioLocation> =
81            AudioLocation::from_bits(five_point_one).collect();
82
83        assert_eq!(6, locations.len());
84
85        let expected_locations = [
86            AudioLocation::FrontLeft,
87            AudioLocation::FrontRight,
88            AudioLocation::FrontCenter,
89            AudioLocation::LowFrequencyEffects1,
90            AudioLocation::BackLeft,
91            AudioLocation::BackRight,
92        ]
93        .into_iter()
94        .collect();
95
96        assert_eq!(locations, expected_locations);
97
98        assert_eq!(AudioLocation::try_from(0x4), Ok(AudioLocation::FrontCenter));
99
100        // Directly decoding a location that is not a single bit is an error.
101        assert!(AudioLocation::try_from(0b1010101).is_err());
102    }
103
104    #[test]
105    fn locations_encodable() {
106        let locations_missing_sub = [
107            AudioLocation::FrontLeft,
108            AudioLocation::FrontRight,
109            AudioLocation::FrontCenter,
110            AudioLocation::BackLeft,
111            AudioLocation::BackRight,
112        ];
113
114        let value = AudioLocation::to_bits(locations_missing_sub.iter());
115
116        assert_eq!(0b110111, value);
117    }
118
119    #[test]
120    fn context_type_decodable() {
121        let contexts: Vec<ContextType> = ContextType::from_bits(0b10).collect();
122        assert_eq!(contexts.len(), 1);
123        assert_eq!(contexts[0], ContextType::Conversational);
124
125        let live_and_instructional = 0b1010000;
126        let contexts: std::collections::HashSet<ContextType> =
127            ContextType::from_bits(live_and_instructional).collect();
128
129        assert_eq!(contexts.len(), 2);
130        assert_eq!(contexts, [ContextType::Live, ContextType::Instructional].into_iter().collect());
131
132        let alerts_and_conversational = 0x0402;
133
134        let contexts: std::collections::HashSet<ContextType> =
135            ContextType::from_bits(alerts_and_conversational).collect();
136
137        assert_eq!(contexts.len(), 2);
138        assert_eq!(
139            contexts,
140            [ContextType::Alerts, ContextType::Conversational].into_iter().collect()
141        );
142    }
143
144    #[test]
145    fn context_type_encodable() {
146        let contexts = [ContextType::Notifications, ContextType::SoundEffects, ContextType::Game];
147
148        let value = ContextType::to_bits(contexts.iter());
149
150        assert_eq!(0x188, value);
151    }
152}