head_music 8.2.1 → 9.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +1 -1
  3. data/.github/workflows/release.yml +1 -1
  4. data/CHANGELOG.md +53 -0
  5. data/CLAUDE.md +151 -0
  6. data/Gemfile.lock +25 -25
  7. data/MUSIC_THEORY.md +120 -0
  8. data/Rakefile +2 -2
  9. data/bin/check_instrument_consistency.rb +86 -0
  10. data/check_instrument_consistency.rb +0 -0
  11. data/head_music.gemspec +1 -1
  12. data/lib/head_music/analysis/diatonic_interval/naming.rb +1 -1
  13. data/lib/head_music/analysis/diatonic_interval.rb +50 -27
  14. data/lib/head_music/analysis/interval_consonance.rb +51 -0
  15. data/lib/head_music/content/note.rb +1 -1
  16. data/lib/head_music/content/placement.rb +1 -1
  17. data/lib/head_music/content/position.rb +1 -1
  18. data/lib/head_music/content/staff.rb +1 -1
  19. data/lib/head_music/instruments/instrument.rb +103 -113
  20. data/lib/head_music/instruments/instrument_families.yml +10 -9
  21. data/lib/head_music/instruments/instrument_family.rb +13 -2
  22. data/lib/head_music/instruments/instrument_type.rb +188 -0
  23. data/lib/head_music/instruments/instruments.yml +350 -368
  24. data/lib/head_music/instruments/score_order.rb +139 -0
  25. data/lib/head_music/instruments/score_orders.yml +130 -0
  26. data/lib/head_music/instruments/variant.rb +6 -0
  27. data/lib/head_music/locales/de.yml +6 -0
  28. data/lib/head_music/locales/en.yml +98 -0
  29. data/lib/head_music/locales/es.yml +6 -0
  30. data/lib/head_music/locales/fr.yml +6 -0
  31. data/lib/head_music/locales/it.yml +6 -0
  32. data/lib/head_music/locales/ru.yml +6 -0
  33. data/lib/head_music/rudiment/alteration.rb +23 -8
  34. data/lib/head_music/rudiment/base.rb +9 -0
  35. data/lib/head_music/rudiment/chromatic_interval.rb +3 -6
  36. data/lib/head_music/rudiment/clef.rb +1 -1
  37. data/lib/head_music/rudiment/consonance.rb +37 -4
  38. data/lib/head_music/rudiment/diatonic_context.rb +25 -0
  39. data/lib/head_music/rudiment/key.rb +77 -0
  40. data/lib/head_music/rudiment/key_signature/enharmonic_equivalence.rb +1 -1
  41. data/lib/head_music/rudiment/key_signature.rb +46 -7
  42. data/lib/head_music/rudiment/letter_name.rb +3 -3
  43. data/lib/head_music/rudiment/meter.rb +19 -9
  44. data/lib/head_music/rudiment/mode.rb +92 -0
  45. data/lib/head_music/rudiment/musical_symbol.rb +1 -1
  46. data/lib/head_music/rudiment/note.rb +112 -0
  47. data/lib/head_music/rudiment/pitch/parser.rb +52 -0
  48. data/lib/head_music/rudiment/pitch.rb +5 -6
  49. data/lib/head_music/rudiment/pitch_class.rb +1 -1
  50. data/lib/head_music/rudiment/quality.rb +1 -1
  51. data/lib/head_music/rudiment/reference_pitch.rb +1 -1
  52. data/lib/head_music/rudiment/register.rb +4 -1
  53. data/lib/head_music/rudiment/rest.rb +36 -0
  54. data/lib/head_music/rudiment/rhythmic_element.rb +53 -0
  55. data/lib/head_music/rudiment/rhythmic_unit/parser.rb +86 -0
  56. data/lib/head_music/rudiment/rhythmic_unit.rb +104 -29
  57. data/lib/head_music/rudiment/rhythmic_units.yml +80 -0
  58. data/lib/head_music/rudiment/rhythmic_value/parser.rb +77 -0
  59. data/lib/head_music/{content → rudiment}/rhythmic_value.rb +23 -5
  60. data/lib/head_music/rudiment/scale.rb +4 -5
  61. data/lib/head_music/rudiment/scale_degree.rb +9 -4
  62. data/lib/head_music/rudiment/scale_type.rb +9 -3
  63. data/lib/head_music/rudiment/solmization.rb +1 -1
  64. data/lib/head_music/rudiment/spelling.rb +5 -4
  65. data/lib/head_music/rudiment/tempo.rb +85 -0
  66. data/lib/head_music/rudiment/tonal_context.rb +35 -0
  67. data/lib/head_music/rudiment/tuning/just_intonation.rb +85 -0
  68. data/lib/head_music/rudiment/tuning/meantone.rb +87 -0
  69. data/lib/head_music/rudiment/tuning/pythagorean.rb +91 -0
  70. data/lib/head_music/rudiment/tuning.rb +18 -4
  71. data/lib/head_music/rudiment/unpitched_note.rb +62 -0
  72. data/lib/head_music/style/annotation.rb +4 -4
  73. data/lib/head_music/style/guidelines/notes_same_length.rb +16 -16
  74. data/lib/head_music/style/medieval_tradition.rb +26 -0
  75. data/lib/head_music/style/modern_tradition.rb +34 -0
  76. data/lib/head_music/style/renaissance_tradition.rb +26 -0
  77. data/lib/head_music/style/tradition.rb +21 -0
  78. data/lib/head_music/utilities/hash_key.rb +34 -2
  79. data/lib/head_music/version.rb +1 -1
  80. data/lib/head_music.rb +33 -9
  81. data/user_stories/active/handle-time.md +7 -0
  82. data/user_stories/active/handle-time.rb +177 -0
  83. data/user_stories/done/epic--score-order/PLAN.md +244 -0
  84. data/user_stories/done/epic--score-order/band-score-order.md +38 -0
  85. data/user_stories/done/epic--score-order/chamber-ensemble-score-order.md +33 -0
  86. data/user_stories/done/epic--score-order/orchestral-score-order.md +43 -0
  87. data/user_stories/done/instrument-variant.md +65 -0
  88. data/user_stories/done/superclass-for-note.md +30 -0
  89. data/user_stories/todo/agentic-daw.md +3 -0
  90. data/user_stories/todo/consonance-dissonance-classification.md +57 -0
  91. data/user_stories/todo/dyad-analysis.md +57 -0
  92. data/user_stories/todo/material-and-scores.md +10 -0
  93. data/user_stories/todo/organizing-content.md +72 -0
  94. data/user_stories/todo/percussion_set.md +1 -0
  95. data/user_stories/todo/pitch-class-set-analysis.md +79 -0
  96. data/user_stories/todo/pitch-set-classification.md +72 -0
  97. data/user_stories/todo/sonority-identification.md +67 -0
  98. metadata +51 -6
  99. data/TODO.md +0 -218
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77530f6691ab779c127d7324218a10a854bb8fb96ccaf0244accd9dbc878f24f
4
- data.tar.gz: 05b082c0029817aa3b3c6773c876000d61b7787915c0f157ad9af966da3ad73c
3
+ metadata.gz: 012ce64dd4174cbeec42f1d4825e9fd10af79ed8f4b83a3beedbc14a9aea34fa
4
+ data.tar.gz: a31dbb0b6b3d732bd41e1dbfe6a042a8395db05832d58848ec4d53e5ae4a4524
5
5
  SHA512:
