Commit graph

81 commits

Author SHA1 Message Date
Michael Winter 1dff1022ce Add custom dims support via --dims-custom and update JSON output format 2026-03-28 15:12:14 +01:00
Michael Winter bb7a9ccb21 Add DIMS_3 support and fix dim_diff for prime 11 2026-03-28 13:51:05 +01:00
Michael Winter 7dd7f23611 Fix uniform symdiff to fallback to other symdiff groups when no valid candidates 2026-03-27 10:14:33 +01:00
Michael Winter 863423ca7a Add uniform symdiff selection and store symdiff in graph edges
- Add symdiff to edge tuple in _find_valid_edges() and store in graph
- Add --uniform-symdiff CLI option to make symdiff selection uniform
- Implement uniform selection in pathfinder by grouping edges by symdiff
- With uniform: path uses symdiff 2 and 4 roughly equally
- Without uniform: path uses mostly symdiff 4 (most common)
2026-03-27 10:05:14 +01:00
Michael Winter 74aa937ec2 Add --target-register-power option to curve target register progression 2026-03-26 15:41:41 +01:00
Michael Winter 8ea5c4fe0c Use --fundamental for frequencies output as well as OSC 2026-03-25 10:28:04 +01:00
Michael Winter 44506eed14 Add source_node and destination_node to path_steps.json 2026-03-25 10:23:44 +01:00
Michael Winter c1ff66265d Fix max-path to represent total chord count instead of edge count 2026-03-24 11:57:04 +01:00
Michael Winter 6245ce5e27 Make JSON output more readable - each pitch on one line 2026-03-24 11:04:21 +01:00
Michael Winter 4804c0c244 Add LilyPond transcriber documentation to README 2026-03-23 20:09:03 +01:00
Michael Winter 0f419a15b5 Add dynamic date placeholder to score template 2026-03-23 20:01:51 +01:00
Michael Winter c0ffaac95f Use SemiStaffGroup in generated score 2026-03-23 20:00:21 +01:00
Michael Winter 4ffbcc79d2 Fix score block structure - wrap staves in StaffGroup 2026-03-23 19:55:48 +01:00
Michael Winter 6bf97e5abe Make LilyPond template dynamic - title and staves generated from parameters 2026-03-23 19:54:50 +01:00
Michael Winter 306de8b5c2 Add --transcribe option to CLI for generating LilyPond transcriptions 2026-03-23 19:46:00 +01:00
Michael Winter 9dbde6a63d Fix PDF output location and set default name 2026-03-23 19:23:53 +01:00
Michael Winter a0723e5ae3 Show dimension markup for first chord using refs within same chord
- Add _find_ref_in_same_chord() to find adjacent pitches within the same chord
- Update first chord handling to show refs to other pitches in chord
2026-03-23 19:15:49 +01:00
Michael Winter 658837b83e Fix transcriber to compute ref and dim_diff from chord data
- Add _is_adjacent() to check if pitches differ by ±1 in one dimension
- Add _compute_dim_diff() to compute prime * direction for dimension changes
- Add _find_ref_and_dim_diff() to find the ref voice and dim_diff for changed pitches
- Update output_chords_to_music_data() to compute ref and dim_diff by comparing consecutive chords
- Update format_dim_diff() to include dim_diff == 0 check and fix ref mapping for 4 voices
2026-03-23 19:12:22 +01:00
Michael Winter d0bd15574d Fix asymmetric connectivity in voice leading graph
- Compute both c1→c2 and c2→c1 edges independently using _find_valid_edges()
- Fix _is_directly_tunable to properly reorder c2_transposed using movement map
- Clean up unused valid_pairings code in _build_movement_maps
- Add edge_data field to PathStep for debugging
2026-03-23 18:46:14 +01:00
Michael Winter dd637e64e2 Add LilyPond transcriber module
- Convert chord data to LilyPond parts and PDF
- Generate part files for voices I, II, III
- Generate full score from template
- Call LilyPond to create PDF automatically
- CLI with --name, --fundamental, --template options
2026-03-23 10:00:44 +01:00
Michael Winter 326ae9da1b Add OSC Sender documentation to README 2026-03-20 06:58:00 +01:00
Michael Winter 8326fc1d11 Add interactive OSC sender with preview mode and chord navigation
- Add preview mode (p key) to toggle between send/preview behavior
- Arrow keys for navigation with optional send
- Enter sends current chord (only when not in preview mode)
- Jump to chord by number + Enter
- k/K keys for kill soft (15.0) and kill hard (0.0) commands
- Display prev/current/next chords with frequencies to 2 decimals
- Add OSC test receiver for debugging
- Use arrow key escape sequences for left/right navigation
2026-03-19 15:57:17 +01:00
Michael Winter 4a1e1f7ec2 Simplify quit: use Escape key instead of Ctrl+C
- Rollback to original tty-based input
- Add Escape key to quit
- Remove arrow key detection (use < > keys only)
- Simplify the play() method
2026-03-17 17:41:09 +01:00
Michael Winter d2c88ccc3f Use select with timeout for keyboard input
- Replaces tty-based input with select-based approach
- Adds signal handler for Ctrl+C
- Allows KeyboardInterrupt to propagate correctly
2026-03-17 17:26:23 +01:00
Michael Winter fd4b89670e Fix Ctrl+C handling in OSC playback 2026-03-17 17:19:52 +01:00
Michael Winter ea3acf9efe Replace --osc-enable with --osc-play
- Add --osc-play with optional file argument
- If no file provided, defaults to output/output_chords.json
- Fix python-osc API (addr -> address)
- Keep --fundamental, --osc-ip, --osc-port
2026-03-17 17:10:37 +01:00
Michael Winter 1ccab2088f Add OSC sender for real-time playback
- Create src/osc_sender.py with OSCSender class
- Add CLI args: --osc-enable, --osc-ip, --osc-port, --fundamental
- Load chords from output_chords.json
- Interactive keyboard control (< > keys)
- Display chord number, frequencies, HS arrays
- Support configurable fundamental frequency
2026-03-17 16:47:30 +01:00
Michael Winter e687087e76 Fix fraction representation in output
Fix to_fraction() in src/pitch.py to handle negative exponents
correctly without floating point precision loss.

