from typing import Optional
from jdxi_editor.midi.parameter.spec import ParameterSpec
from picomidi.sysex.parameter.address import AddressParameter
[docs]
class SystemCommonParam(AddressParameter):
"""System Common parameters (address 02 00 00 xx)"""
def __init__(
self,
address: int,
min_val: int,
max_val: int,
display_min: Optional[int] = None,
display_max: Optional[int] = None,
description: Optional[str] = None,
display_name: Optional[str] = None,
options: Optional[list] = None,
values: Optional[list] = None,
):
super().__init__(address, min_val, max_val)
[docs]
self._display_name = display_name
# Raw (min_val, max_val) per Roland spec; display range in min_display, max_display
[docs]
MASTER_TUNE = ParameterSpec(
0x00, 24, 2024, -100, 100, "Master Tune"
) # 24-2024 -> -100..+100 cents
[docs]
MASTER_KEY_SHIFT = ParameterSpec(
0x04, 40, 88, -24, 24, "Master Key Shift"
) # 40-88 -> -24..+24 semitones
[docs]
MASTER_LEVEL = ParameterSpec(0x05, 0, 127, description="Master Level")
[docs]
PROGRAM_CTRL_CH = ParameterSpec(0x11, 0, 16, description="Program Control Channel")
[docs]
RX_PROGRAM_CHANGE = ParameterSpec(0x29, 0, 1, description="Receive Program Change")
[docs]
RX_BANK_SELECT = ParameterSpec(0x2A, 0, 1, description="Receive Bank Select")
@classmethod
[docs]
def __iter__(cls):
for name in (
"MASTER_TUNE",
"MASTER_KEY_SHIFT",
"MASTER_LEVEL",
"PROGRAM_CTRL_CH",
"RX_PROGRAM_CHANGE",
"RX_BANK_SELECT",
):
p = getattr(cls, name)
p.name = name
yield p
[docs]
def get_display_value(self) -> tuple:
"""Return (min_display, max_display) for slider/UI range."""
val = getattr(self, "value", self)
return (
getattr(val, "min_display", getattr(self, "min_val", 0)),
getattr(val, "max_display", getattr(self, "max_val", 127)),
)
@staticmethod
@staticmethod
[docs]
def get_by_name(param_name: str) -> Optional["SystemCommonParam"]:
"""Get the Parameter by name."""
name_to_param = {
"MASTER_TUNE": SystemCommonParam.MASTER_TUNE,
"MASTER_KEY_SHIFT": SystemCommonParam.MASTER_KEY_SHIFT,
"MASTER_LEVEL": SystemCommonParam.MASTER_LEVEL,
"PROGRAM_CTRL_CH": SystemCommonParam.PROGRAM_CTRL_CH,
"RX_PROGRAM_CHANGE": SystemCommonParam.RX_PROGRAM_CHANGE,
"RX_BANK_SELECT": SystemCommonParam.RX_BANK_SELECT,
}
return name_to_param.get(param_name)
[docs]
def get_nibbled_byte_size(self) -> int:
"""Get the nibbled byte size of the parameter"""
if self.max_value - self.min_value <= 255:
return 1
else:
return 4 # I don't know of any 2 byte parameters
# Attach conversion helpers after class is defined (avoids Enum __init__ issues)
SystemCommonParam.MASTER_TUNE.convert_to_midi = lambda v: max(
24, min(2024, 1024 + int(v * 10))
)
SystemCommonParam.MASTER_TUNE.convert_from_midi = lambda v: (v - 1024) / 10
SystemCommonParam.MASTER_TUNE.get_nibbled_size = lambda: 4
SystemCommonParam.MASTER_KEY_SHIFT.convert_to_midi = lambda v: max(
40, min(88, 64 + int(v))
)
SystemCommonParam.MASTER_KEY_SHIFT.convert_from_midi = lambda v: v - 64