6
- metadata.gz: 965572974769529a7d971b0156c492195e9c7ea491d97e9445b3cc53714d802ffc0f2680559cb8449971391ef2b406f3a6283ca8ce6d8103c47713e71de954df
7
- data.tar.gz: 6e5444274c959f209861c1622002351d255050e6faff66d2b69033d90c372736e15a2c74b8fb66e01841dfc5c1f29887cc753acd736d54e21e1d29608c0ee11f
6
+ metadata.gz: a98367425dcfb4899b24ef2e7986a7fbc594202c960b553ab35d3cecd5ee0b5ecb10f65e1e458fe2d1797b70eded378fd319b2cbfc7b9a5a2e393773ce75f06b
7
+ data.tar.gz: d3baf95b28de6c2df0e3d14250ed9c335cb3793d598eb7cf1c417dd40b6463bb57ddcda19be309a1045e835809ad3b9ad45dc0fd21797563319069da35db76bc
@@ -28,7 +28,7 @@ jobs:
28
28
 
29
29
  - name: Upload coverage to Codecov
30
30
  if: matrix.ruby-version == '3.3.0'
31
- uses: codecov/codecov-action@v4
31
+ uses: codecov/codecov-action@v5
32
32
  with:
33
33
  token: ${{ secrets.CODECOV_TOKEN }}
