head_music 0.25.0 → 0.27.0
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.rb +3 -2
- data/lib/head_music/data/instruments.yml +113 -51
- data/lib/head_music/diatonic_interval.rb +1 -1
- data/lib/head_music/instrument.rb +2 -3
- data/lib/head_music/locales/de.yml +1 -0
- data/lib/head_music/locales/en.yml +1 -0
- data/lib/head_music/locales/es.yml +1 -0
- data/lib/head_music/locales/fr.yml +1 -0
- data/lib/head_music/locales/it.yml +1 -0
- data/lib/head_music/locales/ru.yml +1 -0
- data/lib/head_music/pitch.rb +29 -16
- data/lib/head_music/pitch/octave_equivalence.rb +1 -1
- data/lib/head_music/pitch_set.rb +1 -1
- data/lib/head_music/register.rb +71 -0
- data/lib/head_music/version.rb +1 -1
- metadata +3 -3
- data/lib/head_music/octave.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4274cb71a70fcab6904e99afc282bb1db756e2a1cce0c1e1e50c601e0e848edf
|
4
|
+
data.tar.gz: 47566a6db5b0836686fd789cf2e0b052416a68f8f2ed67b2f6582266a141ea1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b85d9dbd114b70bfea1fa9ce8cb964f6780ad9f64325a75baba02df48e416475f33e26a9dab22309740ff6e7f033fca756906a4fd5935af1f22e6fd325a775d4
|
7
|
+
data.tar.gz: 60a75acc692a1e1014f6042f9b167ecd7a0f6ec8eb98ca5dccb4c16a9bbcc79deff0b56481a713941caad779b7cc17b8487c89f4b4839bb31e3b453cff7558b5
|
data/lib/head_music.rb
CHANGED
@@ -17,7 +17,7 @@ require 'i18n/backend/fallbacks'
|
|
17
17
|
|
18
18
|
I18n::Backend::Simple.include I18n::Backend::Fallbacks
|
19
19
|
I18n.load_path << Dir[File.join(File.dirname(__dir__), 'lib', 'head_music', 'locales', '*.yml')]
|
20
|
-
I18n.config.available_locales = %i[en de
|
20
|
+
I18n.config.available_locales = %i[en fr de it ru es en_US en_GB]
|
21
21
|
I18n.default_locale = :en
|
22
22
|
I18n.fallbacks[:de] = %i[de en_GB en]
|
23
23
|
I18n.fallbacks[:en_US] = %i[en_US en en_GB]
|
@@ -25,6 +25,7 @@ I18n.fallbacks[:en_GB] = %i[en_GB en en_US]
|
|
25
25
|
I18n.fallbacks[:es] = %i[es en]
|
26
26
|
I18n.fallbacks[:fr] = %i[fr en_GB en]
|
27
27
|
I18n.fallbacks[:it] = %i[it en_GB en]
|
28
|
+
I18n.fallbacks[:ru] = %i[ru en_GB en]
|
28
29
|
|
29
30
|
# utilities
|
30
31
|
require 'head_music/utilities/hash_key'
|
@@ -48,7 +49,6 @@ require 'head_music/melodic_interval'
|
|
48
49
|
require 'head_music/meter'
|
49
50
|
require 'head_music/motion'
|
50
51
|
require 'head_music/musical_symbol'
|
51
|
-
require 'head_music/octave'
|
52
52
|
require 'head_music/pitch'
|
53
53
|
require 'head_music/pitch/enharmonic_equivalence'
|
54
54
|
require 'head_music/pitch/octave_equivalence'
|
@@ -57,6 +57,7 @@ require 'head_music/pitch_class_set'
|
|
57
57
|
require 'head_music/pitch_set'
|
58
58
|
require 'head_music/quality'
|
59
59
|
require 'head_music/reference_pitch'
|
60
|
+
require 'head_music/register'
|
60
61
|
require 'head_music/rhythm'
|
61
62
|
require 'head_music/rhythmic_unit'
|
62
63
|
require 'head_music/scale'
|
@@ -1,83 +1,145 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
family: string
|
4
|
-
classifications:
|
5
|
-
- string
|
6
|
-
- chordophone
|
7
|
-
default_clefs:
|
8
|
-
- treble_clef
|
9
|
-
voice: tenor
|
10
|
-
piano:
|
11
|
-
family: string
|
2
|
+
bassoon:
|
12
3
|
classifications:
|
13
|
-
-
|
14
|
-
-
|
15
|
-
-
|
16
|
-
|
17
|
-
-
|
4
|
+
- aerophone
|
5
|
+
- reed
|
6
|
+
- double_reed
|
7
|
+
- wind
|
8
|
+
- woodwind
|
9
|
+
family: woodwind
|
10
|
+
standard_staves:
|
18
11
|
- bass_clef
|
19
|
-
|
20
|
-
family: string
|
12
|
+
cello:
|
21
13
|
alias_name_keys:
|
22
|
-
-
|
14
|
+
- violoncello
|
23
15
|
classifications:
|
24
|
-
- string
|
25
|
-
- chordophone
|
26
16
|
- bowed
|
27
|
-
default_clefs:
|
28
|
-
- treble_clef
|
29
|
-
voice: soprano
|
30
|
-
viola:
|
31
|
-
family: string
|
32
|
-
classifications:
|
33
|
-
- string
|
34
17
|
- chordophone
|
35
|
-
- bowed
|
36
|
-
default_clefs:
|
37
|
-
- alto_clef
|
38
|
-
voice: alto
|
39
|
-
cello:
|
40
|
-
family: string
|
41
|
-
classifications:
|
42
18
|
- string
|
43
|
-
|
44
|
-
|
45
|
-
default_clefs:
|
19
|
+
family: string
|
20
|
+
standard_staves:
|
46
21
|
- bass_clef
|
47
|
-
|
22
|
+
clarinet:
|
23
|
+
classifications:
|
24
|
+
- aerophone
|
25
|
+
- reed
|
26
|
+
- single_reed
|
27
|
+
- wind
|
28
|
+
- woodwind
|
29
|
+
family: woodwind
|
30
|
+
standard_staves:
|
31
|
+
- treble_clef
|
48
32
|
double_bass:
|
49
|
-
family: string
|
50
33
|
classifications:
|
51
34
|
- string
|
52
35
|
- chordophone
|
53
36
|
- bowed
|
54
|
-
|
37
|
+
family: string
|
38
|
+
standard_staves:
|
55
39
|
- bass_clef
|
56
|
-
|
40
|
+
flute:
|
41
|
+
classifications:
|
42
|
+
- aerophone
|
43
|
+
- edge-blown aerophone
|
44
|
+
- wind
|
45
|
+
- woodwind
|
46
|
+
family: woodwind
|
47
|
+
standard_staves:
|
48
|
+
- treble_clef
|
49
|
+
french_horn:
|
50
|
+
alias_name_keys:
|
51
|
+
- horn
|
52
|
+
classifications:
|
53
|
+
- aerophone
|
54
|
+
- brass
|
55
|
+
- wind
|
56
|
+
family: brass
|
57
|
+
standard_staves:
|
58
|
+
- treble_clef
|
59
|
+
guitar:
|
60
|
+
classifications:
|
61
|
+
- chordophone
|
62
|
+
- plucked
|
63
|
+
- string
|
64
|
+
family: string
|
65
|
+
standard_staves:
|
66
|
+
- treble_clef
|
57
67
|
harp:
|
58
68
|
family: string
|
59
69
|
classifications:
|
60
|
-
- string
|
61
70
|
- chordophone
|
62
71
|
- plucked
|
63
|
-
|
72
|
+
- string
|
73
|
+
standard_staves:
|
64
74
|
- treble_clef
|
65
75
|
- bass_clef
|
66
|
-
|
67
|
-
family: woodwind
|
76
|
+
oboe:
|
68
77
|
classifications:
|
69
|
-
- woodwind
|
70
|
-
- wind
|
71
78
|
- aerophone
|
72
|
-
-
|
73
|
-
|
79
|
+
- reed
|
80
|
+
- double_reed
|
81
|
+
- wind
|
82
|
+
- woodwind
|
83
|
+
family: woodwind
|
84
|
+
standard_staves:
|
74
85
|
- treble_clef
|
75
|
-
voice: soprano
|
76
86
|
organ:
|
77
87
|
classifications:
|
78
88
|
- aerophone
|
79
89
|
- keyboard
|
80
|
-
|
90
|
+
standard_staves:
|
91
|
+
- treble_clef
|
92
|
+
- bass_clef
|
93
|
+
- bass_clef
|
94
|
+
piano:
|
95
|
+
classifications:
|
96
|
+
- chordophone
|
97
|
+
- keyboard
|
98
|
+
- percussion
|
99
|
+
- string
|
100
|
+
family: percussion
|
101
|
+
standard_staves:
|
81
102
|
- treble_clef
|
82
103
|
- bass_clef
|
104
|
+
trombone:
|
105
|
+
classifications:
|
106
|
+
- aerophone
|
107
|
+
- brass
|
108
|
+
- wind
|
109
|
+
family: brass
|
110
|
+
standard_staves:
|
111
|
+
- bass_clef
|
112
|
+
trumpet:
|
113
|
+
classifications:
|
114
|
+
- aerophone
|
115
|
+
- brass
|
116
|
+
- wind
|
117
|
+
family: brass
|
118
|
+
standard_staves:
|
119
|
+
- treble_clef
|
120
|
+
tuba:
|
121
|
+
classifications:
|
122
|
+
- aerophone
|
123
|
+
- brass
|
124
|
+
- wind
|
125
|
+
family: brass
|
126
|
+
standard_staves:
|
83
127
|
- bass_clef
|
128
|
+
viola:
|
129
|
+
family: string
|
130
|
+
classifications:
|
131
|
+
- bowed
|
132
|
+
- chordophone
|
133
|
+
- string
|
134
|
+
standard_staves:
|
135
|
+
- alto_clef
|
136
|
+
violin:
|
137
|
+
alias_name_keys:
|
138
|
+
- fiddle
|
139
|
+
classifications:
|
140
|
+
- bowed
|
141
|
+
- chordophone
|
142
|
+
- string
|
143
|
+
family: string
|
144
|
+
standard_staves:
|
145
|
+
- treble_clef
|
@@ -304,7 +304,7 @@ class HeadMusic::DiatonicInterval
|
|
304
304
|
def inversion
|
305
305
|
inverted_low_pitch = lower_pitch
|
306
306
|
while inverted_low_pitch < higher_pitch
|
307
|
-
inverted_low_pitch = HeadMusic::Pitch.fetch_or_create(lower_pitch.spelling, inverted_low_pitch.
|
307
|
+
inverted_low_pitch = HeadMusic::Pitch.fetch_or_create(lower_pitch.spelling, inverted_low_pitch.register + 1)
|
308
308
|
end
|
309
309
|
HeadMusic::DiatonicInterval.new(higher_pitch, inverted_low_pitch)
|
310
310
|
end
|
@@ -17,7 +17,7 @@ class HeadMusic::Instrument
|
|
17
17
|
INSTRUMENTS.map { |key, _data| get(key) }.sort_by(&:name)
|
18
18
|
end
|
19
19
|
|
20
|
-
attr_reader :name_key, :family, :
|
20
|
+
attr_reader :name_key, :family, :standard_staves, :classifications
|
21
21
|
|
22
22
|
def ==(other)
|
23
23
|
to_s == other.to_s
|
@@ -67,9 +67,8 @@ class HeadMusic::Instrument
|
|
67
67
|
def initialize_data_from_record(record)
|
68
68
|
@name_key = record['name_key'].to_sym
|
69
69
|
@family = record['family']
|
70
|
-
@
|
70
|
+
@standard_staves = record['standard_staves'] || []
|
71
71
|
@classifications = record['classifications'] || []
|
72
|
-
@voice = record['voice'].to_s
|
73
72
|
self.name = I18n.translate(name_key, scope: 'instruments', locale: 'en', default: inferred_name)
|
74
73
|
end
|
75
74
|
|
data/lib/head_music/pitch.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# A pitch is a named frequency represented by a spelling and
|
3
|
+
# A pitch is a named frequency represented by a spelling and a register.
|
4
4
|
class HeadMusic::Pitch
|
5
5
|
include Comparable
|
6
6
|
|
7
|
-
attr_reader :spelling
|
8
|
-
attr_reader :octave
|
7
|
+
attr_reader :spelling, :register
|
9
8
|
|
10
9
|
delegate :letter_name, to: :spelling
|
11
10
|
delegate :series_ascending, :series_descending, to: :letter_name, prefix: true
|
@@ -51,7 +50,7 @@ class HeadMusic::Pitch
|
|
51
50
|
def self.from_name(name)
|
52
51
|
return nil unless name == name.to_s
|
53
52
|
|
54
|
-
fetch_or_create(HeadMusic::Spelling.get(name), HeadMusic::
|
53
|
+
fetch_or_create(HeadMusic::Spelling.get(name), HeadMusic::Register.get(name).to_i)
|
55
54
|
end
|
56
55
|
|
57
56
|
def self.from_number(number)
|
@@ -66,7 +65,7 @@ class HeadMusic::Pitch
|
|
66
65
|
sign_interval = natural_letter_pitch.smallest_interval_to(HeadMusic::PitchClass.get(number))
|
67
66
|
sign = HeadMusic::Sign.by(:semitones, sign_interval) if sign_interval != 0
|
68
67
|
spelling = HeadMusic::Spelling.fetch_or_create(letter_name, sign)
|
69
|
-
fetch_or_create(spelling, natural_letter_pitch.
|
68
|
+
fetch_or_create(spelling, natural_letter_pitch.register)
|
70
69
|
end
|
71
70
|
|
72
71
|
def self.natural_letter_pitch(number, letter_name)
|
@@ -76,26 +75,26 @@ class HeadMusic::Pitch
|
|
76
75
|
get(natural_letter_pitch)
|
77
76
|
end
|
78
77
|
|
79
|
-
def self.fetch_or_create(spelling,
|
80
|
-
|
81
|
-
return unless spelling && (-1..9).cover?(
|
78
|
+
def self.fetch_or_create(spelling, register = nil)
|
79
|
+
register ||= HeadMusic::Register::DEFAULT
|
80
|
+
return unless spelling && (-1..9).cover?(register)
|
82
81
|
|
83
82
|
@pitches ||= {}
|
84
|
-
hash_key = [spelling,
|
85
|
-
@pitches[hash_key] ||= new(spelling,
|
83
|
+
hash_key = [spelling, register].join
|
84
|
+
@pitches[hash_key] ||= new(spelling, register)
|
86
85
|
end
|
87
86
|
|
88
|
-
def initialize(spelling,
|
87
|
+
def initialize(spelling, register)
|
89
88
|
@spelling = HeadMusic::Spelling.get(spelling.to_s)
|
90
|
-
@
|
89
|
+
@register = register.to_i
|
91
90
|
end
|
92
91
|
|
93
92
|
def name
|
94
|
-
[spelling,
|
93
|
+
[spelling, register].join
|
95
94
|
end
|
96
95
|
|
97
96
|
def midi_note_number
|
98
|
-
(
|
97
|
+
(register + 1) * 12 + letter_name.pitch_class.to_i + sign_semitones.to_i
|
99
98
|
end
|
100
99
|
|
101
100
|
alias midi midi_note_number
|
@@ -109,6 +108,10 @@ class HeadMusic::Pitch
|
|
109
108
|
midi_note_number
|
110
109
|
end
|
111
110
|
|
111
|
+
def helmholtz_notation
|
112
|
+
helmholtz_letter_name + helmholtz_marks
|
113
|
+
end
|
114
|
+
|
112
115
|
def natural
|
113
116
|
HeadMusic::Pitch.get(to_s.gsub(HeadMusic::Sign.matcher, ''))
|
114
117
|
end
|
@@ -150,7 +153,7 @@ class HeadMusic::Pitch
|
|
150
153
|
end
|
151
154
|
|
152
155
|
def natural_steps(num_steps)
|
153
|
-
HeadMusic::Pitch.get([target_letter_name(num_steps),
|
156
|
+
HeadMusic::Pitch.get([target_letter_name(num_steps), register + octaves_delta(num_steps)].join)
|
154
157
|
end
|
155
158
|
|
156
159
|
def frequency
|
@@ -167,7 +170,7 @@ class HeadMusic::Pitch
|
|
167
170
|
private
|
168
171
|
|
169
172
|
def octave_changes_to(other)
|
170
|
-
other.
|
173
|
+
other.register - register - octave_adjustment_to(other)
|
171
174
|
end
|
172
175
|
|
173
176
|
def octave_adjustment_to(other)
|
@@ -212,4 +215,14 @@ class HeadMusic::Pitch
|
|
212
215
|
@target_letter_name ||= {}
|
213
216
|
@target_letter_name[num_steps] ||= letter_name.steps_up(num_steps)
|
214
217
|
end
|
218
|
+
|
219
|
+
def helmholtz_letter_name
|
220
|
+
return spelling.to_s.downcase if HeadMusic::Register.get(register).helmholtz_case == :lower
|
221
|
+
|
222
|
+
spelling.to_s
|
223
|
+
end
|
224
|
+
|
225
|
+
def helmholtz_marks
|
226
|
+
HeadMusic::Register.get(register).helmholtz_marks
|
227
|
+
end
|
215
228
|
end
|
@@ -15,7 +15,7 @@ class HeadMusic::Pitch::OctaveEquivalence
|
|
15
15
|
|
16
16
|
def octave_equivalent?(other)
|
17
17
|
other = HeadMusic::Pitch.get(other)
|
18
|
-
pitch.spelling == other.spelling && pitch.
|
18
|
+
pitch.spelling == other.spelling && pitch.register != other.register
|
19
19
|
end
|
20
20
|
|
21
21
|
alias equivalent? octave_equivalent?
|
data/lib/head_music/pitch_set.rb
CHANGED
@@ -187,7 +187,7 @@ class HeadMusic::PitchSet
|
|
187
187
|
|
188
188
|
def reduction_pitches
|
189
189
|
pitches.map do |pitch|
|
190
|
-
pitch = HeadMusic::Pitch.fetch_or_create(pitch.spelling, pitch.
|
190
|
+
pitch = HeadMusic::Pitch.fetch_or_create(pitch.spelling, pitch.register - 1) while pitch > bass_pitch + 12
|
191
191
|
pitch
|
192
192
|
end.sort
|
193
193
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# The register is a numeric octave identifier used in scientific pitch notation.
|
4
|
+
#
|
5
|
+
# A pitch is a spelling plus a register. For example, C4 is middle C and C5 is the C one octave higher.
|
6
|
+
# The number changes between the letter names B and C regardless of sharps and flats,
|
7
|
+
# so as an extreme example, Cb5 is actually a semitone below B#4.
|
8
|
+
class HeadMusic::Register
|
9
|
+
include Comparable
|
10
|
+
|
11
|
+
DEFAULT = 4
|
12
|
+
|
13
|
+
def self.get(identifier)
|
14
|
+
from_number(identifier) || from_name(identifier) || default
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_number(identifier)
|
18
|
+
return nil unless identifier.to_s == identifier.to_i.to_s
|
19
|
+
return nil unless (-2..12).cover?(identifier.to_i)
|
20
|
+
|
21
|
+
@registers ||= {}
|
22
|
+
@registers[identifier.to_i] ||= new(identifier.to_i)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.from_name(string)
|
26
|
+
return unless string.to_s.match?(HeadMusic::Spelling::MATCHER)
|
27
|
+
|
28
|
+
_letter, _sign, register_string = string.to_s.match(HeadMusic::Spelling::MATCHER).captures
|
29
|
+
@registers ||= {}
|
30
|
+
@registers[register_string.to_i] ||= new(register_string.to_i) if register_string
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.default
|
34
|
+
@registers[DEFAULT] ||= new(DEFAULT)
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :number
|
38
|
+
|
39
|
+
delegate :to_i, :to_s, to: :number
|
40
|
+
|
41
|
+
def initialize(number)
|
42
|
+
@number = number
|
43
|
+
end
|
44
|
+
|
45
|
+
def <=>(other)
|
46
|
+
to_i <=> other.to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
def +(other)
|
50
|
+
self.class.get(to_i + other.to_i)
|
51
|
+
end
|
52
|
+
|
53
|
+
def -(other)
|
54
|
+
self.class.get(to_i - other.to_i)
|
55
|
+
end
|
56
|
+
|
57
|
+
def helmholtz_case
|
58
|
+
return :upper if number < 3
|
59
|
+
|
60
|
+
:lower
|
61
|
+
end
|
62
|
+
|
63
|
+
def helmholtz_marks
|
64
|
+
return ',' * (2 - number) if number < 2
|
65
|
+
return "'" * (number - 3) if number > 3
|
66
|
+
|
67
|
+
''
|
68
|
+
end
|
69
|
+
|
70
|
+
private_class_method :new
|
71
|
+
end
|
data/lib/head_music/version.rb
CHANGED
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.
|
4
|
+
version: 0.27.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Head
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -166,7 +166,6 @@ files:
|
|
166
166
|
- lib/head_music/motion.rb
|
167
167
|
- lib/head_music/musical_symbol.rb
|
168
168
|
- lib/head_music/named.rb
|
169
|
-
- lib/head_music/octave.rb
|
170
169
|
- lib/head_music/pitch.rb
|
171
170
|
- lib/head_music/pitch/enharmonic_equivalence.rb
|
172
171
|
- lib/head_music/pitch/octave_equivalence.rb
|
@@ -175,6 +174,7 @@ files:
|
|
175
174
|
- lib/head_music/pitch_set.rb
|
176
175
|
- lib/head_music/quality.rb
|
177
176
|
- lib/head_music/reference_pitch.rb
|
177
|
+
- lib/head_music/register.rb
|
178
178
|
- lib/head_music/rhythm.rb
|
179
179
|
- lib/head_music/rhythmic_unit.rb
|
180
180
|
- lib/head_music/scale.rb
|
data/lib/head_music/octave.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# The Octave identifier is a number used in scientific pitch notation.
|
4
|
-
class HeadMusic::Octave
|
5
|
-
include Comparable
|
6
|
-
|
7
|
-
DEFAULT = 4
|
8
|
-
|
9
|
-
def self.get(identifier)
|
10
|
-
from_number(identifier) || from_name(identifier) || default
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.from_number(identifier)
|
14
|
-
return nil unless identifier.to_s == identifier.to_i.to_s
|
15
|
-
return nil unless (-2..12).cover?(identifier.to_i)
|
16
|
-
|
17
|
-
@octaves ||= {}
|
18
|
-
@octaves[identifier.to_i] ||= new(identifier.to_i)
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.from_name(string)
|
22
|
-
return unless string.to_s.match?(HeadMusic::Spelling::MATCHER)
|
23
|
-
|
24
|
-
_letter, _sign, octave_string = string.to_s.match(HeadMusic::Spelling::MATCHER).captures
|
25
|
-
@octaves ||= {}
|
26
|
-
@octaves[octave_string.to_i] ||= new(octave_string.to_i) if octave_string
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.default
|
30
|
-
@octaves[DEFAULT] ||= new(DEFAULT)
|
31
|
-
end
|
32
|
-
|
33
|
-
attr_reader :number
|
34
|
-
|
35
|
-
delegate :to_i, :to_s, to: :number
|
36
|
-
|
37
|
-
def initialize(number)
|
38
|
-
@number = number
|
39
|
-
end
|
40
|
-
|
41
|
-
def <=>(other)
|
42
|
-
to_i <=> other.to_i
|
43
|
-
end
|
44
|
-
|
45
|
-
def +(other)
|
46
|
-
self.class.get(to_i + other.to_i)
|
47
|
-
end
|
48
|
-
|
49
|
-
def -(other)
|
50
|
-
self.class.get(to_i - other.to_i)
|
51
|
-
end
|
52
|
-
|
53
|
-
private_class_method :new
|
54
|
-
end
|