head_music 8.3.0 → 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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -0
  3. data/CLAUDE.md +32 -15
  4. data/Gemfile.lock +1 -1
  5. data/MUSIC_THEORY.md +120 -0
  6. data/lib/head_music/analysis/diatonic_interval.rb +29 -27
  7. data/lib/head_music/analysis/interval_consonance.rb +51 -0
  8. data/lib/head_music/content/note.rb +1 -1
  9. data/lib/head_music/content/placement.rb +1 -1
  10. data/lib/head_music/content/position.rb +1 -1
  11. data/lib/head_music/content/staff.rb +1 -1
  12. data/lib/head_music/instruments/instrument.rb +103 -113
  13. data/lib/head_music/instruments/instrument_family.rb +13 -2
  14. data/lib/head_music/instruments/instrument_type.rb +188 -0
  15. data/lib/head_music/instruments/score_order.rb +139 -0
  16. data/lib/head_music/instruments/score_orders.yml +130 -0
  17. data/lib/head_music/instruments/variant.rb +6 -0
  18. data/lib/head_music/locales/de.yml +6 -0
  19. data/lib/head_music/locales/en.yml +6 -0
  20. data/lib/head_music/locales/es.yml +6 -0
  21. data/lib/head_music/locales/fr.yml +6 -0
  22. data/lib/head_music/locales/it.yml +6 -0
  23. data/lib/head_music/locales/ru.yml +6 -0
  24. data/lib/head_music/rudiment/alteration.rb +23 -8
  25. data/lib/head_music/rudiment/base.rb +9 -0
  26. data/lib/head_music/rudiment/chromatic_interval.rb +3 -6
  27. data/lib/head_music/rudiment/clef.rb +1 -1
  28. data/lib/head_music/rudiment/consonance.rb +37 -4
  29. data/lib/head_music/rudiment/diatonic_context.rb +25 -0
  30. data/lib/head_music/rudiment/key.rb +77 -0
  31. data/lib/head_music/rudiment/key_signature/enharmonic_equivalence.rb +1 -1
  32. data/lib/head_music/rudiment/key_signature.rb +46 -7
  33. data/lib/head_music/rudiment/letter_name.rb +3 -3
  34. data/lib/head_music/rudiment/meter.rb +19 -9
  35. data/lib/head_music/rudiment/mode.rb +92 -0
  36. data/lib/head_music/rudiment/musical_symbol.rb +1 -1
  37. data/lib/head_music/rudiment/note.rb +112 -0
  38. data/lib/head_music/rudiment/pitch/parser.rb +52 -0
  39. data/lib/head_music/rudiment/pitch.rb +5 -6
  40. data/lib/head_music/rudiment/pitch_class.rb +1 -1
  41. data/lib/head_music/rudiment/quality.rb +1 -1
  42. data/lib/head_music/rudiment/reference_pitch.rb +1 -1
  43. data/lib/head_music/rudiment/register.rb +4 -1
  44. data/lib/head_music/rudiment/rest.rb +36 -0
  45. data/lib/head_music/rudiment/rhythmic_element.rb +53 -0
  46. data/lib/head_music/rudiment/rhythmic_unit/parser.rb +86 -0
  47. data/lib/head_music/rudiment/rhythmic_unit.rb +13 -5
  48. data/lib/head_music/rudiment/rhythmic_units.yml +80 -0
  49. data/lib/head_music/rudiment/rhythmic_value/parser.rb +77 -0
  50. data/lib/head_music/{content → rudiment}/rhythmic_value.rb +23 -5
  51. data/lib/head_music/rudiment/scale.rb +4 -5
  52. data/lib/head_music/rudiment/scale_degree.rb +1 -1
  53. data/lib/head_music/rudiment/scale_type.rb +9 -3
  54. data/lib/head_music/rudiment/solmization.rb +1 -1
  55. data/lib/head_music/rudiment/spelling.rb +5 -4
  56. data/lib/head_music/rudiment/tempo.rb +85 -0
  57. data/lib/head_music/rudiment/tonal_context.rb +35 -0
  58. data/lib/head_music/rudiment/tuning.rb +1 -1
  59. data/lib/head_music/rudiment/unpitched_note.rb +62 -0
  60. data/lib/head_music/style/medieval_tradition.rb +26 -0
  61. data/lib/head_music/style/modern_tradition.rb +34 -0
  62. data/lib/head_music/style/renaissance_tradition.rb +26 -0
  63. data/lib/head_music/style/tradition.rb +21 -0
  64. data/lib/head_music/utilities/hash_key.rb +34 -2
  65. data/lib/head_music/version.rb +1 -1
  66. data/lib/head_music.rb +31 -10
  67. data/user_stories/active/handle-time.md +7 -0
  68. data/user_stories/active/handle-time.rb +177 -0
  69. data/user_stories/done/epic--score-order/PLAN.md +244 -0
  70. data/user_stories/done/instrument-variant.md +65 -0
  71. data/user_stories/done/superclass-for-note.md +30 -0
  72. data/user_stories/todo/agentic-daw.md +3 -0
  73. data/user_stories/{backlog → todo}/dyad-analysis.md +2 -10
  74. data/user_stories/todo/material-and-scores.md +10 -0
  75. data/user_stories/todo/organizing-content.md +72 -0
  76. data/user_stories/todo/percussion_set.md +1 -0
  77. data/user_stories/{backlog → todo}/pitch-class-set-analysis.md +40 -0
  78. data/user_stories/{backlog → todo}/pitch-set-classification.md +10 -0
  79. data/user_stories/{backlog → todo}/sonority-identification.md +20 -0
  80. metadata +43 -12
  81. data/TODO.md +0 -109
  82. /data/user_stories/{backlog → done/epic--score-order}/band-score-order.md +0 -0
  83. /data/user_stories/{backlog → done/epic--score-order}/chamber-ensemble-score-order.md +0 -0
  84. /data/user_stories/{backlog → done/epic--score-order}/orchestral-score-order.md +0 -0
  85. /data/user_stories/{backlog → todo}/consonance-dissonance-classification.md +0 -0