34
34
  fail_ci_if_error: false
@@ -31,7 +31,7 @@ jobs:
31
31
  run: gem build *.gemspec
32
32
 
33
33
  - name: Create GitHub Release
34
- uses: softprops/action-gh-release@v1
34
+ uses: softprops/action-gh-release@v2
35
35
  with:
36
36
  files: '*.gem'
37
37
  generate_release_notes: true
data/CHANGELOG.md CHANGED
@@ -7,6 +7,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [9.0.0] - 2025-10-24
11
+
12
+ ### Added
13
+ - Added `HeadMusic::Rudiment::Pitch::Parser` for strict pitch parsing
14
+ - Added `HeadMusic::Rudiment::RhythmicValue::Parser` for rhythmic value parsing
15
+ - Both parsers provide standardized `.parse()` class method API
16
+
17
+ ### Changed
18
+ - `Pitch.from_name` now uses `Pitch::Parser` internally
19
+ - `RhythmicValue.get` now uses `RhythmicValue::Parser` internally
20
+ - `Note.get` now parses "pitch rhythmic_value" strings inline without Parse module
21
+
22
+ ### Removed
23
+ - **BREAKING**: Removed `HeadMusic::Parse::Pitch` class
24
+ - **BREAKING**: Removed `HeadMusic::Parse::RhythmicValue` class
25
+ - **BREAKING**: Removed `HeadMusic::Parse::RhythmicElement` class
26
+ - **BREAKING**: Removed entire `HeadMusic::Parse` module
27
+
28
+ ### Migration Guide
29
+
30
+ If you were using the removed Parse classes, migrate as follows:
31
+
32
+ ```ruby
33
+ # Before (v8.x)
34
+ parser = HeadMusic::Parse::Pitch.new("C#4")
35
+ pitch = parser.pitch
36
+
37
+ # After (v9.x)
38
+ pitch = HeadMusic::Rudiment::Pitch.get("C#4")
39
+ # or for strict parsing:
40
+ pitch = HeadMusic::Rudiment::Pitch::Parser.parse("C#4")
41
+ ```
42
+
43
+ ```ruby
44
+ # Before (v8.x)
45
+ parser = HeadMusic::Parse::RhythmicValue.new("dotted quarter")
46
+ value = parser.rhythmic_value
47
+
48
+ # After (v9.x)
49
+ value = HeadMusic::Rudiment::RhythmicValue.get("dotted quarter")
50
+ # or for strict parsing:
51
+ value = HeadMusic::Rudiment::RhythmicValue::Parser.parse("dotted quarter")
52
+ ```
53
+
54
+ ```ruby
55
+ # Before (v8.x)
56
+ parser = HeadMusic::Parse::RhythmicElement.new("F#4 dotted-quarter")
57
+ note = parser.note
58
+
59
+ # After (v9.x)
60
+ note = HeadMusic::Rudiment::Note.get("F#4 dotted-quarter")
61
+ ```
62
+
10
63
  ## [8.2.1] - 2025-06-21
11
64
 
12
65
  ### Added
