head_music 6.0.1 → 7.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/.rubocop.yml +3 -0
- data/lib/head_music/consonance.rb +1 -1
- data/lib/head_music/content/composition.rb +3 -2
- data/lib/head_music/content/position.rb +0 -4
- data/lib/head_music/data/instruments.yml +118 -127
- data/lib/head_music/diatonic_interval/category.rb +24 -0
- data/lib/head_music/diatonic_interval/naming.rb +73 -0
- data/lib/head_music/diatonic_interval/parser.rb +47 -0
- data/lib/head_music/diatonic_interval/semitones.rb +36 -0
- data/lib/head_music/diatonic_interval/size.rb +50 -0
- data/lib/head_music/diatonic_interval.rb +1 -229
- data/lib/head_music/instrument/staff_scheme.rb +3 -3
- data/lib/head_music/instrument/{pitch_variant.rb → variant.rb} +6 -6
- data/lib/head_music/instrument.rb +9 -9
- data/lib/head_music/key_signature/enharmonic_equivalence.rb +2 -6
- data/lib/head_music/key_signature.rb +0 -1
- data/lib/head_music/locales/en.yml +1 -14
- data/lib/head_music/named.rb +0 -4
- data/lib/head_music/scale_degree.rb +9 -4
- data/lib/head_music/style/guidelines/notes_same_length.rb +0 -4
- data/lib/head_music/style/mark.rb +1 -4
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +6 -1
- metadata +8 -3
@@ -0,0 +1,24 @@
|
|
1
|
+
# Accepts the letter name count between two notes and categorizes the interval
|
2
|
+
class HeadMusic::DiatonicInterval::Category
|
3
|
+
attr_reader :number
|
4
|
+
|
5
|
+
def initialize(number)
|
6
|
+
@number = number
|
7
|
+
end
|
8
|
+
|
9
|
+
def step?
|
10
|
+
number == 2
|
11
|
+
end
|
12
|
+
|
13
|
+
def skip?
|
14
|
+
number == 3
|
15
|
+
end
|
16
|
+
|
17
|
+
def leap?
|
18
|
+
number >= 3
|
19
|
+
end
|
20
|
+
|
21
|
+
def large_leap?
|
22
|
+
number > 3
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Accepts a number and number of semitones and privides the naming methods.
|
2
|
+
class HeadMusic::DiatonicInterval::Naming
|
3
|
+
QUALITY_SEMITONES = HeadMusic::DiatonicInterval::QUALITY_SEMITONES
|
4
|
+
NUMBER_NAMES = HeadMusic::DiatonicInterval::NUMBER_NAMES
|
5
|
+
NAME_SUFFIXES = HeadMusic::DiatonicInterval::NAME_SUFFIXES
|
6
|
+
|
7
|
+
attr_reader :number, :semitones
|
8
|
+
|
9
|
+
def initialize(number:, semitones:)
|
10
|
+
@number = number
|
11
|
+
@semitones = semitones
|
12
|
+
end
|
13
|
+
|
14
|
+
def simple_number
|
15
|
+
@simple_number ||= octave_equivalent? ? 8 : (number - 1) % 7 + 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def simple_name
|
19
|
+
[quality_name, simple_number_name].join(" ")
|
20
|
+
end
|
21
|
+
|
22
|
+
def quality_name
|
23
|
+
starting_quality = QUALITY_SEMITONES[simple_number_name.to_sym].keys.first
|
24
|
+
delta = simple_semitones - (QUALITY_SEMITONES[simple_number_name.to_sym][starting_quality] % 12)
|
25
|
+
delta -= 12 while delta >= 6
|
26
|
+
HeadMusic::Quality.from(starting_quality, delta)
|
27
|
+
end
|
28
|
+
|
29
|
+
def simple_number_name
|
30
|
+
NUMBER_NAMES[simple_number - 1]
|
31
|
+
end
|
32
|
+
|
33
|
+
def number_name
|
34
|
+
NUMBER_NAMES[number - 1] || (number.to_s + NAME_SUFFIXES[number % 10])
|
35
|
+
end
|
36
|
+
|
37
|
+
def name
|
38
|
+
if named_number?
|
39
|
+
[quality_name, number_name].join(" ")
|
40
|
+
elsif simple_name == "perfect octave"
|
41
|
+
"#{octaves.humanize} octaves"
|
42
|
+
else
|
43
|
+
"#{octaves.humanize} octaves and #{quality.article} #{simple_name}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def shorthand
|
48
|
+
step_shorthand = (number == 1) ? "U" : number
|
49
|
+
[quality.shorthand, step_shorthand].join
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def simple_semitones
|
55
|
+
@simple_semitones ||= semitones % 12
|
56
|
+
end
|
57
|
+
|
58
|
+
def named_number?
|
59
|
+
number < NUMBER_NAMES.length
|
60
|
+
end
|
61
|
+
|
62
|
+
def quality
|
63
|
+
@quality ||= HeadMusic::Quality.get(quality_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def octaves
|
67
|
+
@octaves ||= semitones / 12
|
68
|
+
end
|
69
|
+
|
70
|
+
def octave_equivalent?
|
71
|
+
number > 1 && ((number - 1) % 7).zero?
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class HeadMusic::DiatonicInterval::Parser
|
2
|
+
NUMBER_NAMES = HeadMusic::DiatonicInterval::NUMBER_NAMES
|
3
|
+
|
4
|
+
attr_reader :identifier
|
5
|
+
|
6
|
+
def initialize(identifier)
|
7
|
+
@identifier = expand(identifier)
|
8
|
+
end
|
9
|
+
|
10
|
+
def words
|
11
|
+
identifier.to_s.split(/[_ ]+/)
|
12
|
+
end
|
13
|
+
|
14
|
+
def quality_name
|
15
|
+
words[0..-2].join(" ").to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def degree_name
|
19
|
+
words.last
|
20
|
+
end
|
21
|
+
|
22
|
+
def steps
|
23
|
+
NUMBER_NAMES.index(degree_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def higher_letter
|
27
|
+
HeadMusic::Pitch.middle_c.letter_name.steps_up(steps)
|
28
|
+
end
|
29
|
+
|
30
|
+
def expand(identifier)
|
31
|
+
if /[A-Z]\d{1,2}/i.match?(identifier)
|
32
|
+
number = NUMBER_NAMES[identifier.gsub(/[A-Z]/i, "").to_i - 1]
|
33
|
+
return [quality_for(identifier[0]), number].join("_").to_sym
|
34
|
+
end
|
35
|
+
identifier
|
36
|
+
end
|
37
|
+
|
38
|
+
def quality_abbreviations
|
39
|
+
HeadMusic::DiatonicInterval::QUALITY_ABBREVIATIONS
|
40
|
+
end
|
41
|
+
|
42
|
+
def quality_for(abbreviation)
|
43
|
+
quality_abbreviations[abbreviation.to_sym] ||
|
44
|
+
quality_abbreviations[abbreviation.upcase.to_sym] ||
|
45
|
+
quality_abbreviations[abbreviation.downcase.to_sym]
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Accepts a name and a quality and returns the number of semitones
|
2
|
+
class HeadMusic::DiatonicInterval::Semitones
|
3
|
+
QUALITY_SEMITONES = HeadMusic::DiatonicInterval::QUALITY_SEMITONES
|
4
|
+
|
5
|
+
attr_reader :count
|
6
|
+
|
7
|
+
def initialize(name, quality_name)
|
8
|
+
@count = self.class.degree_quality_semitones.dig(name, quality_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.degree_quality_semitones
|
12
|
+
@degree_quality_semitones ||= {}.tap do |degree_quality_semitones|
|
13
|
+
QUALITY_SEMITONES.each do |degree_name, qualities|
|
14
|
+
default_quality = qualities.keys.first
|
15
|
+
default_semitones = qualities[default_quality]
|
16
|
+
degree_quality_semitones[degree_name] = _semitones_for_degree(default_quality, default_semitones)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self._semitones_for_degree(quality, default_semitones)
|
22
|
+
{}.tap do |semitones|
|
23
|
+
_degree_quality_modifications(quality).each do |current_quality, delta|
|
24
|
+
semitones[current_quality] = default_semitones + delta
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self._degree_quality_modifications(quality)
|
30
|
+
if quality == :perfect
|
31
|
+
HeadMusic::Quality::PERFECT_INTERVAL_MODIFICATION.invert
|
32
|
+
else
|
33
|
+
HeadMusic::Quality::MAJOR_INTERVAL_MODIFICATION.invert
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Encapsulate the distance methods of the interval
|
2
|
+
class HeadMusic::DiatonicInterval::Size
|
3
|
+
attr_reader :low_pitch, :high_pitch
|
4
|
+
|
5
|
+
def initialize(pitch1, pitch2)
|
6
|
+
@low_pitch, @high_pitch = *[pitch1, pitch2].sort
|
7
|
+
end
|
8
|
+
|
9
|
+
def number
|
10
|
+
@number ||= @low_pitch.steps_to(@high_pitch) + 1
|
11
|
+
end
|
12
|
+
|
13
|
+
def simple_number
|
14
|
+
@simple_number ||= octave_equivalent? ? 8 : (number - 1) % 7 + 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def octaves
|
18
|
+
@octaves ||= number / 8
|
19
|
+
end
|
20
|
+
|
21
|
+
def simple?
|
22
|
+
number <= 8
|
23
|
+
end
|
24
|
+
|
25
|
+
def compound?
|
26
|
+
!simple?
|
27
|
+
end
|
28
|
+
|
29
|
+
def simple_semitones
|
30
|
+
@simple_semitones ||= semitones % 12
|
31
|
+
end
|
32
|
+
|
33
|
+
def semitones
|
34
|
+
(high_pitch - low_pitch).to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
def steps
|
38
|
+
number - 1
|
39
|
+
end
|
40
|
+
|
41
|
+
def simple_steps
|
42
|
+
steps % 7
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def octave_equivalent?
|
48
|
+
number > 1 && ((number - 1) % 7).zero?
|
49
|
+
end
|
50
|
+
end
|
@@ -43,237 +43,13 @@ class HeadMusic::DiatonicInterval
|
|
43
43
|
delegate :to_s, to: :name
|
44
44
|
delegate :perfect?, :major?, :minor?, :diminished?, :augmented?, :doubly_diminished?, :doubly_augmented?, to: :quality
|
45
45
|
|
46
|
-
# Interprets a string or symbol
|
47
|
-
class Parser
|
48
|
-
attr_reader :identifier
|
49
|
-
|
50
|
-
def initialize(identifier)
|
51
|
-
@identifier = expand(identifier)
|
52
|
-
end
|
53
|
-
|
54
|
-
def words
|
55
|
-
identifier.to_s.split(/[_ ]+/)
|
56
|
-
end
|
57
|
-
|
58
|
-
def quality_name
|
59
|
-
words[0..-2].join(" ").to_sym
|
60
|
-
end
|
61
|
-
|
62
|
-
def degree_name
|
63
|
-
words.last
|
64
|
-
end
|
65
|
-
|
66
|
-
def steps
|
67
|
-
NUMBER_NAMES.index(degree_name)
|
68
|
-
end
|
69
|
-
|
70
|
-
def higher_letter
|
71
|
-
HeadMusic::Pitch.middle_c.letter_name.steps_up(steps)
|
72
|
-
end
|
73
|
-
|
74
|
-
def expand(identifier)
|
75
|
-
if /[A-Z]\d{1,2}/i.match?(identifier)
|
76
|
-
number = NUMBER_NAMES[identifier.gsub(/[A-Z]/i, "").to_i - 1]
|
77
|
-
return [quality_for(identifier[0]), number].join("_").to_sym
|
78
|
-
end
|
79
|
-
identifier
|
80
|
-
end
|
81
|
-
|
82
|
-
def quality_for(abbreviation)
|
83
|
-
QUALITY_ABBREVIATIONS[abbreviation.to_sym] ||
|
84
|
-
QUALITY_ABBREVIATIONS[abbreviation.upcase.to_sym] ||
|
85
|
-
QUALITY_ABBREVIATIONS[abbreviation.downcase.to_sym]
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# Accepts a name and a quality and returns the number of semitones
|
90
|
-
class Semitones
|
91
|
-
attr_reader :count
|
92
|
-
|
93
|
-
def initialize(name, quality_name)
|
94
|
-
@count = Semitones.degree_quality_semitones.dig(name, quality_name)
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.degree_quality_semitones
|
98
|
-
@degree_quality_semitones ||= {}.tap do |degree_quality_semitones|
|
99
|
-
QUALITY_SEMITONES.each do |degree_name, qualities|
|
100
|
-
default_quality = qualities.keys.first
|
101
|
-
default_semitones = qualities[default_quality]
|
102
|
-
degree_quality_semitones[degree_name] = _semitones_for_degree(default_quality, default_semitones)
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def self._semitones_for_degree(quality, default_semitones)
|
108
|
-
{}.tap do |semitones|
|
109
|
-
_degree_quality_modifications(quality).each do |current_quality, delta|
|
110
|
-
semitones[current_quality] = default_semitones + delta
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def self._degree_quality_modifications(quality)
|
116
|
-
if quality == :perfect
|
117
|
-
HeadMusic::Quality::PERFECT_INTERVAL_MODIFICATION.invert
|
118
|
-
else
|
119
|
-
HeadMusic::Quality::MAJOR_INTERVAL_MODIFICATION.invert
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# Accepts the letter name count between two notes and categorizes the interval
|
125
|
-
class Category
|
126
|
-
attr_reader :number
|
127
|
-
|
128
|
-
def initialize(number)
|
129
|
-
@number = number
|
130
|
-
end
|
131
|
-
|
132
|
-
def step?
|
133
|
-
number == 2
|
134
|
-
end
|
135
|
-
|
136
|
-
def skip?
|
137
|
-
number == 3
|
138
|
-
end
|
139
|
-
|
140
|
-
def leap?
|
141
|
-
number >= 3
|
142
|
-
end
|
143
|
-
|
144
|
-
def large_leap?
|
145
|
-
number > 3
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
# Encapsulate the distance methods of the interval
|
150
|
-
class Size
|
151
|
-
attr_reader :low_pitch, :high_pitch
|
152
|
-
|
153
|
-
def initialize(pitch1, pitch2)
|
154
|
-
@low_pitch, @high_pitch = *[pitch1, pitch2].sort
|
155
|
-
end
|
156
|
-
|
157
|
-
def number
|
158
|
-
@number ||= @low_pitch.steps_to(@high_pitch) + 1
|
159
|
-
end
|
160
|
-
|
161
|
-
def simple_number
|
162
|
-
@simple_number ||= octave_equivalent? ? 8 : (number - 1) % 7 + 1
|
163
|
-
end
|
164
|
-
|
165
|
-
def octaves
|
166
|
-
@octaves ||= number / 8
|
167
|
-
end
|
168
|
-
|
169
|
-
def simple?
|
170
|
-
number <= 8
|
171
|
-
end
|
172
|
-
|
173
|
-
def compound?
|
174
|
-
!simple?
|
175
|
-
end
|
176
|
-
|
177
|
-
def simple_semitones
|
178
|
-
@simple_semitones ||= semitones % 12
|
179
|
-
end
|
180
|
-
|
181
|
-
def semitones
|
182
|
-
(high_pitch - low_pitch).to_i
|
183
|
-
end
|
184
|
-
|
185
|
-
def steps
|
186
|
-
number - 1
|
187
|
-
end
|
188
|
-
|
189
|
-
def simple_steps
|
190
|
-
steps % 7
|
191
|
-
end
|
192
|
-
|
193
|
-
private
|
194
|
-
|
195
|
-
def octave_equivalent?
|
196
|
-
number > 1 && ((number - 1) % 7).zero?
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
# Accepts a number and number of semitones and privides the naming methods.
|
201
|
-
class Naming
|
202
|
-
attr_reader :number, :semitones
|
203
|
-
|
204
|
-
def initialize(number:, semitones:)
|
205
|
-
@number = number
|
206
|
-
@semitones = semitones
|
207
|
-
end
|
208
|
-
|
209
|
-
def simple_semitones
|
210
|
-
@simple_semitones ||= semitones % 12
|
211
|
-
end
|
212
|
-
|
213
|
-
def simple_number
|
214
|
-
@simple_number ||= octave_equivalent? ? 8 : (number - 1) % 7 + 1
|
215
|
-
end
|
216
|
-
|
217
|
-
def simple_name
|
218
|
-
[quality_name, simple_number_name].join(" ")
|
219
|
-
end
|
220
|
-
|
221
|
-
def quality_name
|
222
|
-
starting_quality = QUALITY_SEMITONES[simple_number_name.to_sym].keys.first
|
223
|
-
delta = simple_semitones - (QUALITY_SEMITONES[simple_number_name.to_sym][starting_quality] % 12)
|
224
|
-
delta -= 12 while delta >= 6
|
225
|
-
HeadMusic::Quality.from(starting_quality, delta)
|
226
|
-
end
|
227
|
-
|
228
|
-
def simple_number_name
|
229
|
-
NUMBER_NAMES[simple_number - 1]
|
230
|
-
end
|
231
|
-
|
232
|
-
def number_name
|
233
|
-
NUMBER_NAMES[number - 1] || (number.to_s + NAME_SUFFIXES[number % 10])
|
234
|
-
end
|
235
|
-
|
236
|
-
def name
|
237
|
-
if named_number?
|
238
|
-
[quality_name, number_name].join(" ")
|
239
|
-
elsif simple_name == "perfect octave"
|
240
|
-
"#{octaves.humanize} octaves"
|
241
|
-
else
|
242
|
-
"#{octaves.humanize} octaves and #{quality.article} #{simple_name}"
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
def shorthand
|
247
|
-
step_shorthand = (number == 1) ? "U" : number
|
248
|
-
[quality.shorthand, step_shorthand].join
|
249
|
-
end
|
250
|
-
|
251
|
-
private
|
252
|
-
|
253
|
-
def named_number?
|
254
|
-
number < NUMBER_NAMES.length
|
255
|
-
end
|
256
|
-
|
257
|
-
def quality
|
258
|
-
@quality ||= HeadMusic::Quality.get(quality_name)
|
259
|
-
end
|
260
|
-
|
261
|
-
def octaves
|
262
|
-
@octaves ||= semitones / 12
|
263
|
-
end
|
264
|
-
|
265
|
-
def octave_equivalent?
|
266
|
-
number > 1 && ((number - 1) % 7).zero?
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
46
|
delegate :step?, :skip?, :leap?, :large_leap?, to: :category
|
271
47
|
delegate(
|
272
48
|
:simple_number, :octaves, :number, :simple?, :compound?, :semitones, :simple_semitones, :steps, :simple_steps,
|
273
49
|
to: :size
|
274
50
|
)
|
275
51
|
delegate(
|
276
|
-
:
|
52
|
+
:simple_name, :quality_name, :simple_number_name, :number_name, :name, :shorthand,
|
277
53
|
to: :naming
|
278
54
|
)
|
279
55
|
|
@@ -378,10 +154,6 @@ class HeadMusic::DiatonicInterval
|
|
378
154
|
@naming ||= Naming.new(number: number, semitones: semitones)
|
379
155
|
end
|
380
156
|
|
381
|
-
def named_number?
|
382
|
-
number < NUMBER_NAMES.length
|
383
|
-
end
|
384
|
-
|
385
157
|
def consonance_for_perfect(style = :standard_practice)
|
386
158
|
HeadMusic::Consonance.get(dissonant_fourth?(style) ? :dissonant : :perfect) if perfect?
|
387
159
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class HeadMusic::Instrument::StaffScheme
|
2
|
-
attr_reader :
|
2
|
+
attr_reader :variant, :key, :list
|
3
3
|
|
4
|
-
def initialize(
|
5
|
-
@
|
4
|
+
def initialize(variant:, key:, list:)
|
5
|
+
@variant = variant
|
6
6
|
@key = key || "default"
|
7
7
|
@list = list
|
8
8
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class HeadMusic::Instrument::
|
1
|
+
class HeadMusic::Instrument::Variant
|
2
2
|
attr_reader :key, :attributes
|
3
3
|
|
4
4
|
def initialize(key, attributes = {})
|
@@ -6,11 +6,11 @@ class HeadMusic::Instrument::PitchVariant
|
|
6
6
|
@attributes = attributes
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
return unless attributes["
|
9
|
+
def pitch_designation
|
10
|
+
return unless attributes["pitch_designation"].to_s != ""
|
11
11
|
|
12
|
-
@
|
13
|
-
HeadMusic::Spelling.get(attributes["
|
12
|
+
@pitch_designation ||=
|
13
|
+
HeadMusic::Spelling.get(attributes["pitch_designation"])
|
14
14
|
end
|
15
15
|
|
16
16
|
def staff_schemes
|
@@ -18,7 +18,7 @@ class HeadMusic::Instrument::PitchVariant
|
|
18
18
|
(attributes["staff_schemes"] || {}).map do |key, list|
|
19
19
|
HeadMusic::Instrument::StaffScheme.new(
|
20
20
|
key: key,
|
21
|
-
|
21
|
+
variant: self,
|
22
22
|
list: list
|
23
23
|
)
|
24
24
|
end
|
@@ -9,8 +9,8 @@
|
|
9
9
|
# default_clefs: the default clef or system of clefs for the instrument
|
10
10
|
# - [treble] for instruments that use the treble clef
|
11
11
|
# - [treble, bass] for instruments that use the grand staff
|
12
|
-
#
|
13
|
-
# a hash of default and alternative
|
12
|
+
# variants:
|
13
|
+
# a hash of default and alternative pitch designations
|
14
14
|
# Associations:
|
15
15
|
# family: the family of the instrument (e.g. "saxophone")
|
16
16
|
# orchestra_section: the section of the orchestra (e.g. "strings")
|
@@ -32,7 +32,7 @@ class HeadMusic::Instrument
|
|
32
32
|
attr_reader(
|
33
33
|
:name_key, :alias_name_keys,
|
34
34
|
:family_key, :orchestra_section_key,
|
35
|
-
:
|
35
|
+
:variants, :classification_keys
|
36
36
|
)
|
37
37
|
|
38
38
|
def ==(other)
|
@@ -75,12 +75,12 @@ class HeadMusic::Instrument
|
|
75
75
|
default_clefs.any?
|
76
76
|
end
|
77
77
|
|
78
|
-
def
|
79
|
-
|
78
|
+
def default_variant
|
79
|
+
variants.find(&:default?) || variants.first
|
80
80
|
end
|
81
81
|
|
82
82
|
def default_staff_scheme
|
83
|
-
|
83
|
+
default_variant&.default_staff_scheme
|
84
84
|
end
|
85
85
|
|
86
86
|
def default_staves
|
@@ -158,9 +158,9 @@ class HeadMusic::Instrument
|
|
158
158
|
def initialize_attributes(record)
|
159
159
|
@orchestra_section_key ||= record["orchestra_section_key"]
|
160
160
|
@classification_keys = [@classification_keys, record["classification_keys"]].flatten.compact.uniq
|
161
|
-
@
|
162
|
-
(record["
|
163
|
-
HeadMusic::Instrument::
|
161
|
+
@variants =
|
162
|
+
(record["variants"] || {}).map do |key, attributes|
|
163
|
+
HeadMusic::Instrument::Variant.new(key, attributes)
|
164
164
|
end
|
165
165
|
end
|
166
166
|
|
@@ -15,16 +15,12 @@ class HeadMusic::KeySignature::EnharmonicEquivalence
|
|
15
15
|
def enharmonic_equivalent?(other)
|
16
16
|
other = HeadMusic::KeySignature.get(other)
|
17
17
|
|
18
|
-
p key_signature.pitch_classes.map(&:to_i).sort
|
19
|
-
p other.pitch_classes.map(&:to_i).sort
|
20
|
-
p key_signature.alterations.map(&:to_s).sort
|
21
|
-
p other.alterations.map(&:to_s).sort
|
22
18
|
key_signature.pitch_classes.map(&:to_i).sort == other.pitch_classes.map(&:to_i).sort &&
|
23
19
|
key_signature.alterations.map(&:to_s).sort != other.alterations.map(&:to_s).sort
|
24
20
|
end
|
25
21
|
|
26
|
-
|
27
|
-
|
22
|
+
alias_method :enharmonic?, :enharmonic_equivalent?
|
23
|
+
alias_method :equivalent?, :enharmonic_equivalent?
|
28
24
|
|
29
25
|
private_class_method :new
|
30
26
|
end
|
@@ -100,6 +100,7 @@ en:
|
|
100
100
|
wind_instrument: wind instrument
|
101
101
|
wood: wood
|
102
102
|
woodwind: woodwind
|
103
|
+
instrument_with_pitch_designation: "%{instrument} in %{pitch_designation}"
|
103
104
|
instrument_voices:
|
104
105
|
piccolo: piccolo
|
105
106
|
soprano: soprano
|
@@ -126,27 +127,17 @@ en:
|
|
126
127
|
bass_oboe: bass oboe
|
127
128
|
bass_trombone: bass trombone
|
128
129
|
bass_trumpet: bass trumpet
|
129
|
-
bass_trumpet_in_b_flat: bass trumpet in B♭
|
130
|
-
bass_trumpet_in_c: bass trumpet in C
|
131
|
-
bass_trumpet_in_e_flat: bass trumpet in E♭
|
132
130
|
bass_tuba: bass tuba
|
133
|
-
bass_tuba_in_e_flat: bass tuba in E♭
|
134
|
-
bass_tuba_in_f: bass tuba in F
|
135
131
|
bass_voice: bass
|
136
132
|
basset_horn: basset horn
|
137
133
|
bassoon: bassoon
|
138
134
|
bugle: bugle
|
139
|
-
bugle_in_b_flat: bugle in B♭
|
140
135
|
castanets: castanets
|
141
136
|
celesta: celesta
|
142
137
|
cello: cello
|
143
138
|
chimes: chimes
|
144
139
|
cimbalom: cimbalom
|
145
140
|
clarinet: clarinet
|
146
|
-
clarinet_in_a: clarinet in A
|
147
|
-
clarinet_in_c: clarinet in C
|
148
|
-
clarinet_in_d: clarinet in D
|
149
|
-
clarinet_in_e_flat: clarinet in E♭
|
150
141
|
clash_cymbals: clash cymbals
|
151
142
|
clavichord: clavichord
|
152
143
|
contrabass_clarinet: contrabass clarinet
|
@@ -202,11 +193,7 @@ en:
|
|
202
193
|
triangle: triangle
|
203
194
|
trombone: trombone
|
204
195
|
trumpet: trumpet
|
205
|
-
trumpet_in_c: trumpet in C
|
206
|
-
trumpet_in_d: trumpet in D
|
207
|
-
trumpet_in_e_flat: trumpet in E♭
|
208
196
|
tuba: tuba
|
209
|
-
tuba_in_c: tuba in C
|
210
197
|
tubular_bells: tubular bells
|
211
198
|
vibraphone: vibraphone
|
212
199
|
viola: viola
|
data/lib/head_music/named.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# A scale degree is a number indicating the ordinality of the spelling in the key
|
1
|
+
# A scale degree is a number indicating the ordinality of the spelling in the key.
|
2
2
|
# TODO: Rewrite to accept a tonal_center and a scale type.
|
3
3
|
class HeadMusic::ScaleDegree
|
4
4
|
include Comparable
|
@@ -20,20 +20,25 @@ class HeadMusic::ScaleDegree
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def alteration
|
23
|
-
|
23
|
+
spelling_alteration_semitones = spelling.alteration&.semitones || 0
|
24
24
|
usual_sign_semitones = scale_degree_usual_spelling.alteration&.semitones || 0
|
25
|
-
delta =
|
25
|
+
delta = spelling_alteration_semitones - usual_sign_semitones
|
26
26
|
HeadMusic::Alteration.by(:semitones, delta) if delta != 0
|
27
27
|
end
|
28
28
|
|
29
|
+
def alteration_semitones
|
30
|
+
alteration&.semitones || 0
|
31
|
+
end
|
32
|
+
|
29
33
|
def to_s
|
30
34
|
"#{alteration}#{degree}"
|
31
35
|
end
|
32
36
|
|
33
37
|
def <=>(other)
|
34
38
|
if other.is_a?(HeadMusic::ScaleDegree)
|
35
|
-
[degree,
|
39
|
+
[degree, alteration_semitones] <=> [other.degree, other.alteration_semitones]
|
36
40
|
else
|
41
|
+
# TODO: Improve this
|
37
42
|
to_s <=> other.to_s
|
38
43
|
end
|
39
44
|
end
|