head_music 0.7.0 → 0.8.0
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/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
|