data/CLAUDE.md ADDED
@@ -0,0 +1,151 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ HeadMusic is a Ruby gem for Western music theory. It provides a comprehensive toolkit for working with pitches, scales, intervals, chords, and musical analysis. The gem supports internationalization with translations in 7 languages.
8
+
9
+ ## Development Commands
10
+
11
+ ### Essential Commands
12
+
13
+ ```bash
14
+ # Install dependencies
15
+ bin/setup
16
+
17
+ # Run tests with coverage
18
+ bundle exec rake
19
+
20
+ # Run tests without coverage
21
+ bundle exec rspec
22
+
23
+ # Run a specific test file
24
+ bundle exec rspec spec/head_music/rudiments/pitch_spec.rb
25
+
26
+ # Run linting
27
+ bundle exec rubocop
28
+
29
+ # Run all quality checks (tests, linting, security)
30
+ bundle exec rake quality
31
+
32
+ # Open interactive console with gem loaded
33
+ bin/console
34
+ # or
35
+ bundle exec rake console
36
+
37
+ # Generate documentation
38
+ bundle exec rake doc
39
+
40
+ # Check documentation coverage
41
+ bundle exec rake doc_stats
42
+ ```
43
+
44
+ ### Git Etiquette
45
+
46
+ Do not make a commit unless I ask you to.
47
+
48
+ When composing git commit messages, follow best-practices. However, do not mention yourself (claude) or list yourself as a co-author.
49
+
50
+ This project uses a rebase flow and `main` as the mainline branch.
51
+
52
+ ### Code Style, Linting, and Formatting
53
+
54
+ The project uses Standard Ruby for style enforcement. Always run linting after editing and before committing.
55
+
56
+ ```bash
57
+ bundle exec rubocop -a
58
+ ```
59
+
60
+ Always strip trailing whitespace from all lines being added or edited. Always include a blank line at the end of each file.
61
+
62
+ Do not use an assignment inside a condition.
63
+
64
+ ### Testing
65
+
66
+ Tests are written in RSpec and located in the `/spec` directory, mirroring the `/lib` structure. The project requires 90% code coverage minimum.
67
+
68
+ ## Architecture
69
+
70
+ ### Module Structure
71
+
72
+ The codebase follows a domain-driven design with clear module boundaries:
73
+
74
+ 1. **HeadMusic::Rudiment** - Core music theory elements
75
+ - Pitch, Note, Scale, Key, Interval, Chord
76
+ - Factory methods: `.get()` for most rudiments
77
+
78
+ 2. **HeadMusic::Analysis** - Musical analysis tools
79
+ - Intervals, Chords, Motion analysis
80
+ - Harmonic and melodic analysis
81
+
82
+ 3. **HeadMusic::Content** - Musical composition representation
83
+ - Composition, Voice, Note, Rest
84
+ - Rhythmic values and time signatures
85
+
86
+ 4. **HeadMusic::Instruments** - Instrument definitions
87
+ - Instrument families and properties
88
+ - Pitch ranges and transposition
89
+
90
+ 5. **HeadMusic::Style** - Composition rules and guidelines
91
+ - Counterpoint rules
92
+ - Voice leading guidelines
93
+ - Style analysis
94
+
95
+ ### Key Design Patterns
96
+
97
+ - **Factory Pattern**: Most musical objects use `.get()` factory methods
98
+ - **Value Objects**: Immutable objects for musical concepts
99
+ - **Named Mixin**: Provides internationalization support
100
+ - **Delegation**: Extensive use of delegation for clean APIs
101
+
102
+ ### Entry Points
103
+
104
+ - Main file: `lib/head_music.rb`
105
+ - Module loading order is important and defined in the main file
106
+ - Constants like GOLDEN_RATIO are defined at the top level
107
+
108
+ ## Important Implementation Details
109
+
110
+ ### Internationalization
111
+
112
+ The gem supports multiple languages through the HeadMusic::Named mixin:
113
+ - Translations in `lib/head_music/locales/`
114
+ - Languages: en, de, es, fr, it, ja, nl
115
+ - Use `I18n.locale = :de` to change language
116
+
117
+ ### Testing Patterns
118
+
119
+ - Use `described_class` instead of hardcoding class names
120
+ - Test files must end with `_spec.rb`
121
+ - Shared examples in `spec/support/`
122
+ - `composition_context.rb` provides test utilities
123
+
124
+ ### Code Style
125
+
126
+ - Ruby 3.3.0+ features are allowed
127
+ - Follow Standard Ruby style guide
128
+ - Use YARD documentation format for public methods
129
+ - Prefer delegation over inheritance
130
+ - Always run `bundle exec rubocop -a` after editing ruby code
131
+
132
+ ## Common Development Tasks
133
+
134
+ ### Adding a New Musical Concept
135
+
136
+ 1. Create the class in the appropriate module
137
+ 2. Include `HeadMusic::Named` if it needs internationalization
138
+ 3. Add factory method `.get()` if appropriate
139
+ 4. Create corresponding spec file
140
+ 5. Add translations to locale files if using Named
141
+
142
+ ### Modifying Existing Classes
143
+
144
+ 1. Check for dependent classes that might be affected
145
+ 2. Run tests for the specific module: `bundle exec rspec spec/head_music/[module_name]`
146
+ 3. Update documentation with YARD comments
147
+ 4. Ensure translations are updated if names change
148
+
149
+ ## Music theory and concepts
150
+
151
+ Please refer to MUSIC_THEORY.md for music theory domain knowledge.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- head_music (8.2.0)
4
+ head_music (9.0.0)
5
5
  activesupport (~> 7.0)
