@@ -220,7 +220,6 @@ void RhythmPatternExplorerAudioProcessor::processBlock (juce::AudioBuffer<float>
220220
221221    //  Log every 50 calls or when BPM changes, with focus on 200+ BPM
222222    debugCallCount++;
223-     int  currentBPMInt = static_cast <int >(currentBPM);
224223
225224    //  More frequent logging at high BPMs
226225    bool  shouldLog = false ;
@@ -378,6 +377,19 @@ void RhythmPatternExplorerAudioProcessor::processBlock (juce::AudioBuffer<float>
378377        switch  (lengthUnit) {
379378            case  0 : //  Steps mode - each step represents a subdivision, pattern length is IGNORED
380379            {
380+                 //  STEPS MODE DOCUMENTATION:
381+                 //  In Steps mode, each step represents a specific subdivision (16th, 8th, 8th triplet, etc.)
382+                 //  The subdivision parameter determines what each step represents.
383+                 //  Pattern length value is completely ignored - only subdivision and pattern size matter.
384+                 // 
385+                 //  Examples:
386+                 //  - 8 steps × 16th notes = 8 × 0.25 = 2 beats total (half note duration)
387+                 //  - 9 steps × 8th notes = 9 × 0.5 = 4.5 beats total  
388+                 //  - 9 steps × 8th triplets = 9 × (1/3) = 3 beats total
389+                 // 
390+                 //  This creates "microrhythm" functionality where the same pattern can be
391+                 //  played at different rhythmic resolutions by changing the subdivision.
392+                 
381393                //  Convert subdivision index to beat fraction per step
382394                double  subdivisionBeatsPerStep = getSubdivisionInBeats (subdivisionIndex);
383395
@@ -743,6 +755,7 @@ void RhythmPatternExplorerAudioProcessor::syncPositionWithHost(const juce::Audio
743755        switch  (lengthUnit) {
744756            case  0 : //  Steps mode - each step represents a subdivision, pattern length is IGNORED
745757            {
758+                 //  Steps mode: Same logic as main calculation - subdivision determines step duration
746759                double  subdivisionBeatsPerStep = getSubdivisionInBeats (subdivisionIndex);
747760                patternLengthInBeats = subdivisionBeatsPerStep * patternSteps;
748761
@@ -969,7 +982,7 @@ void RhythmPatternExplorerAudioProcessor::setUPIInput(const juce::String& upiPat
969982        if  (progressiveOffset != 0 )
970983        {
971984            auto  currentPattern = patternEngine.getCurrentPattern ();
972-             auto  rotatedPattern = rotatePatternBySteps (currentPattern, progressiveOffset);
985+             auto  rotatedPattern = UPIParser::rotatePattern (currentPattern, progressiveOffset);
973986            patternEngine.setPattern (rotatedPattern);
974987
975988            //  Debug log rotation
@@ -1253,26 +1266,6 @@ void RhythmPatternExplorerAudioProcessor::checkMidiInputForTriggers(juce::MidiBu
12531266    }
12541267}
12551268
1256- std::vector<bool > RhythmPatternExplorerAudioProcessor::rotatePatternBySteps (const  std::vector<bool >& pattern, int  steps)
1257- {
1258-     if  (pattern.empty ()) 
1259-         return  pattern;
1260-     
1261-     std::vector<bool > rotated (pattern.size ());
1262-     int  size = static_cast <int >(pattern.size ());
1263-     
1264-     //  Normalize steps to be within pattern size
1265-     steps = steps % size;
1266-     if  (steps < 0 ) steps += size;
1267-     
1268-     for  (int  i = 0 ; i < size; ++i)
1269-     {
1270-         int  newIndex = (i + steps) % size;
1271-         rotated[newIndex] = pattern[i];
1272-     }
1273-     
1274-     return  rotated;
1275- }
12761269
12771270void  RhythmPatternExplorerAudioProcessor::advanceProgressiveLengthening ()
12781271{
@@ -1379,7 +1372,7 @@ void RhythmPatternExplorerAudioProcessor::applyCurrentScenePattern()
13791372    {
13801373        //  Apply progressive offset by rotating the generated pattern
13811374        auto  currentPattern = patternEngine.getCurrentPattern ();
1382-         auto  rotatedPattern = rotatePatternBySteps (currentPattern, progressiveOffset);
1375+         auto  rotatedPattern = UPIParser::rotatePattern (currentPattern, progressiveOffset);
13831376        patternEngine.setPattern (rotatedPattern);
13841377
13851378        logDebug (DebugCategory::SCENE_CYCLING, 
@@ -1503,8 +1496,19 @@ float RhythmPatternExplorerAudioProcessor::getPatternLengthValue() const
15031496
15041497double  RhythmPatternExplorerAudioProcessor::getSubdivisionInBeats (int  subdivisionIndex) const 
15051498{
1506-     //  Convert subdivision choice to beat fractions
1499+     //  SUBDIVISION PARAMETER DOCUMENTATION:
1500+     //  This function converts subdivision parameter choices to beat fractions for Steps mode.
1501+     //  Each subdivision represents how much of a beat one step occupies.
1502+     // 
15071503    //  Subdivision choices: {"64th Triplet", "64th", "32nd Triplet", "32nd", "16th Triplet", "16th", "8th Triplet", "8th", "Quarter Triplet", "Quarter", "Half Triplet", "Half", "Whole"}
1504+     //  Default: index 5 = "16th" = 0.25 beats per step
1505+     // 
1506+     //  Examples:
1507+     //  - 16th notes: 0.25 beats per step (4 steps = 1 beat)
1508+     //  - 8th notes: 0.5 beats per step (2 steps = 1 beat)  
1509+     //  - 8th triplets: 1/3 beats per step (3 steps = 1 beat)
1510+     //  - Quarter notes: 1.0 beats per step (1 step = 1 beat)
1511+     
15081512    static  const  double  subdivisionBeats[] = {
15091513        1.0 /24.0 ,  //  64th Triplet (1/64 * 2/3 = 1/96, but musically 1/24 makes more sense)
15101514        1.0 /16.0 ,  //  64th
0 commit comments