Source code for jdxi_editor.devel.parse_sysex_hex

#!/usr/bin/env python3
"""
Script to parse SysEx hex strings from JD-Xi and save as JSON.

Usage:
    python3 parse_sysex_hex.py <hex_string> [output_file]

Or provide multiple hex strings as separate arguments.
"""

import json
import sys
from pathlib import Path
from typing import Any, Dict, List

from jdxi_editor.midi.sysex.sections import SysExSection

# Add the project root to the path
sys.path.insert(0, str(Path(__file__).parent))

from decologr import Decologr as log

from jdxi_editor.midi.sysex.parser.sysex import JDXiSysExParser


[docs] def hex_string_to_bytes(hex_string: str) -> bytes: """ Convert a hex string (with or without spaces) to bytes. :param hex_string: str Hex string like "F0 41 10 ..." or "F04110..." :return: bytes """ # Remove spaces and convert to bytes hex_clean = hex_string.replace(" ", "").replace("\n", "").replace("\r", "") return bytes.fromhex(hex_clean)
[docs] def parse_sysex_hex(hex_string: str) -> Dict[str, Any] | None: """ Parse a SysEx hex string and return the parsed data. :param hex_string: str Hex string representation of SysEx message :return: dict Parsed SysEx data or None if parsing fails """ try: sysex_bytes = hex_string_to_bytes(hex_string) parser = JDXiSysExParser() parsed_data = parser.parse_bytes(sysex_bytes) return parsed_data except Exception as ex: log.error(f"Failed to parse SysEx hex: {ex}") return None
[docs] def parse_multiple_sysex(hex_strings: List[str]) -> List[Dict[str, Any]]: """ Parse multiple SysEx hex strings. :param hex_strings: List[str] List of hex string representations :return: List[dict] List of parsed SysEx data """ parsed_results = [] for i, hex_str in enumerate(hex_strings, 1): log.message(f"Parsing SysEx message {i}/{len(hex_strings)}...") parsed = parse_sysex_hex(hex_str) if parsed: parsed_results.append(parsed) address = parsed.get(SysExSection.ADDRESS, "unknown") log.message(f"✓ Parsed message {i} - ADDRESS: {address}") else: log.warning(f"✗ Failed to parse message {i}") return parsed_results
[docs] def main(): """Main function.""" if len(sys.argv) < 2: print( "Usage: python3 parse_sysex_hex.py <hex_string1> [hex_string2] ... [output_file]" ) print("\nExample:") print( ' python3 parse_sysex_hex.py "F0 41 10 00 00 00 0E 12 18 00 00 00 ... F7"' ) sys.exit(1) # Check if last argument is an output file (ends with .json) args = sys.argv[1:] output_file = None if len(args) > 1 and args[-1].endswith(".json"): output_file = Path(args[-1]) hex_strings = args[:-1] else: hex_strings = args output_file = Path.home() / "jdxi_parsed_sysex.json" log.header_message(f"Parsing {len(hex_strings)} SysEx message(s)...") # Parse all hex strings parsed_results = parse_multiple_sysex(hex_strings) if not parsed_results: log.error("No SysEx messages were successfully parsed.") return 1 # Prepare output data output_data = {"total_messages": len(parsed_results), "messages": parsed_results} # Save to file try: with open(output_file, "w", encoding="utf-8") as f: json.dump(output_data, f, indent=2, ensure_ascii=False) log.header_message( f"✓ Successfully parsed {len(parsed_results)} SysEx message(s)" ) log.message(f"Output file: {output_file}") # Show summary addresses = [msg.get(SysExSection.ADDRESS, "unknown") for msg in parsed_results] log.message(f"Parsed addresses: {', '.join(set(addresses))}") return 0 except Exception as ex: log.error(f"Failed to save output: {ex}") return 1
if __name__ == "__main__": sys.exit(main())