head_music 4.0.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +0 -2
- data/Rakefile +0 -2
- data/bin/console +0 -2
- data/head_music.gemspec +0 -2
- data/lib/head_music/{sign.rb → alteration.rb} +13 -15
- data/lib/head_music/chromatic_interval.rb +0 -2
- data/lib/head_music/circle.rb +0 -2
- data/lib/head_music/clef.rb +0 -2
- data/lib/head_music/consonance.rb +0 -2
- data/lib/head_music/content/bar.rb +0 -2
- data/lib/head_music/content/composition.rb +0 -2
- data/lib/head_music/content/note.rb +0 -2
- data/lib/head_music/content/placement.rb +0 -2
- data/lib/head_music/content/position.rb +0 -2
- data/lib/head_music/content/rhythmic_value.rb +0 -2
- data/lib/head_music/content/voice.rb +0 -2
- data/lib/head_music/data/instrument_families.yml +2 -2
- data/lib/head_music/data/instruments.yml +571 -523
- data/lib/head_music/diatonic_interval.rb +0 -2
- data/lib/head_music/harmonic_interval.rb +0 -2
- data/lib/head_music/instrument/pitch_configuration.rb +35 -0
- data/lib/head_music/instrument/staff.rb +30 -0
- data/lib/head_music/instrument/staff_configuration.rb +19 -0
- data/lib/head_music/instrument.rb +31 -19
- data/lib/head_music/interval_cycle.rb +0 -2
- data/lib/head_music/key_signature.rb +0 -2
- data/lib/head_music/letter_name.rb +0 -2
- data/lib/head_music/locales/de.yml +1 -1
- data/lib/head_music/locales/en.yml +4 -3
- data/lib/head_music/locales/es.yml +1 -1
- data/lib/head_music/locales/fr.yml +1 -1
- data/lib/head_music/locales/it.yml +1 -1
- data/lib/head_music/locales/ru.yml +1 -1
- data/lib/head_music/melodic_interval.rb +0 -2
- data/lib/head_music/meter.rb +0 -2
- data/lib/head_music/motion.rb +0 -2
- data/lib/head_music/musical_symbol.rb +0 -2
- data/lib/head_music/named.rb +0 -2
- data/lib/head_music/pitch/enharmonic_equivalence.rb +0 -2
- data/lib/head_music/pitch/octave_equivalence.rb +0 -2
- data/lib/head_music/pitch.rb +7 -9
- data/lib/head_music/pitch_class.rb +0 -2
- data/lib/head_music/pitch_class_set.rb +0 -2
- data/lib/head_music/pitch_set.rb +0 -2
- data/lib/head_music/quality.rb +0 -2
- data/lib/head_music/reference_pitch.rb +0 -2
- data/lib/head_music/register.rb +0 -2
- data/lib/head_music/rhythm.rb +0 -2
- data/lib/head_music/rhythmic_unit.rb +0 -2
- data/lib/head_music/scale.rb +0 -2
- data/lib/head_music/scale_degree.rb +8 -10
- data/lib/head_music/scale_type.rb +0 -2
- data/lib/head_music/solmization.rb +0 -2
- data/lib/head_music/sonority.rb +0 -2
- data/lib/head_music/spelling.rb +18 -20
- data/lib/head_music/staff.rb +0 -2
- data/lib/head_music/style/analysis.rb +0 -2
- data/lib/head_music/style/annotation.rb +0 -2
- data/lib/head_music/style/guidelines/always_move.rb +0 -2
- data/lib/head_music/style/guidelines/approach_perfection_contrarily.rb +0 -2
- data/lib/head_music/style/guidelines/at_least_eight_notes.rb +0 -2
- data/lib/head_music/style/guidelines/avoid_crossing_voices.rb +0 -2
- data/lib/head_music/style/guidelines/avoid_overlapping_voices.rb +0 -2
- data/lib/head_music/style/guidelines/consonant_climax.rb +0 -2
- data/lib/head_music/style/guidelines/consonant_downbeats.rb +0 -2
- data/lib/head_music/style/guidelines/diatonic.rb +1 -3
- data/lib/head_music/style/guidelines/direction_changes.rb +0 -2
- data/lib/head_music/style/guidelines/end_on_perfect_consonance.rb +0 -2
- data/lib/head_music/style/guidelines/end_on_tonic.rb +0 -2
- data/lib/head_music/style/guidelines/frequent_direction_changes.rb +0 -2
- data/lib/head_music/style/guidelines/limit_octave_leaps.rb +0 -2
- data/lib/head_music/style/guidelines/moderate_direction_changes.rb +0 -2
- data/lib/head_music/style/guidelines/mostly_conjunct.rb +0 -2
- data/lib/head_music/style/guidelines/no_rests.rb +0 -2
- data/lib/head_music/style/guidelines/no_unisons_in_middle.rb +0 -2
- data/lib/head_music/style/guidelines/notes_same_length.rb +0 -2
- data/lib/head_music/style/guidelines/one_to_one.rb +0 -2
- data/lib/head_music/style/guidelines/prefer_contrary_motion.rb +0 -2
- data/lib/head_music/style/guidelines/prefer_imperfect.rb +0 -2
- data/lib/head_music/style/guidelines/prepare_octave_leaps.rb +0 -2
- data/lib/head_music/style/guidelines/recover_large_leaps.rb +0 -2
- data/lib/head_music/style/guidelines/singable_intervals.rb +0 -2
- data/lib/head_music/style/guidelines/singable_range.rb +0 -2
- data/lib/head_music/style/guidelines/single_large_leaps.rb +0 -2
- data/lib/head_music/style/guidelines/start_on_perfect_consonance.rb +0 -2
- data/lib/head_music/style/guidelines/start_on_tonic.rb +0 -2
- data/lib/head_music/style/guidelines/step_down_to_final_note.rb +0 -2
- data/lib/head_music/style/guidelines/step_out_of_unison.rb +0 -2
- data/lib/head_music/style/guidelines/step_to_final_note.rb +0 -2
- data/lib/head_music/style/guidelines/step_up_to_final_note.rb +0 -2
- data/lib/head_music/style/guidelines/up_to_fourteen_notes.rb +0 -2
- data/lib/head_music/style/guides/first_species_harmony.rb +0 -2
- data/lib/head_music/style/guides/first_species_melody.rb +0 -2
- data/lib/head_music/style/guides/fux_cantus_firmus.rb +0 -2
- data/lib/head_music/style/guides/modern_cantus_firmus.rb +0 -2
- data/lib/head_music/style/mark.rb +0 -2
- data/lib/head_music/tuning.rb +0 -2
- data/lib/head_music/utilities/hash_key.rb +0 -2
- data/lib/head_music/version.rb +1 -3
- data/lib/head_music.rb +4 -3
- metadata +6 -3
@@ -0,0 +1,35 @@
|
|
1
|
+
class HeadMusic::Instrument::PitchConfiguration
|
2
|
+
attr_reader :key, :attributes
|
3
|
+
|
4
|
+
def initialize(key, attributes = {})
|
5
|
+
@key = key.to_s.to_sym
|
6
|
+
@attributes = attributes
|
7
|
+
end
|
8
|
+
|
9
|
+
def fundamental_pitch_spelling
|
10
|
+
return unless attributes["fundamental_pitch_spelling"].to_s != ""
|
11
|
+
|
12
|
+
@fundamental_pitch_spelling ||=
|
13
|
+
HeadMusic::Spelling.get(attributes["fundamental_pitch_spelling"])
|
14
|
+
end
|
15
|
+
|
16
|
+
def staff_configurations
|
17
|
+
@staff_configurations ||=
|
18
|
+
(attributes["staff_configurations"] || {}).map do |key, list|
|
19
|
+
HeadMusic::Instrument::StaffConfiguration.new(
|
20
|
+
key: key,
|
21
|
+
pitch_configuration: self,
|
22
|
+
list: list
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def default?
|
28
|
+
key.to_s == "default"
|
29
|
+
end
|
30
|
+
|
31
|
+
def default_staff_configuration
|
32
|
+
@default_staff_configuration ||=
|
33
|
+
staff_configurations.find(&:default?) || staff_configurations.first
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class HeadMusic::Instrument::Staff
|
2
|
+
DEFAULT_CLEF = "treble_clef"
|
3
|
+
|
4
|
+
attr_reader :staff_configuration, :attributes
|
5
|
+
|
6
|
+
def initialize(staff_configuration, attributes)
|
7
|
+
@staff_configuration = staff_configuration
|
8
|
+
@attributes = attributes || {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def clef
|
12
|
+
HeadMusic::Clef.get(smart_clef_key)
|
13
|
+
end
|
14
|
+
|
15
|
+
def smart_clef_key
|
16
|
+
"#{attributes["clef"]}_clef".gsub(/_clef_clef$/, "_clef")
|
17
|
+
end
|
18
|
+
|
19
|
+
def sounding_transposition
|
20
|
+
attributes["sounding_transposition"] || 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def name_key
|
24
|
+
attributes["name_key"]
|
25
|
+
end
|
26
|
+
|
27
|
+
def name
|
28
|
+
name_key.to_s.tr("_", " ")
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class HeadMusic::Instrument::StaffConfiguration
|
2
|
+
attr_reader :pitch_configuration, :key, :list
|
3
|
+
|
4
|
+
def initialize(pitch_configuration:, key:, list:)
|
5
|
+
@pitch_configuration = pitch_configuration
|
6
|
+
@key = key || "default"
|
7
|
+
@list = list
|
8
|
+
end
|
9
|
+
|
10
|
+
def default?
|
11
|
+
key.to_s == "default"
|
12
|
+
end
|
13
|
+
|
14
|
+
def staves
|
15
|
+
@staves ||= list.map do |attributes|
|
16
|
+
HeadMusic::Instrument::Staff.new(self, attributes)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
# A musical instrument.
|
4
2
|
# An instrument object can be assigned to a staff object.
|
5
3
|
# Attributes:
|
@@ -8,14 +6,11 @@
|
|
8
6
|
# orchestra_section_key: the section of the orchestra (e.g. "strings")
|
9
7
|
# family_key: the key for the family of the instrument (e.g. "saxophone")
|
10
8
|
# classification_keys: an array of classification_keys
|
11
|
-
# transposition: the number of semitones between the written and the sounding pitch (optional, default: 0)
|
12
9
|
# default_clefs: the default clef or system of clefs for the instrument
|
13
10
|
# - [treble] for instruments that use the treble clef
|
14
11
|
# - [treble, bass] for instruments that use the grand staff
|
15
|
-
#
|
16
|
-
# a hash of default and alternative
|
17
|
-
# each with a staff's key with an array of hashes
|
18
|
-
# including clef and transposition (where applicable)
|
12
|
+
# pitch_configurations:
|
13
|
+
# a hash of default and alternative fundamental pitches.
|
19
14
|
# Associations:
|
20
15
|
# family: the family of the instrument (e.g. "saxophone")
|
21
16
|
# orchestra_section: the section of the orchestra (e.g. "strings")
|
@@ -25,8 +20,7 @@ class HeadMusic::Instrument
|
|
25
20
|
INSTRUMENTS = YAML.load_file(File.expand_path("data/instruments.yml", __dir__)).freeze
|
26
21
|
|
27
22
|
def self.get(name)
|
28
|
-
|
29
|
-
result || new(name)
|
23
|
+
get_by_name(name)
|
30
24
|
end
|
31
25
|
|
32
26
|
def self.all
|
@@ -38,9 +32,7 @@ class HeadMusic::Instrument
|
|
38
32
|
attr_reader(
|
39
33
|
:name_key, :alias_name_keys,
|
40
34
|
:family_key, :orchestra_section_key,
|
41
|
-
:
|
42
|
-
:fundamental_pitch_spelling, :transposition,
|
43
|
-
:default_staves, :default_clefs
|
35
|
+
:pitch_configurations, :classification_keys
|
44
36
|
)
|
45
37
|
|
46
38
|
def ==(other)
|
@@ -61,12 +53,12 @@ class HeadMusic::Instrument
|
|
61
53
|
|
62
54
|
# Returns true if the instrument sounds at a different pitch than written.
|
63
55
|
def transposing?
|
64
|
-
|
56
|
+
default_sounding_transposition != 0
|
65
57
|
end
|
66
58
|
|
67
59
|
# Returns true if the instrument sounds at a different register than written.
|
68
60
|
def transposing_at_the_octave?
|
69
|
-
transposing? &&
|
61
|
+
transposing? && default_sounding_transposition % 12 == 0
|
70
62
|
end
|
71
63
|
|
72
64
|
def single_staff?
|
@@ -78,11 +70,31 @@ class HeadMusic::Instrument
|
|
78
70
|
end
|
79
71
|
|
80
72
|
def pitched?
|
81
|
-
return false if default_clefs.compact.uniq == ["
|
73
|
+
return false if default_clefs.compact.uniq == [HeadMusic::Clef.get("neutral_clef")]
|
82
74
|
|
83
75
|
default_clefs.any?
|
84
76
|
end
|
85
77
|
|
78
|
+
def default_pitch_configuration
|
79
|
+
pitch_configurations.find(&:default?) || pitch_configurations.first
|
80
|
+
end
|
81
|
+
|
82
|
+
def default_staff_configuration
|
83
|
+
default_pitch_configuration&.default_staff_configuration
|
84
|
+
end
|
85
|
+
|
86
|
+
def default_staves
|
87
|
+
default_staff_configuration&.staves || []
|
88
|
+
end
|
89
|
+
|
90
|
+
def default_clefs
|
91
|
+
default_staves&.map(&:clef) || []
|
92
|
+
end
|
93
|
+
|
94
|
+
def default_sounding_transposition
|
95
|
+
default_staves&.first&.sounding_transposition || 0
|
96
|
+
end
|
97
|
+
|
86
98
|
private_class_method :new
|
87
99
|
|
88
100
|
private
|
@@ -146,10 +158,10 @@ class HeadMusic::Instrument
|
|
146
158
|
def initialize_attributes(record)
|
147
159
|
@orchestra_section_key ||= record["orchestra_section_key"]
|
148
160
|
@classification_keys = [@classification_keys, record["classification_keys"]].flatten.compact.uniq
|
149
|
-
@
|
150
|
-
|
151
|
-
|
152
|
-
|
161
|
+
@pitch_configurations =
|
162
|
+
(record["pitch_configurations"] || {}).map do |key, attributes|
|
163
|
+
HeadMusic::Instrument::PitchConfiguration.new(key, attributes)
|
164
|
+
end
|
153
165
|
end
|
154
166
|
|
155
167
|
def inferred_name
|
@@ -53,7 +53,7 @@ en:
|
|
53
53
|
viola_clef: viola clef
|
54
54
|
violin_clef: treble clef
|
55
55
|
instrument_abbreviations:
|
56
|
-
|
56
|
+
accordion:
|
57
57
|
- acc
|
58
58
|
alto_flute:
|
59
59
|
- afl
|
@@ -103,6 +103,7 @@ en:
|
|
103
103
|
instrument_voices:
|
104
104
|
piccolo: piccolo
|
105
105
|
soprano: soprano
|
106
|
+
mezzo_soprano: mezzo-soprano
|
106
107
|
alto: alto
|
107
108
|
tenor: tenor
|
108
109
|
baritone: baritone
|
@@ -110,7 +111,7 @@ en:
|
|
110
111
|
contrabass: contrabass
|
111
112
|
octobass: octobass
|
112
113
|
instruments:
|
113
|
-
|
114
|
+
accordion: accordion
|
114
115
|
alto_clarinet: alto clarinet
|
115
116
|
alto_flute: alto flute
|
116
117
|
alto_voice: alto
|
@@ -252,6 +253,7 @@ en:
|
|
252
253
|
sydney_symphony_orchestra: Sydney Symphony Orchestra
|
253
254
|
verdi: Verdi tuning
|
254
255
|
rudiments:
|
256
|
+
alteration: alteration
|
255
257
|
chromatic_interval: chromatic interval
|
256
258
|
circle: circle
|
257
259
|
clef: clef
|
@@ -279,7 +281,6 @@ en:
|
|
279
281
|
scale: scale
|
280
282
|
scale_degree: scale degree
|
281
283
|
scale_type: scale type
|
282
|
-
sign: sign
|
283
284
|
solmization: solmization
|
284
285
|
sonority: sonority
|
285
286
|
spelling: spelling
|
data/lib/head_music/meter.rb
CHANGED
data/lib/head_music/motion.rb
CHANGED
data/lib/head_music/named.rb
CHANGED
data/lib/head_music/pitch.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
# A pitch is a named frequency represented by a spelling and a register.
|
4
2
|
class HeadMusic::Pitch
|
5
3
|
include Comparable
|
@@ -8,11 +6,11 @@ class HeadMusic::Pitch
|
|
8
6
|
|
9
7
|
delegate :letter_name, to: :spelling
|
10
8
|
delegate :series_ascending, :series_descending, to: :letter_name, prefix: true
|
11
|
-
delegate :
|
9
|
+
delegate :alteration, :sharp?, :flat?, to: :spelling
|
12
10
|
delegate :pitch_class, to: :spelling
|
13
11
|
delegate :number, to: :pitch_class, prefix: true
|
14
12
|
delegate :pitch_class_number, to: :natural, prefix: true
|
15
|
-
delegate :semitones, to: :
|
13
|
+
delegate :semitones, to: :alteration, prefix: true, allow_nil: true
|
16
14
|
delegate :steps_to, to: :letter_name, prefix: true
|
17
15
|
|
18
16
|
delegate :smallest_interval_to, to: :pitch_class
|
@@ -62,9 +60,9 @@ class HeadMusic::Pitch
|
|
62
60
|
def self.from_number_and_letter(number, letter_name)
|
63
61
|
letter_name = HeadMusic::LetterName.get(letter_name)
|
64
62
|
natural_letter_pitch = natural_letter_pitch(number, letter_name)
|
65
|
-
|
66
|
-
|
67
|
-
spelling = HeadMusic::Spelling.fetch_or_create(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)
|
68
66
|
fetch_or_create(spelling, natural_letter_pitch.register)
|
69
67
|
end
|
70
68
|
|
@@ -94,7 +92,7 @@ class HeadMusic::Pitch
|
|
94
92
|
end
|
95
93
|
|
96
94
|
def midi_note_number
|
97
|
-
(register + 1) * 12 + letter_name.pitch_class.to_i +
|
95
|
+
(register + 1) * 12 + letter_name.pitch_class.to_i + alteration_semitones.to_i
|
98
96
|
end
|
99
97
|
|
100
98
|
alias_method :midi, :midi_note_number
|
@@ -113,7 +111,7 @@ class HeadMusic::Pitch
|
|
113
111
|
end
|
114
112
|
|
115
113
|
def natural
|
116
|
-
HeadMusic::Pitch.get(to_s.gsub(HeadMusic::
|
114
|
+
HeadMusic::Pitch.get(to_s.gsub(HeadMusic::Alteration.matcher, ""))
|
117
115
|
end
|
118
116
|
|
119
117
|
def +(other)
|
data/lib/head_music/pitch_set.rb
CHANGED
data/lib/head_music/quality.rb
CHANGED
data/lib/head_music/register.rb
CHANGED
data/lib/head_music/rhythm.rb
CHANGED
data/lib/head_music/scale.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
# A scale degree is a number indicating the ordinality of the spelling in the key signature.
|
4
2
|
# TODO: Rewrite to accept a tonal_center and a scale type.
|
5
3
|
class HeadMusic::ScaleDegree
|
@@ -21,20 +19,20 @@ class HeadMusic::ScaleDegree
|
|
21
19
|
scale.letter_name_series_ascending.index(spelling.letter_name.to_s) + 1
|
22
20
|
end
|
23
21
|
|
24
|
-
def
|
25
|
-
|
26
|
-
usual_sign_semitones = scale_degree_usual_spelling.
|
27
|
-
delta =
|
28
|
-
HeadMusic::
|
22
|
+
def alteration
|
23
|
+
alteration_semitones = spelling.alteration&.semitones || 0
|
24
|
+
usual_sign_semitones = scale_degree_usual_spelling.alteration&.semitones || 0
|
25
|
+
delta = alteration_semitones - usual_sign_semitones
|
26
|
+
HeadMusic::Alteration.by(:semitones, delta) if delta != 0
|
29
27
|
end
|
30
28
|
|
31
29
|
def to_s
|
32
|
-
"#{
|
30
|
+
"#{alteration}#{degree}"
|
33
31
|
end
|
34
32
|
|
35
33
|
def <=>(other)
|
36
34
|
if other.is_a?(HeadMusic::ScaleDegree)
|
37
|
-
[degree,
|
35
|
+
[degree, alteration.semitones] <=> [other.degree, other.alteration.semitones]
|
38
36
|
else
|
39
37
|
to_s <=> other.to_s
|
40
38
|
end
|
@@ -44,7 +42,7 @@ class HeadMusic::ScaleDegree
|
|
44
42
|
return unless scale_type.diatonic?
|
45
43
|
|
46
44
|
NAME_FOR_DIATONIC_DEGREE[degree] ||
|
47
|
-
((scale_type.intervals.last == 1 ||
|
45
|
+
((scale_type.intervals.last == 1 || alteration == "#") ? "leading tone" : "subtonic")
|
48
46
|
end
|
49
47
|
|
50
48
|
private
|
data/lib/head_music/sonority.rb
CHANGED
data/lib/head_music/spelling.rb
CHANGED
@@ -1,18 +1,16 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
# Represents the spelling of a pitch, such as C# or Db.
|
4
|
-
# Composite of a LetterName and an optional
|
2
|
+
# Composite of a LetterName and an optional Alteration.
|
5
3
|
# Does not include the octave. See Pitch for that.
|
6
4
|
class HeadMusic::Spelling
|
7
|
-
MATCHER = /^\s*([A-G])(#{HeadMusic::
|
5
|
+
MATCHER = /^\s*([A-G])(#{HeadMusic::Alteration.matcher}?)(-?\d+)?\s*$/i
|
8
6
|
|
9
|
-
attr_reader :pitch_class, :letter_name, :
|
7
|
+
attr_reader :pitch_class, :letter_name, :alteration
|
10
8
|
|
11
9
|
delegate :number, to: :pitch_class, prefix: true
|
12
10
|
delegate :to_i, to: :pitch_class_number
|
13
11
|
delegate :series_ascending, :series_descending, to: :letter_name, prefix: true
|
14
12
|
delegate :enharmonic?, to: :enharmonic_equivalence
|
15
|
-
delegate :sharp?, :flat?, :double_sharp?, :double_flat?, to: :
|
13
|
+
delegate :sharp?, :flat?, :double_sharp?, :double_flat?, to: :alteration, allow_nil: true
|
16
14
|
|
17
15
|
def self.get(identifier)
|
18
16
|
return identifier if identifier.is_a?(HeadMusic::Spelling)
|
@@ -31,8 +29,8 @@ class HeadMusic::Spelling
|
|
31
29
|
letter_name = HeadMusic::LetterName.get(letter_name)
|
32
30
|
return nil unless letter_name
|
33
31
|
|
34
|
-
|
35
|
-
fetch_or_create(letter_name,
|
32
|
+
alteration = HeadMusic::Alteration.get(sign_string)
|
33
|
+
fetch_or_create(letter_name, alteration)
|
36
34
|
end
|
37
35
|
|
38
36
|
def self.from_number(number)
|
@@ -46,26 +44,26 @@ class HeadMusic::Spelling
|
|
46
44
|
def self.from_number_and_letter(number, letter_name)
|
47
45
|
letter_name = HeadMusic::LetterName.get(letter_name)
|
48
46
|
natural_letter_pitch_class = letter_name.pitch_class
|
49
|
-
|
50
|
-
|
51
|
-
fetch_or_create(letter_name,
|
47
|
+
alteration_interval = natural_letter_pitch_class.smallest_interval_to(HeadMusic::PitchClass.get(number))
|
48
|
+
alteration = HeadMusic::Alteration.by(:semitones, alteration_interval) if alteration_interval != 0
|
49
|
+
fetch_or_create(letter_name, alteration)
|
52
50
|
end
|
53
51
|
|
54
|
-
def self.fetch_or_create(letter_name,
|
52
|
+
def self.fetch_or_create(letter_name, alteration)
|
55
53
|
@spellings ||= {}
|
56
|
-
hash_key = [letter_name,
|
57
|
-
@spellings[hash_key] ||= new(letter_name,
|
54
|
+
hash_key = [letter_name, alteration].join
|
55
|
+
@spellings[hash_key] ||= new(letter_name, alteration)
|
58
56
|
end
|
59
57
|
|
60
|
-
def initialize(letter_name,
|
58
|
+
def initialize(letter_name, alteration = nil)
|
61
59
|
@letter_name = HeadMusic::LetterName.get(letter_name.to_s)
|
62
|
-
@
|
63
|
-
|
64
|
-
@pitch_class = HeadMusic::PitchClass.get(letter_name.pitch_class +
|
60
|
+
@alteration = HeadMusic::Alteration.get(alteration)
|
61
|
+
alteration_semitones = @alteration ? @alteration.semitones : 0
|
62
|
+
@pitch_class = HeadMusic::PitchClass.get(letter_name.pitch_class + alteration_semitones)
|
65
63
|
end
|
66
64
|
|
67
65
|
def name
|
68
|
-
[letter_name,
|
66
|
+
[letter_name, alteration].join
|
69
67
|
end
|
70
68
|
|
71
69
|
def to_s
|
@@ -82,7 +80,7 @@ class HeadMusic::Spelling
|
|
82
80
|
end
|
83
81
|
|
84
82
|
def natural?
|
85
|
-
!
|
83
|
+
!alteration || alteration.natural?
|
86
84
|
end
|
87
85
|
|
88
86
|
private_class_method :new
|
data/lib/head_music/staff.rb
CHANGED