@@ -0,0 +1,72 @@
1
+
2
+ Project = overall container.
3
+ Flow = segment-level unit (supports sketches, movements, cues).
4
+ Sequence = neutral term for the editing canvas (2D space).
5
+ Timeline = strictly the axis.
6
+
7
+
8
+ Material?
9
+
10
+ ScorePart
11
+ @name
12
+ >> instruments
13
+ def primary_instrument
14
+ def default_staff_system
15
+
16
+
17
+ MusicContent
18
+ >> score_parts
19
+ @score_type (orchestral, band, chamber, pop, solo)
20
+ def ordered_score_parts
21
+ def score_parts_grouped_by_orchestra_section
22
+ # so we can square-bracket the sections
23
+
24
+
25
+
26
+ Score < MusicContent
27
+ @title
28
+ @subtitle
29
+ @dedication
30
+ >> score_credits
31
+
32
+
33
+ ScoreCredit
34
+ > person
35
+ - role (composer, songwriter, lyricist, arranger, transcriber)
36
+
37
+
38
+ Person
39
+ - full_name
40
+ - birth_year int optional
41
+ - death_year int optional
42
+
43
+
44
+ ScoreLayout < Layout
45
+
46
+
47
+
48
+ EnsembleSession (rehearsal, recording, or performance)
49
+ >> scores
50
+ >> score_part_players
51
+
52
+
53
+ ScorePartPlayer
54
+ > score_part
55
+ >> players
56
+
57
+
58
+
59
+
60
+ Player
61
+ > person
62
+
63
+
64
+
65
+ Fragment < MusicContent
66
+
67
+
68
+
69
+
70
+
71
+
72
+
@@ -0,0 +1 @@
1
+ Create a PercussionSet staff for percussion or drum kit parts in a score.
@@ -6,6 +6,46 @@ I want to analyze pitch class sets
6
6
 
7
7
  So that I can identify set relationships and transformations
8
8
 
9
+ ## Scenario: Get size of pitch class set
10
+
11
+ Given I have a pitch class set
12
+
13
+ When I call the size method
14
+
15
+ Then it should return the number of unique pitch classes
16
+
17
+ ## Scenario: Check if monad
18
+
19
+ Given I have a pitch class set with one pitch class
20
+
21
+ When I call monad?
22
+
23
+ Then it should return true
24
+
25
+ ## Scenario: Check if dyad
26
+
27
+ Given I have a pitch class set with two pitch classes
28
+
29
+ When I call dyad?
30
+
31
+ Then it should return true
32
+
33
+ ## Scenario: Check if triad
34
+
35
+ Given I have a pitch class set with three pitch classes
36
+
37
+ When I call triad?
38
+
39
+ Then it should return true only if they form stacked thirds
40
+
41
+ ## Scenario: Check if trichord
42
+
43
+ Given I have a pitch class set with three pitch classes
44
+
45
+ When I call trichord?
46
+
47
+ Then it should return true for any 3-pitch class set
48
+
9
49
  ## Scenario: Find normal form
