head_music 8.3.0 → 11.0.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +9 -3
- data/CHANGELOG.md +71 -0
- data/CLAUDE.md +62 -25
- data/Gemfile +7 -1
- data/Gemfile.lock +91 -3
- data/MUSIC_THEORY.md +120 -0
- data/README.md +18 -0
- data/Rakefile +7 -2
- data/head_music.gemspec +1 -1
- data/lib/head_music/analysis/diatonic_interval.rb +29 -27
- data/lib/head_music/analysis/dyad.rb +229 -0
- data/lib/head_music/analysis/interval_consonance.rb +51 -0
- data/lib/head_music/analysis/melodic_interval.rb +1 -1
- data/lib/head_music/analysis/pitch_class_set.rb +111 -14
- data/lib/head_music/analysis/{pitch_set.rb → pitch_collection.rb} +11 -5
- data/lib/head_music/analysis/sonority.rb +50 -12
- data/lib/head_music/content/note.rb +1 -1
- data/lib/head_music/content/placement.rb +1 -1
- data/lib/head_music/content/position.rb +1 -1
- data/lib/head_music/content/voice.rb +1 -1
- data/lib/head_music/instruments/alternate_tuning.rb +102 -0
- data/lib/head_music/instruments/alternate_tunings.yml +78 -0
- data/lib/head_music/instruments/instrument.rb +231 -72
- data/lib/head_music/instruments/instrument_configuration.rb +66 -0
- data/lib/head_music/instruments/instrument_configuration_option.rb +38 -0
- data/lib/head_music/instruments/instrument_configurations.yml +288 -0
- data/lib/head_music/instruments/instrument_families.yml +77 -0
- data/lib/head_music/instruments/instrument_family.rb +15 -5
- data/lib/head_music/instruments/instruments.yml +795 -965
- data/lib/head_music/instruments/playing_technique.rb +75 -0
- data/lib/head_music/instruments/playing_techniques.yml +826 -0
- data/lib/head_music/instruments/score_order.rb +136 -0
- data/lib/head_music/instruments/score_orders.yml +130 -0
- data/lib/head_music/instruments/staff.rb +61 -1
- data/lib/head_music/instruments/staff_scheme.rb +6 -4
- data/lib/head_music/instruments/stringing.rb +115 -0
- data/lib/head_music/instruments/stringing_course.rb +58 -0
- data/lib/head_music/instruments/stringings.yml +168 -0
- data/lib/head_music/instruments/variant.rb +6 -1
- data/lib/head_music/locales/de.yml +29 -0
- data/lib/head_music/locales/en.yml +106 -0
- data/lib/head_music/locales/es.yml +29 -0
- data/lib/head_music/locales/fr.yml +29 -0
- data/lib/head_music/locales/it.yml +29 -0
- data/lib/head_music/locales/ru.yml +29 -0
- data/lib/head_music/{rudiment → notation}/musical_symbol.rb +3 -3
- data/lib/head_music/notation/staff_mapping.rb +70 -0
- data/lib/head_music/notation/staff_position.rb +62 -0
- data/lib/head_music/notation.rb +7 -0
- data/lib/head_music/rudiment/alteration.rb +34 -49
- data/lib/head_music/rudiment/alterations.yml +32 -0
- data/lib/head_music/rudiment/base.rb +9 -0
- data/lib/head_music/rudiment/chromatic_interval.rb +4 -7
- data/lib/head_music/rudiment/clef.rb +2 -2
- data/lib/head_music/rudiment/consonance.rb +39 -5
- data/lib/head_music/rudiment/diatonic_context.rb +25 -0
- data/lib/head_music/rudiment/key.rb +77 -0
- data/lib/head_music/rudiment/key_signature/enharmonic_equivalence.rb +1 -1
- data/lib/head_music/rudiment/key_signature.rb +21 -8
- data/lib/head_music/rudiment/letter_name.rb +3 -3
- data/lib/head_music/rudiment/meter.rb +19 -9
- data/lib/head_music/rudiment/mode.rb +92 -0
- data/lib/head_music/rudiment/note.rb +112 -0
- data/lib/head_music/rudiment/pitch/parser.rb +52 -0
- data/lib/head_music/rudiment/pitch.rb +5 -6
- data/lib/head_music/rudiment/pitch_class.rb +1 -1
- data/lib/head_music/rudiment/quality.rb +1 -1
- data/lib/head_music/rudiment/reference_pitch.rb +1 -1
- data/lib/head_music/rudiment/register.rb +4 -1
- data/lib/head_music/rudiment/rest.rb +36 -0
- data/lib/head_music/rudiment/rhythmic_element.rb +53 -0
- data/lib/head_music/rudiment/rhythmic_unit/parser.rb +86 -0
- data/lib/head_music/rudiment/rhythmic_unit.rb +13 -5
- data/lib/head_music/rudiment/rhythmic_units.yml +80 -0
- data/lib/head_music/rudiment/rhythmic_value/parser.rb +77 -0
- data/lib/head_music/{content → rudiment}/rhythmic_value.rb +23 -5
- data/lib/head_music/rudiment/scale.rb +4 -5
- data/lib/head_music/rudiment/scale_degree.rb +1 -1
- data/lib/head_music/rudiment/scale_type.rb +9 -3
- data/lib/head_music/rudiment/solmization.rb +1 -1
- data/lib/head_music/rudiment/spelling.rb +8 -4
- data/lib/head_music/rudiment/tempo.rb +85 -0
- data/lib/head_music/rudiment/tonal_context.rb +35 -0
- data/lib/head_music/rudiment/tuning/just_intonation.rb +0 -39
- data/lib/head_music/rudiment/tuning/meantone.rb +0 -39
- data/lib/head_music/rudiment/tuning/pythagorean.rb +0 -39
- data/lib/head_music/rudiment/tuning.rb +21 -1
- data/lib/head_music/rudiment/unpitched_note.rb +62 -0
- data/lib/head_music/style/guidelines/consonant_climax.rb +2 -2
- data/lib/head_music/style/medieval_tradition.rb +26 -0
- data/lib/head_music/style/modern_tradition.rb +31 -0
- data/lib/head_music/style/renaissance_tradition.rb +26 -0
- data/lib/head_music/style/tradition.rb +21 -0
- data/lib/head_music/time/clock_position.rb +84 -0
- data/lib/head_music/time/conductor.rb +264 -0
- data/lib/head_music/time/meter_event.rb +37 -0
- data/lib/head_music/time/meter_map.rb +173 -0
- data/lib/head_music/time/musical_position.rb +188 -0
- data/lib/head_music/time/smpte_timecode.rb +164 -0
- data/lib/head_music/time/tempo_event.rb +40 -0
- data/lib/head_music/time/tempo_map.rb +187 -0
- data/lib/head_music/time.rb +32 -0
- data/lib/head_music/utilities/case.rb +27 -0
- data/lib/head_music/utilities/hash_key.rb +34 -2
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +71 -22
- data/user_stories/active/string-pitches.md +41 -0
- data/user_stories/backlog/notation-style.md +183 -0
- data/user_stories/backlog/organizing-content.md +80 -0
- data/user_stories/done/consonance-dissonance-classification.md +117 -0
- data/user_stories/{backlog → done}/dyad-analysis.md +6 -16
- data/user_stories/done/epic--score-order/PLAN.md +244 -0
- data/user_stories/done/expand-playing-techniques.md +38 -0
- data/user_stories/done/handle-time.md +7 -0
- data/user_stories/done/handle-time.rb +163 -0
- data/user_stories/done/instrument-architecture.md +238 -0
- data/user_stories/done/instrument-variant.md +65 -0
- data/user_stories/done/move-musical-symbol-to-notation.md +161 -0
- data/user_stories/done/move-staff-mapping-to-notation.md +158 -0
- data/user_stories/done/move-staff-position-to-notation.md +141 -0
- data/user_stories/done/notation-module-foundation.md +102 -0
- data/user_stories/done/percussion_set.md +260 -0
- data/user_stories/done/sonority-identification.md +37 -0
- data/user_stories/done/superclass-for-note.md +30 -0
- data/user_stories/epics/notation-module.md +135 -0
- data/user_stories/visioning/agentic-daw.md +2 -0
- metadata +84 -18
- data/TODO.md +0 -109
- data/check_instrument_consistency.rb +0 -0
- data/test_translations.rb +0 -15
- data/user_stories/backlog/consonance-dissonance-classification.md +0 -57
- data/user_stories/backlog/pitch-set-classification.md +0 -62
- data/user_stories/backlog/sonority-identification.md +0 -47
- /data/user_stories/{backlog → done/epic--score-order}/band-score-order.md +0 -0
- /data/user_stories/{backlog → done/epic--score-order}/chamber-ensemble-score-order.md +0 -0
- /data/user_stories/{backlog → done/epic--score-order}/orchestral-score-order.md +0 -0
- /data/user_stories/{backlog → done}/pitch-class-set-analysis.md +0 -0
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
|
|
File without changes
|
data/test_translations.rb
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
# Test the translation loading functionality
|
|
4
|
-
|
|
5
|
-
# First test - German translation of solfege
|
|
6
|
-
result = system('bundle exec ruby -e "require \'head_music\'; puts HeadMusic::Rudiment::Solmization.get(\'Solfège\')&.name || \'nil\'"')
|
|
7
|
-
puts "German 'Solfège' test: #{result}"
|
|
8
|
-
|
|
9
|
-
# Second test - Italian translation
|
|
10
|
-
result = system('bundle exec ruby -e "require \'head_music\'; puts HeadMusic::Rudiment::Solmization.get(\'solfeggio\')&.name || \'nil\'"')
|
|
11
|
-
puts "Italian 'solfeggio' test: #{result}"
|
|
12
|
-
|
|
13
|
-
# Third test - Russian translation
|
|
14
|
-
result = system('bundle exec ruby -e "require \'head_music\'; puts HeadMusic::Rudiment::Solmization.get(\'сольфеджио\')&.name || \'nil\'"')
|
|
15
|
-
puts "Russian 'сольфеджио' test: #{result}"
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# Consonance and Dissonance Classification
|
|
2
|
-
|
|
3
|
-
As a music theorist or counterpoint student
|
|
4
|
-
|
|
5
|
-
I want to classify intervals by their consonance and dissonance levels
|
|
6
|
-
|
|
7
|
-
So that I can apply proper voice leading rules
|
|
8
|
-
|
|
9
|
-
## Scenario: Classify open consonances
|
|
10
|
-
|
|
11
|
-
Given I have a perfect fifth or perfect octave
|
|
12
|
-
|
|
13
|
-
When I check the consonance classification
|
|
14
|
-
|
|
15
|
-
Then it should be identified as "open consonance"
|
|
16
|
-
|
|
17
|
-
## Scenario: Classify soft consonances
|
|
18
|
-
|
|
19
|
-
Given I have a third or sixth interval (major or minor)
|
|
20
|
-
|
|
21
|
-
When I check the consonance classification
|
|
22
|
-
|
|
23
|
-
Then it should be identified as "soft consonance"
|
|
24
|
-
|
|
25
|
-
## Scenario: Classify mild dissonances
|
|
26
|
-
|
|
27
|
-
Given I have a major second or minor seventh
|
|
28
|
-
|
|
29
|
-
When I check the consonance classification
|
|
30
|
-
|
|
31
|
-
Then it should be identified as "mild dissonance"
|
|
32
|
-
|
|
33
|
-
## Scenario: Classify sharp dissonances
|
|
34
|
-
|
|
35
|
-
Given I have a minor second or major seventh
|
|
36
|
-
|
|
37
|
-
When I check the consonance classification
|
|
38
|
-
|
|
39
|
-
Then it should be identified as "sharp dissonance"
|
|
40
|
-
|
|
41
|
-
## Scenario: Handle perfect fourth context
|
|
42
|
-
|
|
43
|
-
Given I have a perfect fourth interval
|
|
44
|
-
|
|
45
|
-
When I check the consonance classification
|
|
46
|
-
|
|
47
|
-
Then it should indicate context-dependent classification
|
|
48
|
-
|
|
49
|
-
And note it can be either consonant or dissonant
|
|
50
|
-
|
|
51
|
-
## Scenario: Classify tritone
|
|
52
|
-
|
|
53
|
-
Given I have a tritone interval
|
|
54
|
-
|
|
55
|
-
When I check the consonance classification
|
|
56
|
-
|
|
57
|
-
Then it should be identified as "neutral" or "restless"
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# Pitch Set Classification
|
|
2
|
-
|
|
3
|
-
As a music theorist
|
|
4
|
-
|
|
5
|
-
I want to classify pitch sets by their size and properties
|
|
6
|
-
|
|
7
|
-
So that I can analyze and categorize harmonic structures
|
|
8
|
-
|
|
9
|
-
## Scenario: Identify empty set
|
|
10
|
-
|
|
11
|
-
Given I have no pitches
|
|
12
|
-
|
|
13
|
-
When I create a pitch set
|
|
14
|
-
|
|
15
|
-
Then it should be identified as an EmptySet
|
|
16
|
-
|
|
17
|
-
## Scenario: Identify monad
|
|
18
|
-
|
|
19
|
-
Given I have a single pitch
|
|
20
|
-
|
|
21
|
-
When I check the pitch set type
|
|
22
|
-
|
|
23
|
-
Then it should be identified as a Monad
|
|
24
|
-
|
|
25
|
-
And monad? should return true
|
|
26
|
-
|
|
27
|
-
## Scenario: Identify dyad
|
|
28
|
-
|
|
29
|
-
Given I have exactly two pitches
|
|
30
|
-
|
|
31
|
-
When I check the pitch set type
|
|
32
|
-
|
|
33
|
-
Then it should be identified as a Dyad
|
|
34
|
-
|
|
35
|
-
And dyad? should return true
|
|
36
|
-
|
|
37
|
-
## Scenario: Distinguish triads from trichords
|
|
38
|
-
|
|
39
|
-
Given I have three pitches
|
|
40
|
-
|
|
41
|
-
When I analyze the pitch set
|
|
42
|
-
|
|
43
|
-
Then trichord? should return true for any 3-pitch set
|
|
44
|
-
|
|
45
|
-
And triad? should return true only if they form stacked thirds
|
|
46
|
-
|
|
47
|
-
## Scenario: Identify larger pitch sets
|
|
48
|
-
|
|
49
|
-
Given I have a pitch set with N pitches
|
|
50
|
-
|
|
51
|
-
When I check the classification
|
|
52
|
-
|
|
53
|
-
Then it should be identified as:
|
|
54
|
-
- Tetrachord (4 pitches) with seventh_chord? check
|
|
55
|
-
- Pentachord (5 pitches)
|
|
56
|
-
- Hexachord (6 pitches)
|
|
57
|
-
- Heptachord (7 pitches)
|
|
58
|
-
- Octachord (8 pitches)
|
|
59
|
-
- Nonachord (9 pitches)
|
|
60
|
-
- Decachord (10 pitches)
|
|
61
|
-
- Undecachord (11 pitches)
|
|
62
|
-
- Dodecachord (12 pitches)
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# Sonority Identification
|
|
2
|
-
|
|
3
|
-
As a music theorist or composer
|
|
4
|
-
|
|
5
|
-
I want to identify and work with named sonorities
|
|
6
|
-
|
|
7
|
-
So that I can analyze and create harmonic structures
|
|
8
|
-
|
|
9
|
-
## Scenario: Get sonority by identifier
|
|
10
|
-
|
|
11
|
-
Given I need a specific sonority
|
|
12
|
-
|
|
13
|
-
When I call Sonority.get with an identifier like "major triad"
|
|
14
|
-
|
|
15
|
-
Then I should receive the corresponding sonority object
|
|
16
|
-
|
|
17
|
-
And it should contain the correct interval structure
|
|
18
|
-
|
|
19
|
-
## Scenario: Identify sonority from pitch set
|
|
20
|
-
|
|
21
|
-
Given I have a set of pitches
|
|
22
|
-
|
|
23
|
-
When I call Sonority.for with the pitch set
|
|
24
|
-
|
|
25
|
-
Then I should receive the identified sonority
|
|
26
|
-
|
|
27
|
-
And it should correctly name the harmonic structure
|
|
28
|
-
|
|
29
|
-
## Scenario: Generate pitch set from sonority
|
|
30
|
-
|
|
31
|
-
Given I have a sonority and a root pitch
|
|
32
|
-
|
|
33
|
-
When I call Sonority.pitch_set_for with root pitch and inversion
|
|
34
|
-
|
|
35
|
-
Then I should receive the correct pitches
|
|
36
|
-
|
|
37
|
-
And they should be in the specified inversion
|
|
38
|
-
|
|
39
|
-
## Scenario: Access sonority from pitch set
|
|
40
|
-
|
|
41
|
-
Given I have a PitchSet object
|
|
42
|
-
|
|
43
|
-
When I call the sonority method
|
|
44
|
-
|
|
45
|
-
Then I should receive the corresponding Sonority object
|
|
46
|
-
|
|
47
|
-
And it should correctly identify the harmonic content
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|