head_music 0.19.1 → 0.19.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 838a7ebfc5a6873d62ac2d484b655bcc5fe37302
4
- data.tar.gz: 669f4f3f9ab721dc93208b39ef22ac7170951ba9
3
+ metadata.gz: 2261f261fb02f64ef7142d521b9ac01c1cb1e2c8
4
+ data.tar.gz: de8ced9611c0b4c4db2e7e4449341095eaa82986
5
5
  SHA512:
6
- metadata.gz: 0ca1b44616d4aed6f76e5838b99a546b28889fed30802fec1c8e5ee713af89352e1326e89bf9e5195104722a574c7f8db47755fa6b561685c1b18b190fc38525
7
- data.tar.gz: cc2a14f96a3ec7e7590c59e2b81023ed41a5e9b2f7fb8fd5d25a860c929ece23fa8e2df32adf15faa508b1ed21856a774642d3e4ba40ec4299ce3d2d640df6dd
6
+ metadata.gz: 5e8b3efc74e53e4a3ab83c56c16879c974111a5da9de8abe9c2b362ddea98c96ff43df38d06e6ec110ea00133fd5a208a4b41897b10a76ab01d3799173c1d508
7
+ data.tar.gz: a9765c3240a4cca996d25532d9d513fa3f4a0d0984209cbba2599fd1b7f221a6159e0ac6729e503f65861fdd0f259b9ad8bedc4f66379bbc2cbb895f2a29a97e
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Note is like a placement, except:
3
+ # Note quacks like a placement, but requires a different set of construction arguments
4
4
  # - always has a pitch
5
- # - doesn't require voice and position
5
+ # - receives a voice and position if unspecified
6
6
  class HeadMusic::Note
7
7
  attr_accessor :pitch, :rhythmic_value, :voice, :position
8
8
 
@@ -57,6 +57,9 @@ class HeadMusic::KeySignature
57
57
  flats.any? ? flats : sharps
58
58
  end
59
59
 
60
+ alias sharps_and_flats signs
61
+ alias accidentals signs
62
+
60
63
  def name
61
64
  [tonic_spelling, scale_type].join(' ')
62
65
  end
@@ -14,14 +14,26 @@ class HeadMusic::Pitch
14
14
 
15
15
  delegate :smallest_interval_to, to: :pitch_class
16
16
 
17
+ delegate :enharmonic_equivalent?, :enharmonic?, to: :enharmonic_equivalence
18
+ delegate :octave_equivalent?, to: :octave_equivalence
19
+
17
20
  def self.get(value)
18
- from_name(value) || from_number(value)
21
+ from_pitch_class(value) || from_name(value) || from_number(value)
19
22
  end
20
23
 
21
24
  def self.middle_c
22
25
  get('C4')
23
26
  end
24
27
 
28
+ def self.concert_a
29
+ get('A4')
30
+ end
31
+
32
+ def self.from_pitch_class(pitch_class)
33
+ return nil unless pitch_class.is_a?(HeadMusic::PitchClass)
34
+ fetch_or_create(pitch_class.sharp_spelling)
35
+ end
36
+
25
37
  def self.from_name(name)
26
38
  return nil unless name == name.to_s
27
39
  fetch_or_create(HeadMusic::Spelling.get(name), HeadMusic::Octave.get(name).to_i)
@@ -49,7 +61,8 @@ class HeadMusic::Pitch
49
61
  get(natural_letter_pitch)
50
62
  end
51
63
 
52
- def self.fetch_or_create(spelling, octave)
64
+ def self.fetch_or_create(spelling, octave = nil)
65
+ octave ||= HeadMusic::Octave::DEFAULT
53
66
  return unless spelling && (-1..9).cover?(octave)
54
67
  @pitches ||= {}
55
68
  hash_key = [spelling, octave].join
