head_music 0.7.0 → 0.8.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/composition.rb +7 -1
- data/lib/head_music/key_signature.rb +27 -35
- data/lib/head_music/placement.rb +39 -0
- data/lib/head_music/position.rb +21 -3
- data/lib/head_music/rhythm.rb +3 -0
- data/lib/head_music/rhythmic_unit.rb +1 -3
- data/lib/head_music/rhythmic_value.rb +1 -1
- data/lib/head_music/scale.rb +10 -4
- data/lib/head_music/scale_type.rb +4 -0
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music/voice.rb +23 -0
- 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: 56a211e11bb35239af47cdf2e79e2032b6b97f7b
|
4
|
+
data.tar.gz: 293be36552913da8cd64f946b4b1ece210114991
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d809adbad72a56eca2910a2c234d6497028d3c0a9723bfd02a214cf62f22e44777c38ff08a20707beff1d65e59cd9b5e8ddacacce82d2d64b9b826c48a5c3329
|
7
|
+
data.tar.gz: a2aa89d2b462c9a8dd938ddef98376a78a4a1a2ba7dd721e2f4e39913f5074c53d201465ae1ba0e59e11d6706e4905cb74434bb596b892b903dc298604e2ba06
|
@@ -1,9 +1,10 @@
|
|
1
1
|
class HeadMusic::Composition
|
2
|
-
attr_reader :name, :key_signature, :meter, :measures
|
2
|
+
attr_reader :name, :key_signature, :meter, :measures, :voices
|
3
3
|
|
4
4
|
def initialize(name:, key_signature: nil, meter: nil)
|
5
5
|
ensure_attributes(name, key_signature, meter)
|
6
6
|
add_measure
|
7
|
+
add_voice
|
7
8
|
end
|
8
9
|
|
9
10
|
def add_measure
|
@@ -17,6 +18,11 @@ class HeadMusic::Composition
|
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
21
|
+
def add_voice
|
22
|
+
@voices ||= []
|
23
|
+
@voices << HeadMusic::Voice.new(self)
|
24
|
+
end
|
25
|
+
|
20
26
|
private
|
21
27
|
|
22
28
|
def ensure_attributes(name, key_signature, meter)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class HeadMusic::KeySignature
|
2
2
|
attr_reader :tonic_spelling
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :scale_type
|
4
|
+
attr_reader :scale
|
4
5
|
|
5
6
|
SHARPS = %w{F# C# G# D# A# E# B#}
|
6
7
|
FLATS = %w{Bb Eb Ab Db Gb Cb Fb}
|
@@ -12,65 +13,56 @@ class HeadMusic::KeySignature
|
|
12
13
|
def self.get(identifier)
|
13
14
|
return identifier if identifier.is_a?(HeadMusic::KeySignature)
|
14
15
|
@key_signatures ||= {}
|
15
|
-
tonic_spelling,
|
16
|
+
tonic_spelling, scale_type_name = identifier.strip.split(/\s/)
|
16
17
|
hash_key = HeadMusic::Utilities::HashKey.for(identifier)
|
17
|
-
@key_signatures[hash_key] ||= new(tonic_spelling,
|
18
|
+
@key_signatures[hash_key] ||= new(tonic_spelling, scale_type_name)
|
18
19
|
end
|
19
20
|
|
20
21
|
delegate :pitch_class, to: :tonic_spelling, prefix: :tonic
|
21
22
|
delegate :to_s, to: :name
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@
|
23
|
+
delegate :pitches, to: :scale
|
24
|
+
|
25
|
+
def initialize(tonic_spelling, scale_type = nil)
|
26
|
+
@tonic_spelling = HeadMusic::Spelling.get(tonic_spelling)
|
27
|
+
@scale_type = HeadMusic::ScaleType.get(scale_type) if scale_type
|
28
|
+
@scale_type ||= HeadMusic::ScaleType.default
|
29
|
+
@scale_type = @scale_type.parent || @scale_type
|
30
|
+
@scale = HeadMusic::Scale.get(@tonic_spelling, @scale_type)
|
26
31
|
end
|
27
32
|
|
28
33
|
def sharps
|
29
|
-
|
34
|
+
pitches.map(&:spelling).uniq.select { |spelling|
|
35
|
+
spelling.sharp?
|
36
|
+
}.map(&:to_s).sort_by { |sharp|
|
37
|
+
SHARPS.index(sharp)
|
38
|
+
}
|
30
39
|
end
|
31
40
|
|
32
41
|
def flats
|
33
|
-
|
42
|
+
pitches.map(&:spelling).uniq.select { |spelling|
|
43
|
+
spelling.flat?
|
44
|
+
}.map(&:to_s).sort_by { |flat|
|
45
|
+
FLATS.index(flat)
|
46
|
+
}
|
34
47
|
end
|
35
48
|
|
36
49
|
def num_sharps
|
37
|
-
|
50
|
+
sharps.length
|
38
51
|
end
|
39
52
|
|
40
53
|
def num_flats
|
41
|
-
|
54
|
+
flats.length
|
42
55
|
end
|
43
56
|
|
44
57
|
def sharps_or_flats
|
45
|
-
|
46
|
-
return flats if @tonic_spelling.to_s =~ /b/
|
47
|
-
num_sharps <= num_flats ? sharps : flats
|
58
|
+
flats.length > 0 ? flats : sharps
|
48
59
|
end
|
49
60
|
|
50
61
|
def name
|
51
|
-
[tonic_spelling.to_s,
|
62
|
+
[tonic_spelling.to_s, scale_type.to_s].join(' ')
|
52
63
|
end
|
53
64
|
|
54
65
|
def ==(other)
|
55
|
-
self.
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def quality_name_adjustment
|
61
|
-
quality_name == :minor ? 3 : 0
|
62
|
-
end
|
63
|
-
|
64
|
-
def major?
|
65
|
-
@quality_name.to_sym == :major
|
66
|
-
end
|
67
|
-
|
68
|
-
def minor?
|
69
|
-
@quality_name.to_sym == :minor
|
70
|
-
end
|
71
|
-
|
72
|
-
def relative_major_pitch_class
|
73
|
-
return tonic_pitch_class if major?
|
74
|
-
return (tonic_pitch_class.to_i + 3) % 12 if minor?
|
66
|
+
self.sharps_or_flats == self.class.get(other).sharps_or_flats
|
75
67
|
end
|
76
68
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class HeadMusic::Placement
|
2
|
+
include Comparable
|
3
|
+
|
4
|
+
attr_reader :voice, :position, :rhythmic_value, :pitch
|
5
|
+
delegate :composition, to: :voice
|
6
|
+
|
7
|
+
def initialize(voice, position, rhythmic_value, pitch)
|
8
|
+
ensure_attributes(voice, position, rhythmic_value, pitch)
|
9
|
+
end
|
10
|
+
|
11
|
+
def note?
|
12
|
+
pitch
|
13
|
+
end
|
14
|
+
|
15
|
+
def rest?
|
16
|
+
!note?
|
17
|
+
end
|
18
|
+
|
19
|
+
def <=>(other)
|
20
|
+
self.position <=> other.position
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def ensure_attributes(voice, position, rhythmic_value, pitch)
|
26
|
+
@voice = voice
|
27
|
+
ensure_position(position)
|
28
|
+
@rhythmic_value = rhythmic_value
|
29
|
+
@pitch = HeadMusic::Pitch.get(pitch)
|
30
|
+
end
|
31
|
+
|
32
|
+
def ensure_position(position)
|
33
|
+
if position.is_a?(HeadMusic::Position)
|
34
|
+
@position = position
|
35
|
+
else
|
36
|
+
@position = HeadMusic::Position.new(composition, position)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/head_music/position.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
class HeadMusic::Position
|
2
|
+
include Comparable
|
3
|
+
|
2
4
|
attr_reader :composition, :measure_number, :count, :tick
|
3
5
|
delegate :to_s, to: :code
|
4
6
|
delegate :meter, to: :composition
|
@@ -12,15 +14,31 @@ class HeadMusic::Position
|
|
12
14
|
end
|
13
15
|
|
14
16
|
def code
|
15
|
-
|
17
|
+
values.join(':')
|
16
18
|
end
|
17
19
|
|
18
20
|
def state
|
19
21
|
[composition.name, code].join(' ')
|
20
22
|
end
|
21
23
|
|
22
|
-
def
|
23
|
-
|
24
|
+
def values
|
25
|
+
[measure_number, count, tick]
|
26
|
+
end
|
27
|
+
|
28
|
+
def <=>(other)
|
29
|
+
self.values <=> other.values
|
30
|
+
end
|
31
|
+
|
32
|
+
def strength
|
33
|
+
meter.beat_strength(count, tick: tick)
|
34
|
+
end
|
35
|
+
|
36
|
+
def strong?
|
37
|
+
strength >= 80
|
38
|
+
end
|
39
|
+
|
40
|
+
def weak?
|
41
|
+
!strong?
|
24
42
|
end
|
25
43
|
|
26
44
|
private
|
@@ -5,8 +5,6 @@ class HeadMusic::RhythmicUnit
|
|
5
5
|
BRITISH_MULTIPLE_NAMES = %w[semibreve breve longa maxima]
|
6
6
|
BRITISH_DIVISION_NAMES = %w[semibreve minim crotchet quaver semiquaver demisemiquaver hemidemisemiquaver semihemidemisemiquaver demisemihemidemisemiquaver]
|
7
7
|
|
8
|
-
PPQN = PULSES_PER_QUARTER_NOTE = 960
|
9
|
-
|
10
8
|
def self.get(name)
|
11
9
|
@rhythmic_units ||= {}
|
12
10
|
hash_key = HeadMusic::Utilities::HashKey.for(name)
|
@@ -32,7 +30,7 @@ class HeadMusic::RhythmicUnit
|
|
32
30
|
end
|
33
31
|
|
34
32
|
def ticks
|
35
|
-
PPQN * 4 * relative_value
|
33
|
+
HeadMusic::Rhythm::PPQN * 4 * relative_value
|
36
34
|
end
|
37
35
|
|
38
36
|
def notehead
|
data/lib/head_music/scale.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
class HeadMusic::Scale
|
2
|
-
|
2
|
+
SCALE_REGEX = /^[A-G][#b]?\s+\w+$/
|
3
|
+
|
4
|
+
def self.get(root_pitch, scale_type = nil)
|
5
|
+
if root_pitch.is_a?(String) && scale_type =~ SCALE_REGEX
|
6
|
+
root_pitch, scale_type = root_pitch.split(/\s+/)
|
7
|
+
end
|
3
8
|
root_pitch = HeadMusic::Pitch.get(root_pitch)
|
4
|
-
scale_type
|
9
|
+
scale_type = HeadMusic::ScaleType.get(scale_type || :major)
|
5
10
|
@scales ||= {}
|
6
|
-
|
7
|
-
|
11
|
+
name = [root_pitch.to_s, scale_type].join(' ')
|
12
|
+
hash_key = HeadMusic::Utilities::HashKey.for(name)
|
13
|
+
@scales[hash_key] ||= new(root_pitch, scale_type)
|
8
14
|
end
|
9
15
|
|
10
16
|
attr_reader :root_pitch, :scale_type
|
@@ -66,6 +66,10 @@ class HeadMusic::ScaleType
|
|
66
66
|
end
|
67
67
|
singleton_class.send(:alias_method, :[], :get)
|
68
68
|
|
69
|
+
def self.default
|
70
|
+
get(:major)
|
71
|
+
end
|
72
|
+
|
69
73
|
attr_reader :name, :ascending_intervals, :descending_intervals, :parent_name
|
70
74
|
alias_method :intervals, :ascending_intervals
|
71
75
|
|
data/lib/head_music/version.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
class HeadMusic::Voice
|
2
|
+
include Comparable
|
3
|
+
|
4
|
+
attr_reader :composition, :placements
|
5
|
+
|
6
|
+
def initialize(composition = nil)
|
7
|
+
@composition = composition || Composition.new(name: "Unnamed")
|
8
|
+
@placements = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def place(position, rhythmic_value, pitch = nil)
|
12
|
+
HeadMusic::Placement.new(self, position, rhythmic_value, pitch).tap { |placement|
|
13
|
+
insert_into_placements(placement)
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def insert_into_placements(placement)
|
20
|
+
@placements << placement
|
21
|
+
@placements = @placements.sort
|
22
|
+
end
|
23
|
+
end
|
data/lib/head_music.rb
CHANGED
@@ -21,14 +21,17 @@ require 'head_music/note'
|
|
21
21
|
require 'head_music/octave'
|
22
22
|
require 'head_music/pitch_class'
|
23
23
|
require 'head_music/pitch'
|
24
|
+
require 'head_music/placement'
|
24
25
|
require 'head_music/position'
|
25
26
|
require 'head_music/quality'
|
27
|
+
require 'head_music/rhythm'
|
26
28
|
require 'head_music/rhythmic_unit'
|
27
29
|
require 'head_music/rhythmic_value'
|
28
30
|
require 'head_music/scale'
|
29
31
|
require 'head_music/scale_type'
|
30
32
|
require 'head_music/spelling'
|
31
33
|
require 'head_music/staff'
|
34
|
+
require 'head_music/voice'
|
32
35
|
require 'head_music/utilities/hash_key'
|
33
36
|
|
34
37
|
module HeadMusic
|
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.8.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: 2017-03-
|
11
|
+
date: 2017-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -133,8 +133,10 @@ files:
|
|
133
133
|
- lib/head_music/octave.rb
|
134
134
|
- lib/head_music/pitch.rb
|
135
135
|
- lib/head_music/pitch_class.rb
|
136
|
+
- lib/head_music/placement.rb
|
136
137
|
- lib/head_music/position.rb
|
137
138
|
- lib/head_music/quality.rb
|
139
|
+
- lib/head_music/rhythm.rb
|
138
140
|
- lib/head_music/rhythmic_unit.rb
|
139
141
|
- lib/head_music/rhythmic_value.rb
|
140
142
|
- lib/head_music/scale.rb
|
@@ -143,6 +145,7 @@ files:
|
|
143
145
|
- lib/head_music/staff.rb
|
144
146
|
- lib/head_music/utilities/hash_key.rb
|
145
147
|
- lib/head_music/version.rb
|
148
|
+
- lib/head_music/voice.rb
|
146
149
|
homepage: https://github.com/roberthead/head_music
|
147
150
|
licenses:
|
148
151
|
- MIT
|