Source code for jdxi_editor.midi.sysex.parser.tone_mapper

"""
tone mapper functions
"""

from __future__ import annotations

from decologr import Decologr as log

from jdxi_editor.midi.data.address.address import JDXiSysExOffsetProgramLMB
from jdxi_editor.midi.map import JDXiMapSynthTone, JDXiMapTemporaryArea
from jdxi_editor.midi.map.drum_tone import JDXiMapDrumTone
from jdxi_editor.midi.message.sysex.offset import JDXiSysExMessageLayout

# LMB byte → section name for TEMPORARY_PROGRAM area (0x18).
# Same LMB values mean different things in TEMPORARY_TONE (0x19).
[docs] PROGRAM_SECTION_MAP = { JDXiSysExOffsetProgramLMB.COMMON.value: "COMMON", JDXiSysExOffsetProgramLMB.VOCAL_EFFECT.value: "VOCAL_EFFECT", JDXiSysExOffsetProgramLMB.EFFECT_1.value: "EFFECT_1", JDXiSysExOffsetProgramLMB.EFFECT_2.value: "EFFECT_2", JDXiSysExOffsetProgramLMB.DELAY.value: "DELAY", JDXiSysExOffsetProgramLMB.REVERB.value: "REVERB", JDXiSysExOffsetProgramLMB.PART_DIGITAL_SYNTH_1.value: "PART_DIGITAL_SYNTH_1", JDXiSysExOffsetProgramLMB.PART_DIGITAL_SYNTH_2.value: "PART_DIGITAL_SYNTH_2", JDXiSysExOffsetProgramLMB.PART_ANALOG.value: "PART_ANALOG", JDXiSysExOffsetProgramLMB.PART_DRUM.value: "PART_DRUM", JDXiSysExOffsetProgramLMB.ZONE_DIGITAL_SYNTH_1.value: "ZONE_DIGITAL_SYNTH_1", JDXiSysExOffsetProgramLMB.ZONE_DIGITAL_SYNTH_2.value: "ZONE_DIGITAL_SYNTH_2", JDXiSysExOffsetProgramLMB.ZONE_ANALOG.value: "ZONE_ANALOG", JDXiSysExOffsetProgramLMB.ZONE_DRUM.value: "ZONE_DRUM", JDXiSysExOffsetProgramLMB.CONTROLLER.value: "CONTROLLER", }
[docs] def get_program_section(byte_value: int) -> tuple[str, int]: """ Map LMB byte to section name for TEMPORARY_PROGRAM area. :param byte_value: int LMB byte from SysEx address :return: tuple[str, int] section name and offset (0 for program sections) """ return PROGRAM_SECTION_MAP.get(byte_value, "COMMON"), 0
[docs] class TemporaryArea:
[docs] TEMPORARY_PROGRAM = "TEMPORARY_PROGRAM"
[docs] ANALOG_SYNTH = "ANALOG_SYNTH"
[docs] DIGITAL_SYNTH_1 = "DIGITAL_SYNTH_1"
[docs] DIGITAL_SYNTH_2 = "DIGITAL_SYNTH_2"
[docs] DRUM_KIT = "DRUM_KIT"
[docs] def get_temporary_area(data: bytes) -> str: """ Map address bytes to corresponding temporary area. :param data: bytes SysEx message data :return: str Temporary Area: TEMPORARY_PROGRAM, ANALOG_SYNTH, SYSTEM_COMMON, ... """ if len(data) < JDXiSysExMessageLayout.ADDRESS.LSB: return "Unknown" msb = data[JDXiSysExMessageLayout.ADDRESS.MSB] umb = data[JDXiSysExMessageLayout.ADDRESS.UMB] lmb = data[JDXiSysExMessageLayout.ADDRESS.LMB] # System area (0x02): use 3-byte key to distinguish Common vs Controller if msb == 0x02: from jdxi_editor.midi.map.temporary_area import SYSTEM_AREA_MAP return SYSTEM_AREA_MAP.get((msb, umb, lmb), "Unknown") temp_area_bytes = data[ JDXiSysExMessageLayout.ADDRESS.MSB : JDXiSysExMessageLayout.ADDRESS.LMB ] return JDXiMapTemporaryArea.MAP.get(tuple(temp_area_bytes), "Unknown")
[docs] def get_partial_address(part_name: str) -> str: """ Map partial address to corresponding temporary area. :param part_name: str :return: str """ for key, value in JDXiMapSynthTone.MAP.items(): if value == part_name: return key return "COMMON"
[docs] def get_drum_tone(byte_value: int) -> tuple[str, int]: """ Map byte value to corresponding synth tone. :param byte_value: int :return: str """ try: offset = 0 drum_tone = JDXiMapDrumTone.MAP.get(byte_value) if drum_tone is None: drum_tone = JDXiMapDrumTone.MAP.get(byte_value - 1, "COMMON") offset = 1 return drum_tone, offset except Exception as ex: log.error(f"Error {ex} occurred getting drum type") return "COMMON", 0
[docs] def get_synth_tone(byte_value: int) -> tuple[str, int]: """ Map byte value to corresponding synth tone. :param byte_value: int byte value to query :return: tuple[str, int] """ return JDXiMapSynthTone.MAP.get(byte_value, "COMMON"), 0