head_music 0.5.2 → 0.5.3
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/lib/head_music/accidental.rb +1 -0
- data/lib/head_music/circle.rb +4 -3
- data/lib/head_music/consonance.rb +1 -0
- data/lib/head_music/functional_interval.rb +1 -0
- data/lib/head_music/interval.rb +1 -0
- data/lib/head_music/{letter.rb → letter_name.rb} +3 -2
- data/lib/head_music/meter.rb +50 -0
- data/lib/head_music/octave.rb +1 -0
- data/lib/head_music/pitch.rb +3 -2
- data/lib/head_music/pitch_class.rb +2 -0
- data/lib/head_music/quality.rb +1 -0
- data/lib/head_music/rhythmic_value.rb +63 -0
- data/lib/head_music/scale_type.rb +1 -0
- data/lib/head_music/spelling.rb +8 -7
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +20 -18
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 326a9d839392519ee341ca47ec34eb63ea461bd1
|
4
|
+
data.tar.gz: 95dbb21634187c2ba6764829ce37e16759347d39
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7b40960fcf36045cfd9d512e27d41024fc2a1a37c10cbcffa1c89c125ac2fd3fba06108b457f6ae99c612bc956c23763636999239c05250c0842314be75fb11
|
7
|
+
data.tar.gz: 6a7971653dad527be88f1321e9e3cab80b4855e8a460a82f885fd17d96a78fe1bb98e0b3fd21bfe2b6f49529bda891fadce86979dcf9669da5657ad82c51d5eb
|
data/lib/head_music/circle.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
class HeadMusic::Circle
|
2
2
|
def self.of_fifths
|
3
|
-
|
3
|
+
get(7)
|
4
4
|
end
|
5
5
|
|
6
6
|
def self.of_fourths
|
7
|
-
|
7
|
+
get(5)
|
8
8
|
end
|
9
9
|
|
10
|
-
def self.
|
10
|
+
def self.get(interval = 7)
|
11
11
|
@circles ||= {}
|
12
12
|
@circles[interval.to_i] ||= new(interval)
|
13
13
|
end
|
14
|
+
singleton_class.send(:alias_method, :[], :get)
|
14
15
|
|
15
16
|
attr_reader :interval, :pitch_classes
|
16
17
|
|
@@ -38,6 +38,7 @@ class HeadMusic::FunctionalInterval
|
|
38
38
|
higher_pitch = HeadMusic::Pitch.from_number_and_letter(lower_pitch + semitones, higher_letter)
|
39
39
|
new(lower_pitch, higher_pitch)
|
40
40
|
end
|
41
|
+
singleton_class.send(:alias_method, :[], :get)
|
41
42
|
|
42
43
|
def self.degree_quality_semitones
|
43
44
|
@degree_quality_semitones ||= begin
|
data/lib/head_music/interval.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
class HeadMusic::
|
1
|
+
class HeadMusic::LetterName
|
2
2
|
# Defines the natural relationship between the natural letter-named notes
|
3
3
|
|
4
4
|
NAMES = ('A'..'G').to_a
|
@@ -20,6 +20,7 @@ class HeadMusic::Letter
|
|
20
20
|
def self.get(identifier)
|
21
21
|
from_name(identifier) || from_pitch_class(identifier)
|
22
22
|
end
|
23
|
+
singleton_class.send(:alias_method, :[], :get)
|
23
24
|
|
24
25
|
def self.from_name(name)
|
25
26
|
@letters ||= {}
|
@@ -63,7 +64,7 @@ class HeadMusic::Letter
|
|
63
64
|
end
|
64
65
|
|
65
66
|
def steps_to(other, direction = :ascending)
|
66
|
-
other =
|
67
|
+
other = LetterName.get(other)
|
67
68
|
other_position = other.position
|
68
69
|
if direction == :descending
|
69
70
|
other_position -= NAMES.length if other_position > position
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class HeadMusic::Meter
|
2
|
+
attr_reader :top_number, :bottom_number
|
3
|
+
|
4
|
+
NAMED = {
|
5
|
+
common_time: '4/4',
|
6
|
+
cut_time: '2/2'
|
7
|
+
}
|
8
|
+
|
9
|
+
def self.get(identifier)
|
10
|
+
identifer = identifer.to_s
|
11
|
+
identifier = NAMED[identifier.to_sym] || identifier
|
12
|
+
new(*identifier.split('/').map(&:to_i))
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(top_number, bottom_number)
|
16
|
+
@top_number, @bottom_number = top_number, bottom_number
|
17
|
+
end
|
18
|
+
|
19
|
+
def simple?
|
20
|
+
!compound?
|
21
|
+
end
|
22
|
+
|
23
|
+
def compound?
|
24
|
+
top_number > 3 && top_number / 3 == top_number / 3.0
|
25
|
+
end
|
26
|
+
|
27
|
+
def duple?
|
28
|
+
top_number == 2
|
29
|
+
end
|
30
|
+
|
31
|
+
def triple?
|
32
|
+
top_number % 3 == 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def quadruple?
|
36
|
+
top_number == 4
|
37
|
+
end
|
38
|
+
|
39
|
+
def beats_per_measure
|
40
|
+
compound? ? top_number / 3 : top_number
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
[top_number, bottom_number].join('/')
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
to_s == other.to_s
|
49
|
+
end
|
50
|
+
end
|
data/lib/head_music/octave.rb
CHANGED
@@ -8,6 +8,7 @@ class HeadMusic::Octave
|
|
8
8
|
def self.get(identifier)
|
9
9
|
from_number(identifier) || from_name(identifier) || default
|
10
10
|
end
|
11
|
+
singleton_class.send(:alias_method, :[], :get)
|
11
12
|
|
12
13
|
def self.from_number(identifier)
|
13
14
|
return nil unless identifier.to_s == identifier.to_i.to_s
|
data/lib/head_music/pitch.rb
CHANGED
@@ -14,6 +14,7 @@ class HeadMusic::Pitch
|
|
14
14
|
def self.get(value)
|
15
15
|
from_name(value) || from_number(value)
|
16
16
|
end
|
17
|
+
singleton_class.send(:alias_method, :[], :get)
|
17
18
|
|
18
19
|
def self.from_name(name)
|
19
20
|
return nil unless name == name.to_s
|
@@ -28,8 +29,8 @@ class HeadMusic::Pitch
|
|
28
29
|
end
|
29
30
|
|
30
31
|
def self.from_number_and_letter(number, letter)
|
31
|
-
letter = HeadMusic::
|
32
|
-
natural_letter_pitch = get(HeadMusic::
|
32
|
+
letter = HeadMusic::LetterName.get(letter)
|
33
|
+
natural_letter_pitch = get(HeadMusic::LetterName.get(letter).pitch_class)
|
33
34
|
natural_letter_pitch += 12 while (number - natural_letter_pitch.to_i).to_i >= 11
|
34
35
|
natural_letter_pitch = get(natural_letter_pitch)
|
35
36
|
accidental_interval = natural_letter_pitch.smallest_interval_to(HeadMusic::PitchClass.get(number))
|
@@ -11,6 +11,7 @@ class HeadMusic::PitchClass
|
|
11
11
|
number ||= identifier.to_i % 12
|
12
12
|
@pitch_classes[number] ||= new(number)
|
13
13
|
end
|
14
|
+
singleton_class.send(:alias_method, :[], :get)
|
14
15
|
|
15
16
|
class << self
|
16
17
|
alias_method :[], :get
|
@@ -35,6 +36,7 @@ class HeadMusic::PitchClass
|
|
35
36
|
def ==(value)
|
36
37
|
to_i == value.to_i
|
37
38
|
end
|
39
|
+
alias_method :enharmonic?, :==
|
38
40
|
|
39
41
|
def intervals_to(other)
|
40
42
|
delta = other.to_i - to_i
|
data/lib/head_music/quality.rb
CHANGED
@@ -31,6 +31,7 @@ class HeadMusic::Quality
|
|
31
31
|
identifier = identifier.to_s.to_sym
|
32
32
|
@qualities[identifier] ||= new(identifier) if NAMES.include?(identifier)
|
33
33
|
end
|
34
|
+
singleton_class.send(:alias_method, :[], :get)
|
34
35
|
|
35
36
|
def self.from(starting_quality, delta)
|
36
37
|
if starting_quality == :perfect
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class HeadMusic::RhythmicValue
|
2
|
+
MULTIPLES = ['whole', 'double whole', 'longa', 'maxima']
|
3
|
+
DIVISIONS = ['whole', 'half', 'quarter', 'eighth', 'sixteenth', 'thirty-second', 'sixty-fourth', 'hundred twenty-eighth note', 'two hundred fifty-sixth note']
|
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_values ||= {}
|
10
|
+
@rhythmic_values[name.to_s] ||= new(name.to_s)
|
11
|
+
end
|
12
|
+
singleton_class.send(:alias_method, :[], :get)
|
13
|
+
|
14
|
+
attr_reader :name
|
15
|
+
delegate :to_s, to: :name
|
16
|
+
delegate :to_i, to: :relative_value
|
17
|
+
|
18
|
+
def initialize(canonical_name)
|
19
|
+
@name ||= canonical_name
|
20
|
+
end
|
21
|
+
|
22
|
+
def relative_value
|
23
|
+
@relative_value ||=
|
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
|
29
|
+
end
|
30
|
+
|
31
|
+
def per_whole
|
32
|
+
@per_whole ||=
|
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
|
38
|
+
end
|
39
|
+
|
40
|
+
def note_head
|
41
|
+
return :breve if relative_value == 2
|
42
|
+
return :open if relative_value >= 0.5
|
43
|
+
:closed
|
44
|
+
end
|
45
|
+
|
46
|
+
def flags
|
47
|
+
DIVISIONS.include?(name) ? [DIVISIONS.index(name) - 2, 0].max : 0
|
48
|
+
end
|
49
|
+
|
50
|
+
def has_stem?
|
51
|
+
relative_value < 1
|
52
|
+
end
|
53
|
+
|
54
|
+
def british_name
|
55
|
+
if MULTIPLES.include?(name)
|
56
|
+
BRITISH_MULTIPLE_NAMES[MULTIPLES.index(name)]
|
57
|
+
elsif DIVISIONS.include?(name)
|
58
|
+
BRITISH_DIVISION_NAMES[DIVISIONS.index(name)]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private_class_method :new
|
63
|
+
end
|
@@ -64,6 +64,7 @@ class HeadMusic::ScaleType
|
|
64
64
|
attributes = SCALE_TYPES[name]
|
65
65
|
@scale_types[name] ||= new(name, attributes)
|
66
66
|
end
|
67
|
+
singleton_class.send(:alias_method, :[], :get)
|
67
68
|
|
68
69
|
attr_reader :name, :ascending_intervals, :descending_intervals, :parent_name
|
69
70
|
alias_method :intervals, :ascending_intervals
|
data/lib/head_music/spelling.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
# A Spelling is a pitch class with a letter and possibly an accidental
|
2
|
-
|
1
|
+
# A Spelling is a pitch class with a letter name and possibly an accidental
|
3
2
|
class HeadMusic::Spelling
|
4
3
|
MATCHER = /^\s*([A-G])([b#]*)(\-?\d+)?\s*$/
|
5
4
|
|
@@ -10,10 +9,12 @@ class HeadMusic::Spelling
|
|
10
9
|
delegate :number, to: :pitch_class, prefix: true
|
11
10
|
delegate :to_i, to: :pitch_class_number
|
12
11
|
delegate :cycle, to: :letter, prefix: true
|
12
|
+
delegate :enharmonic?, to: :pitch_class
|
13
13
|
|
14
14
|
def self.get(identifier)
|
15
15
|
from_name(identifier) || from_number(identifier)
|
16
16
|
end
|
17
|
+
singleton_class.send(:alias_method, :[], :get)
|
17
18
|
|
18
19
|
def self.match(string)
|
19
20
|
string.to_s.match(MATCHER)
|
@@ -22,7 +23,7 @@ class HeadMusic::Spelling
|
|
22
23
|
def self.from_name(name)
|
23
24
|
if match(name)
|
24
25
|
letter_name, accidental_string, _octave = match(name).captures
|
25
|
-
letter = HeadMusic::
|
26
|
+
letter = HeadMusic::LetterName.get(letter_name)
|
26
27
|
return nil unless letter
|
27
28
|
accidental = HeadMusic::Accidental.get(accidental_string)
|
28
29
|
fetch_or_create(letter, accidental)
|
@@ -32,13 +33,13 @@ class HeadMusic::Spelling
|
|
32
33
|
def self.from_number(number)
|
33
34
|
return nil unless number == number.to_i
|
34
35
|
pitch_class_number = number.to_i % 12
|
35
|
-
letter = HeadMusic::
|
36
|
+
letter = HeadMusic::LetterName.from_pitch_class(pitch_class_number)
|
36
37
|
from_number_and_letter(number, letter)
|
37
38
|
end
|
38
39
|
|
39
40
|
def self.from_number_and_letter(number, letter)
|
40
|
-
letter = HeadMusic::
|
41
|
-
natural_letter_pitch_class = HeadMusic::
|
41
|
+
letter = HeadMusic::LetterName.get(letter)
|
42
|
+
natural_letter_pitch_class = HeadMusic::LetterName.get(letter).pitch_class
|
42
43
|
accidental_interval = letter.pitch_class.smallest_interval_to(HeadMusic::PitchClass.get(number))
|
43
44
|
accidental = HeadMusic::Accidental.for_interval(accidental_interval)
|
44
45
|
fetch_or_create(letter, accidental)
|
@@ -51,7 +52,7 @@ class HeadMusic::Spelling
|
|
51
52
|
end
|
52
53
|
|
53
54
|
def initialize(letter, accidental = nil)
|
54
|
-
@letter = HeadMusic::
|
55
|
+
@letter = HeadMusic::LetterName.get(letter.to_s)
|
55
56
|
@accidental = HeadMusic::Accidental.get(accidental.to_s)
|
56
57
|
accidental_semitones = @accidental ? @accidental.semitones : 0
|
57
58
|
@pitch_class = HeadMusic::PitchClass.get(letter.pitch_class + accidental_semitones)
|
data/lib/head_music/version.rb
CHANGED
data/lib/head_music.rb
CHANGED
@@ -1,23 +1,25 @@
|
|
1
|
-
require
|
1
|
+
require 'head_music/version'
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'active_support/core_ext/module/delegation'
|
4
|
+
require 'active_support/core_ext/string/access'
|
5
|
+
require 'humanize'
|
6
6
|
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
7
|
+
require 'head_music/accidental'
|
8
|
+
require 'head_music/circle'
|
9
|
+
require 'head_music/consonance'
|
10
|
+
require 'head_music/functional_interval'
|
11
|
+
require 'head_music/interval'
|
12
|
+
require 'head_music/key_signature'
|
13
|
+
require 'head_music/letter_name'
|
14
|
+
require 'head_music/meter'
|
15
|
+
require 'head_music/octave'
|
16
|
+
require 'head_music/pitch_class'
|
17
|
+
require 'head_music/pitch'
|
18
|
+
require 'head_music/quality'
|
19
|
+
require 'head_music/rhythmic_value'
|
20
|
+
require 'head_music/scale'
|
21
|
+
require 'head_music/scale_type'
|
22
|
+
require 'head_music/spelling'
|
21
23
|
|
22
24
|
module HeadMusic
|
23
25
|
end
|
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.3
|
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-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -120,11 +120,13 @@ files:
|
|
120
120
|
- lib/head_music/functional_interval.rb
|
121
121
|
- lib/head_music/interval.rb
|
122
122
|
- lib/head_music/key_signature.rb
|
123
|
-
- lib/head_music/
|
123
|
+
- lib/head_music/letter_name.rb
|
124
|
+
- lib/head_music/meter.rb
|
124
125
|
- lib/head_music/octave.rb
|
125
126
|
- lib/head_music/pitch.rb
|
126
127
|
- lib/head_music/pitch_class.rb
|
127
128
|
- lib/head_music/quality.rb
|
129
|
+
- lib/head_music/rhythmic_value.rb
|
128
130
|
- lib/head_music/scale.rb
|
129
131
|
- lib/head_music/scale_type.rb
|
130
132
|
- lib/head_music/spelling.rb
|