Fix Issue #34: fractional_beat=0.0 with sparse pulse data#35
Conversation
- Add sparse pulse detection in get_musical_time() fractional_beat calculation - Implement _calculate_proportional_fractional_beat() for fallback timing - When pulse indices are out of bounds, use proportional timing instead of returning 0.0 - Add comprehensive test case that reproduces and validates the fix - All existing tests continue to pass This fixes the issue where meters created via Meter.from_json() with sparse pulse data would return fractional_beat=0.0 for most trajectory points, preventing smooth curve visualization.
febba23 to
caf12a7
Compare
|
📦 Test Package Built Successfully! This PR has been automatically built and uploaded to TestPyPI for testing. 🔗 TestPyPI Link: https://test.pypi.org/project/idtap/ To test this version: pip install --index-url https://test.pypi.org/simple/ idtap✅ All tests passed and package builds successfully. |
|
@claude review |
|
Claude finished @jon-myers's task —— View job PR Review Complete ✅I've thoroughly reviewed this pull request fixing Issue #34. Overall, this is a well-implemented solution that addresses the core problem with sparse pulse data gracefully. SummaryIssue: When meters have sparse pulse data (< 50% of expected pulses), Solution: Implements a proportional timing fallback system that maintains accurate musical time calculations even with incomplete pulse data. Code Quality Assessment✅ Excellent Design Patterns
✅ Robust Error HandlingThe bounds checking at meter.py:484-486 and meter.py:677-680 prevents IndexError exceptions: if start_pulse_index < 0 or start_pulse_index >= len(self.all_pulses):
# Fall back to proportional calculation
return self._calculate_proportional_level_start_time(positions, cycle_number, reference_level)This is excellent defensive programming. ✅ Mathematical AccuracyThe proportional calculations correctly handle hierarchical musical structures:
Potential Issues & Suggestions
|
The all_pulses property was incorrectly returning only pulses from the first structure of the last layer (e.g., 2 pulses) instead of ALL pulses from all structures in that layer (e.g., 256 pulses). This matches the TypeScript implementation which correctly does: lastLayer.map(ps => ps.pulses).flat() This was the root cause of Issue #34 - not actually sparse pulse data, but incorrect pulse retrieval. The proportional timing fallback is now only needed for truly sparse cases (which shouldn't normally occur). Also adds SPARSE_PULSE_THRESHOLD constant and documentation for clarity.
|
📦 Test Package Built Successfully! This PR has been automatically built and uploaded to TestPyPI for testing. 🔗 TestPyPI Link: https://test.pypi.org/project/idtap/ To test this version: pip install --index-url https://test.pypi.org/simple/ idtap✅ All tests passed and package builds successfully. |
Since the root cause was the all_pulses property bug (now fixed), we no longer need the complex sparse pulse detection and proportional timing fallback. Removed: - SPARSE_PULSE_THRESHOLD constant - _is_sparse_cached instance variable - _calculate_proportional_fractional_beat method - _calculate_proportional_level_start_time method - _calculate_proportional_level_duration method - Sparse pulse detection logic in get_musical_time - Sparse pulse detection in helper methods - test_sparse_pulse_data_fractional_beat test The code is now simpler and cleaner, relying on the correct all_pulses implementation to provide complete pulse data as it should.
|
📦 Test Package Built Successfully! This PR has been automatically built and uploaded to TestPyPI for testing. 🔗 TestPyPI Link: https://test.pypi.org/project/idtap/ To test this version: pip install --index-url https://test.pypi.org/simple/ idtap✅ All tests passed and package builds successfully. |
Summary
get_musical_time()returningfractional_beat=0.0for trajectory points with sparse pulse dataRoot Cause
When meters have sparse pulse data (< 50% of expected pulses), calculated pulse indices often fall out of bounds. The previous implementation would simply return
fractional_beat = 0.0instead of using proportional timing calculations.Solution
_calculate_proportional_fractional_beat()methodTest Results
Before fix:
After fix:
Impact
Resolves #34
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com