head_music 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/head_music/clef.rb +54 -0
- data/lib/head_music/functional_interval.rb +2 -2
- data/lib/head_music/letter_name.rb +6 -8
- data/lib/head_music/note.rb +14 -0
- data/lib/head_music/pitch.rb +22 -6
- data/lib/head_music/rhythmic_unit.rb +59 -0
- data/lib/head_music/rhythmic_value.rb +38 -41
- data/lib/head_music/scale.rb +7 -7
- data/lib/head_music/spelling.rb +19 -20
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +3 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26fa77af760e9919b585bfb9db2e53d30b382ecf
|
4
|
+
data.tar.gz: 244d2c9e9d85800cfc0f897773e17d4a1410c885
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89befc698de10095285b790246b6102bcd6d1ca94c3cf50ea19ee39c9f60e00de277c954172a652e7af1209d3660d33e8d002111aa1c4a426e3be82a574d683e
|
7
|
+
data.tar.gz: 71e3d3c4ed45c6b7407d077fb500227d1a372518190bd3cc3400d8bcdb49aea803c2d11b8815c4568b44ef2e5b5a2cc0022e1bef9eabee0eedb7c6ab5d23ec22
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class HeadMusic::Clef
|
2
|
+
CLEFS = [
|
3
|
+
{ pitch: 'G4', line: 2, names: ['treble', 'G-clef'], modern: true },
|
4
|
+
{ pitch: 'G4', line: 1, names: ['French', 'French violin'] },
|
5
|
+
{ pitch: 'G3', line: 2, names: ['tenor'], modern: true },
|
6
|
+
|
7
|
+
{ pitch: 'F3', line: 3, names: ['baritone'] },
|
8
|
+
{ pitch: 'F3', line: 4, names: ['bass', 'F-clef'], modern: true },
|
9
|
+
{ pitch: 'F3', line: 5, names: ['sub-bass'] },
|
10
|
+
|
11
|
+
{ pitch: 'C4', line: 1, names: ['soprano'] },
|
12
|
+
{ pitch: 'C4', line: 2, names: ['mezzo-soprano'] },
|
13
|
+
{ pitch: 'C4', line: 3, names: ['alto', 'viola', 'counter-tenor', 'countertenor'], modern: true },
|
14
|
+
{ pitch: 'C4', line: 4, names: ['tenor'], modern: true },
|
15
|
+
{ pitch: 'C4', line: 5, names: ['baritone'] },
|
16
|
+
|
17
|
+
{ pitch: nil, line: 3, names: ['neutral', 'percussion'] }
|
18
|
+
]
|
19
|
+
|
20
|
+
def self.get(name)
|
21
|
+
name = name.to_s
|
22
|
+
@clefs ||= {}
|
23
|
+
@clefs[name.to_s.to_sym] = new(name)
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :name, :pitch, :line
|
27
|
+
|
28
|
+
def initialize(name)
|
29
|
+
@name = name.to_s
|
30
|
+
clef_data = CLEFS.detect { |clef| clef[:names].include?(name) }
|
31
|
+
@pitch = HeadMusic::Pitch.get(clef_data[:pitch])
|
32
|
+
@line = clef_data[:line]
|
33
|
+
end
|
34
|
+
|
35
|
+
def clef_type
|
36
|
+
"#{pitch.letter_name}-clef"
|
37
|
+
end
|
38
|
+
|
39
|
+
def line_pitch(line_number)
|
40
|
+
@line_pitches ||= {}
|
41
|
+
@line_pitches[line_number] ||= begin
|
42
|
+
steps = (line_number - line) * 2
|
43
|
+
pitch.natural_steps(steps)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def space_pitch(space_number)
|
48
|
+
@space_pitches ||= {}
|
49
|
+
@space_pitches[space_number] ||= begin
|
50
|
+
steps = (space_number - line) * 2 + 1
|
51
|
+
pitch.natural_steps(steps)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -33,7 +33,7 @@ class HeadMusic::FunctionalInterval
|
|
33
33
|
quality_name, degree_name = words[0..-2].join(' '), words.last
|
34
34
|
lower_pitch = HeadMusic::Pitch.get('C4')
|
35
35
|
steps = NUMBER_NAMES.index(degree_name)
|
36
|
-
higher_letter = lower_pitch.
|
36
|
+
higher_letter = lower_pitch.letter_name.steps(steps)
|
37
37
|
semitones = degree_quality_semitones.dig(degree_name.to_sym, quality_name.to_sym)
|
38
38
|
higher_pitch = HeadMusic::Pitch.from_number_and_letter(lower_pitch + semitones, higher_letter)
|
39
39
|
new(lower_pitch, higher_pitch)
|
@@ -70,7 +70,7 @@ class HeadMusic::FunctionalInterval
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def simple_number
|
73
|
-
@simple_number ||= @lower_pitch.
|
73
|
+
@simple_number ||= @lower_pitch.letter_name.steps_to(@higher_pitch.letter_name) + 1
|
74
74
|
end
|
75
75
|
|
76
76
|
def simple_semitones
|
@@ -1,7 +1,5 @@
|
|
1
1
|
class HeadMusic::LetterName
|
2
|
-
|
3
|
-
|
4
|
-
NAMES = ('A'..'G').to_a
|
2
|
+
NAMES = %w[C D E F G A B]
|
5
3
|
|
6
4
|
NATURAL_PITCH_CLASS_NUMBERS = {
|
7
5
|
'C' => 0,
|
@@ -23,18 +21,18 @@ class HeadMusic::LetterName
|
|
23
21
|
singleton_class.send(:alias_method, :[], :get)
|
24
22
|
|
25
23
|
def self.from_name(name)
|
26
|
-
@
|
24
|
+
@letter_names ||= {}
|
27
25
|
name = name.to_s.first.upcase
|
28
|
-
@
|
26
|
+
@letter_names[name] ||= new(name) if NAMES.include?(name)
|
29
27
|
end
|
30
28
|
|
31
29
|
def self.from_pitch_class(pitch_class)
|
32
|
-
@
|
30
|
+
@letter_names ||= {}
|
33
31
|
return nil if pitch_class.to_s == pitch_class
|
34
32
|
pitch_class = pitch_class.to_i % 12
|
35
33
|
name = NAMES.detect { |name| pitch_class == NATURAL_PITCH_CLASS_NUMBERS[name] }
|
36
34
|
name ||= HeadMusic::PitchClass::PREFERRED_SPELLINGS[pitch_class].first
|
37
|
-
@
|
35
|
+
@letter_names[name] ||= new(name) if NAMES.include?(name)
|
38
36
|
end
|
39
37
|
|
40
38
|
attr_reader :name
|
@@ -60,7 +58,7 @@ class HeadMusic::LetterName
|
|
60
58
|
end
|
61
59
|
|
62
60
|
def steps(num)
|
63
|
-
cycle[num]
|
61
|
+
HeadMusic::LetterName.get(cycle[num % NAMES.length])
|
64
62
|
end
|
65
63
|
|
66
64
|
def steps_to(other, direction = :ascending)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class HeadMusic::Note
|
2
|
+
attr_reader :pitch, :rhythmic_value
|
3
|
+
|
4
|
+
delegate :ticks, to: :rhythmic_value
|
5
|
+
|
6
|
+
def initialize(pitch, rhythmic_unit, rhythmic_value_modifiers = {})
|
7
|
+
@pitch = HeadMusic::Pitch.get(pitch)
|
8
|
+
@rhythmic_value = HeadMusic::RhythmicValue.new(rhythmic_unit, rhythmic_value_modifiers)
|
9
|
+
end
|
10
|
+
|
11
|
+
def duration
|
12
|
+
rhythmic_value.total_value
|
13
|
+
end
|
14
|
+
end
|
data/lib/head_music/pitch.rb
CHANGED
@@ -4,7 +4,7 @@ class HeadMusic::Pitch
|
|
4
4
|
attr_reader :spelling
|
5
5
|
attr_reader :octave
|
6
6
|
|
7
|
-
delegate :
|
7
|
+
delegate :letter_name, :letter_name_cycle, to: :spelling
|
8
8
|
delegate :accidental, :sharp?, :flat?, to: :spelling
|
9
9
|
delegate :pitch_class, to: :spelling
|
10
10
|
delegate :semitones, to: :accidental, prefix: true, allow_nil: true
|
@@ -28,14 +28,14 @@ class HeadMusic::Pitch
|
|
28
28
|
fetch_or_create(spelling, octave)
|
29
29
|
end
|
30
30
|
|
31
|
-
def self.from_number_and_letter(number,
|
32
|
-
|
33
|
-
natural_letter_pitch = get(HeadMusic::LetterName.get(
|
31
|
+
def self.from_number_and_letter(number, letter_name)
|
32
|
+
letter_name = HeadMusic::LetterName.get(letter_name)
|
33
|
+
natural_letter_pitch = get(HeadMusic::LetterName.get(letter_name).pitch_class)
|
34
34
|
natural_letter_pitch += 12 while (number - natural_letter_pitch.to_i).to_i >= 11
|
35
35
|
natural_letter_pitch = get(natural_letter_pitch)
|
36
36
|
accidental_interval = natural_letter_pitch.smallest_interval_to(HeadMusic::PitchClass.get(number))
|
37
37
|
accidental = HeadMusic::Accidental.for_interval(accidental_interval)
|
38
|
-
spelling = HeadMusic::Spelling.fetch_or_create(
|
38
|
+
spelling = HeadMusic::Spelling.fetch_or_create(letter_name, accidental)
|
39
39
|
fetch_or_create(spelling, natural_letter_pitch.octave)
|
40
40
|
end
|
41
41
|
|
@@ -57,7 +57,7 @@ class HeadMusic::Pitch
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def midi_note_number
|
60
|
-
(octave + 1) * 12 +
|
60
|
+
(octave + 1) * 12 + letter_name.pitch_class.to_i + accidental_semitones.to_i
|
61
61
|
end
|
62
62
|
|
63
63
|
alias_method :midi, :midi_note_number
|
@@ -71,6 +71,10 @@ class HeadMusic::Pitch
|
|
71
71
|
midi_note_number
|
72
72
|
end
|
73
73
|
|
74
|
+
def natural
|
75
|
+
HeadMusic::Pitch.get(self.to_s.gsub(/[#b]/, ''))
|
76
|
+
end
|
77
|
+
|
74
78
|
def enharmonic?(other)
|
75
79
|
self.midi_note_number == other.midi_note_number
|
76
80
|
end
|
@@ -101,5 +105,17 @@ class HeadMusic::Pitch
|
|
101
105
|
HeadMusic::Scale.get(self, scale_type_name)
|
102
106
|
end
|
103
107
|
|
108
|
+
def natural_steps(num_steps)
|
109
|
+
target_letter_name = self.letter_name.steps(num_steps)
|
110
|
+
direction = num_steps >= 0 ? 1 : -1
|
111
|
+
octaves_delta = (num_steps.abs / 7) * direction
|
112
|
+
if num_steps < 0 && target_letter_name.position > letter_name.position
|
113
|
+
octaves_delta -= 1
|
114
|
+
elsif num_steps > 0 && target_letter_name.position < letter_name.position
|
115
|
+
octaves_delta += 1
|
116
|
+
end
|
117
|
+
HeadMusic::Pitch.get([target_letter_name, octave + octaves_delta].join)
|
118
|
+
end
|
119
|
+
|
104
120
|
private_class_method :new
|
105
121
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class HeadMusic::RhythmicUnit
|
2
|
+
MULTIPLES = ['whole', 'double whole', 'longa', 'maxima']
|
3
|
+
DIVISIONS = ['whole', 'half', 'quarter', 'eighth', 'sixteenth', 'thirty-second', 'sixty-fourth', 'hundred twenty-eighth', 'two hundred fifty-sixth']
|
4
|
+
|
5
|
+
BRITISH_MULTIPLE_NAMES = %w[semibreve breve longa maxima]
|
6
|
+
BRITISH_DIVISION_NAMES = %w[semibreve minim crotchet quaver semiquaver demisemiquaver hemidemisemiquaver semihemidemisemiquaver demisemihemidemisemiquaver]
|
7
|
+
|
8
|
+
def self.get(name)
|
9
|
+
@rhythmic_units ||= {}
|
10
|
+
@rhythmic_units[name.to_s] ||= new(name.to_s)
|
11
|
+
end
|
12
|
+
singleton_class.send(:alias_method, :[], :get)
|
13
|
+
|
14
|
+
attr_reader :name, :numerator, :denominator
|
15
|
+
delegate :to_s, to: :name
|
16
|
+
|
17
|
+
def initialize(canonical_name)
|
18
|
+
@name ||= canonical_name
|
19
|
+
@numerator ||= MULTIPLES.include?(name) ? 2**MULTIPLES.index(name) : 1
|
20
|
+
@denominator ||= DIVISIONS.include?(name) ? 2**DIVISIONS.index(name) : 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def relative_value
|
24
|
+
@numerator.to_f / @denominator
|
25
|
+
end
|
26
|
+
|
27
|
+
def notehead
|
28
|
+
case relative_value
|
29
|
+
when 8
|
30
|
+
:maxima
|
31
|
+
when 4
|
32
|
+
:longa
|
33
|
+
when 2
|
34
|
+
:breve
|
35
|
+
when 0.5, 1
|
36
|
+
:open
|
37
|
+
else
|
38
|
+
:closed
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def flags
|
43
|
+
DIVISIONS.include?(name) ? [DIVISIONS.index(name) - 2, 0].max : 0
|
44
|
+
end
|
45
|
+
|
46
|
+
def has_stem?
|
47
|
+
relative_value < 1
|
48
|
+
end
|
49
|
+
|
50
|
+
def british_name
|
51
|
+
if MULTIPLES.include?(name)
|
52
|
+
BRITISH_MULTIPLE_NAMES[MULTIPLES.index(name)]
|
53
|
+
elsif DIVISIONS.include?(name)
|
54
|
+
BRITISH_DIVISION_NAMES[DIVISIONS.index(name)]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private_class_method :new
|
59
|
+
end
|
@@ -1,63 +1,60 @@
|
|
1
1
|
class HeadMusic::RhythmicValue
|
2
|
-
|
3
|
-
DIVISIONS = ['whole', 'half', 'quarter', 'eighth', 'sixteenth', 'thirty-second', 'sixty-fourth', 'hundred twenty-eighth note', 'two hundred fifty-sixth note']
|
2
|
+
PPQN = PULSES_PER_QUARTER_NOTE = 960
|
4
3
|
|
5
|
-
|
6
|
-
BRITISH_DIVISION_NAMES = %w[semibreve minim crotchet quaver semiquaver demisemiquaver hemidemisemiquaver semihemidemisemiquaver demisemihemidemisemiquaver]
|
4
|
+
attr_reader :unit, :dots, :tied_value
|
7
5
|
|
8
|
-
|
9
|
-
@rhythmic_values ||= {}
|
10
|
-
@rhythmic_values[name.to_s] ||= new(name.to_s)
|
11
|
-
end
|
12
|
-
singleton_class.send(:alias_method, :[], :get)
|
6
|
+
delegate :name, to: :unit, prefix: true
|
13
7
|
|
14
|
-
|
15
|
-
|
16
|
-
|
8
|
+
def initialize(unit, dots: nil, tied_value: nil)
|
9
|
+
@unit = HeadMusic::RhythmicUnit.get(unit)
|
10
|
+
@dots = [0, 1, 2, 3].include?(dots) ? dots : 0
|
11
|
+
@tied_value = tied_value
|
12
|
+
end
|
17
13
|
|
18
|
-
def
|
19
|
-
|
14
|
+
def unit_value
|
15
|
+
unit.relative_value
|
20
16
|
end
|
21
17
|
|
22
18
|
def relative_value
|
23
|
-
|
24
|
-
if MULTIPLES.include?(name)
|
25
|
-
1.0 * 2**MULTIPLES.index(name)
|
26
|
-
elsif DIVISIONS.include?(name)
|
27
|
-
1.0 / 2**DIVISIONS.index(name)
|
28
|
-
end
|
19
|
+
unit_value * multiplier
|
29
20
|
end
|
30
21
|
|
31
|
-
def
|
32
|
-
|
33
|
-
if MULTIPLES.include?(name)
|
34
|
-
1.0 / 2**MULTIPLES.index(name)
|
35
|
-
elsif DIVISIONS.include?(name)
|
36
|
-
1.0 * 2**DIVISIONS.index(name)
|
37
|
-
end
|
22
|
+
def total_value
|
23
|
+
relative_value + (tied_value ? tied_value.total_value : 0)
|
38
24
|
end
|
39
25
|
|
40
|
-
def
|
41
|
-
|
42
|
-
return :open if relative_value >= 0.5
|
43
|
-
:closed
|
26
|
+
def multiplier
|
27
|
+
(0..dots).reduce(0) { |sum, i| sum += (1.0/2)**i }
|
44
28
|
end
|
45
29
|
|
46
|
-
def
|
47
|
-
|
30
|
+
def ticks
|
31
|
+
PPQN * 4 * total_value
|
48
32
|
end
|
49
33
|
|
50
|
-
def
|
51
|
-
|
34
|
+
def per_whole
|
35
|
+
1.0 / relative_value
|
52
36
|
end
|
53
37
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
38
|
+
def name_modifier_prefix
|
39
|
+
case dots
|
40
|
+
when 1
|
41
|
+
'dotted'
|
42
|
+
when 2
|
43
|
+
'double-dotted'
|
44
|
+
when 3
|
45
|
+
'triple-dotted'
|
59
46
|
end
|
60
47
|
end
|
61
48
|
|
62
|
-
|
49
|
+
def single_value_name
|
50
|
+
[name_modifier_prefix, unit_name].reject(&:nil?).join(' ')
|
51
|
+
end
|
52
|
+
|
53
|
+
def name
|
54
|
+
if tied_value
|
55
|
+
[single_value_name, tied_value.name].reject(&:nil?).join(' tied to ')
|
56
|
+
else
|
57
|
+
single_value_name
|
58
|
+
end
|
59
|
+
end
|
63
60
|
end
|
data/lib/head_music/scale.rb
CHANGED
@@ -19,7 +19,7 @@ class HeadMusic::Scale
|
|
19
19
|
@pitches ||= {}
|
20
20
|
@pitches[direction] ||= {}
|
21
21
|
@pitches[direction][octaves] ||= begin
|
22
|
-
|
22
|
+
letter_name_cycle = root_pitch.letter_name_cycle
|
23
23
|
semitones_from_root = 0
|
24
24
|
[root_pitch].tap do |pitches|
|
25
25
|
if [:ascending, :both].include?(direction)
|
@@ -50,8 +50,8 @@ class HeadMusic::Scale
|
|
50
50
|
pitches(direction: direction, octaves: octaves).map(&:name)
|
51
51
|
end
|
52
52
|
|
53
|
-
def
|
54
|
-
@
|
53
|
+
def letter_name_cycle
|
54
|
+
@letter_name_cycle ||= root_pitch.letter_name_cycle
|
55
55
|
end
|
56
56
|
|
57
57
|
def root_pitch_number
|
@@ -77,9 +77,9 @@ class HeadMusic::Scale
|
|
77
77
|
def letter_for_step(step, semitones_from_root, direction)
|
78
78
|
pitch_class_number = (root_pitch.pitch_class.to_i + semitones_from_root) % 12
|
79
79
|
if scale_type.intervals.length == 7
|
80
|
-
direction == :ascending ?
|
80
|
+
direction == :ascending ? letter_name_cycle[step % 7] : letter_name_cycle[-step % 7]
|
81
81
|
elsif scale_type.intervals.length < 7 && parent_scale_pitches
|
82
|
-
parent_scale_pitch_for(semitones_from_root).
|
82
|
+
parent_scale_pitch_for(semitones_from_root).letter_name
|
83
83
|
elsif root_pitch.flat?
|
84
84
|
HeadMusic::PitchClass::FLAT_SPELLINGS[pitch_class_number]
|
85
85
|
else
|
@@ -89,7 +89,7 @@ class HeadMusic::Scale
|
|
89
89
|
|
90
90
|
def pitch_for_step(step, semitones_from_root, direction)
|
91
91
|
pitch_number = root_pitch_number + semitones_from_root
|
92
|
-
|
93
|
-
HeadMusic::Pitch.from_number_and_letter(pitch_number,
|
92
|
+
letter_name = letter_for_step(step, semitones_from_root, direction)
|
93
|
+
HeadMusic::Pitch.from_number_and_letter(pitch_number, letter_name)
|
94
94
|
end
|
95
95
|
end
|
data/lib/head_music/spelling.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
|
-
# A Spelling is a pitch class with a letter name and possibly an accidental
|
2
1
|
class HeadMusic::Spelling
|
3
2
|
MATCHER = /^\s*([A-G])([b#]*)(\-?\d+)?\s*$/
|
4
3
|
|
5
4
|
attr_reader :pitch_class
|
6
|
-
attr_reader :
|
5
|
+
attr_reader :letter_name
|
7
6
|
attr_reader :accidental
|
8
7
|
|
9
8
|
delegate :number, to: :pitch_class, prefix: true
|
10
9
|
delegate :to_i, to: :pitch_class_number
|
11
|
-
delegate :cycle, to: :
|
10
|
+
delegate :cycle, to: :letter_name, prefix: true
|
12
11
|
delegate :enharmonic?, to: :pitch_class
|
13
12
|
|
14
13
|
def self.get(identifier)
|
@@ -23,43 +22,43 @@ class HeadMusic::Spelling
|
|
23
22
|
def self.from_name(name)
|
24
23
|
if match(name)
|
25
24
|
letter_name, accidental_string, _octave = match(name).captures
|
26
|
-
|
27
|
-
return nil unless
|
25
|
+
letter_name = HeadMusic::LetterName.get(letter_name)
|
26
|
+
return nil unless letter_name
|
28
27
|
accidental = HeadMusic::Accidental.get(accidental_string)
|
29
|
-
fetch_or_create(
|
28
|
+
fetch_or_create(letter_name, accidental)
|
30
29
|
end
|
31
30
|
end
|
32
31
|
|
33
32
|
def self.from_number(number)
|
34
33
|
return nil unless number == number.to_i
|
35
34
|
pitch_class_number = number.to_i % 12
|
36
|
-
|
37
|
-
from_number_and_letter(number,
|
35
|
+
letter_name = HeadMusic::LetterName.from_pitch_class(pitch_class_number)
|
36
|
+
from_number_and_letter(number, letter_name)
|
38
37
|
end
|
39
38
|
|
40
|
-
def self.from_number_and_letter(number,
|
41
|
-
|
42
|
-
natural_letter_pitch_class = HeadMusic::LetterName.get(
|
43
|
-
accidental_interval =
|
39
|
+
def self.from_number_and_letter(number, letter_name)
|
40
|
+
letter_name = HeadMusic::LetterName.get(letter_name)
|
41
|
+
natural_letter_pitch_class = HeadMusic::LetterName.get(letter_name).pitch_class
|
42
|
+
accidental_interval = letter_name.pitch_class.smallest_interval_to(HeadMusic::PitchClass.get(number))
|
44
43
|
accidental = HeadMusic::Accidental.for_interval(accidental_interval)
|
45
|
-
fetch_or_create(
|
44
|
+
fetch_or_create(letter_name, accidental)
|
46
45
|
end
|
47
46
|
|
48
|
-
def self.fetch_or_create(
|
47
|
+
def self.fetch_or_create(letter_name, accidental)
|
49
48
|
@spellings ||= {}
|
50
|
-
key = [
|
51
|
-
@spellings[key] ||= new(
|
49
|
+
key = [letter_name, accidental].join
|
50
|
+
@spellings[key] ||= new(letter_name, accidental)
|
52
51
|
end
|
53
52
|
|
54
|
-
def initialize(
|
55
|
-
@
|
53
|
+
def initialize(letter_name, accidental = nil)
|
54
|
+
@letter_name = HeadMusic::LetterName.get(letter_name.to_s)
|
56
55
|
@accidental = HeadMusic::Accidental.get(accidental.to_s)
|
57
56
|
accidental_semitones = @accidental ? @accidental.semitones : 0
|
58
|
-
@pitch_class = HeadMusic::PitchClass.get(
|
57
|
+
@pitch_class = HeadMusic::PitchClass.get(letter_name.pitch_class + accidental_semitones)
|
59
58
|
end
|
60
59
|
|
61
60
|
def name
|
62
|
-
[
|
61
|
+
[letter_name, accidental].join
|
63
62
|
end
|
64
63
|
|
65
64
|
def to_s
|
data/lib/head_music/version.rb
CHANGED
data/lib/head_music.rb
CHANGED
@@ -6,16 +6,19 @@ require 'humanize'
|
|
6
6
|
|
7
7
|
require 'head_music/accidental'
|
8
8
|
require 'head_music/circle'
|
9
|
+
require 'head_music/clef'
|
9
10
|
require 'head_music/consonance'
|
10
11
|
require 'head_music/functional_interval'
|
11
12
|
require 'head_music/interval'
|
12
13
|
require 'head_music/key_signature'
|
13
14
|
require 'head_music/letter_name'
|
14
15
|
require 'head_music/meter'
|
16
|
+
require 'head_music/note'
|
15
17
|
require 'head_music/octave'
|
16
18
|
require 'head_music/pitch_class'
|
17
19
|
require 'head_music/pitch'
|
18
20
|
require 'head_music/quality'
|
21
|
+
require 'head_music/rhythmic_unit'
|
19
22
|
require 'head_music/rhythmic_value'
|
20
23
|
require 'head_music/scale'
|
21
24
|
require 'head_music/scale_type'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: head_music
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Head
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-02-
|
11
|
+
date: 2017-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -116,16 +116,19 @@ files:
|
|
116
116
|
- lib/head_music.rb
|
117
117
|
- lib/head_music/accidental.rb
|
118
118
|
- lib/head_music/circle.rb
|
119
|
+
- lib/head_music/clef.rb
|
119
120
|
- lib/head_music/consonance.rb
|
120
121
|
- lib/head_music/functional_interval.rb
|
121
122
|
- lib/head_music/interval.rb
|
122
123
|
- lib/head_music/key_signature.rb
|
123
124
|
- lib/head_music/letter_name.rb
|
124
125
|
- lib/head_music/meter.rb
|
126
|
+
- lib/head_music/note.rb
|
125
127
|
- lib/head_music/octave.rb
|
126
128
|
- lib/head_music/pitch.rb
|
127
129
|
- lib/head_music/pitch_class.rb
|
128
130
|
- lib/head_music/quality.rb
|
131
|
+
- lib/head_music/rhythmic_unit.rb
|
129
132
|
- lib/head_music/rhythmic_value.rb
|
130
133
|
- lib/head_music/scale.rb
|
131
134
|
- lib/head_music/scale_type.rb
|