Source code for jdxi_editor.midi.sysex.request.factory

"""
This module contains constants, functions, and logic to generate and compute
Roland JD-Xi SysEx messages for requesting program and tone names.

It defines a set of SysEx constants specific to the JD-Xi and provides the
following functionality:

1. **Constants**: A dictionary `SYSEX_CONSTANTS` containing key constants
   required to construct the SysEx messages, including the JD-Xi header,
   tone areas, and other necessary fields.
2. **Checksum Calculation**: A function `roland_checksum` that computes the
   Roland checksum for a given SysEx data string. This checksum is used
   to ensure the integrity of the message.
3. **Request Generation**: A function `create_request` that dynamically generates
   a SysEx message based on the header, tone area, and parameters provided.
4. **Program and Tone Requests**: A list `PROGRAM_AND_TONE_NAME_REQUESTS`
   containing pre-generated SysEx requests for various program and tone areas
   like digital, analog, and drums.

SysEx message format includes:
- Start byte `F0`, end byte `F7`
- JD-Xi specific header and command information
- Roland checksum for data integrity

Functions:
- `roland_checksum(data: str) -> str`: Computes and returns the Roland checksum
  based on the input SysEx data string.
- `create_request(header, tone_area, param1) -> str`: Constructs a complete
  SysEx request message, appends the checksum, and returns the full message.

    Examples
    --------
    To generate a request for the program common area:

    >>> request = create_request(
    ...     TEMPORARY_PROGRAM_RQ11_HEADER,
    ...     SYSEX_CONSTANTS['PROGRAM_COMMON_AREA'],
    ...     "00 00 00 00 00 40"
    )

    To retrieve a list of all program and tone name requests:
    all_requests = PROGRAM_AND_TONE_NAME_REQUESTS
"""

from enum import Enum
from typing import Union

from jdxi_editor.midi.sysex.request.hex import JDXISysExHex


[docs] def roland_checksum(data: str) -> str: """ Compute Roland checksum for a given SysEx data string (space-separated hex). """ byte_values = [int(x, 16) for x in data.split()] checksum = (128 - (sum(byte_values) % 128)) % 128 return f"{checksum:02X}"
[docs] def create_request(header: str, temp_area: Union[str, JDXISysExHex], part: str) -> str: """ Create a SysEx request string using header, area, parameter, and Roland checksum. The Roland RQ1 checksum must cover all bytes from the first byte after the command (0x11) through the last data byte. The header's final token is the first address byte (e.g. 18 for TEMPORARY_PROGRAM), so it must be included in the checksum input. """ temp_area_str = temp_area.value if isinstance(temp_area, Enum) else temp_area data = f"{temp_area_str} {part}" # Include header's last token (first address byte) in checksum per Roland spec checksum_data = f"{header.split()[-1]} {data}" checksum = roland_checksum(checksum_data) return f"{header} {data} {checksum} {JDXISysExHex.END}"