@ -54,6 +54,30 @@ class ArpeggiatorControls(QWidget):
self . armed_note_limit_button = None
# Speed changes apply immediately - no armed state needed
# Parameter override tracking - when checked, preset changes ignore these parameters
self . parameter_overrides = {
' root_note ' : False ,
' octave ' : False ,
' scale ' : False ,
' scale_note_start ' : False ,
' pattern_type ' : False ,
' pattern_length ' : False ,
' note_limit ' : False ,
' channel_distribution ' : False ,
' note_speed ' : False ,
' gate ' : False ,
' swing ' : False ,
' velocity ' : False ,
' tempo ' : False ,
' delay_enabled ' : False ,
' delay_length ' : False ,
' delay_timing ' : False ,
' delay_fade ' : False
}
# Override checkbox widgets
self . override_checkboxes = { }
self . setup_ui ( )
self . connect_signals ( )
@ -169,6 +193,39 @@ class ArpeggiatorControls(QWidget):
else :
self . apply_button_style ( button , 12 , " normal " )
def create_parameter_label_with_override ( self , text , param_name ) :
""" Create a label with an override checkbox """
from PyQt5.QtWidgets import QCheckBox
container = QWidget ( )
layout = QHBoxLayout ( container )
layout . setContentsMargins ( 0 , 0 , 0 , 0 )
layout . setSpacing ( 8 )
# Create override checkbox
checkbox = QCheckBox ( )
checkbox . setFixedSize ( 16 , 16 )
checkbox . setToolTip ( f " Override {text} - when checked, presets won ' t change this parameter " )
checkbox . stateChanged . connect ( lambda state , param = param_name : self . on_parameter_override_changed ( param , state == 2 ) )
# Create label
label = QLabel ( text )
# Add to layout
layout . addWidget ( checkbox )
layout . addWidget ( label )
layout . addStretch ( ) # Push everything to the left
# Store checkbox reference
self . override_checkboxes [ param_name ] = checkbox
return container
def on_parameter_override_changed ( self , param_name , is_overridden ) :
""" Handle parameter override checkbox changes """
self . parameter_overrides [ param_name ] = is_overridden
print ( f " DEBUG: Parameter {param_name} override: {is_overridden} " )
def setup_ui ( self ) :
""" Clean quadrant layout with readable buttons """
# Main grid with better spacing and expansion
@ -207,7 +264,7 @@ class ArpeggiatorControls(QWidget):
layout . setContentsMargins ( 8 , 8 , 8 , 8 )
# Root notes - 12 buttons in horizontal row, NO spacing between buttons
layout . addWidget ( QLabel ( " Root Note: " ) )
layout . addWidget ( self . create_parameter_label_with_override ( " Root Note: " , " root_note " ) )
notes_widget = QWidget ( )
notes_layout = QHBoxLayout ( notes_widget )
notes_layout . setSpacing ( 0 ) # NO spacing between buttons
@ -228,7 +285,7 @@ class ArpeggiatorControls(QWidget):
layout . addWidget ( notes_widget )
# Octaves - 6 buttons in horizontal row, NO spacing between buttons
layout . addWidget ( QLabel ( " Octave: " ) )
layout . addWidget ( self . create_parameter_label_with_override ( " Octave: " , " octave " ) )
octave_widget = QWidget ( )
octave_layout = QHBoxLayout ( octave_widget )
octave_layout . setSpacing ( 0 ) # NO spacing between buttons
@ -248,7 +305,7 @@ class ArpeggiatorControls(QWidget):
layout . addWidget ( octave_widget )
# Scales - 2 rows of 4, minimal vertical spacing
layout . addWidget ( QLabel ( " Scale: " ) )
layout . addWidget ( self . create_parameter_label_with_override ( " Scale: " , " scale " ) )
scales_widget = QWidget ( )
scales_layout = QGridLayout ( scales_widget )
scales_layout . setSpacing ( 0 ) # NO horizontal spacing
@ -274,7 +331,7 @@ class ArpeggiatorControls(QWidget):
layout . addWidget ( scales_widget )
# Scale notes selection
layout . addWidget ( QLabel ( " Scale Notes: " ) )
layout . addWidget ( self . create_parameter_label_with_override ( " Scale Notes: " , " scale_note_start " ) )
scale_notes_widget = QWidget ( )
self . scale_notes_layout = QGridLayout ( scale_notes_widget )
self . scale_notes_layout . setSpacing ( 2 )
@ -302,7 +359,7 @@ class ArpeggiatorControls(QWidget):
layout . setSpacing ( 6 )
layout . setContentsMargins ( 8 , 8 , 8 , 8 )
layout . addWidget ( QLabel ( " Distribution Pattern: " ) )
layout . addWidget ( self . create_parameter_label_with_override ( " Distribution Pattern: " , " channel_distribution " ) )
# 2 rows of 4 distribution buttons
dist_widget = QWidget ( )
@ -358,7 +415,7 @@ class ArpeggiatorControls(QWidget):
layout . setSpacing ( 6 )
layout . setContentsMargins ( 8 , 8 , 8 , 8 )
layout . addWidget ( QLabel ( " Arpeggio Pattern: " ) )
layout . addWidget ( self . create_parameter_label_with_override ( " Arpeggio Pattern: " , " pattern_type " ) )
# 2 rows of 4 pattern buttons
pattern_widget = QWidget ( )
@ -389,7 +446,7 @@ class ArpeggiatorControls(QWidget):
layout . addWidget ( pattern_widget )
# Pattern length buttons
layout . addWidget ( QLabel ( " Pattern Length: " ) )
layout . addWidget ( self . create_parameter_label_with_override ( " Pattern Length: " , " pattern_length " ) )
length_widget = QWidget ( )
length_layout = QGridLayout ( length_widget )
length_layout . setSpacing ( 0 ) # NO horizontal spacing
@ -414,7 +471,7 @@ class ArpeggiatorControls(QWidget):
layout . addWidget ( length_widget )
# Note limit buttons
layout . addWidget ( QLabel ( " Note Limit: " ) )
layout . addWidget ( self . create_parameter_label_with_override ( " Note Limit: " , " note_limit " ) )
note_limit_widget = QWidget ( )
note_limit_layout = QGridLayout ( note_limit_widget )
note_limit_layout . setSpacing ( 0 ) # NO horizontal spacing
@ -446,7 +503,7 @@ class ArpeggiatorControls(QWidget):
# Tempo
tempo_layout = QHBoxLayout ( )
tempo_layout . addWidget ( QLabel ( " Tempo: " ) )
tempo_layout . addWidget ( self . create_parameter_label_with_override ( " Tempo: " , " tempo " ) )
self . tempo_spin = QSpinBox ( )
self . tempo_spin . setRange ( 40 , 200 )
self . tempo_spin . setValue ( 120 )
@ -456,7 +513,7 @@ class ArpeggiatorControls(QWidget):
layout . addLayout ( tempo_layout )
# Speed buttons
layout . addWidget ( QLabel ( " Note Speed: " ) )
layout . addWidget ( self . create_parameter_label_with_override ( " Note Speed: " , " note_speed " ) )
speed_widget = QWidget ( )
speed_layout = QHBoxLayout ( speed_widget )
speed_layout . setSpacing ( 0 ) # NO spacing between buttons
@ -482,7 +539,7 @@ class ArpeggiatorControls(QWidget):
# Gate
gate_layout = QHBoxLayout ( )
gate_layout . addWidget ( QLabel ( " Gate: " ) )
gate_layout . addWidget ( self . create_parameter_label_with_override ( " Gate: " , " gate " ) )
self . gate_slider = QSlider ( Qt . Horizontal )
self . gate_slider . setRange ( 10 , 200 )
self . gate_slider . setValue ( 100 )
@ -495,7 +552,7 @@ class ArpeggiatorControls(QWidget):
# Swing
swing_layout = QHBoxLayout ( )
swing_layout . addWidget ( QLabel ( " Swing: " ) )
swing_layout . addWidget ( self . create_parameter_label_with_override ( " Swing: " , " swing " ) )
self . swing_slider = QSlider ( Qt . Horizontal )
self . swing_slider . setRange ( - 100 , 100 )
self . swing_slider . setValue ( 0 )
@ -508,7 +565,7 @@ class ArpeggiatorControls(QWidget):
# Velocity
velocity_layout = QHBoxLayout ( )
velocity_layout . addWidget ( QLabel ( " Velocity: " ) )
velocity_layout . addWidget ( self . create_parameter_label_with_override ( " Velocity: " , " velocity " ) )
self . velocity_slider = QSlider ( Qt . Horizontal )
self . velocity_slider . setRange ( 1 , 127 )
self . velocity_slider . setValue ( 80 )
@ -525,7 +582,7 @@ class ArpeggiatorControls(QWidget):
# Delay toggle
self . delay_enabled = False
delay_toggle_layout = QHBoxLayout ( )
delay_toggle_layout . addWidget ( QLabel ( " Delay/Echo: " ) )
delay_toggle_layout . addWidget ( self . create_parameter_label_with_override ( " Delay/Echo: " , " delay_enabled " ) )
self . delay_toggle = QPushButton ( " OFF " )
self . delay_toggle . setFixedSize ( 50 , 20 )
self . delay_toggle . setCheckable ( True )
@ -537,7 +594,7 @@ class ArpeggiatorControls(QWidget):
# Delay length (0-8 repeats)
delay_length_layout = QHBoxLayout ( )
delay_length_layout . addWidget ( QLabel ( " Delay Length: " ) )
delay_length_layout . addWidget ( self . create_parameter_label_with_override ( " Delay Length: " , " delay_length " ) )
self . delay_length_spin = QSpinBox ( )
self . delay_length_spin . setRange ( 0 , 8 )
self . delay_length_spin . setValue ( 3 )
@ -548,7 +605,7 @@ class ArpeggiatorControls(QWidget):
delay_layout . addLayout ( delay_length_layout )
# Delay timing buttons (same as note speed)
delay_timing_label = QLabel ( " Delay Timing: " )
delay_timing_label = self . create_parameter_label_with_override ( " Delay Timing: " , " delay_timing " )
delay_timing_label . setEnabled ( False )
delay_layout . addWidget ( delay_timing_label )
self . delay_timing_label = delay_timing_label
@ -583,7 +640,7 @@ class ArpeggiatorControls(QWidget):
# Delay fade slider (percentage)
delay_fade_layout = QHBoxLayout ( )
delay_fade_label = QLabel ( " Delay Fade: " )
delay_fade_label = self . create_parameter_label_with_override ( " Delay Fade: " , " delay_fade " )
delay_fade_label . setEnabled ( False )
delay_fade_layout . addWidget ( delay_fade_label )
self . delay_fade_label = delay_fade_label