@@ -84,10 +97,6 @@ class HeadMusic::Pitch
84
97
  HeadMusic::Pitch.get(to_s.gsub(/[#b]/, ''))
85
98
  end
86
99
 
87
- def enharmonic?(other)
88
- midi_note_number == other.midi_note_number
89
- end
90
-
91
100
  def +(other)
92
101
  HeadMusic::Pitch.get(to_i + other.to_i)
93
102
  end
@@ -119,10 +128,26 @@ class HeadMusic::Pitch
119
128
  HeadMusic::Pitch.get([target_letter_name(num_steps), octave + octaves_delta(num_steps)].join)
120
129
  end
121
130
 
131
+ def frequency
132
+ tuning.frequency_for(self)
133
+ end
134
+
122
135
  private_class_method :new
123
136
 
124
137
  private
125
138
 
139
+ def enharmonic_equivalence
140
+ @enharmonic_equivalence ||= EnharmonicEquivalence.get(self)
141
+ end
142
+
143
+ def octave_equivalence
144
+ @octave_equivalence ||= OctaveEquivalence.get(self)
145
+ end
146
+
147
+ def tuning
148
+ @tuning ||= HeadMusic::Tuning.new
149
+ end
150
+
126
151
  def octaves_delta(num_steps)
127
152
  octaves_delta = (num_steps.abs / 7) * (num_steps >= 0 ? 1 : -1)
128
153
  if wrapped_down?(num_steps)
@@ -145,4 +170,54 @@ class HeadMusic::Pitch
145
170
  @target_letter_name ||= {}
146
171
  @target_letter_name[num_steps] ||= letter_name.steps(num_steps)
147
172
  end
173
+
174
+ # Enharmonic equivalence occurs when two pitches are spelled differently but refer to the same frequency, such as D♯ and E♭.
175
+ class EnharmonicEquivalence
176
+ def self.get(pitch)
177
+ pitch = HeadMusic::Pitch.get(pitch)
178
+ @enharmonic_equivalences ||= {}
179
+ @enharmonic_equivalences[pitch.to_s] ||= new(pitch)
180
+ end
181
+
182
+ attr_reader :pitch
183
+
184
+ delegate :pitch_class, to: :pitch
185
+
186
+ def initialize(pitch)
187
+ @pitch = HeadMusic::Pitch.get(pitch)
188
+ end
189
+
190
+ def enharmonic_equivalent?(other)
191
+ other = HeadMusic::Pitch.get(other)
192
+ pitch.midi_note_number == other.midi_note_number && pitch.spelling != other.spelling
193
+ end
194
+
195
+ alias enharmonic? enharmonic_equivalent?
196
+ alias equivalent? enharmonic_equivalent?
197
+
198
+ private_class_method :new
199
+ end
200
+
201
+ # Octave equivalence is the functional equivalence of pitches with the same spelling separated by one or more octaves.
202
+ class OctaveEquivalence
203
+ def self.get(pitch)
204
+ @octave_equivalences ||= {}
205
+ @octave_equivalences[pitch.to_s] ||= new(pitch)
206
+ end
207
+
208
+ attr_reader :pitch
209
+
210
+ def initialize(pitch)
211
+ @pitch = pitch
212
+ end
213
+
214
+ def octave_equivalent?(other)
215
+ other = HeadMusic::Pitch.get(other)
216
+ pitch.spelling == other.spelling && pitch.octave != other.octave
217
+ end
218
+
219
+ alias equivalent? octave_equivalent?
220
+
221
+ private_class_method :new
222
+ end
148
223
  end
@@ -1,15 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # A pitch class is a set of notes separated by octaves.
3
+ # A pitch class is a set of pitches separated by octaves.
4
4
  class HeadMusic::PitchClass
5
5
  attr_reader :number
6
+ attr_reader :spelling
6
7
 
7
8
  SHARP_SPELLINGS = %w[C C# D D# E F F# G G# A A# B].freeze
8
9
  FLAT_SPELLINGS = %w[C Db D Eb E F Gb G Ab A Bb B].freeze
9
10
 
10
11
  def self.get(identifier)
11
12
  @pitch_classes ||= {}
12
- number = HeadMusic::Spelling.get(identifier).pitch_class.to_i if HeadMusic::Spelling.matching_string(identifier)
13
+ if HeadMusic::Spelling.matching_string(identifier)
14
+ spelling = HeadMusic::Spelling.get(identifier)
15
+ number = spelling.pitch_class.to_i
16
+ end
13
17
  number ||= identifier.to_i % 12
14
18
  @pitch_classes[number] ||= new(number)
15
19
  end
@@ -26,6 +30,14 @@ class HeadMusic::PitchClass
26
30
  number
27
31
  end
28
32
 
33
+ def sharp_spelling
34
+ SHARP_SPELLINGS[number]
35
+ end
36
+
37
+ def flat_spelling
38
+ FLAT_SPELLINGS[number]
39
+ end
40
+
29
41
  # Pass in the number of semitones
30
42
  def +(other)
31
43
  HeadMusic::PitchClass.get(to_i + other.to_i)
@@ -51,5 +63,13 @@ class HeadMusic::PitchClass
51
63
  intervals_to(other).first
52
64
  end
53
65
 
66
+ def white_key?
67
+ [0,2,4,5,7,9,11].include?(number)
68
+ end
69
+
70
+ def black_key?
71
+ !white_key?
72
+ end
73
+
54
74
  private_class_method :new
55
75
  end
@@ -87,4 +87,29 @@ class HeadMusic::Spelling
87
87
  end
88
88
 
89
89
  private_class_method :new
90
+
91
+ # Enharmonic equivalence occurs when two spellings refer to the same pitch class, such as D♯ and E♭.
92
+ class EnharmonicEquivalence
93
+ def self.get(spelling)
94
+ spelling = HeadMusic::Spelling.get(spelling)
95
+ @enharmonic_equivalences ||= {}
96
+ @enharmonic_equivalences[spelling.to_s] ||= new(spelling)
97
+ end
98
+
99
+ attr_reader :spelling
100
+
101
+ def initialize(spelling)
102
+ @spelling = HeadMusic::Spelling.get(spelling)
103
+ end
104
+
105
+ def enharmonic_equivalent?(other)
106
+ other = HeadMusic::Spelling.get(other)
107
+ spelling != other && spelling.pitch_class_number == other.pitch_class_number
108
+ end
109
+
110
+ alias enharmonic? enharmonic_equivalent?
111
+ alias equivalent? enharmonic_equivalent?
112
+
113
+ private_class_method :new
114
+ end
90
115
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A tuning has a reference pitch and frequency and provides frequencies for all pitches
4
+ # The base class assumes equal temperament tuning. By default, A4 = 440.0 Hz
5
+ class HeadMusic::Tuning
6
+ REFERENCE_FREQUENCY = 440.0
7
+ REFERENCE_PITCH_NAME = 'A4'
8
+
9
+ attr_reader :reference_pitch, :reference_frequency
10
+
11
+ def initialize(reference_pitch: nil, reference_frequency: nil)
12
+ @reference_pitch = reference_pitch || HeadMusic::Pitch.get(REFERENCE_PITCH_NAME)
13
+ @reference_frequency = reference_frequency || REFERENCE_FREQUENCY
14
+ end
15
+
16
+ def frequency_for(pitch)
17
+ pitch = HeadMusic::Pitch.get(pitch) unless pitch.is_a?(HeadMusic::Pitch)
18
+ reference_frequency * (2**(1.0 / 12))**(pitch - reference_pitch).semitones
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HeadMusic
4
- VERSION = '0.19.1'
4
+ VERSION = '0.19.2'
5
5
  end
data/lib/head_music.rb CHANGED
@@ -46,6 +46,7 @@ require 'head_music/scale_type'
46
46
  require 'head_music/sign'
47
47
  require 'head_music/spelling'
48
48
  require 'head_music/staff'
49
+ require 'head_music/tuning'
49
50
 
50
51
  # content
51
52
  require 'head_music/content/bar'
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.19.1
4
+ version: 0.19.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Head
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-18 00:00:00.000000000 Z
11
+ date: 2018-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -190,6 +190,7 @@ files:
190
190
  - lib/head_music/style/guides/fux_cantus_firmus.rb
191
191
  - lib/head_music/style/guides/modern_cantus_firmus.rb
192
192
  - lib/head_music/style/mark.rb
193
+ - lib/head_music/tuning.rb
193
194
  - lib/head_music/utilities/hash_key.rb
194
195
  - lib/head_music/version.rb
195
196
  homepage: https://github.com/roberthead/head_music