Before: 2573485501354569/2251799813685248
After:  8/7
2026-03-17 15:30:05 +01:00
Michael Winter e4b6d2cfdc Rename 'target range' to 'target register' throughout
- CLI: --target-range -> --target-register
- CLI: --weight-target-range -> --weight-target-register
- Config keys: target_range -> target_register
- Update README, tests, and all internal references
2026-03-17 14:11:21 +01:00
Michael Winter 24709c3eda Update README with complete CLI docs and rename args
- Rename --voice-crossing to --allow-voice-crossing
- Change --direct-tuning to --disable-direct-tuning (defaults to require)
- Add explicit documentation for every CLI parameter
- Add detailed explanations for each factor
- Add more examples
2026-03-17 09:11:25 +01:00
Michael Winter 146918c596 final weight is now product of factors raised to an exponent rather than sum 2026-03-17 08:41:18 +01:00
Michael Winter 7809fa5a76 Refactor: Move factor methods to Path class, add normalized_scores
- Move all _factor_* methods from pathfinder.py to path.py
- Add get_candidates() and compute_weights() to Path class
- Simplify step() to just commit chosen candidate
- Add normalized_scores field for consistent influence calculation
- Remove duplicate transposition/voice_map logic between get_candidates and step
- dca_voice_movement and target_range now use destination_chord directly
2026-03-16 18:59:13 +01:00
Michael Winter 482f2b0df5 Refactor: Rename sustain/last_visited fields for consistency 2026-03-16 17:35:07 +01:00
Michael Winter 861d012a95 Refactor: Unify Candidate and PathStep, fix DCA Hamiltonian
- Remove Candidate class, use PathStep for both hypothetical and actual steps
- Simplify Path.step() to accept a PathStep
- Fix DCA Hamiltonian to return visit_count directly instead of normalized score
- Tests pass and DCA properly discriminates
2026-03-16 16:53:22 +01:00
Michael Winter 400f970858 Rename graph.py to pathfinder.py
- Rename graph.py to pathfinder.py for clearer naming
- Update all imports
- Test CLI and tests pass
2026-03-16 16:13:19 +01:00
Michael Winter b349ee89bc Extract DIMS to separate dims.py module
- Create src/dims.py with DIMS_4, DIMS_5, DIMS_7, DIMS_8 constants
- Update pitch.py to import from dims
- Update harmonic_space.py to import from dims
- Update io.py to import from dims
- Fix circular import issue
2026-03-16 15:50:02 +01:00
Michael Winter ed13c006b0 removing errant session file 2026-03-16 15:34:14 +01:00
Michael Winter 6d3cbd83e9 Fix cent_diffs to store signed values
- harmonic_space.py: remove abs() to store signed cent differences
- graph.py: melodic threshold now uses abs(cents) for magnitude
- Change melodic power from 2 to 3 for sharper penalization

