Coverage for src / bluetooth_sig / registry / core / coding_format.py: 79%

61 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-11 20:14 +0000

1"""Coding Format registry for Bluetooth SIG audio codec definitions. 

2 

3Used during LE Audio codec negotiation to identify codec types (LC3, mSBC, etc.) 

4in capability exchange and stream configuration. 

5""" 

6 

7from __future__ import annotations 

8 

9import logging 

10 

11import msgspec 

12 

13from bluetooth_sig.registry.base import BaseGenericRegistry 

14from bluetooth_sig.registry.utils import find_bluetooth_sig_path 

15from bluetooth_sig.types.registry.coding_format import CodingFormatInfo 

16 

17logger = logging.getLogger(__name__) 

18 

19 

20class CodingFormatRegistry(BaseGenericRegistry[CodingFormatInfo]): 

21 """Registry for Bluetooth audio coding formats with lazy loading. 

22 

23 This registry loads coding format definitions from the official Bluetooth SIG 

24 assigned_numbers YAML file, providing codec identification for LE Audio 

25 and Classic Audio profiles. 

26 

27 Examples: 

28 >>> from bluetooth_sig.registry.core.coding_format import coding_format_registry 

29 >>> info = coding_format_registry.get_coding_format_info(0x06) 

30 >>> info.name 

31 'LC3' 

32 """ 

33 

34 def __init__(self) -> None: 

35 """Initialize the coding format registry.""" 

36 super().__init__() 

37 self._coding_formats: dict[int, CodingFormatInfo] = {} 

38 self._coding_formats_by_name: dict[str, CodingFormatInfo] = {} 

39 

40 def _load(self) -> None: 

41 """Perform the actual loading of coding format data.""" 

42 base_path = find_bluetooth_sig_path() 

43 if not base_path: 

44 logger.warning("Bluetooth SIG path not found. Coding format registry will be empty.") 

45 self._loaded = True 

46 return 

47 

48 yaml_path = base_path.parent / "core" / "coding_format.yaml" 

49 if not yaml_path.exists(): 

50 logger.warning( 

51 "Coding format YAML file not found at %s. Registry will be empty.", 

52 yaml_path, 

53 ) 

54 self._loaded = True 

55 return 

56 

57 try: 

58 with yaml_path.open("r", encoding="utf-8") as f: 

59 data = msgspec.yaml.decode(f.read()) 

60 

61 if not data or "coding_formats" not in data: 

62 logger.warning("Invalid coding format YAML format. Registry will be empty.") 

63 self._loaded = True 

64 return 

65 

66 for item in data["coding_formats"]: 

67 value = item.get("value") 

68 name = item.get("name") 

69 

70 if value is None or not name: 

71 continue 

72 

73 # Handle hex values in YAML (e.g., 0x06) 

74 if isinstance(value, str): 

75 value = int(value, 16) 

76 

77 coding_format_info = CodingFormatInfo( 

78 value=value, 

79 name=name, 

80 ) 

81 

82 self._coding_formats[value] = coding_format_info 

83 self._coding_formats_by_name[name.lower()] = coding_format_info 

84 

85 logger.info("Loaded %d coding formats from specification", len(self._coding_formats)) 

86 except (FileNotFoundError, OSError, msgspec.DecodeError, KeyError) as e: 

87 logger.warning( 

88 "Failed to load coding formats from YAML: %s. Registry will be empty.", 

89 e, 

90 ) 

91 

92 self._loaded = True 

93 

94 def get_coding_format_info(self, value: int) -> CodingFormatInfo | None: 

95 """Get coding format info by value (lazy loads on first call). 

96 

97 Args: 

98 value: The coding format value (e.g., 0x06 for LC3) 

99 

100 Returns: 

101 CodingFormatInfo object, or None if not found 

102 """ 

103 self._ensure_loaded() 

104 with self._lock: 

105 return self._coding_formats.get(value) 

106 

107 def get_coding_format_by_name(self, name: str) -> CodingFormatInfo | None: 

108 """Get coding format info by name (lazy loads on first call). 

109 

110 Args: 

111 name: Coding format name (case-insensitive, e.g., "LC3", "mSBC") 

112 

113 Returns: 

114 CodingFormatInfo object, or None if not found 

115 """ 

116 self._ensure_loaded() 

117 with self._lock: 

118 return self._coding_formats_by_name.get(name.lower()) 

119 

120 def is_known_coding_format(self, value: int) -> bool: 

121 """Check if coding format is known (lazy loads on first call). 

122 

123 Args: 

124 value: The coding format value to check 

125 

126 Returns: 

127 True if the coding format is registered, False otherwise 

128 """ 

129 self._ensure_loaded() 

130 with self._lock: 

131 return value in self._coding_formats 

132 

133 def get_all_coding_formats(self) -> dict[int, CodingFormatInfo]: 

134 """Get all registered coding formats (lazy loads on first call). 

135 

136 Returns: 

137 Dictionary mapping coding format values to CodingFormatInfo objects 

138 """ 

139 self._ensure_loaded() 

140 with self._lock: 

141 return self._coding_formats.copy() 

142 

143 

144# Global singleton instance 

145coding_format_registry = CodingFormatRegistry()