@@ -621,99 +621,40 @@ def get_musical_time(self, real_time: float, reference_level: Optional[int] = No
621621 remaining_time = remaining_time % subdivision_duration
622622
623623 # Step 4: Fractional beat calculation
624- if ref_level == len (self .hierarchy ) - 1 :
625- # Default behavior: pulse-based calculation, but check for sparse pulse data
626- expected_pulses = self ._pulses_per_cycle * self .repetitions
627- if len (self .all_pulses ) < expected_pulses * 0.5 : # Less than 50% of expected pulses
628- # Fall back to proportional timing calculation for sparse pulse data
629- # For fractional_beat, we need the containing unit (parent) duration and start time
630- if ref_level == 0 :
631- # Ref level 0: fractional position within the cycle
632- current_level_start_time = self .start_time + cycle_number * self .cycle_dur
633- level_duration = self .cycle_dur
634- else :
635- # Ref level > 0: fractional position within the parent unit
636- parent_positions = positions [:ref_level ] # Parent positions
637- current_level_start_time = self ._calculate_proportional_level_start_time (parent_positions , cycle_number , ref_level - 1 )
638- level_duration = self ._calculate_proportional_level_duration (parent_positions , cycle_number , ref_level - 1 )
639-
640- if level_duration <= 0 :
641- fractional_beat = 0.0
642- else :
643- time_from_level_start = real_time - current_level_start_time
644- fractional_beat = time_from_level_start / level_duration
645- else :
646- # Use pulse-based calculation for complete pulse data
647- current_pulse_index = self ._hierarchical_position_to_pulse_index (positions , cycle_number )
648-
649- # Add bounds checking for pulse access
650- if current_pulse_index < 0 or current_pulse_index >= len (self .all_pulses ):
651- # Fall back to proportional calculation if pulse index out of bounds
652- # For fractional_beat, we need the containing unit (parent) duration and start time
653- if ref_level == 0 :
654- # Ref level 0: fractional position within the cycle
655- current_level_start_time = self .start_time + cycle_number * self .cycle_dur
656- level_duration = self .cycle_dur
657- else :
658- # Ref level > 0: fractional position within the parent unit
659- parent_positions = positions [:ref_level ] # Parent positions
660- current_level_start_time = self ._calculate_proportional_level_start_time (parent_positions , cycle_number , ref_level - 1 )
661- level_duration = self ._calculate_proportional_level_duration (parent_positions , cycle_number , ref_level - 1 )
662-
663- if level_duration <= 0 :
664- fractional_beat = 0.0
665- else :
666- time_from_level_start = real_time - current_level_start_time
667- fractional_beat = time_from_level_start / level_duration
668- else :
669- # Safe pulse-based calculation - use parent unit logic for reference levels
670- if ref_level == 0 :
671- # Ref level 0: fractional position within the cycle
672- current_level_start_time = self .start_time + cycle_number * self .cycle_dur
673- level_duration = self .cycle_dur
674- else :
675- # For ref_level > 0: fractional position within the parent unit
676- parent_positions = positions [:ref_level ] # Truncate to parent level
677- current_level_start_time = self ._calculate_proportional_level_start_time (parent_positions , cycle_number , ref_level - 1 )
678- level_duration = self ._calculate_proportional_level_duration (parent_positions , cycle_number , ref_level - 1 )
679-
680- if level_duration <= 0 :
681- fractional_beat = 0.0
682- else :
683- time_from_level_start = real_time - current_level_start_time
684- fractional_beat = time_from_level_start / level_duration
685-
686- # Clamp to [0, 1] range
687- fractional_beat = max (0.0 , min (1.0 , fractional_beat ))
688-
624+ # ALWAYS calculate fractional_beat as position within finest level unit (between pulses)
625+ # This is independent of reference_level, which only affects hierarchical_position truncation
626+ current_pulse_index = self ._hierarchical_position_to_pulse_index (positions , cycle_number )
627+
628+ # Bounds checking
629+ if current_pulse_index < 0 or current_pulse_index >= len (self .all_pulses ):
630+ fractional_beat = 0.0
689631 else :
690- # Reference level behavior
691- truncated_positions = positions [:ref_level + 1 ]
632+ current_pulse_time = self .all_pulses [current_pulse_index ].real_time
692633
693- # For fractional_beat calculation, we need the containing unit (parent) duration and start time
694- if ref_level == 0 :
695- # Ref level 0: fractional position within the cycle
696- current_level_start_time = self .start_time + cycle_number * self .cycle_dur
697- level_duration = self .cycle_dur
634+ # Handle next pulse
635+ if current_pulse_index + 1 < len (self .all_pulses ):
636+ next_pulse_time = self .all_pulses [current_pulse_index + 1 ].real_time
698637 else :
699- # Ref level > 0: fractional position within the parent unit
700- parent_positions = truncated_positions [:- 1 ] # Remove the last position for parent unit
701- current_level_start_time = self ._calculate_proportional_level_start_time (parent_positions , cycle_number , ref_level - 1 )
702- level_duration = self ._calculate_proportional_level_duration (parent_positions , cycle_number , ref_level - 1 )
638+ # Last pulse - use next cycle start
639+ next_cycle_start = self .start_time + (cycle_number + 1 ) * self .cycle_dur
640+ next_pulse_time = next_cycle_start
703641
704- if level_duration <= 0 :
642+ pulse_duration = next_pulse_time - current_pulse_time
643+ if pulse_duration <= 0 :
705644 fractional_beat = 0.0
706645 else :
707- time_from_level_start = real_time - current_level_start_time
708- fractional_beat = time_from_level_start / level_duration
709-
710- # Clamp to [0, 1] range
711- fractional_beat = max (0.0 , min (1.0 , fractional_beat ))
712-
713- # Update positions to only include levels up to reference for final result
714- positions = truncated_positions
646+ time_from_current_pulse = real_time - current_pulse_time
647+ fractional_beat = time_from_current_pulse / pulse_duration
648+
649+ # Clamp to [0, 1] range
650+ fractional_beat = max (0.0 , min (1.0 , fractional_beat ))
651+
652+ # Step 5: Handle reference level truncation (if specified)
653+ if ref_level is not None and ref_level < len (self .hierarchy ) - 1 :
654+ # Truncate positions to reference level for final result
655+ positions = positions [:ref_level + 1 ]
715656
716- # Step 5 : Result construction
657+ # Step 6 : Result construction
717658 return MusicalTime (
718659 cycle_number = cycle_number ,
719660 hierarchical_position = positions ,
0 commit comments