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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +53 -0
- data/CLAUDE.md +32 -15
- data/Gemfile.lock +1 -1
- data/MUSIC_THEORY.md +120 -0
- data/lib/head_music/analysis/diatonic_interval.rb +29 -27
- data/lib/head_music/analysis/interval_consonance.rb +51 -0
- 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/staff.rb +1 -1
- data/lib/head_music/instruments/instrument.rb +103 -113
- data/lib/head_music/instruments/instrument_family.rb +13 -2
- data/lib/head_music/instruments/instrument_type.rb +188 -0
- data/lib/head_music/instruments/score_order.rb +139 -0
- data/lib/head_music/instruments/score_orders.yml +130 -0
- data/lib/head_music/instruments/variant.rb +6 -0
- data/lib/head_music/locales/de.yml +6 -0
- data/lib/head_music/locales/en.yml +6 -0
- data/lib/head_music/locales/es.yml +6 -0
- data/lib/head_music/locales/fr.yml +6 -0
- data/lib/head_music/locales/it.yml +6 -0
- data/lib/head_music/locales/ru.yml +6 -0
- data/lib/head_music/rudiment/alteration.rb +23 -8
- data/lib/head_music/rudiment/base.rb +9 -0
- data/lib/head_music/rudiment/chromatic_interval.rb +3 -6
- data/lib/head_music/rudiment/clef.rb +1 -1
- data/lib/head_music/rudiment/consonance.rb +37 -4
- 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 +46 -7
- 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/musical_symbol.rb +1 -1
- 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 +5 -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.rb +1 -1
- data/lib/head_music/rudiment/unpitched_note.rb +62 -0
- data/lib/head_music/style/medieval_tradition.rb +26 -0
- data/lib/head_music/style/modern_tradition.rb +34 -0
- data/lib/head_music/style/renaissance_tradition.rb +26 -0
- data/lib/head_music/style/tradition.rb +21 -0
- data/lib/head_music/utilities/hash_key.rb +34 -2
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +31 -10
- data/user_stories/active/handle-time.md +7 -0
- data/user_stories/active/handle-time.rb +177 -0
- data/user_stories/done/epic--score-order/PLAN.md +244 -0
- data/user_stories/done/instrument-variant.md +65 -0
- data/user_stories/done/superclass-for-note.md +30 -0
- data/user_stories/todo/agentic-daw.md +3 -0
- data/user_stories/{backlog → todo}/dyad-analysis.md +2 -10
- data/user_stories/todo/material-and-scores.md +10 -0
- data/user_stories/todo/organizing-content.md +72 -0
- data/user_stories/todo/percussion_set.md +1 -0
- data/user_stories/{backlog → todo}/pitch-class-set-analysis.md +40 -0
- data/user_stories/{backlog → todo}/pitch-set-classification.md +10 -0
- data/user_stories/{backlog → todo}/sonority-identification.md +20 -0
- metadata +43 -12
- data/TODO.md +0 -109
- /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 → todo}/consonance-dissonance-classification.md +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 012ce64dd4174cbeec42f1d4825e9fd10af79ed8f4b83a3beedbc14a9aea34fa
|
|
4
|
+
data.tar.gz: a31dbb0b6b3d732bd41e1dbfe6a042a8395db05832d58848ec4d53e5ae4a4524
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a98367425dcfb4899b24ef2e7986a7fbc594202c960b553ab35d3cecd5ee0b5ecb10f65e1e458fe2d1797b70eded378fd319b2cbfc7b9a5a2e393773ce75f06b
|
|
7
|
+
data.tar.gz: d3baf95b28de6c2df0e3d14250ed9c335cb3793d598eb7cf1c417dd40b6463bb57ddcda19be309a1045e835809ad3b9ad45dc0fd21797563319069da35db76bc
|
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
CHANGED
|
@@ -41,14 +41,26 @@ bundle exec rake doc
|
|
|
41
41
|
bundle exec rake doc_stats
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
###
|
|
44
|
+
### Git Etiquette
|
|
45
45
|
|
|
46
|
-
|
|
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.
|
|
47
55
|
|
|
48
56
|
```bash
|
|
49
|
-
bundle exec rubocop
|
|
57
|
+
bundle exec rubocop -a
|
|
50
58
|
```
|
|
51
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
|
+
|
|
52
64
|
### Testing
|
|
53
65
|
|
|
54
66
|
Tests are written in RSpec and located in the `/spec` directory, mirroring the `/lib` structure. The project requires 90% code coverage minimum.
|
|
@@ -60,25 +72,25 @@ Tests are written in RSpec and located in the `/spec` directory, mirroring the `
|
|
|
60
72
|
The codebase follows a domain-driven design with clear module boundaries:
|
|
61
73
|
|
|
62
74
|
1. **HeadMusic::Rudiment** - Core music theory elements
|
|
63
|
-
|
|
64
|
-
|
|
75
|
+
- Pitch, Note, Scale, Key, Interval, Chord
|
|
76
|
+
- Factory methods: `.get()` for most rudiments
|
|
65
77
|
|
|
66
78
|
2. **HeadMusic::Analysis** - Musical analysis tools
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
- Intervals, Chords, Motion analysis
|
|
80
|
+
- Harmonic and melodic analysis
|
|
69
81
|
|
|
70
82
|
3. **HeadMusic::Content** - Musical composition representation
|
|
71
|
-
|
|
72
|
-
|
|
83
|
+
- Composition, Voice, Note, Rest
|
|
84
|
+
- Rhythmic values and time signatures
|
|
73
85
|
|
|
74
86
|
4. **HeadMusic::Instruments** - Instrument definitions
|
|
75
|
-
|
|
76
|
-
|
|
87
|
+
- Instrument families and properties
|
|
88
|
+
- Pitch ranges and transposition
|
|
77
89
|
|
|
78
90
|
5. **HeadMusic::Style** - Composition rules and guidelines
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
91
|
+
- Counterpoint rules
|
|
92
|
+
- Voice leading guidelines
|
|
93
|
+
- Style analysis
|
|
82
94
|
|
|
83
95
|
### Key Design Patterns
|
|
84
96
|
|
|
@@ -115,6 +127,7 @@ The gem supports multiple languages through the HeadMusic::Named mixin:
|
|
|
115
127
|
- Follow Standard Ruby style guide
|
|
116
128
|
- Use YARD documentation format for public methods
|
|
117
129
|
- Prefer delegation over inheritance
|
|
130
|
+
- Always run `bundle exec rubocop -a` after editing ruby code
|
|
118
131
|
|
|
119
132
|
## Common Development Tasks
|
|
120
133
|
|
|
@@ -131,4 +144,8 @@ The gem supports multiple languages through the HeadMusic::Named mixin:
|
|
|
131
144
|
1. Check for dependent classes that might be affected
|
|
132
145
|
2. Run tests for the specific module: `bundle exec rspec spec/head_music/[module_name]`
|
|
133
146
|
3. Update documentation with YARD comments
|
|
134
|
-
4. Ensure translations are updated if names change
|
|
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
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
|
|
@@ -57,16 +57,17 @@ class HeadMusic::Analysis::DiatonicInterval
|
|
|
57
57
|
|
|
58
58
|
alias_method :to_i, :semitones
|
|
59
59
|
|
|
60
|
-
# Override Named module
|
|
60
|
+
# Override Named module method to try I18n and fall back to computed name
|
|
61
61
|
def name(locale_code: nil)
|
|
62
62
|
if locale_code
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
name_key = HeadMusic::Utilities::HashKey.for(naming.name)
|
|
64
|
+
if I18n.backend.translations[locale_code]
|
|
65
|
+
locale_data = I18n.backend.translations[locale_code][:head_music] || {}
|
|
66
|
+
return locale_data[:diatonic_intervals][name_key] if locale_data.dig(:diatonic_intervals, name_key)
|
|
67
|
+
return locale_data[:chromatic_intervals][name_key] if locale_data.dig(:chromatic_intervals, name_key)
|
|
68
|
+
end
|
|
69
69
|
end
|
|
70
|
+
naming.name
|
|
70
71
|
end
|
|
71
72
|
|
|
72
73
|
def to_s
|
|
@@ -111,26 +112,39 @@ class HeadMusic::Analysis::DiatonicInterval
|
|
|
111
112
|
alias_method :invert, :inversion
|
|
112
113
|
|
|
113
114
|
def consonance(style = :standard_practice)
|
|
114
|
-
|
|
115
|
-
consonance_for_major_and_minor ||
|
|
116
|
-
HeadMusic::Rudiment::Consonance.get(:dissonant)
|
|
115
|
+
consonance_analysis(style).consonance
|
|
117
116
|
end
|
|
118
117
|
|
|
119
118
|
def consonance?(style = :standard_practice)
|
|
120
|
-
consonance(style).
|
|
119
|
+
consonance(style).consonant?
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def consonant?(style = :standard_practice)
|
|
123
|
+
consonance_analysis(style).consonant?
|
|
121
124
|
end
|
|
122
|
-
alias_method :consonant?, :consonance?
|
|
123
125
|
|
|
124
126
|
def perfect_consonance?(style = :standard_practice)
|
|
125
|
-
|
|
127
|
+
consonance_analysis(style).perfect_consonance?
|
|
126
128
|
end
|
|
127
129
|
|
|
128
130
|
def imperfect_consonance?(style = :standard_practice)
|
|
129
|
-
|
|
131
|
+
consonance_analysis(style).imperfect_consonance?
|
|
130
132
|
end
|
|
131
133
|
|
|
132
134
|
def dissonance?(style = :standard_practice)
|
|
133
|
-
|
|
135
|
+
consonance_analysis(style).dissonant?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def dissonant?(style = :standard_practice)
|
|
139
|
+
consonance_analysis(style).dissonant?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def consonance_analysis(style = :standard_practice)
|
|
143
|
+
HeadMusic::Analysis::IntervalConsonance.new(self, style)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def consonance_classification(style: :standard_practice)
|
|
147
|
+
consonance_analysis(style).classification
|
|
134
148
|
end
|
|
135
149
|
|
|
136
150
|
def above(pitch)
|
|
@@ -181,16 +195,4 @@ class HeadMusic::Analysis::DiatonicInterval
|
|
|
181
195
|
def naming
|
|
182
196
|
@naming ||= Naming.new(number: number, semitones: semitones)
|
|
183
197
|
end
|
|
184
|
-
|
|
185
|
-
def consonance_for_perfect(style = :standard_practice)
|
|
186
|
-
HeadMusic::Rudiment::Consonance.get(dissonant_fourth?(style) ? :dissonant : :perfect) if perfect?
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
def consonance_for_major_and_minor
|
|
190
|
-
HeadMusic::Rudiment::Consonance.get((third_or_compound? || sixth_or_compound?) ? :imperfect : :dissonant) if major? || minor?
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
def dissonant_fourth?(style = :standard_practice)
|
|
194
|
-
fourth_or_compound? && style == :two_part_harmony
|
|
195
|
-
end
|
|
196
198
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Analysis class that combines an interval with a style tradition to determine consonance
|
|
2
|
+
class HeadMusic::Analysis::IntervalConsonance
|
|
3
|
+
attr_reader :interval, :style_tradition
|
|
4
|
+
|
|
5
|
+
def initialize(interval, style_tradition = HeadMusic::Style::ModernTradition.new)
|
|
6
|
+
@interval = interval
|
|
7
|
+
@style_tradition = style_tradition.is_a?(HeadMusic::Style::Tradition) ?
|
|
8
|
+
style_tradition :
|
|
9
|
+
HeadMusic::Style::Tradition.get(style_tradition)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def classification
|
|
13
|
+
@classification ||= style_tradition.consonance_classification(interval)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def consonance
|
|
17
|
+
@consonance ||= HeadMusic::Rudiment::Consonance.get(classification)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def consonant?
|
|
21
|
+
[HeadMusic::Rudiment::Consonance::PERFECT_CONSONANCE, HeadMusic::Rudiment::Consonance::IMPERFECT_CONSONANCE].include?(classification)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def dissonant?
|
|
25
|
+
[HeadMusic::Rudiment::Consonance::MILD_DISSONANCE, HeadMusic::Rudiment::Consonance::HARSH_DISSONANCE, HeadMusic::Rudiment::Consonance::DISSONANCE].include?(classification)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def contextual?
|
|
29
|
+
classification == HeadMusic::Rudiment::Consonance::CONTEXTUAL
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def perfect_consonance?
|
|
33
|
+
classification == HeadMusic::Rudiment::Consonance::PERFECT_CONSONANCE
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def imperfect_consonance?
|
|
37
|
+
classification == HeadMusic::Rudiment::Consonance::IMPERFECT_CONSONANCE
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def mild_dissonance?
|
|
41
|
+
classification == HeadMusic::Rudiment::Consonance::MILD_DISSONANCE
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def harsh_dissonance?
|
|
45
|
+
classification == HeadMusic::Rudiment::Consonance::HARSH_DISSONANCE
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def dissonance?
|
|
49
|
+
classification == HeadMusic::Rudiment::Consonance::DISSONANCE
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -11,7 +11,7 @@ class HeadMusic::Content::Note
|
|
|
11
11
|
|
|
12
12
|
def initialize(pitch, rhythmic_value, voice = nil, position = nil)
|
|
13
13
|
@pitch = HeadMusic::Rudiment::Pitch.get(pitch)
|
|
14
|
-
@rhythmic_value = HeadMusic::
|
|
14
|
+
@rhythmic_value = HeadMusic::Rudiment::RhythmicValue.get(rhythmic_value)
|
|
15
15
|
@voice = voice || HeadMusic::Content::Voice.new
|
|
16
16
|
@position = position || HeadMusic::Content::Position.new(@voice.composition, "1:1")
|
|
17
17
|
end
|
|
@@ -55,7 +55,7 @@ class HeadMusic::Content::Placement
|
|
|
55
55
|
def ensure_attributes(voice, position, rhythmic_value, pitch)
|
|
56
56
|
@voice = voice
|
|
57
57
|
ensure_position(position)
|
|
58
|
-
@rhythmic_value = HeadMusic::
|
|
58
|
+
@rhythmic_value = HeadMusic::Rudiment::RhythmicValue.get(rhythmic_value)
|
|
59
59
|
@pitch = HeadMusic::Rudiment::Pitch.get(pitch)
|
|
60
60
|
end
|
|
61
61
|
|
|
@@ -53,7 +53,7 @@ class HeadMusic::Content::Position
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def +(other)
|
|
56
|
-
other = HeadMusic::
|
|
56
|
+
other = HeadMusic::Rudiment::RhythmicValue.new(other) if [HeadMusic::Rudiment::RhythmicUnit, Symbol, String].include?(other.class)
|
|
57
57
|
self.class.new(composition, bar_number, count, tick + other.ticks)
|
|
58
58
|
end
|
|
59
59
|
|
|
@@ -8,7 +8,7 @@ class HeadMusic::Content::Staff
|
|
|
8
8
|
attr_reader :default_clef, :line_count, :instrument
|
|
9
9
|
|
|
10
10
|
def initialize(default_clef_key, instrument: nil, line_count: nil)
|
|
11
|
-
@instrument = HeadMusic::Instruments::
|
|
11
|
+
@instrument = HeadMusic::Instruments::InstrumentType.get(instrument) if instrument
|
|
12
12
|
begin
|
|
13
13
|
@default_clef = HeadMusic::Rudiment::Clef.get(default_clef_key)
|
|
14
14
|
rescue KeyError, NoMethodError
|