head_music 0.20.0 → 0.22.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.circleci/config.yml +22 -0
- data/.circleci/setup-rubygems.sh +3 -0
- data/.gitignore +1 -0
- data/.pairs +8 -0
- data/.rubocop.yml +11 -2
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +3 -6
- data/Gemfile +2 -2
- data/README.md +0 -1
- data/TODO.md +18 -0
- data/head_music.gemspec +1 -2
- data/lib/head_music/chord.rb +9 -2
- data/lib/head_music/circle.rb +1 -0
- data/lib/head_music/clef.rb +10 -5
- data/lib/head_music/consonance.rb +1 -1
- data/lib/head_music/content/composition.rb +1 -2
- data/lib/head_music/content/note.rb +2 -0
- data/lib/head_music/content/placement.rb +1 -1
- data/lib/head_music/content/rhythmic_value.rb +1 -0
- data/lib/head_music/content/voice.rb +4 -2
- data/lib/head_music/functional_interval.rb +43 -17
- data/lib/head_music/grand_staff.rb +2 -1
- data/lib/head_music/harmonic_interval.rb +1 -0
- data/lib/head_music/key_signature.rb +1 -0
- data/lib/head_music/letter_name.rb +20 -7
- data/lib/head_music/melodic_interval.rb +4 -2
- data/lib/head_music/meter.rb +1 -0
- data/lib/head_music/octave.rb +2 -0
- data/lib/head_music/pitch/enharmonic_equivalence.rb +28 -0
- data/lib/head_music/pitch/octave_equivalence.rb +24 -0
- data/lib/head_music/pitch.rb +41 -57
- data/lib/head_music/pitch_class.rb +5 -0
- data/lib/head_music/rhythmic_unit.rb +1 -0
- data/lib/head_music/scale.rb +7 -3
- data/lib/head_music/scale_degree.rb +2 -1
- data/lib/head_music/sign.rb +2 -1
- data/lib/head_music/spelling.rb +6 -2
- data/lib/head_music/style/analysis.rb +1 -0
- data/lib/head_music/style/annotation.rb +2 -2
- data/lib/head_music/style/guidelines/at_least_eight_notes.rb +1 -0
- data/lib/head_music/style/guidelines/direction_changes.rb +2 -0
- data/lib/head_music/style/guidelines/limit_octave_leaps.rb +1 -0
- data/lib/head_music/style/guidelines/mostly_conjunct.rb +1 -0
- data/lib/head_music/style/guidelines/notes_same_length.rb +2 -1
- data/lib/head_music/style/guidelines/one_to_one.rb +1 -0
- data/lib/head_music/style/guidelines/prefer_contrary_motion.rb +2 -0
- data/lib/head_music/style/guidelines/prefer_imperfect.rb +1 -0
- data/lib/head_music/style/guidelines/single_large_leaps.rb +1 -0
- data/lib/head_music/style/guidelines/start_on_perfect_consonance.rb +1 -0
- data/lib/head_music/style/guidelines/step_down_to_final_note.rb +1 -0
- data/lib/head_music/style/guidelines/step_out_of_unison.rb +1 -1
- data/lib/head_music/style/guidelines/step_up_to_final_note.rb +1 -0
- data/lib/head_music/style/mark.rb +3 -2
- data/lib/head_music/tuning.rb +6 -1
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +2 -0
- metadata +10 -4
- data/circle.yml +0 -9
data/lib/head_music/pitch.rb
CHANGED
@@ -7,10 +7,14 @@ class HeadMusic::Pitch
|
|
7
7
|
attr_reader :spelling
|
8
8
|
attr_reader :octave
|
9
9
|
|
10
|
-
delegate :letter_name,
|
10
|
+
delegate :letter_name, to: :spelling
|
11
|
+
delegate :series_ascending, :series_descending, to: :letter_name, prefix: true
|
11
12
|
delegate :sign, :sharp?, :flat?, to: :spelling
|
12
13
|
delegate :pitch_class, to: :spelling
|
14
|
+
delegate :number, to: :pitch_class, prefix: true
|
15
|
+
delegate :pitch_class_number, to: :natural, prefix: true
|
13
16
|
delegate :semitones, to: :sign, prefix: true, allow_nil: true
|
17
|
+
delegate :steps_to, to: :letter_name, prefix: true
|
14
18
|
|
15
19
|
delegate :smallest_interval_to, to: :pitch_class
|
16
20
|
|
@@ -31,16 +35,19 @@ class HeadMusic::Pitch
|
|
31
35
|
|
32
36
|
def self.from_pitch_class(pitch_class)
|
33
37
|
return nil unless pitch_class.is_a?(HeadMusic::PitchClass)
|
38
|
+
|
34
39
|
fetch_or_create(pitch_class.sharp_spelling)
|
35
40
|
end
|
36
41
|
|
37
42
|
def self.from_name(name)
|
38
43
|
return nil unless name == name.to_s
|
44
|
+
|
39
45
|
fetch_or_create(HeadMusic::Spelling.get(name), HeadMusic::Octave.get(name).to_i)
|
40
46
|
end
|
41
47
|
|
42
48
|
def self.from_number(number)
|
43
49
|
return nil unless number == number.to_i
|
50
|
+
|
44
51
|
spelling = HeadMusic::Spelling.from_number(number)
|
45
52
|
octave = (number.to_i / 12) - 1
|
46
53
|
fetch_or_create(spelling, octave)
|
@@ -64,6 +71,7 @@ class HeadMusic::Pitch
|
|
64
71
|
def self.fetch_or_create(spelling, octave = nil)
|
65
72
|
octave ||= HeadMusic::Octave::DEFAULT
|
66
73
|
return unless spelling && (-1..9).cover?(octave)
|
74
|
+
|
67
75
|
@pitches ||= {}
|
68
76
|
hash_key = [spelling, octave].join
|
69
77
|
@pitches[hash_key] ||= new(spelling, octave)
|
@@ -94,15 +102,24 @@ class HeadMusic::Pitch
|
|
94
102
|
end
|
95
103
|
|
96
104
|
def natural
|
97
|
-
HeadMusic::Pitch.get(to_s.gsub(
|
105
|
+
HeadMusic::Pitch.get(to_s.gsub(HeadMusic::Sign.matcher, ''))
|
98
106
|
end
|
99
107
|
|
100
108
|
def +(other)
|
101
|
-
|
109
|
+
if other.is_a?(HeadMusic::FunctionalInterval)
|
110
|
+
# return a pitch
|
111
|
+
other.above(self)
|
112
|
+
else
|
113
|
+
# assume value represents an interval in semitones and return another pitch
|
114
|
+
HeadMusic::Pitch.get(to_i + other.to_i)
|
115
|
+
end
|
102
116
|
end
|
103
117
|
|
104
118
|
def -(other)
|
105
|
-
if other.is_a?(HeadMusic::
|
119
|
+
if other.is_a?(HeadMusic::FunctionalInterval)
|
120
|
+
# return a pitch
|
121
|
+
other.below(self)
|
122
|
+
elsif other.is_a?(HeadMusic::Pitch)
|
106
123
|
# return an interval
|
107
124
|
HeadMusic::Interval.get(to_i - other.to_i)
|
108
125
|
else
|
@@ -132,16 +149,33 @@ class HeadMusic::Pitch
|
|
132
149
|
tuning.frequency_for(self)
|
133
150
|
end
|
134
151
|
|
152
|
+
def steps_to(other)
|
153
|
+
other = HeadMusic::Pitch.get(other)
|
154
|
+
letter_name_steps_to(other) + 7 * octave_changes_to(other)
|
155
|
+
end
|
156
|
+
|
135
157
|
private_class_method :new
|
136
158
|
|
137
159
|
private
|
138
160
|
|
161
|
+
def octave_changes_to(other)
|
162
|
+
other.octave - octave - octave_adjustment_to(other)
|
163
|
+
end
|
164
|
+
|
165
|
+
def octave_adjustment_to(other)
|
166
|
+
(pitch_class_above?(other) ? 1 : 0)
|
167
|
+
end
|
168
|
+
|
169
|
+
def pitch_class_above?(other)
|
170
|
+
natural_pitch_class_number > other.natural_pitch_class_number
|
171
|
+
end
|
172
|
+
|
139
173
|
def enharmonic_equivalence
|
140
|
-
@enharmonic_equivalence ||= EnharmonicEquivalence.get(self)
|
174
|
+
@enharmonic_equivalence ||= HeadMusic::Pitch::EnharmonicEquivalence.get(self)
|
141
175
|
end
|
142
176
|
|
143
177
|
def octave_equivalence
|
144
|
-
@octave_equivalence ||= OctaveEquivalence.get(self)
|
178
|
+
@octave_equivalence ||= HeadMusic::Pitch::OctaveEquivalence.get(self)
|
145
179
|
end
|
146
180
|
|
147
181
|
def tuning
|
@@ -168,56 +202,6 @@ class HeadMusic::Pitch
|
|
168
202
|
|
169
203
|
def target_letter_name(num_steps)
|
170
204
|
@target_letter_name ||= {}
|
171
|
-
@target_letter_name[num_steps] ||= letter_name.
|
172
|
-
end
|
173
|
-
|
174
|
-
# An enharmonic equivalent pitch is the same frequency spelled differently, such as D# and Eb.
|
175
|
-
class EnharmonicEquivalence
|
176
|
-
def self.get(pitch)
|
177
|
-
pitch = HeadMusic::Pitch.get(pitch)
|
178
|
-
@enharmonic_equivalences ||= {}
|
179
|
-
@enharmonic_equivalences[pitch.to_s] ||= new(pitch)
|
180
|
-
end
|
181
|
-
|
182
|
-
attr_reader :pitch
|
183
|
-
|
184
|
-
delegate :pitch_class, to: :pitch
|
185
|
-
|
186
|
-
def initialize(pitch)
|
187
|
-
@pitch = HeadMusic::Pitch.get(pitch)
|
188
|
-
end
|
189
|
-
|
190
|
-
def enharmonic_equivalent?(other)
|
191
|
-
other = HeadMusic::Pitch.get(other)
|
192
|
-
pitch.midi_note_number == other.midi_note_number && pitch.spelling != other.spelling
|
193
|
-
end
|
194
|
-
|
195
|
-
alias enharmonic? enharmonic_equivalent?
|
196
|
-
alias equivalent? enharmonic_equivalent?
|
197
|
-
|
198
|
-
private_class_method :new
|
199
|
-
end
|
200
|
-
|
201
|
-
# Octave equivalence is the functional equivalence of pitches with the same spelling separated by one or more octaves.
|
202
|
-
class OctaveEquivalence
|
203
|
-
def self.get(pitch)
|
204
|
-
@octave_equivalences ||= {}
|
205
|
-
@octave_equivalences[pitch.to_s] ||= new(pitch)
|
206
|
-
end
|
207
|
-
|
208
|
-
attr_reader :pitch
|
209
|
-
|
210
|
-
def initialize(pitch)
|
211
|
-
@pitch = pitch
|
212
|
-
end
|
213
|
-
|
214
|
-
def octave_equivalent?(other)
|
215
|
-
other = HeadMusic::Pitch.get(other)
|
216
|
-
pitch.spelling == other.spelling && pitch.octave != other.octave
|
217
|
-
end
|
218
|
-
|
219
|
-
alias equivalent? octave_equivalent?
|
220
|
-
|
221
|
-
private_class_method :new
|
205
|
+
@target_letter_name[num_steps] ||= letter_name.steps_up(num_steps)
|
222
206
|
end
|
223
207
|
end
|
@@ -7,6 +7,7 @@ class HeadMusic::PitchClass
|
|
7
7
|
|
8
8
|
SHARP_SPELLINGS = %w[C C# D D# E F F# G G# A A# B].freeze
|
9
9
|
FLAT_SPELLINGS = %w[C Db D Eb E F Gb G Ab A Bb B].freeze
|
10
|
+
INTEGER_NOTATION = %w[0 1 2 3 4 5 6 7 8 9 t e].freeze
|
10
11
|
|
11
12
|
def self.get(identifier)
|
12
13
|
@pitch_classes ||= {}
|
@@ -30,6 +31,10 @@ class HeadMusic::PitchClass
|
|
30
31
|
number
|
31
32
|
end
|
32
33
|
|
34
|
+
def to_integer_notation
|
35
|
+
INTEGER_NOTATION[number]
|
36
|
+
end
|
37
|
+
|
33
38
|
def sharp_spelling
|
34
39
|
SHARP_SPELLINGS[number]
|
35
40
|
end
|
data/lib/head_music/scale.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# A scale contains ordered pitches starting at a tonal center.
|
4
4
|
class HeadMusic::Scale
|
5
|
-
SCALE_REGEX = /^[A-G][#b]?\s+\w
|
5
|
+
SCALE_REGEX = /^[A-G][#b]?\s+\w+$/.freeze
|
6
6
|
|
7
7
|
def self.get(root_pitch, scale_type = nil)
|
8
8
|
root_pitch, scale_type = root_pitch.split(/\s+/) if root_pitch.is_a?(String) && scale_type =~ SCALE_REGEX
|
@@ -15,7 +15,7 @@ class HeadMusic::Scale
|
|
15
15
|
@scales[hash_key] ||= new(root_pitch, scale_type)
|
16
16
|
end
|
17
17
|
|
18
|
-
delegate :
|
18
|
+
delegate :letter_name_series_ascending, :letter_name_series_descending, to: :root_pitch
|
19
19
|
|
20
20
|
attr_reader :root_pitch, :scale_type
|
21
21
|
|
@@ -53,6 +53,7 @@ class HeadMusic::Scale
|
|
53
53
|
pitches = [root_pitch]
|
54
54
|
%i[ascending descending].each do |single_direction|
|
55
55
|
next unless [single_direction, :both].include?(direction)
|
56
|
+
|
56
57
|
(1..octaves).each do
|
57
58
|
pitches += octave_scale_pitches(single_direction, semitones_from_root)
|
58
59
|
semitones_from_root += 12 * direction_sign(single_direction)
|
@@ -101,16 +102,19 @@ class HeadMusic::Scale
|
|
101
102
|
|
102
103
|
def diatonic_letter_for_step(direction, step)
|
103
104
|
return unless scale_type.diatonic?
|
104
|
-
|
105
|
+
|
106
|
+
direction == :ascending ? letter_name_series_ascending[step % 7] : letter_name_series_descending[step % 7]
|
105
107
|
end
|
106
108
|
|
107
109
|
def child_scale_letter_for_step(semitones_from_root)
|
108
110
|
return unless scale_type.parent
|
111
|
+
|
109
112
|
parent_scale_pitch_for(semitones_from_root).letter_name
|
110
113
|
end
|
111
114
|
|
112
115
|
def flat_letter_for_step(semitones_from_root)
|
113
116
|
return unless root_pitch.flat?
|
117
|
+
|
114
118
|
HeadMusic::PitchClass::FLAT_SPELLINGS[pitch_class_number(semitones_from_root)]
|
115
119
|
end
|
116
120
|
|
@@ -17,7 +17,7 @@ class HeadMusic::ScaleDegree
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def degree
|
20
|
-
scale.
|
20
|
+
scale.letter_name_series_ascending.index(spelling.letter_name.to_s) + 1
|
21
21
|
end
|
22
22
|
|
23
23
|
def sign
|
@@ -41,6 +41,7 @@ class HeadMusic::ScaleDegree
|
|
41
41
|
|
42
42
|
def name_for_degree
|
43
43
|
return unless scale_type.diatonic?
|
44
|
+
|
44
45
|
NAME_FOR_DIATONIC_DEGREE[degree] ||
|
45
46
|
(scale_type.intervals.last == 1 || sign == '#' ? 'leading tone' : 'subtonic')
|
46
47
|
end
|
data/lib/head_music/sign.rb
CHANGED
@@ -17,7 +17,7 @@ class HeadMusic::Sign
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.symbols
|
20
|
-
@
|
20
|
+
@symbols ||= all.map { |sign| [sign.ascii, sign.unicode] }.flatten.reject { |s| s.nil? || s.empty? }
|
21
21
|
end
|
22
22
|
|
23
23
|
def self.matcher
|
@@ -30,6 +30,7 @@ class HeadMusic::Sign
|
|
30
30
|
|
31
31
|
def self.get(identifier)
|
32
32
|
return identifier if identifier.is_a?(HeadMusic::Sign)
|
33
|
+
|
33
34
|
all.detect do |sign|
|
34
35
|
sign.representions.include?(identifier)
|
35
36
|
end
|
data/lib/head_music/spelling.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
# Composite of a LetterName and an optional Sign.
|
5
5
|
# Does not include the octave. See Pitch for that.
|
6
6
|
class HeadMusic::Spelling
|
7
|
-
MATCHER = /^\s*([A-G])(#{HeadMusic::Sign.matcher}?)(\-?\d+)?\s*$/i
|
7
|
+
MATCHER = /^\s*([A-G])(#{HeadMusic::Sign.matcher}?)(\-?\d+)?\s*$/i.freeze
|
8
8
|
|
9
9
|
attr_reader :pitch_class
|
10
10
|
attr_reader :letter_name
|
@@ -12,11 +12,12 @@ class HeadMusic::Spelling
|
|
12
12
|
|
13
13
|
delegate :number, to: :pitch_class, prefix: true
|
14
14
|
delegate :to_i, to: :pitch_class_number
|
15
|
-
delegate :
|
15
|
+
delegate :series_ascending, :series_descending, to: :letter_name, prefix: true
|
16
16
|
delegate :enharmonic?, to: :enharmonic_equivalence
|
17
17
|
|
18
18
|
def self.get(identifier)
|
19
19
|
return identifier if identifier.is_a?(HeadMusic::Spelling)
|
20
|
+
|
20
21
|
from_name(identifier) || from_number(identifier)
|
21
22
|
end
|
22
23
|
|
@@ -26,15 +27,18 @@ class HeadMusic::Spelling
|
|
26
27
|
|
27
28
|
def self.from_name(name)
|
28
29
|
return nil unless matching_string(name)
|
30
|
+
|
29
31
|
letter_name, sign_string, _octave = matching_string(name).captures
|
30
32
|
letter_name = HeadMusic::LetterName.get(letter_name)
|
31
33
|
return nil unless letter_name
|
34
|
+
|
32
35
|
sign = HeadMusic::Sign.get(sign_string)
|
33
36
|
fetch_or_create(letter_name, sign)
|
34
37
|
end
|
35
38
|
|
36
39
|
def self.from_number(number)
|
37
40
|
return nil unless number == number.to_i
|
41
|
+
|
38
42
|
pitch_class_number = number.to_i % 12
|
39
43
|
letter_name = HeadMusic::LetterName.from_pitch_class(pitch_class_number)
|
40
44
|
from_number_and_letter(number, letter_name)
|
@@ -41,11 +41,11 @@ class HeadMusic::Style::Annotation
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def start_position
|
44
|
-
[marks].flatten.compact.map(&:start_position).
|
44
|
+
[marks].flatten.compact.map(&:start_position).min
|
45
45
|
end
|
46
46
|
|
47
47
|
def end_position
|
48
|
-
[marks].flatten.compact.map(&:end_position).
|
48
|
+
[marks].flatten.compact.map(&:end_position).max
|
49
49
|
end
|
50
50
|
|
51
51
|
def marks
|
@@ -7,6 +7,7 @@ module HeadMusic::Style::Guidelines; end
|
|
7
7
|
class HeadMusic::Style::Guidelines::DirectionChanges < HeadMusic::Style::Annotation
|
8
8
|
def marks
|
9
9
|
return unless overage.positive?
|
10
|
+
|
10
11
|
penalty_exponent = overage**0.5
|
11
12
|
HeadMusic::Style::Mark.for_all(notes, fitness: HeadMusic::PENALTY_FACTOR**penalty_exponent)
|
12
13
|
end
|
@@ -15,6 +16,7 @@ class HeadMusic::Style::Guidelines::DirectionChanges < HeadMusic::Style::Annotat
|
|
15
16
|
|
16
17
|
def overage
|
17
18
|
return 0 if notes.length < 2
|
19
|
+
|
18
20
|
[notes_per_direction - self.class.maximum_notes_per_direction, 0].max
|
19
21
|
end
|
20
22
|
|
@@ -53,8 +53,9 @@ class HeadMusic::Style::Guidelines::NotesSameLength < HeadMusic::Style::Annotati
|
|
53
53
|
|
54
54
|
def most_common_rhythmic_values
|
55
55
|
return [] if notes.empty?
|
56
|
+
|
56
57
|
occurrences = occurrences_by_rhythmic_value
|
57
|
-
highest_count = occurrences.values.
|
58
|
+
highest_count = occurrences.values.max
|
58
59
|
occurrences.select { |_rhythmic_value, count| count == highest_count }.keys
|
59
60
|
end
|
60
61
|
|
@@ -10,6 +10,7 @@ class HeadMusic::Style::Guidelines::OneToOne < HeadMusic::Style::Annotation
|
|
10
10
|
def marks
|
11
11
|
return unless cantus_firmus&.notes
|
12
12
|
return if cantus_firmus.notes.empty?
|
13
|
+
|
13
14
|
HeadMusic::Style::Mark.for_each(
|
14
15
|
notes_without_match(voice, cantus_firmus) + notes_without_match(cantus_firmus, voice)
|
15
16
|
)
|
@@ -10,6 +10,7 @@ class HeadMusic::Style::Guidelines::PreferContraryMotion < HeadMusic::Style::Ann
|
|
10
10
|
def marks
|
11
11
|
return nil if notes.length < 2
|
12
12
|
return nil if direct_motion_ratio <= 0.5
|
13
|
+
|
13
14
|
direct_motions.map { |motion| HeadMusic::Style::Mark.for_all(motion.notes) }
|
14
15
|
end
|
15
16
|
|
@@ -21,6 +22,7 @@ class HeadMusic::Style::Guidelines::PreferContraryMotion < HeadMusic::Style::Ann
|
|
21
22
|
|
22
23
|
def direct_motion_ratio
|
23
24
|
return 0 if motions.empty?
|
25
|
+
|
24
26
|
direct_motions.count / motions.count.to_f
|
25
27
|
end
|
26
28
|
end
|
@@ -14,6 +14,7 @@ class HeadMusic::Style::Guidelines::SingleLargeLeaps < HeadMusic::Style::Guideli
|
|
14
14
|
return false if spelling_consonant_triad?(first_interval, second_interval, third_interval)
|
15
15
|
return false if second_interval.step?
|
16
16
|
return false if second_interval.repetition?
|
17
|
+
|
17
18
|
!direction_changed?(first_interval, second_interval) && second_interval.leap?
|
18
19
|
end
|
19
20
|
end
|
@@ -9,6 +9,7 @@ class HeadMusic::Style::Guidelines::StepDownToFinalNote < HeadMusic::Style::Anno
|
|
9
9
|
|
10
10
|
def marks
|
11
11
|
return if last_melodic_interval.nil?
|
12
|
+
|
12
13
|
fitness = 1
|
13
14
|
fitness *= HeadMusic::PENALTY_FACTOR unless step?
|
14
15
|
fitness *= HeadMusic::PENALTY_FACTOR unless descending?
|
@@ -29,6 +29,6 @@ class HeadMusic::Style::Guidelines::StepOutOfUnison < HeadMusic::Style::Annotati
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def perfect_unisons
|
32
|
-
@
|
32
|
+
@perfect_unisons ||= harmonic_intervals.select(&:perfect_consonance?).select(&:unison?)
|
33
33
|
end
|
34
34
|
end
|
@@ -12,8 +12,9 @@ class HeadMusic::Style::Mark
|
|
12
12
|
def self.for_all(placements, fitness: nil)
|
13
13
|
placements = [placements].flatten.compact
|
14
14
|
return [] if placements.empty?
|
15
|
-
|
16
|
-
|
15
|
+
|
16
|
+
start_position = placements.map(&:position).min
|
17
|
+
end_position = placements.map(&:next_position).max
|
17
18
|
new(start_position, end_position, placements: placements, fitness: fitness)
|
18
19
|
end
|
19
20
|
|
data/lib/head_music/tuning.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
# A tuning has a reference pitch and frequency and provides frequencies for all pitches
|
4
4
|
# The base class assumes equal temperament tuning. By default, A4 = 440.0 Hz
|
5
5
|
class HeadMusic::Tuning
|
6
|
-
REFERENCE_FREQUENCY = 440.0
|
7
6
|
REFERENCE_PITCH_NAME = 'A4'
|
7
|
+
REFERENCE_FREQUENCY = 440.0
|
8
8
|
|
9
9
|
attr_reader :reference_pitch, :reference_frequency
|
10
10
|
|
@@ -18,3 +18,8 @@ class HeadMusic::Tuning
|
|
18
18
|
reference_frequency * (2**(1.0 / 12))**(pitch - reference_pitch).semitones
|
19
19
|
end
|
20
20
|
end
|
21
|
+
|
22
|
+
# TODO: other tunings
|
23
|
+
# Create website that hosts videos on theory and history, handy charts, etc.
|
24
|
+
# one of those charts can be a frequency table in various tunings
|
25
|
+
# maybe show pythagorean commas and such. or cents sharp or flat relative to either equal temperment or just intonation
|
data/lib/head_music/version.rb
CHANGED
data/lib/head_music.rb
CHANGED
@@ -36,6 +36,8 @@ require 'head_music/meter'
|
|
36
36
|
require 'head_music/motion'
|
37
37
|
require 'head_music/octave'
|
38
38
|
require 'head_music/pitch'
|
39
|
+
require 'head_music/pitch/enharmonic_equivalence'
|
40
|
+
require 'head_music/pitch/octave_equivalence'
|
39
41
|
require 'head_music/pitch_class'
|
40
42
|
require 'head_music/quality'
|
41
43
|
require 'head_music/rhythm'
|
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.22.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: 2018-
|
11
|
+
date: 2018-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -102,18 +102,22 @@ executables: []
|
|
102
102
|
extensions: []
|
103
103
|
extra_rdoc_files: []
|
104
104
|
files:
|
105
|
+
- ".circleci/config.yml"
|
106
|
+
- ".circleci/setup-rubygems.sh"
|
105
107
|
- ".gitignore"
|
108
|
+
- ".pairs"
|
106
109
|
- ".rspec"
|
107
110
|
- ".rubocop.yml"
|
111
|
+
- ".ruby-version"
|
108
112
|
- ".travis.yml"
|
109
113
|
- CODE_OF_CONDUCT.md
|
110
114
|
- Gemfile
|
111
115
|
- LICENSE.txt
|
112
116
|
- README.md
|
113
117
|
- Rakefile
|
118
|
+
- TODO.md
|
114
119
|
- bin/console
|
115
120
|
- bin/setup
|
116
|
-
- circle.yml
|
117
121
|
- head_music.gemspec
|
118
122
|
- lib/head_music.rb
|
119
123
|
- lib/head_music/chord.rb
|
@@ -140,6 +144,8 @@ files:
|
|
140
144
|
- lib/head_music/named_rudiment.rb
|
141
145
|
- lib/head_music/octave.rb
|
142
146
|
- lib/head_music/pitch.rb
|
147
|
+
- lib/head_music/pitch/enharmonic_equivalence.rb
|
148
|
+
- lib/head_music/pitch/octave_equivalence.rb
|
143
149
|
- lib/head_music/pitch_class.rb
|
144
150
|
- lib/head_music/quality.rb
|
145
151
|
- lib/head_music/rhythm.rb
|
@@ -213,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
213
219
|
version: '0'
|
214
220
|
requirements: []
|
215
221
|
rubyforge_project:
|
216
|
-
rubygems_version: 2.6
|
222
|
+
rubygems_version: 2.7.6
|
217
223
|
signing_key:
|
218
224
|
specification_version: 4
|
219
225
|
summary: The rudiments of western music theory.
|