Source code for jdxi_editor.ui.editors.analog.oscillator.section

"""
Analog Oscillator Section
"""

from typing import Callable

from decologr import Decologr as log

from jdxi_editor.midi.data.address.address import JDXiSysExAddress
from jdxi_editor.midi.data.analog.oscillator import AnalogOscillatorWidgetTypes
from jdxi_editor.midi.data.parameter.analog.spec import JDXiMidiAnalog as Analog
from jdxi_editor.midi.io.helper import MidiIOHelper
from jdxi_editor.ui.editors.analog.oscillator.widget import AnalogOscillatorWidgets
from jdxi_editor.ui.editors.analog.oscillator.widget_spec import (
    AnalogOscillatorLayoutSpec,
)
from jdxi_editor.ui.editors.base.layout.spec import OscillatorFeature
from jdxi_editor.ui.editors.base.oscillator.section import BaseOscillatorSection
from jdxi_editor.ui.editors.base.oscillator.widget import OscillatorWidgets
from jdxi_editor.ui.widgets.editor import IconType
from jdxi_editor.ui.widgets.spec import (
    PitchEnvelopeSpec,
    PWMSpec,
    SliderSpec,
    SwitchSpec,
)


[docs] class AnalogOscillatorSection(BaseOscillatorSection): """Analog Oscillator Section"""
[docs] SYNTH_SPEC = Analog
def __init__( self, waveform_selected_callback: Callable, wave_buttons: dict, midi_helper: MidiIOHelper, address: JDXiSysExAddress, send_midi_parameter: Callable = None, ): """ Initialize the AnalogOscillatorSection :param waveform_selected_callback: Callable :param wave_buttons: dict to store waveform buttons (waveform -> button mapping) :param midi_helper: MidiIOHelper :param address: RolandSysExAddress :param send_midi_parameter: Callable to send MIDI parameter updates """ log.info( scope=self.__class__.__name__, message="AnalogOscillatorSection __init__ start (address=%s)" % (getattr(address, "__repr__", lambda: address)(),), )
[docs] self.widgets: OscillatorWidgets | None = None
[docs] self._on_waveform_selected = waveform_selected_callback
[docs] self.analog: bool = True
[docs] self.wave_shapes = self.generate_mode_button_specs()
log.info(scope=self.__class__.__name__, message="_define_spec ...") self._define_spec() log.info(scope=self.__class__.__name__, message="calling super().__init__ ...") super().__init__( icons_row_type=IconType.OSCILLATOR, analog=True, midi_helper=midi_helper, address=address, send_midi_parameter=send_midi_parameter, ) log.info( scope=self.__class__.__name__, message="after super().__init__ (controls keys: %s)" % (list(self.controls.keys()) if getattr(self, "controls", None) else [],), )
[docs] self.address = address
log.info(scope=self.__class__.__name__, message="calling finalize() ...") self.finalize() log.info( scope=self.__class__.__name__, message="AnalogOscillatorSection __init__ done.", )
[docs] def _define_spec(self): self.spec: AnalogOscillatorLayoutSpec = self._build_layout_spec()
[docs] def build_widgets(self): """Build widgets: run base to create waveform buttons, pitch env, PWM, then analog-specific (sub-osc switch, tuning).""" super().build_widgets() # All oscillator widgets in one container self.widgets = AnalogOscillatorWidgets( waveform_buttons=self._create_waveform_buttons(), pitch_env_widget=self._create_pitch_env_widget(), pwm_widget=self._create_pwm_widget(), switches=( [self.sub_oscillator_type_switch] if self.sub_oscillator_type_switch else [] ), tuning=self.tuning_sliders or [], env=( [self.osc_pitch_env_velocity_sensitivity_slider] if self.osc_pitch_env_velocity_sensitivity_slider else [] ), sub_oscillator_type_switch=self.sub_oscillator_type_switch, osc_pitch_env_velocity_sensitivity_slider=self.osc_pitch_env_velocity_sensitivity_slider, osc_pitch_coarse_slider=self._resolve_rule_widget( AnalogOscillatorWidgetTypes.OSC_PITCH_COARSE ), osc_pitch_fine_slider=self._resolve_rule_widget( AnalogOscillatorWidgetTypes.OSC_PITCH_FINE ), pitch_env_widgets=getattr( self, AnalogOscillatorWidgetTypes.PITCH_ENV_WIDGETS, [] ), ) # Aliases to old widgets for back compatibility self._register_widget( AnalogOscillatorWidgetTypes.PITCH_ENV, self.widgets.pitch_env_widget, ) self._register_widget( AnalogOscillatorWidgetTypes.PWM, self.widgets.pwm_widget, ) self.pitch_env_widgets = [self.widgets.pitch_env_widget]
[docs] def generate_mode_button_specs(self) -> list: """Generate waveform button specs (same pattern as Analog LFO / Analog Filter).""" return self._build_wave_specs(self.ANALOG_WAVES)
[docs] def _build_additional_widgets(self): """Build additional Analog Widgets""" self._create_env_slider() self._create_suboscillator_switches()
[docs] def _create_suboscillator_switches(self): """Sub Oscillator Type switch; optional when SWITCH_SPECS is empty""" if not hasattr(self, "spec") or not hasattr(self.spec, "switches"): switches = [] else: switches = self._build_switches(self.spec.switches) self.sub_oscillator_type_switch = switches[0] if len(switches) == 1 else None
[docs] def _create_env_slider(self): """Create env sliders""" # Only one slider BTW, despite the plural name env_sliders = self._build_sliders(self.spec.env) self.osc_pitch_env_velocity_sensitivity_slider = ( env_sliders[0] if len(env_sliders) == 1 else None )
[docs] def _build_layout_spec(self) -> AnalogOscillatorLayoutSpec: """Build Analog Oscillator Layout Spec""" S = self.SYNTH_SPEC switches = [ SwitchSpec( S.Param.SUB_OSCILLATOR_TYPE, S.Param.SUB_OSCILLATOR_TYPE.display_name, options=S.Display.Options.SUB_OSCILLATOR_TYPE, ), ] tuning = [ SliderSpec( S.Param.OSC_PITCH_COARSE, S.Display.Name.OSC_PITCH_COARSE, vertical=True, ), SliderSpec( S.Param.OSC_PITCH_FINE, S.Display.Name.OSC_PITCH_FINE, vertical=True, ), ] # --- Analog has this extra parameter, so here is the slider env = [ SliderSpec( S.Param.OSC_PITCH_ENV_VELOCITY_SENSITIVITY, S.Display.Name.OSC_PITCH_ENV_VELOCITY_SENSITIVITY, vertical=True, ) ] pwm = PWMSpec( pulse_width_param=Analog.Param.OSC_PULSE_WIDTH, mod_depth_param=Analog.Param.OSC_PULSE_WIDTH_MOD_DEPTH, ) pitch_env = PitchEnvelopeSpec( attack_param=Analog.Param.OSC_PITCH_ENV_ATTACK_TIME, decay_param=Analog.Param.OSC_PITCH_ENV_DECAY_TIME, depth_param=Analog.Param.OSC_PITCH_ENV_DEPTH, ) return AnalogOscillatorLayoutSpec( switches=switches, tuning=tuning, env=env, pwm=pwm, pitch_env=pitch_env, features={ OscillatorFeature.WAVEFORM, OscillatorFeature.TUNING, OscillatorFeature.PWM, OscillatorFeature.PITCH_ENV, OscillatorFeature.PW_SHIFT, }, feature_tabs={ OscillatorFeature.TUNING: "_add_tuning_tab", OscillatorFeature.PWM: "_add_pwm_tab", OscillatorFeature.PITCH_ENV: "_add_pitch_env_tab", }, )