10
50
 
11
51
  Given I have a pitch class set
@@ -6,6 +6,16 @@ I want to classify pitch sets by their size and properties
6
6
 
7
7
  So that I can analyze and categorize harmonic structures
8
8
 
9
+ Note: A PitchSet is unlike a PitchClassSet in that the pitches have spellings with octaves rather than Spellings only or octave-less 0-11 designations.
10
+
11
+ ## Scenario: Get size of pitch set
12
+
13
+ Given I have a pitch set with N pitches
14
+
15
+ When I call the size method
16
+
17
+ Then it should return the number of pitches in the set
18
+
9
19
  ## Scenario: Identify empty set
10
20
 
11
21
  Given I have no pitches
@@ -45,3 +45,23 @@ When I call the sonority method
45
45
  Then I should receive the corresponding Sonority object
46
46
 
47
47
  And it should correctly identify the harmonic content
48
+
49
+ ## Scenario: Work with triads
50
+
51
+ Given I need to analyze triadic harmony
52
+
53
+ When I work with Triad objects
54
+
55
+ Then I should be able to identify major, minor, diminished, and augmented triads
56
+
57
+ And access their specific properties and methods
58
+
59
+ ## Scenario: Work with seventh chords
60
+
61
+ Given I need to analyze seventh chord harmony
62
+
63
+ When I work with SeventhChord objects
64
+
65
+ Then I should be able to identify all common seventh chord types
66
+
67
+ And note that nothing beyond seventh chords is needed to analyze pre-Romantic music
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: head_music
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.3.0
4
+ version: 9.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Head
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-07-26 00:00:00.000000000 Z
11
+ date: 2025-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -150,9 +150,9 @@ files:
150
150
  - Gemfile
151
151
  - Gemfile.lock
152
152
  - LICENSE.txt
153
+ - MUSIC_THEORY.md
153
154
  - README.md
154
155
  - Rakefile
155
- - TODO.md
156
156
  - bin/check_instrument_consistency.rb
157
157
  - bin/console
158
158
  - bin/setup
@@ -167,6 +167,7 @@ files:
167
167
  - lib/head_music/analysis/diatonic_interval/semitones.rb
168
168
  - lib/head_music/analysis/diatonic_interval/size.rb
169
169
  - lib/head_music/analysis/harmonic_interval.rb
170
+ - lib/head_music/analysis/interval_consonance.rb
170
171
  - lib/head_music/analysis/interval_cycle.rb
171
172
  - lib/head_music/analysis/melodic_interval.rb
172
173
  - lib/head_music/analysis/motion.rb
@@ -178,13 +179,15 @@ files:
178
179
  - lib/head_music/content/note.rb
179
180
  - lib/head_music/content/placement.rb
180
181
  - lib/head_music/content/position.rb
181
- - lib/head_music/content/rhythmic_value.rb
182
182
  - lib/head_music/content/staff.rb
183
183
  - lib/head_music/content/voice.rb
184
184
  - lib/head_music/instruments/instrument.rb
185
185
  - lib/head_music/instruments/instrument_families.yml
186
186
  - lib/head_music/instruments/instrument_family.rb
187
+ - lib/head_music/instruments/instrument_type.rb
187
188
  - lib/head_music/instruments/instruments.yml
189
+ - lib/head_music/instruments/score_order.rb
190
+ - lib/head_music/instruments/score_orders.yml
188
191
  - lib/head_music/instruments/staff.rb
189
192
  - lib/head_music/instruments/staff_scheme.rb
190
193
  - lib/head_music/instruments/variant.rb
@@ -197,34 +200,49 @@ files:
197
200
  - lib/head_music/locales/ru.yml
198
201
  - lib/head_music/named.rb
199
202
  - lib/head_music/rudiment/alteration.rb
203
+ - lib/head_music/rudiment/base.rb
200
204
  - lib/head_music/rudiment/chromatic_interval.rb
