@ -8,7 +8,8 @@ Generates arpeggio patterns, handles timing, and integrates with routing and vol
import time
import math
import threading
from typing import Dict , List , Optional , Tuple , Set
import random
from typing import Dict , List , Optional , Tuple , Set , Union
from PyQt5.QtCore import QObject , pyqtSignal , QTimer
from .midi_channel_manager import MIDIChannelManager
@ -237,14 +238,20 @@ class ArpeggiatorEngine(QObject):
""" Set base velocity 0-127 """
self . velocity = max ( 0 , min ( 127 , velocity ) )
def set_scale_note_start ( self , scale_note_index : int ) :
""" Set which scale note to start the arpeggio from """
self . scale_note_start = max ( 0 , scale_note_index )
def set_scale_note_start ( self , scale_note_index : Union [ int , str ] ) :
""" Set which scale note to start the arpeggio from (integer index or ' random ' ) """
if scale_note_index == " random " :
self . scale_note_start = " random "
else :
self . scale_note_start = max ( 0 , scale_note_index )
self . regenerate_pattern ( )
def arm_scale_note_start ( self , scale_note_index : int ) :
def arm_scale_note_start ( self , scale_note_index : Union [ int , str ] ) :
""" Arm a scale note start position to change at pattern end """
self . armed_scale_note_start = max ( 0 , scale_note_index )
if scale_note_index == " random " :
self . armed_scale_note_start = " random "
else :
self . armed_scale_note_start = max ( 0 , scale_note_index )
self . armed_state_changed . emit ( )
def clear_armed_scale_note_start ( self ) :
@ -618,7 +625,8 @@ class ArpeggiatorEngine(QObject):
root_in_octave = self . root_note % 12
# Get the interval for the selected scale degree
start_degree = self . scale_note_start % len ( scale_intervals )
actual_start = self . _get_actual_scale_note_start ( )
start_degree = actual_start % len ( scale_intervals )
start_interval = scale_intervals [ start_degree ]
starting_note = base_octave * 12 + root_in_octave + start_interval
@ -651,7 +659,8 @@ class ArpeggiatorEngine(QObject):
root_in_octave = self . root_note % 12
# Get the interval for the selected scale degree
start_degree = self . scale_note_start % len ( scale_intervals )
actual_start = self . _get_actual_scale_note_start ( )
start_degree = actual_start % len ( scale_intervals )
start_interval = scale_intervals [ start_degree ]
starting_note = base_octave * 12 + root_in_octave + start_interval
@ -695,7 +704,8 @@ class ArpeggiatorEngine(QObject):
root_in_octave = self . root_note % 12
# Get the interval for the selected scale degree
start_degree = self . scale_note_start % len ( scale_intervals )
actual_start = self . _get_actual_scale_note_start ( )
start_degree = actual_start % len ( scale_intervals )
# Generate only the number of notes specified by note_limit
for i in range ( self . note_limit ) :
@ -713,6 +723,14 @@ class ArpeggiatorEngine(QObject):
return notes
def _get_actual_scale_note_start ( self ) - > int :
""" Get the actual scale note start index to use, handling random selection """
if self . scale_note_start == " random " :
scale_intervals = self . SCALES [ self . scale ]
return random . randint ( 0 , len ( scale_intervals ) - 1 )
else :
return self . scale_note_start
def _get_all_scale_notes ( self ) - > List [ int ] :
""" Get all available scale notes in both directions for patterns that need full range """
# Use limited scale notes if note_limit is less than full scale