head_music 7.0.5 → 8.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/.ruby-version +1 -1
- data/Gemfile +1 -1
- data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/category.rb +1 -1
- data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/naming.rb +6 -6
- data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/parser.rb +4 -4
- data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/semitones.rb +4 -4
- data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/size.rb +1 -1
- data/lib/head_music/{diatonic_interval.rb → analysis/diatonic_interval.rb} +19 -16
- data/lib/head_music/{harmonic_interval.rb → analysis/harmonic_interval.rb} +5 -2
- data/lib/head_music/{melodic_interval.rb → analysis/melodic_interval.rb} +6 -3
- data/lib/head_music/{motion.rb → analysis/motion.rb} +6 -3
- data/lib/head_music/content/bar.rb +2 -2
- data/lib/head_music/content/composition.rb +3 -3
- 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/rhythmic_value.rb +3 -3
- data/lib/head_music/{staff.rb → content/staff.rb} +6 -3
- data/lib/head_music/content/voice.rb +2 -2
- data/lib/head_music/{instrument.rb → instruments/instrument.rb} +10 -7
- data/lib/head_music/{data → instruments}/instrument_families.yml +49 -0
- data/lib/head_music/{instrument_family.rb → instruments/instrument_family.rb} +5 -2
- data/lib/head_music/{data → instruments}/instruments.yml +116 -0
- data/lib/head_music/{instrument → instruments}/staff.rb +5 -2
- data/lib/head_music/{instrument → instruments}/staff_scheme.rb +7 -2
- data/lib/head_music/{instrument → instruments}/variant.rb +6 -3
- data/lib/head_music/locales/en.yml +22 -0
- data/lib/head_music/{alteration.rb → rudiment/alteration.rb} +9 -6
- data/lib/head_music/{chromatic_interval.rb → rudiment/chromatic_interval.rb} +7 -4
- data/lib/head_music/{circle.rb → rudiment/circle.rb} +9 -6
- data/lib/head_music/{clef.rb → rudiment/clef.rb} +7 -4
- data/lib/head_music/{consonance.rb → rudiment/consonance.rb} +4 -1
- data/lib/head_music/{interval_cycle.rb → rudiment/interval_cycle.rb} +12 -9
- data/lib/head_music/{key_signature → rudiment/key_signature}/enharmonic_equivalence.rb +4 -4
- data/lib/head_music/{key_signature.rb → rudiment/key_signature.rb} +10 -7
- data/lib/head_music/{letter_name.rb → rudiment/letter_name.rb} +9 -6
- data/lib/head_music/{meter.rb → rudiment/meter.rb} +6 -3
- data/lib/head_music/{musical_symbol.rb → rudiment/musical_symbol.rb} +4 -1
- data/lib/head_music/{pitch → rudiment/pitch}/enharmonic_equivalence.rb +4 -4
- data/lib/head_music/{pitch → rudiment/pitch}/octave_equivalence.rb +2 -2
- data/lib/head_music/{pitch.rb → rudiment/pitch.rb} +30 -27
- data/lib/head_music/{pitch_class.rb → rudiment/pitch_class.rb} +14 -11
- data/lib/head_music/{pitch_class_set.rb → rudiment/pitch_class_set.rb} +5 -2
- data/lib/head_music/{pitch_set.rb → rudiment/pitch_set.rb} +14 -11
- data/lib/head_music/{quality.rb → rudiment/quality.rb} +4 -1
- data/lib/head_music/{reference_pitch.rb → rudiment/reference_pitch.rb} +5 -2
- data/lib/head_music/{register.rb → rudiment/register.rb} +6 -3
- data/lib/head_music/rudiment/rhythm.rb +6 -0
- data/lib/head_music/{rhythmic_unit.rb → rudiment/rhythmic_unit.rb} +5 -2
- data/lib/head_music/{scale.rb → rudiment/scale.rb} +12 -9
- data/lib/head_music/{scale_degree.rb → rudiment/scale_degree.rb} +8 -5
- data/lib/head_music/{scale_type.rb → rudiment/scale_type.rb} +4 -1
- data/lib/head_music/{solmization.rb → rudiment/solmization.rb} +4 -1
- data/lib/head_music/{sonority.rb → rudiment/sonority.rb} +7 -4
- data/lib/head_music/{spelling.rb → rudiment/spelling.rb} +20 -17
- data/lib/head_music/{tuning.rb → rudiment/tuning.rb} +6 -3
- data/lib/head_music/style/annotation.rb +6 -6
- data/lib/head_music/style/guidelines/consonant_climax.rb +4 -4
- data/lib/head_music/style/guidelines/diatonic.rb +1 -1
- data/lib/head_music/style/guidelines/step_out_of_unison.rb +1 -1
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +50 -46
- metadata +51 -51
- data/lib/head_music/rhythm.rb +0 -3
- /data/lib/head_music/{data → rudiment}/clefs.yml +0 -0
- /data/lib/head_music/{solmizations.yml → rudiment/solmizations.yml} +0 -0
@@ -1,5 +1,8 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# Meter is the rhythmic size of a measure, such as 4/4 or 6/8
|
2
|
-
class HeadMusic::Meter
|
5
|
+
class HeadMusic::Rudiment::Meter
|
3
6
|
attr_reader :top_number, :bottom_number
|
4
7
|
|
5
8
|
NAMED = {
|
@@ -74,13 +77,13 @@ class HeadMusic::Meter
|
|
74
77
|
end
|
75
78
|
|
76
79
|
def count_unit
|
77
|
-
HeadMusic::RhythmicUnit.for_denominator_value(bottom_number)
|
80
|
+
HeadMusic::Rudiment::RhythmicUnit.for_denominator_value(bottom_number)
|
78
81
|
end
|
79
82
|
|
80
83
|
def beat_unit
|
81
84
|
@beat_unit ||=
|
82
85
|
if compound?
|
83
|
-
HeadMusic::Content::RhythmicValue.new(HeadMusic::RhythmicUnit.for_denominator_value(bottom_number / 2), dots: 1)
|
86
|
+
HeadMusic::Content::RhythmicValue.new(HeadMusic::Rudiment::RhythmicUnit.for_denominator_value(bottom_number / 2), dots: 1)
|
84
87
|
else
|
85
88
|
HeadMusic::Content::RhythmicValue.new(count_unit)
|
86
89
|
end
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A symbol is a mark or sign that signifies a particular rudiment of music
|
2
|
-
class HeadMusic::MusicalSymbol
|
5
|
+
class HeadMusic::Rudiment::MusicalSymbol
|
3
6
|
attr_reader :ascii, :unicode, :html_entity
|
4
7
|
|
5
8
|
def initialize(ascii: nil, unicode: nil, html_entity: nil)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# An enharmonic equivalent pitch is the same frequency spelled differently, such as D# and Eb.
|
2
|
-
class HeadMusic::Pitch::EnharmonicEquivalence
|
2
|
+
class HeadMusic::Rudiment::Pitch::EnharmonicEquivalence
|
3
3
|
def self.get(pitch)
|
4
|
-
pitch = HeadMusic::Pitch.get(pitch)
|
4
|
+
pitch = HeadMusic::Rudiment::Pitch.get(pitch)
|
5
5
|
@enharmonic_equivalences ||= {}
|
6
6
|
@enharmonic_equivalences[pitch.to_s] ||= new(pitch)
|
7
7
|
end
|
@@ -11,11 +11,11 @@ class HeadMusic::Pitch::EnharmonicEquivalence
|
|
11
11
|
delegate :pitch_class, to: :pitch
|
12
12
|
|
13
13
|
def initialize(pitch)
|
14
|
-
@pitch = HeadMusic::Pitch.get(pitch)
|
14
|
+
@pitch = HeadMusic::Rudiment::Pitch.get(pitch)
|
15
15
|
end
|
16
16
|
|
17
17
|
def enharmonic_equivalent?(other)
|
18
|
-
other = HeadMusic::Pitch.get(other)
|
18
|
+
other = HeadMusic::Rudiment::Pitch.get(other)
|
19
19
|
pitch.midi_note_number == other.midi_note_number && pitch.spelling != other.spelling
|
20
20
|
end
|
21
21
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Octave equivalence is the functional equivalence of pitches with the same spelling separated by one or more octaves.
|
2
|
-
class HeadMusic::Pitch::OctaveEquivalence
|
2
|
+
class HeadMusic::Rudiment::Pitch::OctaveEquivalence
|
3
3
|
def self.get(pitch)
|
4
4
|
@octave_equivalences ||= {}
|
5
5
|
@octave_equivalences[pitch.to_s] ||= new(pitch)
|
@@ -12,7 +12,7 @@ class HeadMusic::Pitch::OctaveEquivalence
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def octave_equivalent?(other)
|
15
|
-
other = HeadMusic::Pitch.get(other)
|
15
|
+
other = HeadMusic::Rudiment::Pitch.get(other)
|
16
16
|
pitch.spelling == other.spelling && pitch.register != other.register
|
17
17
|
end
|
18
18
|
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A pitch is a named frequency represented by a spelling and a register.
|
2
|
-
class HeadMusic::Pitch
|
5
|
+
class HeadMusic::Rudiment::Pitch
|
3
6
|
include Comparable
|
4
7
|
|
5
8
|
attr_reader :spelling, :register
|
@@ -40,7 +43,7 @@ class HeadMusic::Pitch
|
|
40
43
|
end
|
41
44
|
|
42
45
|
def self.from_pitch_class(pitch_class)
|
43
|
-
return nil unless pitch_class.is_a?(HeadMusic::PitchClass)
|
46
|
+
return nil unless pitch_class.is_a?(HeadMusic::Rudiment::PitchClass)
|
44
47
|
|
45
48
|
fetch_or_create(pitch_class.sharp_spelling)
|
46
49
|
end
|
@@ -48,33 +51,33 @@ class HeadMusic::Pitch
|
|
48
51
|
def self.from_name(name)
|
49
52
|
return nil unless name == name.to_s
|
50
53
|
|
51
|
-
fetch_or_create(HeadMusic::Spelling.get(name), HeadMusic::Register.get(name).to_i)
|
54
|
+
fetch_or_create(HeadMusic::Rudiment::Spelling.get(name), HeadMusic::Rudiment::Register.get(name).to_i)
|
52
55
|
end
|
53
56
|
|
54
57
|
def self.from_number(number)
|
55
58
|
return nil unless number == number.to_i
|
56
59
|
|
57
|
-
fetch_or_create(HeadMusic::Spelling.from_number(number), (number.to_i / 12) - 1)
|
60
|
+
fetch_or_create(HeadMusic::Rudiment::Spelling.from_number(number), (number.to_i / 12) - 1)
|
58
61
|
end
|
59
62
|
|
60
63
|
def self.from_number_and_letter(number, letter_name)
|
61
|
-
letter_name = HeadMusic::LetterName.get(letter_name)
|
64
|
+
letter_name = HeadMusic::Rudiment::LetterName.get(letter_name)
|
62
65
|
natural_letter_pitch = natural_letter_pitch(number, letter_name)
|
63
|
-
alteration_interval = natural_letter_pitch.smallest_interval_to(HeadMusic::PitchClass.get(number))
|
64
|
-
alteration = HeadMusic::Alteration.by(:semitones, alteration_interval) if alteration_interval != 0
|
65
|
-
spelling = HeadMusic::Spelling.fetch_or_create(letter_name, alteration)
|
66
|
+
alteration_interval = natural_letter_pitch.smallest_interval_to(HeadMusic::Rudiment::PitchClass.get(number))
|
67
|
+
alteration = HeadMusic::Rudiment::Alteration.by(:semitones, alteration_interval) if alteration_interval != 0
|
68
|
+
spelling = HeadMusic::Rudiment::Spelling.fetch_or_create(letter_name, alteration)
|
66
69
|
fetch_or_create(spelling, natural_letter_pitch.register)
|
67
70
|
end
|
68
71
|
|
69
72
|
def self.natural_letter_pitch(number, letter_name)
|
70
|
-
natural_letter_pitch = get(HeadMusic::LetterName.get(letter_name).pitch_class)
|
73
|
+
natural_letter_pitch = get(HeadMusic::Rudiment::LetterName.get(letter_name).pitch_class)
|
71
74
|
natural_letter_pitch += 12 while (number.to_i - natural_letter_pitch.to_i) >= 6
|
72
75
|
natural_letter_pitch -= 12 while (number.to_i - natural_letter_pitch.to_i) <= -6
|
73
76
|
get(natural_letter_pitch)
|
74
77
|
end
|
75
78
|
|
76
79
|
def self.fetch_or_create(spelling, register = nil)
|
77
|
-
register ||= HeadMusic::Register::DEFAULT
|
80
|
+
register ||= HeadMusic::Rudiment::Register::DEFAULT
|
78
81
|
return unless spelling && (-1..9).cover?(register)
|
79
82
|
|
80
83
|
@pitches ||= {}
|
@@ -83,7 +86,7 @@ class HeadMusic::Pitch
|
|
83
86
|
end
|
84
87
|
|
85
88
|
def initialize(spelling, register)
|
86
|
-
@spelling = HeadMusic::Spelling.get(spelling.to_s)
|
89
|
+
@spelling = HeadMusic::Rudiment::Spelling.get(spelling.to_s)
|
87
90
|
@register = register.to_i
|
88
91
|
end
|
89
92
|
|
@@ -111,35 +114,35 @@ class HeadMusic::Pitch
|
|
111
114
|
end
|
112
115
|
|
113
116
|
def natural
|
114
|
-
HeadMusic::Pitch.get(to_s.gsub(HeadMusic::Alteration.matcher, ""))
|
117
|
+
HeadMusic::Rudiment::Pitch.get(to_s.gsub(HeadMusic::Rudiment::Alteration.matcher, ""))
|
115
118
|
end
|
116
119
|
|
117
120
|
def +(other)
|
118
|
-
if other.is_a?(HeadMusic::DiatonicInterval)
|
121
|
+
if other.is_a?(HeadMusic::Analysis::DiatonicInterval)
|
119
122
|
# return a pitch
|
120
123
|
other.above(self)
|
121
124
|
else
|
122
125
|
# assume value represents an interval in semitones and return another pitch
|
123
|
-
HeadMusic::Pitch.get(to_i + other.to_i)
|
126
|
+
HeadMusic::Rudiment::Pitch.get(to_i + other.to_i)
|
124
127
|
end
|
125
128
|
end
|
126
129
|
|
127
130
|
def -(other)
|
128
131
|
case other
|
129
|
-
when HeadMusic::DiatonicInterval
|
132
|
+
when HeadMusic::Analysis::DiatonicInterval
|
130
133
|
# return a pitch
|
131
134
|
other.below(self)
|
132
|
-
when HeadMusic::Pitch
|
135
|
+
when HeadMusic::Rudiment::Pitch
|
133
136
|
# return an interval
|
134
|
-
HeadMusic::ChromaticInterval.get(to_i - other.to_i)
|
137
|
+
HeadMusic::Rudiment::ChromaticInterval.get(to_i - other.to_i)
|
135
138
|
else
|
136
139
|
# assume value represents an interval in semitones and return another pitch
|
137
|
-
HeadMusic::Pitch.get(to_i - other.to_i)
|
140
|
+
HeadMusic::Rudiment::Pitch.get(to_i - other.to_i)
|
138
141
|
end
|
139
142
|
end
|
140
143
|
|
141
144
|
def ==(other)
|
142
|
-
other = HeadMusic::Pitch.get(other)
|
145
|
+
other = HeadMusic::Rudiment::Pitch.get(other)
|
143
146
|
to_s == other.to_s
|
144
147
|
end
|
145
148
|
|
@@ -148,11 +151,11 @@ class HeadMusic::Pitch
|
|
148
151
|
end
|
149
152
|
|
150
153
|
def scale(scale_type_name = nil)
|
151
|
-
HeadMusic::Scale.get(self, scale_type_name)
|
154
|
+
HeadMusic::Rudiment::Scale.get(self, scale_type_name)
|
152
155
|
end
|
153
156
|
|
154
157
|
def natural_steps(num_steps)
|
155
|
-
HeadMusic::Pitch.get([target_letter_name(num_steps), register + octaves_delta(num_steps)].join)
|
158
|
+
HeadMusic::Rudiment::Pitch.get([target_letter_name(num_steps), register + octaves_delta(num_steps)].join)
|
156
159
|
end
|
157
160
|
|
158
161
|
def frequency
|
@@ -160,7 +163,7 @@ class HeadMusic::Pitch
|
|
160
163
|
end
|
161
164
|
|
162
165
|
def steps_to(other)
|
163
|
-
other = HeadMusic::Pitch.get(other)
|
166
|
+
other = HeadMusic::Rudiment::Pitch.get(other)
|
164
167
|
letter_name_steps_to(other) + 7 * octave_changes_to(other)
|
165
168
|
end
|
166
169
|
|
@@ -181,15 +184,15 @@ class HeadMusic::Pitch
|
|
181
184
|
end
|
182
185
|
|
183
186
|
def enharmonic_equivalence
|
184
|
-
@enharmonic_equivalence ||= HeadMusic::Pitch::EnharmonicEquivalence.get(self)
|
187
|
+
@enharmonic_equivalence ||= HeadMusic::Rudiment::Pitch::EnharmonicEquivalence.get(self)
|
185
188
|
end
|
186
189
|
|
187
190
|
def octave_equivalence
|
188
|
-
@octave_equivalence ||= HeadMusic::Pitch::OctaveEquivalence.get(self)
|
191
|
+
@octave_equivalence ||= HeadMusic::Rudiment::Pitch::OctaveEquivalence.get(self)
|
189
192
|
end
|
190
193
|
|
191
194
|
def tuning
|
192
|
-
@tuning ||= HeadMusic::Tuning.new
|
195
|
+
@tuning ||= HeadMusic::Rudiment::Tuning.new
|
193
196
|
end
|
194
197
|
|
195
198
|
def octaves_delta(num_steps)
|
@@ -216,12 +219,12 @@ class HeadMusic::Pitch
|
|
216
219
|
end
|
217
220
|
|
218
221
|
def helmholtz_letter_name
|
219
|
-
return spelling.to_s.downcase if HeadMusic::Register.get(register).helmholtz_case == :lower
|
222
|
+
return spelling.to_s.downcase if HeadMusic::Rudiment::Register.get(register).helmholtz_case == :lower
|
220
223
|
|
221
224
|
spelling.to_s
|
222
225
|
end
|
223
226
|
|
224
227
|
def helmholtz_marks
|
225
|
-
HeadMusic::Register.get(register).helmholtz_marks
|
228
|
+
HeadMusic::Rudiment::Register.get(register).helmholtz_marks
|
226
229
|
end
|
227
230
|
end
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A pitch class is a set of pitches separated by octaves.
|
2
|
-
class HeadMusic::PitchClass
|
5
|
+
class HeadMusic::Rudiment::PitchClass
|
3
6
|
include Comparable
|
4
7
|
|
5
8
|
attr_reader :number, :spelling
|
@@ -11,8 +14,8 @@ class HeadMusic::PitchClass
|
|
11
14
|
|
12
15
|
def self.get(identifier)
|
13
16
|
@pitch_classes ||= {}
|
14
|
-
if HeadMusic::Spelling.matching_string(identifier)
|
15
|
-
spelling = HeadMusic::Spelling.get(identifier)
|
17
|
+
if HeadMusic::Rudiment::Spelling.matching_string(identifier)
|
18
|
+
spelling = HeadMusic::Rudiment::Spelling.get(identifier)
|
16
19
|
number = spelling.pitch_class.to_i
|
17
20
|
end
|
18
21
|
number ||= identifier.to_i % 12
|
@@ -48,23 +51,23 @@ class HeadMusic::PitchClass
|
|
48
51
|
end
|
49
52
|
|
50
53
|
def smart_spelling(max_sharps_in_major_key_signature: 6)
|
51
|
-
sharp_key = HeadMusic::KeySignature.get(sharp_spelling)
|
52
|
-
return HeadMusic::Spelling.get(sharp_spelling) if sharp_key.num_sharps <= max_sharps_in_major_key_signature
|
54
|
+
sharp_key = HeadMusic::Rudiment::KeySignature.get(sharp_spelling)
|
55
|
+
return HeadMusic::Rudiment::Spelling.get(sharp_spelling) if sharp_key.num_sharps <= max_sharps_in_major_key_signature
|
53
56
|
|
54
|
-
flat_key = HeadMusic::KeySignature.get(flat_spelling)
|
55
|
-
return HeadMusic::Spelling.get(flat_spelling) if flat_key.num_sharps <= max_sharps_in_major_key_signature
|
57
|
+
flat_key = HeadMusic::Rudiment::KeySignature.get(flat_spelling)
|
58
|
+
return HeadMusic::Rudiment::Spelling.get(flat_spelling) if flat_key.num_sharps <= max_sharps_in_major_key_signature
|
56
59
|
|
57
|
-
HeadMusic::Spelling.get(flatter_spelling)
|
60
|
+
HeadMusic::Rudiment::Spelling.get(flatter_spelling)
|
58
61
|
end
|
59
62
|
|
60
63
|
# Pass in the number of semitones
|
61
64
|
def +(other)
|
62
|
-
HeadMusic::PitchClass.get(to_i + other.to_i)
|
65
|
+
HeadMusic::Rudiment::PitchClass.get(to_i + other.to_i)
|
63
66
|
end
|
64
67
|
|
65
68
|
# Pass in the number of semitones
|
66
69
|
def -(other)
|
67
|
-
HeadMusic::PitchClass.get(to_i - other.to_i)
|
70
|
+
HeadMusic::Rudiment::PitchClass.get(to_i - other.to_i)
|
68
71
|
end
|
69
72
|
|
70
73
|
def ==(other)
|
@@ -79,7 +82,7 @@ class HeadMusic::PitchClass
|
|
79
82
|
def intervals_to(other)
|
80
83
|
delta = other.to_i - to_i
|
81
84
|
inverse = delta.positive? ? delta - 12 : delta + 12
|
82
|
-
[delta, inverse].sort_by(&:abs).map { |interval| HeadMusic::ChromaticInterval.get(interval) }
|
85
|
+
[delta, inverse].sort_by(&:abs).map { |interval| HeadMusic::Rudiment::ChromaticInterval.get(interval) }
|
83
86
|
end
|
84
87
|
|
85
88
|
def smallest_interval_to(other)
|
@@ -1,13 +1,16 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A PitchClassSet represents a pitch-class set or pitch collection.
|
2
5
|
# See also: PitchSet, PitchClass
|
3
|
-
class HeadMusic::PitchClassSet
|
6
|
+
class HeadMusic::Rudiment::PitchClassSet
|
4
7
|
attr_reader :pitch_classes
|
5
8
|
|
6
9
|
delegate :empty?, to: :pitch_classes
|
7
10
|
alias_method :empty_set?, :empty?
|
8
11
|
|
9
12
|
def initialize(identifiers)
|
10
|
-
@pitch_classes = identifiers.map { |identifier| HeadMusic::PitchClass.get(identifier) }.uniq.sort
|
13
|
+
@pitch_classes = identifiers.map { |identifier| HeadMusic::Rudiment::PitchClass.get(identifier) }.uniq.sort
|
11
14
|
end
|
12
15
|
|
13
16
|
def to_s
|
@@ -1,6 +1,9 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A PitchSet is a collection of one or more pitches.
|
2
5
|
# See also: PitchClassSet
|
3
|
-
class HeadMusic::PitchSet
|
6
|
+
class HeadMusic::Rudiment::PitchSet
|
4
7
|
TERTIAN_SONORITIES = {
|
5
8
|
implied_triad: [3],
|
6
9
|
triad: [3, 5],
|
@@ -20,7 +23,7 @@ class HeadMusic::PitchSet
|
|
20
23
|
delegate :size, to: :pitch_class_set, prefix: true
|
21
24
|
|
22
25
|
def initialize(pitches)
|
23
|
-
@pitches = pitches.map { |pitch| HeadMusic::Pitch.get(pitch) }.sort.uniq
|
26
|
+
@pitches = pitches.map { |pitch| HeadMusic::Rudiment::Pitch.get(pitch) }.sort.uniq
|
24
27
|
end
|
25
28
|
|
26
29
|
def pitch_classes
|
@@ -28,22 +31,22 @@ class HeadMusic::PitchSet
|
|
28
31
|
end
|
29
32
|
|
30
33
|
def pitch_class_set
|
31
|
-
@pitch_class_set ||= HeadMusic::PitchClassSet.new(pitch_classes)
|
34
|
+
@pitch_class_set ||= HeadMusic::Rudiment::PitchClassSet.new(pitch_classes)
|
32
35
|
end
|
33
36
|
|
34
37
|
def reduction
|
35
|
-
@reduction ||= HeadMusic::PitchSet.new(reduction_pitches)
|
38
|
+
@reduction ||= HeadMusic::Rudiment::PitchSet.new(reduction_pitches)
|
36
39
|
end
|
37
40
|
|
38
41
|
def diatonic_intervals
|
39
42
|
@diatonic_intervals ||= pitches.each_cons(2).map do |pitch_pair|
|
40
|
-
HeadMusic::DiatonicInterval.new(*pitch_pair)
|
43
|
+
HeadMusic::Analysis::DiatonicInterval.new(*pitch_pair)
|
41
44
|
end
|
42
45
|
end
|
43
46
|
|
44
47
|
def diatonic_intervals_above_bass_pitch
|
45
48
|
@diatonic_intervals_above_bass_pitch ||= pitches_above_bass_pitch.map do |pitch|
|
46
|
-
HeadMusic::DiatonicInterval.new(bass_pitch, pitch)
|
49
|
+
HeadMusic::Analysis::DiatonicInterval.new(bass_pitch, pitch)
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
@@ -62,15 +65,15 @@ class HeadMusic::PitchSet
|
|
62
65
|
end
|
63
66
|
|
64
67
|
def invert
|
65
|
-
inverted_pitch = pitches[0] + HeadMusic::DiatonicInterval.get("perfect octave")
|
68
|
+
inverted_pitch = pitches[0] + HeadMusic::Analysis::DiatonicInterval.get("perfect octave")
|
66
69
|
new_pitches = pitches.drop(1) + [inverted_pitch]
|
67
|
-
HeadMusic::PitchSet.new(new_pitches)
|
70
|
+
HeadMusic::Rudiment::PitchSet.new(new_pitches)
|
68
71
|
end
|
69
72
|
|
70
73
|
def uninvert
|
71
|
-
inverted_pitch = pitches[-1] - HeadMusic::DiatonicInterval.get("perfect octave")
|
74
|
+
inverted_pitch = pitches[-1] - HeadMusic::Analysis::DiatonicInterval.get("perfect octave")
|
72
75
|
new_pitches = [inverted_pitch] + pitches[0..-2]
|
73
|
-
HeadMusic::PitchSet.new(new_pitches)
|
76
|
+
HeadMusic::Rudiment::PitchSet.new(new_pitches)
|
74
77
|
end
|
75
78
|
|
76
79
|
def bass_pitch
|
@@ -188,7 +191,7 @@ class HeadMusic::PitchSet
|
|
188
191
|
|
189
192
|
def reduction_pitches
|
190
193
|
pitches.map do |pitch|
|
191
|
-
pitch = HeadMusic::Pitch.fetch_or_create(pitch.spelling, pitch.register - 1) while pitch > bass_pitch + 12
|
194
|
+
pitch = HeadMusic::Rudiment::Pitch.fetch_or_create(pitch.spelling, pitch.register - 1) while pitch > bass_pitch + 12
|
192
195
|
pitch
|
193
196
|
end.sort
|
194
197
|
end
|
@@ -1,6 +1,9 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A reference pitch has a pitch and a frequency
|
2
5
|
# With no arguments, it assumes that A4 = 440.0 Hz
|
3
|
-
class HeadMusic::ReferencePitch
|
6
|
+
class HeadMusic::Rudiment::ReferencePitch
|
4
7
|
include HeadMusic::Named
|
5
8
|
|
6
9
|
DEFAULT_PITCH_NAME = "A4"
|
@@ -81,7 +84,7 @@ class HeadMusic::ReferencePitch
|
|
81
84
|
|
82
85
|
def initialize(name = DEFAULT_REFERENCE_PITCH_NAME)
|
83
86
|
record = named_reference_pitch_record_for_name(name)
|
84
|
-
@pitch = HeadMusic::Pitch.get(record.fetch(:pitch, DEFAULT_PITCH_NAME))
|
87
|
+
@pitch = HeadMusic::Rudiment::Pitch.get(record.fetch(:pitch, DEFAULT_PITCH_NAME))
|
85
88
|
@frequency = record.fetch(:frequency, DEFAULT_FREQUENCY)
|
86
89
|
initialize_keys_from_record(record)
|
87
90
|
end
|
@@ -1,9 +1,12 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# The register is a numeric octave identifier used in scientific pitch notation.
|
2
5
|
#
|
3
6
|
# A pitch is a spelling plus a register. For example, C4 is middle C and C5 is the C one octave higher.
|
4
7
|
# The number changes between the letter names B and C regardless of sharps and flats,
|
5
8
|
# so as an extreme example, Cb5 is actually a semitone below B#4.
|
6
|
-
class HeadMusic::Register
|
9
|
+
class HeadMusic::Rudiment::Register
|
7
10
|
include Comparable
|
8
11
|
|
9
12
|
DEFAULT = 4
|
@@ -21,9 +24,9 @@ class HeadMusic::Register
|
|
21
24
|
end
|
22
25
|
|
23
26
|
def self.from_name(string)
|
24
|
-
return unless string.to_s.match?(HeadMusic::Spelling::MATCHER)
|
27
|
+
return unless string.to_s.match?(HeadMusic::Rudiment::Spelling::MATCHER)
|
25
28
|
|
26
|
-
_letter, _sign, register_string = string.to_s.match(HeadMusic::Spelling::MATCHER).captures
|
29
|
+
_letter, _sign, register_string = string.to_s.match(HeadMusic::Rudiment::Spelling::MATCHER).captures
|
27
30
|
@registers ||= {}
|
28
31
|
@registers[register_string.to_i] ||= new(register_string.to_i) if register_string
|
29
32
|
end
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A rhythmic unit is a rudiment of duration consisting of doublings and divisions of a whole note.
|
2
|
-
class HeadMusic::RhythmicUnit
|
5
|
+
class HeadMusic::Rudiment::RhythmicUnit
|
3
6
|
include HeadMusic::Named
|
4
7
|
|
5
8
|
MULTIPLES = ["whole", "double whole", "longa", "maxima"].freeze
|
@@ -35,7 +38,7 @@ class HeadMusic::RhythmicUnit
|
|
35
38
|
end
|
36
39
|
|
37
40
|
def ticks
|
38
|
-
HeadMusic::Rhythm::PPQN * 4 * relative_value
|
41
|
+
HeadMusic::Rudiment::Rhythm::PPQN * 4 * relative_value
|
39
42
|
end
|
40
43
|
|
41
44
|
def notehead
|
@@ -1,11 +1,14 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A scale contains ordered pitches starting at a tonal center.
|
2
|
-
class HeadMusic::Scale
|
5
|
+
class HeadMusic::Rudiment::Scale
|
3
6
|
SCALE_REGEX = /^[A-G][#b]?\s+\w+$/
|
4
7
|
|
5
8
|
def self.get(root_pitch, scale_type = nil)
|
6
9
|
root_pitch, scale_type = root_pitch.split(/\s+/) if root_pitch.is_a?(String) && scale_type =~ SCALE_REGEX
|
7
|
-
root_pitch = HeadMusic::Pitch.get(root_pitch)
|
8
|
-
scale_type = HeadMusic::ScaleType.get(scale_type || :major)
|
10
|
+
root_pitch = HeadMusic::Rudiment::Pitch.get(root_pitch)
|
11
|
+
scale_type = HeadMusic::Rudiment::ScaleType.get(scale_type || :major)
|
9
12
|
@scales ||= {}
|
10
13
|
hash_key = HeadMusic::Utilities::HashKey.for(
|
11
14
|
[root_pitch, scale_type].join(" ").gsub(/#|♯/, "sharp").gsub(/(\w)[b♭]/, '\\1flat')
|
@@ -18,8 +21,8 @@ class HeadMusic::Scale
|
|
18
21
|
attr_reader :root_pitch, :scale_type
|
19
22
|
|
20
23
|
def initialize(root_pitch, scale_type)
|
21
|
-
@root_pitch = HeadMusic::Pitch.get(root_pitch)
|
22
|
-
@scale_type = HeadMusic::ScaleType.get(scale_type)
|
24
|
+
@root_pitch = HeadMusic::Rudiment::Pitch.get(root_pitch)
|
25
|
+
@scale_type = HeadMusic::Rudiment::ScaleType.get(scale_type)
|
23
26
|
end
|
24
27
|
|
25
28
|
def pitches(direction: :ascending, octaves: 1)
|
@@ -80,7 +83,7 @@ class HeadMusic::Scale
|
|
80
83
|
end
|
81
84
|
|
82
85
|
def parent_scale_pitches
|
83
|
-
HeadMusic::Scale.get(root_pitch, scale_type.parent_name).pitches if scale_type.parent
|
86
|
+
HeadMusic::Rudiment::Scale.get(root_pitch, scale_type.parent_name).pitches if scale_type.parent
|
84
87
|
end
|
85
88
|
|
86
89
|
def parent_scale_pitch_for(semitones_from_root)
|
@@ -92,7 +95,7 @@ class HeadMusic::Scale
|
|
92
95
|
def pitch_for_step(step, semitones_from_root, direction)
|
93
96
|
pitch_number = root_pitch_number + semitones_from_root
|
94
97
|
letter_name = letter_for_step(step, semitones_from_root, direction)
|
95
|
-
HeadMusic::Pitch.from_number_and_letter(pitch_number, letter_name)
|
98
|
+
HeadMusic::Rudiment::Pitch.from_number_and_letter(pitch_number, letter_name)
|
96
99
|
end
|
97
100
|
|
98
101
|
def letter_for_step(step, semitones_from_root, direction)
|
@@ -117,11 +120,11 @@ class HeadMusic::Scale
|
|
117
120
|
def flat_letter_for_step(semitones_from_root)
|
118
121
|
return unless root_pitch.flat?
|
119
122
|
|
120
|
-
HeadMusic::PitchClass::FLAT_SPELLINGS[pitch_class_number(semitones_from_root)]
|
123
|
+
HeadMusic::Rudiment::PitchClass::FLAT_SPELLINGS[pitch_class_number(semitones_from_root)]
|
121
124
|
end
|
122
125
|
|
123
126
|
def sharp_letter_for_step(semitones_from_root)
|
124
|
-
HeadMusic::PitchClass::SHARP_SPELLINGS[pitch_class_number(semitones_from_root)]
|
127
|
+
HeadMusic::Rudiment::PitchClass::SHARP_SPELLINGS[pitch_class_number(semitones_from_root)]
|
125
128
|
end
|
126
129
|
|
127
130
|
def pitch_class_number(semitones_from_root)
|
@@ -1,6 +1,9 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A scale degree is a number indicating the ordinality of the spelling in the key.
|
2
5
|
# TODO: Rewrite to accept a tonal_center and a scale type.
|
3
|
-
class HeadMusic::ScaleDegree
|
6
|
+
class HeadMusic::Rudiment::ScaleDegree
|
4
7
|
include Comparable
|
5
8
|
|
6
9
|
NAME_FOR_DIATONIC_DEGREE = [nil, "tonic", "supertonic", "mediant", "subdominant", "dominant", "submediant"].freeze
|
@@ -12,7 +15,7 @@ class HeadMusic::ScaleDegree
|
|
12
15
|
|
13
16
|
def initialize(key_signature, spelling)
|
14
17
|
@key_signature = key_signature
|
15
|
-
@spelling = HeadMusic::Spelling.get(spelling)
|
18
|
+
@spelling = HeadMusic::Rudiment::Spelling.get(spelling)
|
16
19
|
end
|
17
20
|
|
18
21
|
def degree
|
@@ -23,7 +26,7 @@ class HeadMusic::ScaleDegree
|
|
23
26
|
spelling_alteration_semitones = spelling.alteration&.semitones || 0
|
24
27
|
usual_sign_semitones = scale_degree_usual_spelling.alteration&.semitones || 0
|
25
28
|
delta = spelling_alteration_semitones - usual_sign_semitones
|
26
|
-
HeadMusic::Alteration.by(:semitones, delta) if delta != 0
|
29
|
+
HeadMusic::Rudiment::Alteration.by(:semitones, delta) if delta != 0
|
27
30
|
end
|
28
31
|
|
29
32
|
def alteration_semitones
|
@@ -35,7 +38,7 @@ class HeadMusic::ScaleDegree
|
|
35
38
|
end
|
36
39
|
|
37
40
|
def <=>(other)
|
38
|
-
if other.is_a?(HeadMusic::ScaleDegree)
|
41
|
+
if other.is_a?(HeadMusic::Rudiment::ScaleDegree)
|
39
42
|
[degree, alteration_semitones] <=> [other.degree, other.alteration_semitones]
|
40
43
|
else
|
41
44
|
# TODO: Improve this
|
@@ -53,6 +56,6 @@ class HeadMusic::ScaleDegree
|
|
53
56
|
private
|
54
57
|
|
55
58
|
def scale_degree_usual_spelling
|
56
|
-
HeadMusic::Spelling.get(scale.spellings[degree - 1])
|
59
|
+
HeadMusic::Rudiment::Spelling.get(scale.spellings[degree - 1])
|
57
60
|
end
|
58
61
|
end
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A ScaleType represents a particular scale pattern, such as major, lydian, or minor pentatonic.
|
2
|
-
class HeadMusic::ScaleType
|
5
|
+
class HeadMusic::Rudiment::ScaleType
|
3
6
|
H = 1 # whole step
|
4
7
|
W = 2 # half step
|
5
8
|
|
@@ -1,6 +1,9 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A scale degree is a number indicating the ordinality of the spelling in the key signature.
|
2
5
|
# TODO: Rewrite to accept a tonal_center and a scale type.
|
3
|
-
class HeadMusic::Solmization
|
6
|
+
class HeadMusic::Rudiment::Solmization
|
4
7
|
include HeadMusic::Named
|
5
8
|
|
6
9
|
DEFAULT_SOLMIZATION = "solfège"
|
@@ -1,7 +1,10 @@
|
|
1
|
+
# A module for music rudiments
|
2
|
+
module HeadMusic::Rudiment; end
|
3
|
+
|
1
4
|
# A Sonority describes a set of pitch class intervalic relationships.
|
2
5
|
# For example, a minor triad, or a major-minor seventh chord.
|
3
6
|
# The Sonority class is a factory for returning one of its subclasses.
|
4
|
-
class HeadMusic::Sonority
|
7
|
+
class HeadMusic::Rudiment::Sonority
|
5
8
|
SONORITIES = {
|
6
9
|
major_triad: %w[M3 P5],
|
7
10
|
minor_triad: %w[m3 P5],
|
@@ -110,12 +113,12 @@ class HeadMusic::Sonority
|
|
110
113
|
return nil unless identifier
|
111
114
|
|
112
115
|
@diatonic_intervals_above_bass_pitch ||=
|
113
|
-
SONORITIES[identifier].map { |shorthand| HeadMusic::DiatonicInterval.get(shorthand) }
|
116
|
+
SONORITIES[identifier].map { |shorthand| HeadMusic::Analysis::DiatonicInterval.get(shorthand) }
|
114
117
|
end
|
115
118
|
|
116
119
|
def ==(other)
|
117
|
-
other = HeadMusic::PitchSet.new(other) if other.is_a?(Array)
|
118
|
-
other = self.class.new(other) if other.is_a?(HeadMusic::PitchSet)
|
120
|
+
other = HeadMusic::Rudiment::PitchSet.new(other) if other.is_a?(Array)
|
121
|
+
other = self.class.new(other) if other.is_a?(HeadMusic::Rudiment::PitchSet)
|
119
122
|
identifier == other.identifier
|
120
123
|
end
|
121
124
|
end
|