201
205
  - lib/head_music/rudiment/clef.rb
202
206
  - lib/head_music/rudiment/clefs.yml
203
207
  - lib/head_music/rudiment/consonance.rb
208
+ - lib/head_music/rudiment/diatonic_context.rb
209
+ - lib/head_music/rudiment/key.rb
204
210
  - lib/head_music/rudiment/key_signature.rb
205
211
  - lib/head_music/rudiment/key_signature/enharmonic_equivalence.rb
206
212
  - lib/head_music/rudiment/letter_name.rb
207
213
  - lib/head_music/rudiment/meter.rb
214
+ - lib/head_music/rudiment/mode.rb
208
215
  - lib/head_music/rudiment/musical_symbol.rb
216
+ - lib/head_music/rudiment/note.rb
209
217
  - lib/head_music/rudiment/pitch.rb
210
218
  - lib/head_music/rudiment/pitch/enharmonic_equivalence.rb
211
219
  - lib/head_music/rudiment/pitch/octave_equivalence.rb
220
+ - lib/head_music/rudiment/pitch/parser.rb
212
221
  - lib/head_music/rudiment/pitch_class.rb
213
222
  - lib/head_music/rudiment/quality.rb
214
223
  - lib/head_music/rudiment/reference_pitch.rb
215
224
  - lib/head_music/rudiment/register.rb
225
+ - lib/head_music/rudiment/rest.rb
216
226
  - lib/head_music/rudiment/rhythm.rb
227
+ - lib/head_music/rudiment/rhythmic_element.rb
217
228
  - lib/head_music/rudiment/rhythmic_unit.rb
229
+ - lib/head_music/rudiment/rhythmic_unit/parser.rb
230
+ - lib/head_music/rudiment/rhythmic_units.yml
231
+ - lib/head_music/rudiment/rhythmic_value.rb
232
+ - lib/head_music/rudiment/rhythmic_value/parser.rb
218
233
  - lib/head_music/rudiment/scale.rb
219
234
  - lib/head_music/rudiment/scale_degree.rb
220
235
  - lib/head_music/rudiment/scale_type.rb
221
236
  - lib/head_music/rudiment/solmization.rb
222
237
  - lib/head_music/rudiment/solmizations.yml
223
238
  - lib/head_music/rudiment/spelling.rb
239
+ - lib/head_music/rudiment/tempo.rb
240
+ - lib/head_music/rudiment/tonal_context.rb
224
241
  - lib/head_music/rudiment/tuning.rb
225
242
  - lib/head_music/rudiment/tuning/just_intonation.rb
226
243
  - lib/head_music/rudiment/tuning/meantone.rb
227
244
  - lib/head_music/rudiment/tuning/pythagorean.rb
245
+ - lib/head_music/rudiment/unpitched_note.rb
228
246
  - lib/head_music/style/analysis.rb
229
247
  - lib/head_music/style/annotation.rb
230
248
  - lib/head_music/style/guidelines/always_move.rb
@@ -265,17 +283,30 @@ files:
265
283
  - lib/head_music/style/guides/fux_cantus_firmus.rb
266
284
  - lib/head_music/style/guides/modern_cantus_firmus.rb
267
285
  - lib/head_music/style/mark.rb
286
+ - lib/head_music/style/medieval_tradition.rb
287
+ - lib/head_music/style/modern_tradition.rb
288
+ - lib/head_music/style/renaissance_tradition.rb
289
+ - lib/head_music/style/tradition.rb
268
290
  - lib/head_music/utilities/hash_key.rb
269
291
  - lib/head_music/version.rb
270
292
  - test_translations.rb
