Source code for jdxi_editor.midi.data.address.helpers

"""
helpers.py
Helper functions for SysEx address manipulation and parsing.
This module provides utilities to apply address offsets, convert addresses to hex strings,
and parse SysEx addresses into a JSON-like structure.
It also includes functions to find matching symbols in provided base classes.
Functions:
- apply_address_offset: Applies an offset to a base SysEx address.
- address_to_hex_string: Converts a SysEx address to a hex string.
- parse_sysex_address_json: Parses a SysEx address into a JSON-like structure.
- find_matching_symbol: Finds a matching symbol in provided base classes.

"""

import inspect
from enum import IntEnum
from typing import Any, Dict, Tuple, Type, Union

from picomidi.sysex.parameter.address import AddressParameter

from jdxi_editor.midi.data.address.address import RolandSysExAddress


[docs] def apply_address_offset( base_address: RolandSysExAddress, param: AddressParameter ) -> RolandSysExAddress: """ Applies the offset of a parameter to a base address. :param base_address: RolandSysExAddress :param param: AddressParameter :return: RolandSysExAddress """ offset = param.get_offset() if offset is None: return base_address final_address = base_address.add_offset(offset) return final_address
[docs] def address_to_hex_string(address: Tuple[int, int, int, int]) -> str: """ Converts a 4-byte SysEx address into a hex string. :param address: Tuple[int, int, int, int] :return: str """ return " ".join(f"{b:02X}" for b in address)
[docs] def parse_sysex_address_json( address: Tuple[int, int, int, int], base_classes: Tuple[Type[Any], ...] ) -> Dict[str, Any]: """ Parses a SysEx address into a JSON-like structure. :param address: Tuple[int, int, int, int] :param base_classes: Tuple[Type[Any], ...] :return: Dict[str, Any] """ if len(address) != 4: return {} levels = [] remaining = list(address) for i, byte in enumerate(remaining): match = find_matching_symbol(byte, base_classes) if match: levels.append( {"class": match["class"].__name__, "name": match["name"], "value": byte} ) else: levels.append({"class": "Unknown", "name": f"0x{byte:02X}", "value": byte}) return {f"level_{i + 1}": level for i, level in enumerate(levels)}
[docs] def find_matching_symbol( value: int, base_classes: Tuple[Type[Any], ...] ) -> Union[Dict[str, Any], None]: """ Finds a matching symbol in the provided base classes. :param value: int :param base_classes: Tuple[Type[Any], ...] :return: Union[Dict[str, Any], None] """ for cls in base_classes: if issubclass(cls, IntEnum): for member in cls: if member.value == value: return {"class": cls, "name": member.name} else: # Look for attributes that are (address, min, max) tuples for name, member in inspect.getmembers(cls): if not name.startswith("__") and isinstance(member, tuple): if len(member) >= 1 and member[0] == value: return {"class": cls, "name": name} return None