6
6
  humanize (~> 2.0)
7
7
  i18n (~> 1.8)
@@ -22,54 +22,54 @@ GEM
22
22
  securerandom (>= 0.3)
23
23
  tzinfo (~> 2.0, >= 2.0.5)
24
24
  ast (2.4.3)
25
- base64 (0.2.0)
26
- benchmark (0.4.0)
27
- bigdecimal (3.1.9)
25
+ base64 (0.3.0)
26
+ benchmark (0.4.1)
27
+ bigdecimal (3.2.2)
28
28
  bundler-audit (0.9.2)
29
29
  bundler (>= 1.2.0, < 3)
30
30
  thor (~> 1.0)
31
31
  concurrent-ruby (1.3.5)
32
32
  connection_pool (2.5.3)
33
- diff-lcs (1.6.1)
33
+ diff-lcs (1.6.2)
34
34
  docile (1.4.1)
35
- drb (2.2.1)
35
+ drb (2.2.3)
36
36
  humanize (2.5.1)
37
37
  i18n (1.14.7)
38
38
  concurrent-ruby (~> 1.0)
39
- json (2.11.3)
39
+ json (2.13.1)
40
40
  kramdown (2.5.1)
41
41
  rexml (>= 3.3.9)
42
- language_server-protocol (3.17.0.4)
42
+ language_server-protocol (3.17.0.5)
43
43
  lint_roller (1.1.0)
44
44
  logger (1.7.0)
45
45
  minitest (5.25.5)
46
46
  parallel (1.27.0)
47
- parser (3.3.8.0)
47
+ parser (3.3.9.0)
48
48
  ast (~> 2.4.1)
49
49
  racc
50
50
  prism (1.4.0)
51
51
  racc (1.8.1)
52
52
  rainbow (3.1.1)
53
- rake (13.2.1)
53
+ rake (13.3.0)
54
54
  regexp_parser (2.10.0)
55
55
  rexml (3.4.1)
56
- rspec (3.13.0)
56
+ rspec (3.13.1)
57
57
  rspec-core (~> 3.13.0)
58
58
  rspec-expectations (~> 3.13.0)
59
59
  rspec-mocks (~> 3.13.0)
60
- rspec-core (3.13.3)
60
+ rspec-core (3.13.5)
61
61
  rspec-support (~> 3.13.0)
62
- rspec-expectations (3.13.4)
62
+ rspec-expectations (3.13.5)
63
63
  diff-lcs (>= 1.2.0, < 2.0)
64
64
  rspec-support (~> 3.13.0)
65
- rspec-its (1.3.1)
66
- rspec-core (>= 3.0.0)
67
- rspec-expectations (>= 3.0.0)
68
- rspec-mocks (3.13.3)
65
+ rspec-its (2.0.0)
66
+ rspec-core (>= 3.13.0)
67
+ rspec-expectations (>= 3.13.0)
68
+ rspec-mocks (3.13.5)
69
69
  diff-lcs (>= 1.2.0, < 2.0)
70
70
  rspec-support (~> 3.13.0)
71
- rspec-support (3.13.3)
72
- rubocop (1.75.4)
71
+ rspec-support (3.13.4)
72
+ rubocop (1.75.8)
73
73
  json (~> 2.3)
74
74
  language_server-protocol (~> 3.17.0.2)
