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
« 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.
3Used during LE Audio codec negotiation to identify codec types (LC3, mSBC, etc.)
4in capability exchange and stream configuration.
5"""
7from __future__ import annotations
9import logging
11import msgspec
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
17logger = logging.getLogger(__name__)
20class CodingFormatRegistry(BaseGenericRegistry[CodingFormatInfo]):
21 """Registry for Bluetooth audio coding formats with lazy loading.
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.
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 """
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] = {}
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
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
57 try:
58 with yaml_path.open("r", encoding="utf-8") as f:
59 data = msgspec.yaml.decode(f.read())
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
66 for item in data["coding_formats"]:
67 value = item.get("value")
68 name = item.get("name")
70 if value is None or not name:
71 continue
73 # Handle hex values in YAML (e.g., 0x06)
74 if isinstance(value, str):
75 value = int(value, 16)
77 coding_format_info = CodingFormatInfo(
78 value=value,
79 name=name,
80 )
82 self._coding_formats[value] = coding_format_info
83 self._coding_formats_by_name[name.lower()] = coding_format_info
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 )
92 self._loaded = True
94 def get_coding_format_info(self, value: int) -> CodingFormatInfo | None:
95 """Get coding format info by value (lazy loads on first call).
97 Args:
98 value: The coding format value (e.g., 0x06 for LC3)
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)
107 def get_coding_format_by_name(self, name: str) -> CodingFormatInfo | None:
108 """Get coding format info by name (lazy loads on first call).
110 Args:
111 name: Coding format name (case-insensitive, e.g., "LC3", "mSBC")
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())
120 def is_known_coding_format(self, value: int) -> bool:
121 """Check if coding format is known (lazy loads on first call).
123 Args:
124 value: The coding format value to check
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
133 def get_all_coding_formats(self) -> dict[int, CodingFormatInfo]:
134 """Get all registered coding formats (lazy loads on first call).
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()
144# Global singleton instance
145coding_format_registry = CodingFormatRegistry()