head_music 0.5.2 → 0.5.3
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/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
|