75
75
  lint_roller (~> 1.1.0)
@@ -80,7 +80,7 @@ GEM
80
80
  rubocop-ast (>= 1.44.0, < 2.0)
81
81
  ruby-progressbar (~> 1.7)
82
82
  unicode-display_width (>= 2.4.0, < 4.0)
83
- rubocop-ast (1.44.1)
83
+ rubocop-ast (1.46.0)
84
84
  parser (>= 3.3.7.2)
85
85
  prism (~> 1.4)
86
86
  rubocop-performance (1.25.0)
@@ -99,12 +99,12 @@ GEM
99
99
  docile (~> 1.1)
100
100
  simplecov-html (~> 0.11)
101
101
  simplecov_json_formatter (~> 0.1)
102
- simplecov-html (0.13.1)
102
+ simplecov-html (0.13.2)
103
103
  simplecov_json_formatter (0.1.4)
104
- standard (1.49.0)
104
+ standard (1.50.0)
105
105
  language_server-protocol (~> 3.17.0.2)
106
106
  lint_roller (~> 1.0)
107
- rubocop (~> 1.75.2)
107
+ rubocop (~> 1.75.5)
108
108
  standard-custom (~> 1.0.0)
109
109
  standard-performance (~> 1.8)
110
110
  standard-custom (1.0.2)
@@ -113,7 +113,7 @@ GEM
113
113
  standard-performance (1.8.0)
114
114
  lint_roller (~> 1.1)
115
115
  rubocop-performance (~> 1.25.0)
116
- thor (1.3.2)
116
+ thor (1.4.0)
117
117
  tzinfo (2.0.6)
118
118
  concurrent-ruby (~> 1.0)
119
119
  unicode-display_width (3.1.4)
@@ -132,7 +132,7 @@ DEPENDENCIES
132
132
  kramdown
133
133
  rake (~> 13.0)
134
134
  rspec (~> 3.0)
135
- rspec-its (~> 1.2)
135
+ rspec-its (~> 2.0)
136
136
  rubocop
137
137
  rubocop-rake
138
138
  rubocop-rspec