271
- - user_stories/backlog/band-score-order.md
272
- - user_stories/backlog/chamber-ensemble-score-order.md
273
- - user_stories/backlog/consonance-dissonance-classification.md
274
- - user_stories/backlog/dyad-analysis.md
275
- - user_stories/backlog/orchestral-score-order.md
276
- - user_stories/backlog/pitch-class-set-analysis.md
277
- - user_stories/backlog/pitch-set-classification.md
278
- - user_stories/backlog/sonority-identification.md
293
+ - user_stories/active/handle-time.md
294
+ - user_stories/active/handle-time.rb
295
+ - user_stories/done/epic--score-order/PLAN.md
296
+ - user_stories/done/epic--score-order/band-score-order.md
297
+ - user_stories/done/epic--score-order/chamber-ensemble-score-order.md
298
+ - user_stories/done/epic--score-order/orchestral-score-order.md
299
+ - user_stories/done/instrument-variant.md
300
+ - user_stories/done/superclass-for-note.md
301
+ - user_stories/todo/agentic-daw.md
302
+ - user_stories/todo/consonance-dissonance-classification.md
303
+ - user_stories/todo/dyad-analysis.md
304
+ - user_stories/todo/material-and-scores.md
305
+ - user_stories/todo/organizing-content.md
306
+ - user_stories/todo/percussion_set.md
307
+ - user_stories/todo/pitch-class-set-analysis.md
308
+ - user_stories/todo/pitch-set-classification.md
309
+ - user_stories/todo/sonority-identification.md
279
310
  homepage: https://github.com/roberthead/head_music
280
311
  licenses:
281
312
  - MIT
data/TODO.md DELETED
@@ -1,109 +0,0 @@
1
- # TODO
2
-
3
- ## User Stories
4
-
5
- Sets
6
- DurationSet?
7
-
8
- Triad
9
- SeventhChord
10
- Don't need anything beyond seventh chords to analyze pre-Romantic music.
11
-
12
-
13
- ## User stories
14
-
15
-
16
- ### Done
17
-
18
- As a developer
19
- When instantiating a DiatonicInterval
20
- When passing an abbreviation, such as 'P5' or 'm2'
21
- I want to receive that instance.
22
-
23
- As a developer
24
- Given a pitch
25
- I want to be able to add a diatonic interval to get another pitch.
26
-
27
- DiatonicInterval
28
- - def above(pitch) -> pitch
29
- DiatonicInterval
30
- - def below(pitch) -> pitch
31
-
32
- Pitch addition and subtraction
33
- - define `Pitch#+`, `Pitch#-`
34
- - use DiatonicInterval methods
35
-
36
- PitchSet
37
-
38
- A PitchSet is unlike a PitchClassSet in that the pitches have spellings with octaves rather than Spellings only or octave-less 0-11 designations.
39
-
40
- PitchClassSet
41
- .size?
42
- .monad?
43
- .dyad?
44
- .triad? (must be stacked thirds to be a 'triad')
45
- .trichord? (all 3-pitch sets)
46
-
47
- Should every group of pitches have one or more strategies for describing it? Such as Dyad?
48
-
49
- Set (superclass?)
50
- PitchSet
51
- EmptySet
52
- Monad
53
- Dyad
54
- Trichord (or Triad)
55
- - triad?
56
- Tetrachord (or Tetrad)
57
- - seventh_chord?
58
- Pentachord (or Pentad)
59
- Hexachord (or Hexad)
60
- Heptachords (or Heptad or, sometimes, mixing Latin and Greek roots, "Septachord")
61
- Octachords (Octad)
62
- Nonachords (Nonad)
63
- Decachords (Decad)
64
- Undecachords
65
- Dodecachord
66
-
67
- PitchClassSet
68
- .normal_form? (most compact)
69
- .prime_form (most compact normal form of the original or any inversion)
70
-
71
-
72
- ## Text Conversation with Brian Head (May 7, 2023)
73
-
74
- Brian Head, brother and faculty at USC Thornton School of Music
75
- https://music.usc.edu/brian-head/
76
-
77
- Robert Head:
78
- 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”.
79
-
80
- Brian Head:
81
- Those are good, I’d say. In what context?
82
-
83
- Robert Head:
84
- 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”.
85
- Is there some more precise terminology or adjective that can disambiguate those two terms?
86
-
87
- Brian Head:
88
- Hmmm. Already “family” is informal. Blatter distinguishes between “choir” and “family” as in the trumpet family” within the “brass choir”.
89
-
90
- Robert Head:
91
- Ah, yes! I do like that.
92
- But does it apply to percussion?
93
- “Section” is another candidate, but that gets used for string parts as well.
94
-
95
- Brian Head:
96
- 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.
97
- Section is a good word, too, which easily flows between smaller and larger meanings.
98
-
99
- Robert Head:
100
- 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”.
101
-
102
- Brian Head:
103
- 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.
104
-
105
- Robert Head:
106
- Cool
107
-
108
- Action Item: Call them orchestra_section
109
- DONE