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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c74af27a5ca661511c85f7cb4bc516a8b9dac3ad
4
- data.tar.gz: aa5a6603255d9a06c2233acc03584e6765641d24
3
+ metadata.gz: 56a211e11bb35239af47cdf2e79e2032b6b97f7b
4
+ data.tar.gz: 293be36552913da8cd64f946b4b1ece210114991
5
5
  SHA512:
6
- metadata.gz: fef4a15562ef45e51da6ed0b132cfe7d3da2db38717b164dd638a0a89c61ad5d67bfdf42b8ca5d91cd37b4e7b097200399ab5e6feb0fcfe611053b4b1fbf9b98
7
- data.tar.gz: 5373d9a300e8fbcd3ba92002655a1fc5d6d70839947b2b76830421f319a39019d7858fa35d11ee7ef1e77563af375e17903242f54cb0cf608bf33d8d20083a7f
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 :quality_name
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, quality_name = identifier.split(/\s/)
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, quality_name)
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
- def initialize(tonic_spelling, quality_name = nil)
24
- @tonic_spelling = tonic_spelling
25
- @quality_name = quality_name || :major
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
- SHARPS.first(num_sharps)
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
- FLATS.first(num_flats)
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
- (HeadMusic::Circle.of_fifths.index(tonic_pitch_class) - quality_name_adjustment) % 12
50
+ sharps.length
38
51
  end
39
52
 
40
53
  def num_flats
41
- (HeadMusic::Circle.of_fourths.index(tonic_pitch_class) + quality_name_adjustment) % 12
54
+ flats.length
42
55
  end
43
56
 
44
57
  def sharps_or_flats
45
- return sharps if @tonic_spelling.to_s =~ /#/
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, quality_name.to_s].join(' ')
62
+ [tonic_spelling.to_s, scale_type.to_s].join(' ')
52
63
  end
53
64
 
54
65
  def ==(other)
55
- self.to_s == other.to_s
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
@@ -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
- [measure_number, count, tick].join(':')
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 ==(other)
23
- self.state == other.state
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
@@ -0,0 +1,3 @@
1
+ module HeadMusic::Rhythm
2
+ PPQN = PULSES_PER_QUARTER_NOTE = 960
3
+ end
@@ -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
@@ -27,7 +27,7 @@ class HeadMusic::RhythmicValue
27
27
  end
28
28
 
29
29
  def ticks
30
- HeadMusic::RhythmicUnit::PPQN * 4 * total_value
30
+ HeadMusic::Rhythm::PPQN * 4 * total_value
31
31
  end
32
32
 
33
33
  def per_whole
@@ -1,10 +1,16 @@
1
1
  class HeadMusic::Scale
2
- def self.get(root_pitch, scale_type_name = nil)
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 ||= HeadMusic::ScaleType.get(scale_type_name || :major)
9
+ scale_type = HeadMusic::ScaleType.get(scale_type || :major)
5
10
  @scales ||= {}
6
- @scales[root_pitch.to_s] ||= {}
7
- @scales[root_pitch.to_s][scale_type.name] ||= new(root_pitch, scale_type)
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
 
@@ -1,3 +1,3 @@
1
1
  module HeadMusic
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -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.7.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-03 00:00:00.000000000 Z
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