data/MUSIC_THEORY.md ADDED
@@ -0,0 +1,120 @@
1
+ # Music Theory
2
+ Domain-specific knowledge for this project.
3
+
4
+ ## Rudiments
5
+
6
+ The rudiments of music theory are built up piece-by-piece.
7
+
8
+ letter name
9
+ - C, D, E, F, G, A, B, …
10
+ - equivalent to:
11
+ - do, re, mi, fa, sol, la, si/ti, …
12
+
13
+ alteration
14
+ - sharp, flat, double sharp, double flat
15
+ - optional
16
+ - none is same as 'natural'
17
+ - found in key signatures
18
+ - found in bars as accidentals
19
+
20
+ spelling
21
+ - letter name + alteration
22
+ - example: "E♭"
23
+
24
+ register
25
+ - an integer typically between 0 and 8
26
+ - represents the octaves of an 88-key piano
27
+ - scientific pitch notation
28
+ - middle C is C4
29
+ - increments between B and C
30
+ - a half step below C4 is B3.
31
+
32
+ pitch
33
+ - spelling + register
34
+ - example: "E♭3"
35
+
36
+ rhythmic unit
37
+ - duration
38
+ - expressed in fractions (or multiples) of a standard whole note
39
+ - whole, half, quarter, eighth, sixteenth, …
40
+ - breve, …
41
+
42
+ rhythmic value (duration)
43
+ - a rhythmic unit plus optional augmentation dots
44
+ - augmentation dots extend the duration of the rhythmic unit
45
+ - one dot extends the rhythmic value 50%
46
+ - two dots extend the rhythmic value 75%
47
+ - two dots extend the rhythmic value 87.5%
48
+
49
+ rest
50
+ - a rhythmic value that passes in silence
51
+
52
+ note
53
+ - pitch + rhythmic value
54
+ - example: "E♭3 dotted quarter"
55
+
56
+ unpitched note
57
+ - an unpitched percussion note, such as a drum hit
58
+ - a sounded rhythmic value without a specific pitch
59
+
60
+ tied duration
61
+ - combines multiple rhythmic values into one longer note
62
+ - ties allow rhythmic values to be combined across barlines or strong beats
63
+
64
+ articulation
65
+ - a category of expressions that modify how one or more notes are performed.
66
+ - level of connection
67
+ - staccatissimo - the most staccato, even shorted and more clipped
68
+ - staccato – detatched (shortened to lease space between adjacent notes)
69
+ - tenuto - held of the full value
70
+ - legato - smoothly connected
71
+ - emphasis
72
+ - accent - play with emphasis or a stronger attack
73
+ - marcato - markedly emphasized
74
+ - instrument specific
75
+ - bowings
76
+ - breath mark
77
+ - roll mark
78
+
79
+ ## Instrument Families
80
+
81
+ There are several ways that people talk about families and other categorizations of instruments. For example, the "string family" or "woodwind family", but also more specifically, the word family is applied more specifically (e.g. the "oboe family").
82
+
83
+ ### Text Conversation with Brian Head (May 7, 2023)
84
+
85
+ Brian Head, brother and faculty at USC Thornton School of Music
86
+ https://music.usc.edu/brian-head/
87
+
88
+ Robert Head:
89
+ Hey, Brian. You have a sec for a music question? I’m trying to come up with different terms for the way “family” is used. “Woodwind family” vs. “Oboe family”.
90
+
91
+ Brian Head:
92
+ Those are good, I’d say. In what context?
93
+
94
+ Robert Head:
95
+ In my software project, I’m trying to define those relationships. One is a section of the orchestra and the other is species of instrument. But usually people, in my experience, just say “family”.
96
+ Is there some more precise terminology or adjective that can disambiguate those two terms?
97
+
98
+ Brian Head:
99
+ Hmmm. Already “family” is informal. Blatter distinguishes between “choir” and “family” as in the trumpet family” within the “brass choir”.
100
+
101
+ Robert Head:
102
+ Ah, yes! I do like that.
103
+ But does it apply to percussion?
104
+ “Section” is another candidate, but that gets used for string parts as well.
105
+
106
+ Brian Head:
107
+ Strings and percussion probably don’t think of themselves as a choir, but if you’re mainly looking for a taxonomically consistent word, that’s the feat I can think of at the moment.
108
+ Section is a good word, too, which easily flows between smaller and larger meanings.
109
+
110
+ Robert Head:
111
+ Obviously, the four “families” is a garbage way to classify all instruments and it really is more applicable to the orchestral context, so maybe “orchestra family” or “orchestra section”.
112
+
113
+ Brian Head:
114
+ I’d say that “family” is best used at the instrument level, as in “saxophone family”. Choir or section are better for larger collections. Still, both of those words connote membership in an orchestra. Or large ensemble. “Woodwinds” or “percussion” describe the class of instruments themselves.
115
+
116
+ Robert Head:
117
+ Cool
118
+
119
+ Action Item: Call them orchestra_section
120
+ DONE
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ begin
12
12
  end
13
13
 
14
14
  desc "Generate documentation and show stats"
15
- task :doc_stats => :doc do
15
+ task doc_stats: :doc do
16
16
  sh "yard stats --list-undoc"
17
17
  end
18
18
  rescue LoadError
@@ -29,7 +29,7 @@ end
29
29
  task default: :spec
30
30
 
31
31
  desc "Run all quality checks (tests, linting, security audit)"
32
- task :quality => [:spec, :standard, "bundle:audit:check"]
32
+ task quality: [:spec, :standard, "bundle:audit:check"]
33
33
 
34
34
  desc "Open an irb session preloaded with this library"
35
35
  task :console do