This fixes contrary_motion factor which was broken (always 0 due to absolute values)
2026-03-16 14:53:13 +01:00
Michael Winter 1eb95ffad7 Refine melodic threshold to continuous scoring
- Use continuous scoring instead of binary pass/fail
- Below min: score = (cents / min)^2
- Above max: score = ((1200 - cents) / (1200 - max))^2
- Use product across voices to penalize worst offenders
2026-03-16 14:11:27 +01:00
Michael Winter 1926930c3d Refactor PathStep to represent edges with source/destination and before/after state
- Add callback and interval parameters to find_stochastic_path() for adaptive weights
- Add get_influence() method to compute weighted score contribution per factor
- Rename graph_node/output_chord to source_node/destination_node/source_chord/destination_chord
- Rename voice_stay_count to sustain_count_before/after
- Rename node_visit_counts to last_visited_count_before/after
- Remove redundant internal state from Path - derive from steps
- Each PathStep now fully self-contained with before/after state
2026-03-16 14:00:10 +01:00
Michael Winter c682a1df02 Fix voice stay count to match master behavior
Changed voice stay count calculation in Path.step() to compare
by position (position i with position i) instead of tracking
voice identity. This makes dca_voice_movement factor behave
identically to master.
2026-03-16 02:27:21 +01:00
Michael Winter f2b785c98e Encapsulate voice-leading in Path.step()
- Move transposition, voice mapping into Path.step()
- Add node_visit_counts and voice_stay_count to each PathStep
- Fix voice tracking to use voice identity, not position
- Clean up unused last_graph_nodes code
- Full encapsulation: Path handles all voice-leading state
2026-03-16 01:11:15 +01:00
Michael Winter 66669de00f Add Path and Candidate classes for path state tracking
- Add src/path.py with Path and PathStep classes
- Path stores initial_chord, steps, weights_config
- PathStep stores graph_node, output_chord, transposition, movements, scores, candidates
- Refactor find_stochastic_path to use candidates approach
- Separate _build_candidates (raw scores) from _compute_weights
- Simplify return type to Path only (graph_chords available via property)
- Update io.py to use new Path API
2026-03-16 00:39:32 +01:00
Michael Winter b20f02b60f Fix DCA Hamiltonian: normalize by max instead of sum
- Change normalization from sum to max: visit_count / max^2
- This gives stronger discrimination toward unvisited nodes
- Weight 1 now gives 66.2% coverage (vs 63.6% baseline)
- Rename CLI: --weight-dca -> --weight-dca-voice-movement
- Rename CLI: --weight-hamiltonian -> --weight-dca-hamiltonian
- Restore DCA Hamiltonian display in analysis output
- Update README with new CLI names and factor descriptions
2026-03-15 13:04:00 +01:00
Michael Winter 218d7a55ff Refactor DCA factor: rename to DCA Voice Movement and DCA Hamiltonian
- Rename _factor_dca to _factor_dca_voice_movement (tracks voice pitch changes)
- Rename _factor_hamiltonian to _factor_dca_hamiltonian (tracks node visit counts)
- Update CLI: --weight-dca -> --weight-dca-voice-movement
- Update CLI: --weight-hamiltonian -> --weight-dca-hamiltonian
- Remove hamiltonian coverage from analysis (no longer tracking)
2026-03-15 12:20:12 +01:00
Michael Winter 8cdbe90501 Normalize edge weights with sum normalization
- Each soft factor is sum-normalized across all edge candidates
- Ensures factors compete equally regardless of native value ranges
- Skip factors with no variance (all candidates same value)
- Base weight of 1.0 ensures minimum probability
2026-03-15 12:04:08 +01:00
Michael Winter ebbb288844 Implement weighted contrary motion factor
- Half of moving voices should go one direction, half opposite
- Weighted by closeness to ideal half split
- factor = 1.0 - (distance_from_half / half)
- Works with odd (near-half) and even (exact half) voices
- Analysis shows contrary_motion_steps, percent, and avg_score
2026-03-15 11:31:24 +01:00
Michael Winter 559c868313 Fix Hamiltonian analysis and add DCA stay count metrics
- Save graph_path to output for accurate Hamiltonian tracking
- DCA analysis now shows avg/max voice stay counts
- Fix: use actual graph node hashes instead of rehashing transposed chords
2026-03-15 11:13:24 +01:00
Michael Winter 34a6ebfabd Add per-factor analysis metrics
- Melodic: violations, max violation, avg movement
- Contrary motion: steps with contrary, percentage
- DCA: avg voices changing, all voices change count/percent
- Hamiltonian: unique nodes, coverage percentage
- Target range: start/end cents, achieved percentage
- CLI: --stats now shows all metrics automatically
2026-03-15 10:48:06 +01:00
Michael Winter 16ecb192d1 Add analysis tool for chord sequences
- Add src/analyze.py: standalone analysis script
- Add --stats CLI flag to show stats after generation
- Analyze: melodic violations, target range %, voice changes
2026-03-15 10:42:22 +01:00