Source code for envelope.base

from typing import Callable, Optional

from decologr import Decologr as log
from picomidi.sysex.parameter.address import AddressParameter
from PySide6.QtCore import Signal
from PySide6.QtWidgets import QWidget

from jdxi_editor.midi.data.address.address import RolandSysExAddress
from jdxi_editor.midi.io.helper import MidiIOHelper
from jdxi_editor.midi.utils.conversions import midi_value_to_ms, ms_to_midi_value
from jdxi_editor.ui.widgets.slider import Slider

[docs] TOOLTIPS = { "attack_time": "Time taken for the pitch to reach peak after note-on.", "decay_time": "Time taken for the pitch to fall from peak to sustain.", "sustain_level": "Sustain level of the pitch envelope (0.0 to 1.0).", "initial_level": "Initial pitch level at note-on (0.0 to 1.0).", "release_time": "Time taken for the pitch to fall to zero after note-off.", "peak_level": "Maximum pitch modulation depth.", "pulse_width": "Width of the pulse waveform (0% = narrow, 100% = square).", "mod_depth": "Modulation depth of the PWM LFO.", }
[docs] class EnvelopeWidgetBase(QWidget): """Base class for envelope widgets in the JD-Xi editor"""
[docs] envelope_changed = Signal(dict)
def __init__( self, parameters: list[AddressParameter], envelope_keys: list[str], create_parameter_slider: Callable, midi_helper: Optional[MidiIOHelper] = None, address: Optional[RolandSysExAddress] = None, controls: Optional[dict[AddressParameter, Slider]] = None, parent: Optional[QWidget] = None, ): super().__init__(parent)
[docs] self.plot = None
[docs] self.address = address
[docs] self.midi_helper = midi_helper
[docs] self.controls = controls if controls else {}
[docs] self.envelope = {}
[docs] self._create_parameter_slider = create_parameter_slider
[docs] self._params = parameters
[docs] self._keys = envelope_keys
[docs] self._control_widgets = []
[docs] def setEnabled(self, enabled: bool): super().setEnabled(enabled) for control in self._control_widgets: control.setEnabled(enabled) if hasattr(self, "plot") and self.plot: self.plot.setEnabled(enabled)
[docs] def update(self): """Update the envelope values and plot""" super().update() if hasattr(self, "plot") and self.plot: self.plot.update()
[docs] def set_values(self, envelope: dict) -> None: """ Update envelope values and trigger address redraw :param envelope: dict :return: None """ self.envelope = envelope self.update()
[docs] def emit_envelope_changed(self) -> None: """ Emit the envelope changed signal :return: None """ if hasattr(self, "plot") and self.plot: self.plot.set_values(self.envelope)
[docs] def update_envelope_from_controls(self) -> None: """ Update envelope values from slider controls. :return: """ try: for param, slider in self.controls.items(): key = param.get_envelope_param_type() val = slider.value() if isinstance(self.envelope.get(key), float): if key.endswith("_level"): self.envelope[key] = val # 0.0 to 1.0 float else: self.envelope[key] = midi_value_to_ms(val) else: self.envelope[key] = val self.envelope_changed.emit(self.envelope) except Exception as ex: log.error(f"Error updating envelope from controls: {ex}") if hasattr(self, "plot") and self.plot: self.plot.set_values(self.envelope)
[docs] def update_controls_from_envelope(self) -> None: """ Update slider controls from envelope values. :return: None """ try: for param, slider in self.controls.items(): key = param.get_envelope_param_type() if key is None: log.info( f"parameter {param.name} is not in envelope, skipping update" ) elif hasattr(slider, "setValue"): if param.name in ["OSC_WAVE"]: continue elif isinstance(self.envelope.get(key), float) and key.endswith( "_level" ): if hasattr(slider, "setValue"): slider.setValue(self.envelope[key]) elif isinstance(self.envelope.get(key), float) and key.endswith( "_width" ): if hasattr(slider, "setValue"): slider.setValue(self.envelope[key]) elif isinstance(self.envelope.get(key), float): if hasattr(slider, "setValue"): slider.setValue(self.envelope[key]) else: pass # Not in envelope or not a float except Exception as ex: log.error(f"Error updating controls from envelope: {ex}") if hasattr(self, "plot") and self.plot: self.plot.set_values(self.envelope)