@@ -0,0 +1,86 @@
1
+ require "yaml"
2
+
3
+ # Load the YAML files
4
+ instruments_file = "/Users/roberthead/github.com/roberthead/head_music/lib/head_music/instruments/instruments.yml"
5
+ families_file = "/Users/roberthead/github.com/roberthead/head_music/lib/head_music/instruments/instrument_families.yml"
6
+
7
+ instruments = YAML.load_file(instruments_file)
8
+ families = YAML.load_file(families_file)
9
+
10
+ puts "Checking for inconsistencies between instruments and their families...\n\n"
11
+
12
+ inconsistencies = []
13
+
14
+ instruments.each do |instrument_name, instrument_data|
15
+ family_key = instrument_data["family_key"]
16
+ next unless family_key
17
+
18
+ family_data = families[family_key]
19
+ next unless family_data
20
+
21
+ # Check orchestra_section_key consistency
22
+ instrument_section = instrument_data["orchestra_section_key"]
23
+ family_section = family_data["orchestra_section_key"]
24
+
25
+ if instrument_section && family_section && instrument_section != family_section
26
+ inconsistencies << {
27
+ instrument: instrument_name,
28
+ family: family_key,
29
+ field: "orchestra_section_key",
30
+ instrument_value: instrument_section,
31
+ family_value: family_section
32
+ }
33
+ end
34
+
35
+ # Check classification_keys consistency
36
+ instrument_classifications = instrument_data["classification_keys"] || []
37
+ family_classifications = family_data["classification_keys"] || []
38
+
39
+ # Check if instrument has classifications that conflict with family
40
+ if !instrument_classifications.empty? && !family_classifications.empty?
41
+ conflicts = instrument_classifications - family_classifications
42
+ if !conflicts.empty?
43
+ inconsistencies << {
44
+ instrument: instrument_name,
45
+ family: family_key,
46
+ field: "classification_keys",
47
+ instrument_value: instrument_classifications,
48
+ family_value: family_classifications,
49
+ conflicts: conflicts
50
+ }
51
+ end
52
+ end
53
+ end
54
+
55
+ if inconsistencies.empty?
56
+ puts "No inconsistencies found!"
57
+ else
58
+ puts "Found #{inconsistencies.length} inconsistencies:\n\n"
59
+
60
+ inconsistencies.each_with_index do |issue, index|
61
+ puts "#{index + 1}. #{issue[:instrument]} (family: #{issue[:family]})"
62
+ puts " Field: #{issue[:field]}"
63
+ puts " Instrument value: #{issue[:instrument_value]}"
64
+ puts " Family value: #{issue[:family_value]}"
65
+ if issue[:conflicts]
66
+ puts " Conflicting classifications: #{issue[:conflicts]}"
67
+ end
68
+ puts ""
69
+ end
70
+ end
71
+
72
+ # Also check for instruments with orchestra_section_key but no family
73
+ puts "\nInstruments with orchestra_section_key but no family_key:"
74
+ instruments.each do |instrument_name, instrument_data|
75
+ if instrument_data["orchestra_section_key"] && !instrument_data["family_key"]
76
+ puts "- #{instrument_name}: #{instrument_data["orchestra_section_key"]}"
77
+ end
78
+ end
79
+
80
+ # Check for instruments with classification_keys but no family
81
+ puts "\nInstruments with classification_keys but no family_key:"
82
+ instruments.each do |instrument_name, instrument_data|
83
+ if instrument_data["classification_keys"] && !instrument_data["family_key"]
84
+ puts "- #{instrument_name}: #{instrument_data["classification_keys"]}"
85
+ end
86
+ end
File without changes
data/head_music.gemspec CHANGED
@@ -38,7 +38,7 @@ Gem::Specification.new do |spec|
38
38
 
39
39
  spec.add_development_dependency "rake", "~> 13.0"
40
40
  spec.add_development_dependency "rspec", "~> 3.0"
41
- spec.add_development_dependency "rspec-its", "~> 1.2"
41
+ spec.add_development_dependency "rspec-its", "~> 2.0"
42
42
  spec.add_development_dependency "bundler-audit", "~> 0.9"
43
43
  spec.add_development_dependency "yard", "~> 0.9"
44
44
  end
@@ -1,4 +1,4 @@
1
- # Accepts a number and number of semitones and privides the naming methods.
1
+ # Accepts a number and number of semitones and provides the naming methods.
2
2
  class HeadMusic::Analysis::DiatonicInterval::Naming
3
3
  QUALITY_SEMITONES = HeadMusic::Analysis::DiatonicInterval::QUALITY_SEMITONES
4
4
  NUMBER_NAMES = HeadMusic::Analysis::DiatonicInterval::NUMBER_NAMES