head_music 7.0.5 → 8.0.1

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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +1 -1
  4. data/lib/head_music/{circle.rb → analysis/circle.rb} +9 -6
  5. data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/category.rb +1 -1
  6. data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/naming.rb +6 -6
  7. data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/parser.rb +4 -4
  8. data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/semitones.rb +4 -4
  9. data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/size.rb +1 -1
  10. data/lib/head_music/{diatonic_interval.rb → analysis/diatonic_interval.rb} +19 -16
  11. data/lib/head_music/{harmonic_interval.rb → analysis/harmonic_interval.rb} +5 -2
  12. data/lib/head_music/{interval_cycle.rb → analysis/interval_cycle.rb} +12 -9
  13. data/lib/head_music/{melodic_interval.rb → analysis/melodic_interval.rb} +7 -4
  14. data/lib/head_music/{motion.rb → analysis/motion.rb} +6 -3
  15. data/lib/head_music/{pitch_class_set.rb → analysis/pitch_class_set.rb} +5 -2
  16. data/lib/head_music/{pitch_set.rb → analysis/pitch_set.rb} +14 -11
  17. data/lib/head_music/{sonority.rb → analysis/sonority.rb} +7 -4
  18. data/lib/head_music/content/bar.rb +2 -2
  19. data/lib/head_music/content/composition.rb +3 -3
  20. data/lib/head_music/content/note.rb +1 -1
  21. data/lib/head_music/content/placement.rb +1 -1
  22. data/lib/head_music/content/position.rb +1 -1
  23. data/lib/head_music/content/rhythmic_value.rb +3 -3
  24. data/lib/head_music/{staff.rb → content/staff.rb} +6 -3
  25. data/lib/head_music/content/voice.rb +2 -2
  26. data/lib/head_music/{instrument.rb → instruments/instrument.rb} +10 -7
  27. data/lib/head_music/{data → instruments}/instrument_families.yml +49 -0
  28. data/lib/head_music/{instrument_family.rb → instruments/instrument_family.rb} +5 -2
  29. data/lib/head_music/{data → instruments}/instruments.yml +116 -0
  30. data/lib/head_music/{instrument → instruments}/staff.rb +5 -2
  31. data/lib/head_music/{instrument → instruments}/staff_scheme.rb +7 -2
  32. data/lib/head_music/{instrument → instruments}/variant.rb +6 -3
  33. data/lib/head_music/locales/en.yml +22 -0
  34. data/lib/head_music/{alteration.rb → rudiment/alteration.rb} +9 -6
  35. data/lib/head_music/{chromatic_interval.rb → rudiment/chromatic_interval.rb} +7 -4
  36. data/lib/head_music/{clef.rb → rudiment/clef.rb} +7 -4
  37. data/lib/head_music/{consonance.rb → rudiment/consonance.rb} +4 -1
  38. data/lib/head_music/{key_signature → rudiment/key_signature}/enharmonic_equivalence.rb +4 -4
  39. data/lib/head_music/{key_signature.rb → rudiment/key_signature.rb} +10 -7
  40. data/lib/head_music/{letter_name.rb → rudiment/letter_name.rb} +9 -6
  41. data/lib/head_music/{meter.rb → rudiment/meter.rb} +6 -3
  42. data/lib/head_music/{musical_symbol.rb → rudiment/musical_symbol.rb} +4 -1
  43. data/lib/head_music/{pitch → rudiment/pitch}/enharmonic_equivalence.rb +4 -4
  44. data/lib/head_music/{pitch → rudiment/pitch}/octave_equivalence.rb +2 -2
  45. data/lib/head_music/{pitch.rb → rudiment/pitch.rb} +30 -27
  46. data/lib/head_music/{pitch_class.rb → rudiment/pitch_class.rb} +14 -11
  47. data/lib/head_music/{quality.rb → rudiment/quality.rb} +4 -1
  48. data/lib/head_music/{reference_pitch.rb → rudiment/reference_pitch.rb} +5 -2
  49. data/lib/head_music/{register.rb → rudiment/register.rb} +6 -3
  50. data/lib/head_music/rudiment/rhythm.rb +6 -0
  51. data/lib/head_music/{rhythmic_unit.rb → rudiment/rhythmic_unit.rb} +5 -2
  52. data/lib/head_music/{scale.rb → rudiment/scale.rb} +12 -9
  53. data/lib/head_music/{scale_degree.rb → rudiment/scale_degree.rb} +8 -5
  54. data/lib/head_music/{scale_type.rb → rudiment/scale_type.rb} +4 -1
  55. data/lib/head_music/{solmization.rb → rudiment/solmization.rb} +4 -1
  56. data/lib/head_music/{spelling.rb → rudiment/spelling.rb} +20 -17
  57. data/lib/head_music/{tuning.rb → rudiment/tuning.rb} +6 -3
  58. data/lib/head_music/style/annotation.rb +6 -6
  59. data/lib/head_music/style/guidelines/consonant_climax.rb +4 -4
  60. data/lib/head_music/style/guidelines/diatonic.rb +1 -1
  61. data/lib/head_music/style/guidelines/step_out_of_unison.rb +1 -1
  62. data/lib/head_music/version.rb +1 -1
  63. data/lib/head_music.rb +50 -46
  64. metadata +51 -51
  65. data/lib/head_music/rhythm.rb +0 -3
  66. /data/lib/head_music/{data → rudiment}/clefs.yml +0 -0
  67. /data/lib/head_music/{solmizations.yml → rudiment/solmizations.yml} +0 -0
@@ -1,19 +1,19 @@
1
1
  # Key signatures are enharmonic when all pitch classes in one are respellings of the pitch classes in the other.
2
- class HeadMusic::KeySignature::EnharmonicEquivalence
2
+ class HeadMusic::Rudiment::KeySignature::EnharmonicEquivalence
3
3
  attr_reader :key_signature
4
4
 
5
5
  def self.get(key_signature)
6
- key_signature = HeadMusic::KeySignature.get(key_signature)
6
+ key_signature = HeadMusic::Rudiment::KeySignature.get(key_signature)
7
7
  @enharmonic_equivalences ||= {}
8
8
  @enharmonic_equivalences[key_signature.to_s] ||= new(key_signature)
9
9
  end
10
10
 
11
11
  def initialize(key_signature)
12
- @key_signature = HeadMusic::KeySignature.get(key_signature)
12
+ @key_signature = HeadMusic::Rudiment::KeySignature.get(key_signature)
13
13
  end
14
14
 
15
15
  def enharmonic_equivalent?(other)
16
- other = HeadMusic::KeySignature.get(other)
16
+ other = HeadMusic::Rudiment::KeySignature.get(other)
17
17
 
18
18
  key_signature.pitch_classes.map(&:to_i).sort == other.pitch_classes.map(&:to_i).sort &&
19
19
  key_signature.alterations.map(&:to_s).sort != other.alterations.map(&:to_s).sort
@@ -1,5 +1,8 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # Represents a key signature.
2
- class HeadMusic::KeySignature
5
+ class HeadMusic::Rudiment::KeySignature
3
6
  attr_reader :tonic_spelling, :scale_type, :scale
4
7
 
5
8
  ORDERED_LETTER_NAMES_OF_SHARPS = %w[F C G D A E B].freeze
@@ -10,7 +13,7 @@ class HeadMusic::KeySignature
10
13
  end
11
14
 
12
15
  def self.get(identifier)
13
- return identifier if identifier.is_a?(HeadMusic::KeySignature)
16
+ return identifier if identifier.is_a?(HeadMusic::Rudiment::KeySignature)
14
17
 
15
18
  @key_signatures ||= {}
16
19
  tonic_spelling, scale_type_name = identifier.strip.split(/\s/)
@@ -23,11 +26,11 @@ class HeadMusic::KeySignature
23
26
  delegate :pitches, :pitch_classes, to: :scale
24
27
 
25
28
  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
+ @tonic_spelling = HeadMusic::Rudiment::Spelling.get(tonic_spelling)
30
+ @scale_type = HeadMusic::Rudiment::ScaleType.get(scale_type) if scale_type
31
+ @scale_type ||= HeadMusic::Rudiment::ScaleType.default
29
32
  @scale_type = @scale_type.parent || @scale_type
30
- @scale = HeadMusic::Scale.get(@tonic_spelling, @scale_type)
33
+ @scale = HeadMusic::Rudiment::Scale.get(@tonic_spelling, @scale_type)
31
34
  end
32
35
 
33
36
  def spellings
@@ -102,6 +105,6 @@ class HeadMusic::KeySignature
102
105
  private
103
106
 
104
107
  def enharmonic_equivalence
105
- @enharmonic_equivalence ||= HeadMusic::KeySignature::EnharmonicEquivalence.get(self)
108
+ @enharmonic_equivalence ||= HeadMusic::Rudiment::KeySignature::EnharmonicEquivalence.get(self)
106
109
  end
107
110
  end
@@ -1,5 +1,8 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # Music has seven lette names that are used to identify pitches and pitch classes.
2
- class HeadMusic::LetterName
5
+ class HeadMusic::Rudiment::LetterName
3
6
  NAMES = %w[C D E F G A B].freeze
4
7
 
5
8
  NATURAL_PITCH_CLASS_NUMBERS = {
@@ -32,7 +35,7 @@ class HeadMusic::LetterName
32
35
 
33
36
  pitch_class = pitch_class.to_i % 12
34
37
  name = NAMES.detect { |candidate| pitch_class == NATURAL_PITCH_CLASS_NUMBERS[candidate] }
35
- name ||= HeadMusic::PitchClass::SHARP_SPELLINGS[pitch_class].first
38
+ name ||= HeadMusic::Rudiment::PitchClass::SHARP_SPELLINGS[pitch_class].first
36
39
  @letter_names[name] ||= new(name) if NAMES.include?(name)
37
40
  end
38
41
 
@@ -47,7 +50,7 @@ class HeadMusic::LetterName
47
50
  end
48
51
 
49
52
  def pitch_class
50
- HeadMusic::PitchClass.get(NATURAL_PITCH_CLASS_NUMBERS[name])
53
+ HeadMusic::Rudiment::PitchClass.get(NATURAL_PITCH_CLASS_NUMBERS[name])
51
54
  end
52
55
 
53
56
  def ==(other)
@@ -59,15 +62,15 @@ class HeadMusic::LetterName
59
62
  end
60
63
 
61
64
  def steps_up(num)
62
- HeadMusic::LetterName.get(series_ascending[num % NAMES.length])
65
+ HeadMusic::Rudiment::LetterName.get(series_ascending[num % NAMES.length])
63
66
  end
64
67
 
65
68
  def steps_down(num)
66
- HeadMusic::LetterName.get(series_descending[num % NAMES.length])
69
+ HeadMusic::Rudiment::LetterName.get(series_descending[num % NAMES.length])
67
70
  end
68
71
 
69
72
  def steps_to(other, direction = :ascending)
70
- other = HeadMusic::LetterName.get(other)
73
+ other = HeadMusic::Rudiment::LetterName.get(other)
71
74
  other_position = other.position
72
75
  if direction == :descending
73
76
  other_position -= NAMES.length if other_position > position
@@ -1,5 +1,8 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # Meter is the rhythmic size of a measure, such as 4/4 or 6/8
2
- class HeadMusic::Meter
5
+ class HeadMusic::Rudiment::Meter
3
6
  attr_reader :top_number, :bottom_number
4
7
 
5
8
  NAMED = {
@@ -74,13 +77,13 @@ class HeadMusic::Meter
74
77
  end
75
78
 
76
79
  def count_unit
77
- HeadMusic::RhythmicUnit.for_denominator_value(bottom_number)
80
+ HeadMusic::Rudiment::RhythmicUnit.for_denominator_value(bottom_number)
78
81
  end
79
82
 
80
83
  def beat_unit
81
84
  @beat_unit ||=
82
85
  if compound?
83
- HeadMusic::Content::RhythmicValue.new(HeadMusic::RhythmicUnit.for_denominator_value(bottom_number / 2), dots: 1)
86
+ HeadMusic::Content::RhythmicValue.new(HeadMusic::Rudiment::RhythmicUnit.for_denominator_value(bottom_number / 2), dots: 1)
84
87
  else
85
88
  HeadMusic::Content::RhythmicValue.new(count_unit)
86
89
  end
@@ -1,5 +1,8 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # A symbol is a mark or sign that signifies a particular rudiment of music
2
- class HeadMusic::MusicalSymbol
5
+ class HeadMusic::Rudiment::MusicalSymbol
3
6
  attr_reader :ascii, :unicode, :html_entity
4
7
 
5
8
  def initialize(ascii: nil, unicode: nil, html_entity: nil)
@@ -1,7 +1,7 @@
1
1
  # An enharmonic equivalent pitch is the same frequency spelled differently, such as D# and Eb.
2
- class HeadMusic::Pitch::EnharmonicEquivalence
2
+ class HeadMusic::Rudiment::Pitch::EnharmonicEquivalence
3
3
  def self.get(pitch)
4
- pitch = HeadMusic::Pitch.get(pitch)
4
+ pitch = HeadMusic::Rudiment::Pitch.get(pitch)
5
5
  @enharmonic_equivalences ||= {}
6
6
  @enharmonic_equivalences[pitch.to_s] ||= new(pitch)
7
7
  end
@@ -11,11 +11,11 @@ class HeadMusic::Pitch::EnharmonicEquivalence
11
11
  delegate :pitch_class, to: :pitch
12
12
 
13
13
  def initialize(pitch)
14
- @pitch = HeadMusic::Pitch.get(pitch)
14
+ @pitch = HeadMusic::Rudiment::Pitch.get(pitch)
15
15
  end
16
16
 
17
17
  def enharmonic_equivalent?(other)
18
- other = HeadMusic::Pitch.get(other)
18
+ other = HeadMusic::Rudiment::Pitch.get(other)
19
19
  pitch.midi_note_number == other.midi_note_number && pitch.spelling != other.spelling
20
20
  end
21
21
 
@@ -1,5 +1,5 @@
1
1
  # Octave equivalence is the functional equivalence of pitches with the same spelling separated by one or more octaves.
2
- class HeadMusic::Pitch::OctaveEquivalence
2
+ class HeadMusic::Rudiment::Pitch::OctaveEquivalence
3
3
  def self.get(pitch)
4
4
  @octave_equivalences ||= {}
5
5
  @octave_equivalences[pitch.to_s] ||= new(pitch)
@@ -12,7 +12,7 @@ class HeadMusic::Pitch::OctaveEquivalence
12
12
  end
13
13
 
14
14
  def octave_equivalent?(other)
15
- other = HeadMusic::Pitch.get(other)
15
+ other = HeadMusic::Rudiment::Pitch.get(other)
16
16
  pitch.spelling == other.spelling && pitch.register != other.register
17
17
  end
18
18
 
@@ -1,5 +1,8 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # A pitch is a named frequency represented by a spelling and a register.
2
- class HeadMusic::Pitch
5
+ class HeadMusic::Rudiment::Pitch
3
6
  include Comparable
4
7
 
5
8
  attr_reader :spelling, :register
@@ -40,7 +43,7 @@ class HeadMusic::Pitch
40
43
  end
41
44
 
42
45
  def self.from_pitch_class(pitch_class)
43
- return nil unless pitch_class.is_a?(HeadMusic::PitchClass)
46
+ return nil unless pitch_class.is_a?(HeadMusic::Rudiment::PitchClass)
44
47
 
45
48
  fetch_or_create(pitch_class.sharp_spelling)
46
49
  end
@@ -48,33 +51,33 @@ class HeadMusic::Pitch
48
51
  def self.from_name(name)
49
52
  return nil unless name == name.to_s
50
53
 
51
- fetch_or_create(HeadMusic::Spelling.get(name), HeadMusic::Register.get(name).to_i)
54
+ fetch_or_create(HeadMusic::Rudiment::Spelling.get(name), HeadMusic::Rudiment::Register.get(name).to_i)
52
55
  end
53
56
 
54
57
  def self.from_number(number)
55
58
  return nil unless number == number.to_i
56
59
 
57
- fetch_or_create(HeadMusic::Spelling.from_number(number), (number.to_i / 12) - 1)
60
+ fetch_or_create(HeadMusic::Rudiment::Spelling.from_number(number), (number.to_i / 12) - 1)
58
61
  end
59
62
 
60
63
  def self.from_number_and_letter(number, letter_name)
61
- letter_name = HeadMusic::LetterName.get(letter_name)
64
+ letter_name = HeadMusic::Rudiment::LetterName.get(letter_name)
62
65
  natural_letter_pitch = natural_letter_pitch(number, letter_name)
63
- alteration_interval = natural_letter_pitch.smallest_interval_to(HeadMusic::PitchClass.get(number))
64
- alteration = HeadMusic::Alteration.by(:semitones, alteration_interval) if alteration_interval != 0
65
- spelling = HeadMusic::Spelling.fetch_or_create(letter_name, alteration)
66
+ alteration_interval = natural_letter_pitch.smallest_interval_to(HeadMusic::Rudiment::PitchClass.get(number))
67
+ alteration = HeadMusic::Rudiment::Alteration.by(:semitones, alteration_interval) if alteration_interval != 0
68
+ spelling = HeadMusic::Rudiment::Spelling.fetch_or_create(letter_name, alteration)
66
69
  fetch_or_create(spelling, natural_letter_pitch.register)
67
70
  end
68
71
 
69
72
  def self.natural_letter_pitch(number, letter_name)
70
- natural_letter_pitch = get(HeadMusic::LetterName.get(letter_name).pitch_class)
73
+ natural_letter_pitch = get(HeadMusic::Rudiment::LetterName.get(letter_name).pitch_class)
71
74
  natural_letter_pitch += 12 while (number.to_i - natural_letter_pitch.to_i) >= 6
72
75
  natural_letter_pitch -= 12 while (number.to_i - natural_letter_pitch.to_i) <= -6
73
76
  get(natural_letter_pitch)
74
77
  end
75
78
 
76
79
  def self.fetch_or_create(spelling, register = nil)
77
- register ||= HeadMusic::Register::DEFAULT
80
+ register ||= HeadMusic::Rudiment::Register::DEFAULT
78
81
  return unless spelling && (-1..9).cover?(register)
79
82
 
80
83
  @pitches ||= {}
@@ -83,7 +86,7 @@ class HeadMusic::Pitch
83
86
  end
84
87
 
85
88
  def initialize(spelling, register)
86
- @spelling = HeadMusic::Spelling.get(spelling.to_s)
89
+ @spelling = HeadMusic::Rudiment::Spelling.get(spelling.to_s)
87
90
  @register = register.to_i
88
91
  end
89
92
 
@@ -111,35 +114,35 @@ class HeadMusic::Pitch
111
114
  end
112
115
 
113
116
  def natural
114
- HeadMusic::Pitch.get(to_s.gsub(HeadMusic::Alteration.matcher, ""))
117
+ HeadMusic::Rudiment::Pitch.get(to_s.gsub(HeadMusic::Rudiment::Alteration.matcher, ""))
115
118
  end
116
119
 
117
120
  def +(other)
118
- if other.is_a?(HeadMusic::DiatonicInterval)
121
+ if other.is_a?(HeadMusic::Analysis::DiatonicInterval)
119
122
  # return a pitch
120
123
  other.above(self)
121
124
  else
122
125
  # assume value represents an interval in semitones and return another pitch
123
- HeadMusic::Pitch.get(to_i + other.to_i)
126
+ HeadMusic::Rudiment::Pitch.get(to_i + other.to_i)
124
127
  end
125
128
  end
126
129
 
127
130
  def -(other)
128
131
  case other
129
- when HeadMusic::DiatonicInterval
132
+ when HeadMusic::Analysis::DiatonicInterval
130
133
  # return a pitch
131
134
  other.below(self)
132
- when HeadMusic::Pitch
135
+ when HeadMusic::Rudiment::Pitch
133
136
  # return an interval
134
- HeadMusic::ChromaticInterval.get(to_i - other.to_i)
137
+ HeadMusic::Rudiment::ChromaticInterval.get(to_i - other.to_i)
135
138
  else
136
139
  # assume value represents an interval in semitones and return another pitch
137
- HeadMusic::Pitch.get(to_i - other.to_i)
140
+ HeadMusic::Rudiment::Pitch.get(to_i - other.to_i)
138
141
  end
139
142
  end
140
143
 
141
144
  def ==(other)
142
- other = HeadMusic::Pitch.get(other)
145
+ other = HeadMusic::Rudiment::Pitch.get(other)
143
146
  to_s == other.to_s
144
147
  end
145
148
 
@@ -148,11 +151,11 @@ class HeadMusic::Pitch
148
151
  end
149
152
 
150
153
  def scale(scale_type_name = nil)
151
- HeadMusic::Scale.get(self, scale_type_name)
154
+ HeadMusic::Rudiment::Scale.get(self, scale_type_name)
152
155
  end
153
156
 
154
157
  def natural_steps(num_steps)
155
- HeadMusic::Pitch.get([target_letter_name(num_steps), register + octaves_delta(num_steps)].join)
158
+ HeadMusic::Rudiment::Pitch.get([target_letter_name(num_steps), register + octaves_delta(num_steps)].join)
156
159
  end
157
160
 
158
161
  def frequency
@@ -160,7 +163,7 @@ class HeadMusic::Pitch
160
163
  end
161
164
 
162
165
  def steps_to(other)
163
- other = HeadMusic::Pitch.get(other)
166
+ other = HeadMusic::Rudiment::Pitch.get(other)
164
167
  letter_name_steps_to(other) + 7 * octave_changes_to(other)
165
168
  end
166
169
 
@@ -181,15 +184,15 @@ class HeadMusic::Pitch
181
184
  end
182
185
 
183
186
  def enharmonic_equivalence
184
- @enharmonic_equivalence ||= HeadMusic::Pitch::EnharmonicEquivalence.get(self)
187
+ @enharmonic_equivalence ||= HeadMusic::Rudiment::Pitch::EnharmonicEquivalence.get(self)
185
188
  end
186
189
 
187
190
  def octave_equivalence
188
- @octave_equivalence ||= HeadMusic::Pitch::OctaveEquivalence.get(self)
191
+ @octave_equivalence ||= HeadMusic::Rudiment::Pitch::OctaveEquivalence.get(self)
189
192
  end
190
193
 
191
194
  def tuning
192
- @tuning ||= HeadMusic::Tuning.new
195
+ @tuning ||= HeadMusic::Rudiment::Tuning.new
193
196
  end
194
197
 
195
198
  def octaves_delta(num_steps)
@@ -216,12 +219,12 @@ class HeadMusic::Pitch
216
219
  end
217
220
 
218
221
  def helmholtz_letter_name
219
- return spelling.to_s.downcase if HeadMusic::Register.get(register).helmholtz_case == :lower
222
+ return spelling.to_s.downcase if HeadMusic::Rudiment::Register.get(register).helmholtz_case == :lower
220
223
 
221
224
  spelling.to_s
222
225
  end
223
226
 
224
227
  def helmholtz_marks
225
- HeadMusic::Register.get(register).helmholtz_marks
228
+ HeadMusic::Rudiment::Register.get(register).helmholtz_marks
226
229
  end
227
230
  end
@@ -1,5 +1,8 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # A pitch class is a set of pitches separated by octaves.
2
- class HeadMusic::PitchClass
5
+ class HeadMusic::Rudiment::PitchClass
3
6
  include Comparable
4
7
 
5
8
  attr_reader :number, :spelling
@@ -11,8 +14,8 @@ class HeadMusic::PitchClass
11
14
 
12
15
  def self.get(identifier)
13
16
  @pitch_classes ||= {}
14
- if HeadMusic::Spelling.matching_string(identifier)
15
- spelling = HeadMusic::Spelling.get(identifier)
17
+ if HeadMusic::Rudiment::Spelling.matching_string(identifier)
18
+ spelling = HeadMusic::Rudiment::Spelling.get(identifier)
16
19
  number = spelling.pitch_class.to_i
17
20
  end
18
21
  number ||= identifier.to_i % 12
@@ -48,23 +51,23 @@ class HeadMusic::PitchClass
48
51
  end
49
52
 
50
53
  def smart_spelling(max_sharps_in_major_key_signature: 6)
51
- sharp_key = HeadMusic::KeySignature.get(sharp_spelling)
52
- return HeadMusic::Spelling.get(sharp_spelling) if sharp_key.num_sharps <= max_sharps_in_major_key_signature
54
+ sharp_key = HeadMusic::Rudiment::KeySignature.get(sharp_spelling)
55
+ return HeadMusic::Rudiment::Spelling.get(sharp_spelling) if sharp_key.num_sharps <= max_sharps_in_major_key_signature
53
56
 
54
- flat_key = HeadMusic::KeySignature.get(flat_spelling)
55
- return HeadMusic::Spelling.get(flat_spelling) if flat_key.num_sharps <= max_sharps_in_major_key_signature
57
+ flat_key = HeadMusic::Rudiment::KeySignature.get(flat_spelling)
58
+ return HeadMusic::Rudiment::Spelling.get(flat_spelling) if flat_key.num_sharps <= max_sharps_in_major_key_signature
56
59
 
57
- HeadMusic::Spelling.get(flatter_spelling)
60
+ HeadMusic::Rudiment::Spelling.get(flatter_spelling)
58
61
  end
59
62
 
60
63
  # Pass in the number of semitones
61
64
  def +(other)
62
- HeadMusic::PitchClass.get(to_i + other.to_i)
65
+ HeadMusic::Rudiment::PitchClass.get(to_i + other.to_i)
63
66
  end
64
67
 
65
68
  # Pass in the number of semitones
66
69
  def -(other)
67
- HeadMusic::PitchClass.get(to_i - other.to_i)
70
+ HeadMusic::Rudiment::PitchClass.get(to_i - other.to_i)
68
71
  end
69
72
 
70
73
  def ==(other)
@@ -79,7 +82,7 @@ class HeadMusic::PitchClass
79
82
  def intervals_to(other)
80
83
  delta = other.to_i - to_i
81
84
  inverse = delta.positive? ? delta - 12 : delta + 12
82
- [delta, inverse].sort_by(&:abs).map { |interval| HeadMusic::ChromaticInterval.get(interval) }
85
+ [delta, inverse].sort_by(&:abs).map { |interval| HeadMusic::Rudiment::ChromaticInterval.get(interval) }
83
86
  end
84
87
 
85
88
  def smallest_interval_to(other)
@@ -1,5 +1,8 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # A quality is a categorization of an interval.
2
- class HeadMusic::Quality
5
+ class HeadMusic::Rudiment::Quality
3
6
  SHORTHAND = {
4
7
  perfect: "P",
5
8
  major: "M",
@@ -1,6 +1,9 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # A reference pitch has a pitch and a frequency
2
5
  # With no arguments, it assumes that A4 = 440.0 Hz
3
- class HeadMusic::ReferencePitch
6
+ class HeadMusic::Rudiment::ReferencePitch
4
7
  include HeadMusic::Named
5
8
 
6
9
  DEFAULT_PITCH_NAME = "A4"
@@ -81,7 +84,7 @@ class HeadMusic::ReferencePitch
81
84
 
82
85
  def initialize(name = DEFAULT_REFERENCE_PITCH_NAME)
83
86
  record = named_reference_pitch_record_for_name(name)
84
- @pitch = HeadMusic::Pitch.get(record.fetch(:pitch, DEFAULT_PITCH_NAME))
87
+ @pitch = HeadMusic::Rudiment::Pitch.get(record.fetch(:pitch, DEFAULT_PITCH_NAME))
85
88
  @frequency = record.fetch(:frequency, DEFAULT_FREQUENCY)
86
89
  initialize_keys_from_record(record)
87
90
  end
@@ -1,9 +1,12 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # The register is a numeric octave identifier used in scientific pitch notation.
2
5
  #
3
6
  # A pitch is a spelling plus a register. For example, C4 is middle C and C5 is the C one octave higher.
4
7
  # The number changes between the letter names B and C regardless of sharps and flats,
5
8
  # so as an extreme example, Cb5 is actually a semitone below B#4.
6
- class HeadMusic::Register
9
+ class HeadMusic::Rudiment::Register
7
10
  include Comparable
8
11
 
9
12
  DEFAULT = 4
@@ -21,9 +24,9 @@ class HeadMusic::Register
21
24
  end
22
25
 
23
26
  def self.from_name(string)
24
- return unless string.to_s.match?(HeadMusic::Spelling::MATCHER)
27
+ return unless string.to_s.match?(HeadMusic::Rudiment::Spelling::MATCHER)
25
28
 
26
- _letter, _sign, register_string = string.to_s.match(HeadMusic::Spelling::MATCHER).captures
29
+ _letter, _sign, register_string = string.to_s.match(HeadMusic::Rudiment::Spelling::MATCHER).captures
27
30
  @registers ||= {}
28
31
  @registers[register_string.to_i] ||= new(register_string.to_i) if register_string
29
32
  end
@@ -0,0 +1,6 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
4
+ module HeadMusic::Rudiment::Rhythm
5
+ PPQN = PULSES_PER_QUARTER_NOTE = 960
6
+ end
@@ -1,5 +1,8 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # A rhythmic unit is a rudiment of duration consisting of doublings and divisions of a whole note.
2
- class HeadMusic::RhythmicUnit
5
+ class HeadMusic::Rudiment::RhythmicUnit
3
6
  include HeadMusic::Named
4
7
 
5
8
  MULTIPLES = ["whole", "double whole", "longa", "maxima"].freeze
@@ -35,7 +38,7 @@ class HeadMusic::RhythmicUnit
35
38
  end
36
39
 
37
40
  def ticks
38
- HeadMusic::Rhythm::PPQN * 4 * relative_value
41
+ HeadMusic::Rudiment::Rhythm::PPQN * 4 * relative_value
39
42
  end
40
43
 
41
44
  def notehead
@@ -1,11 +1,14 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # A scale contains ordered pitches starting at a tonal center.
2
- class HeadMusic::Scale
5
+ class HeadMusic::Rudiment::Scale
3
6
  SCALE_REGEX = /^[A-G][#b]?\s+\w+$/
4
7
 
5
8
  def self.get(root_pitch, scale_type = nil)
6
9
  root_pitch, scale_type = root_pitch.split(/\s+/) if root_pitch.is_a?(String) && scale_type =~ SCALE_REGEX
7
- root_pitch = HeadMusic::Pitch.get(root_pitch)
8
- scale_type = HeadMusic::ScaleType.get(scale_type || :major)
10
+ root_pitch = HeadMusic::Rudiment::Pitch.get(root_pitch)
11
+ scale_type = HeadMusic::Rudiment::ScaleType.get(scale_type || :major)
9
12
  @scales ||= {}
10
13
  hash_key = HeadMusic::Utilities::HashKey.for(
11
14
  [root_pitch, scale_type].join(" ").gsub(/#|♯/, "sharp").gsub(/(\w)[b♭]/, '\\1flat')
@@ -18,8 +21,8 @@ class HeadMusic::Scale
18
21
  attr_reader :root_pitch, :scale_type
19
22
 
20
23
  def initialize(root_pitch, scale_type)
21
- @root_pitch = HeadMusic::Pitch.get(root_pitch)
22
- @scale_type = HeadMusic::ScaleType.get(scale_type)
24
+ @root_pitch = HeadMusic::Rudiment::Pitch.get(root_pitch)
25
+ @scale_type = HeadMusic::Rudiment::ScaleType.get(scale_type)
23
26
  end
24
27
 
25
28
  def pitches(direction: :ascending, octaves: 1)
@@ -80,7 +83,7 @@ class HeadMusic::Scale
80
83
  end
81
84
 
82
85
  def parent_scale_pitches
83
- HeadMusic::Scale.get(root_pitch, scale_type.parent_name).pitches if scale_type.parent
86
+ HeadMusic::Rudiment::Scale.get(root_pitch, scale_type.parent_name).pitches if scale_type.parent
84
87
  end
85
88
 
86
89
  def parent_scale_pitch_for(semitones_from_root)
@@ -92,7 +95,7 @@ class HeadMusic::Scale
92
95
  def pitch_for_step(step, semitones_from_root, direction)
93
96
  pitch_number = root_pitch_number + semitones_from_root
94
97
  letter_name = letter_for_step(step, semitones_from_root, direction)
95
- HeadMusic::Pitch.from_number_and_letter(pitch_number, letter_name)
98
+ HeadMusic::Rudiment::Pitch.from_number_and_letter(pitch_number, letter_name)
96
99
  end
97
100
 
98
101
  def letter_for_step(step, semitones_from_root, direction)
@@ -117,11 +120,11 @@ class HeadMusic::Scale
117
120
  def flat_letter_for_step(semitones_from_root)
118
121
  return unless root_pitch.flat?
119
122
 
120
- HeadMusic::PitchClass::FLAT_SPELLINGS[pitch_class_number(semitones_from_root)]
123
+ HeadMusic::Rudiment::PitchClass::FLAT_SPELLINGS[pitch_class_number(semitones_from_root)]
121
124
  end
122
125
 
123
126
  def sharp_letter_for_step(semitones_from_root)
124
- HeadMusic::PitchClass::SHARP_SPELLINGS[pitch_class_number(semitones_from_root)]
127
+ HeadMusic::Rudiment::PitchClass::SHARP_SPELLINGS[pitch_class_number(semitones_from_root)]
125
128
  end
126
129
 
127
130
  def pitch_class_number(semitones_from_root)
@@ -1,6 +1,9 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # A scale degree is a number indicating the ordinality of the spelling in the key.
2
5
  # TODO: Rewrite to accept a tonal_center and a scale type.
3
- class HeadMusic::ScaleDegree
6
+ class HeadMusic::Rudiment::ScaleDegree
4
7
  include Comparable
5
8
 
6
9
  NAME_FOR_DIATONIC_DEGREE = [nil, "tonic", "supertonic", "mediant", "subdominant", "dominant", "submediant"].freeze
@@ -12,7 +15,7 @@ class HeadMusic::ScaleDegree
12
15
 
13
16
  def initialize(key_signature, spelling)
14
17
  @key_signature = key_signature
15
- @spelling = HeadMusic::Spelling.get(spelling)
18
+ @spelling = HeadMusic::Rudiment::Spelling.get(spelling)
16
19
  end
17
20
 
18
21
  def degree
@@ -23,7 +26,7 @@ class HeadMusic::ScaleDegree
23
26
  spelling_alteration_semitones = spelling.alteration&.semitones || 0
24
27
  usual_sign_semitones = scale_degree_usual_spelling.alteration&.semitones || 0
25
28
  delta = spelling_alteration_semitones - usual_sign_semitones
26
- HeadMusic::Alteration.by(:semitones, delta) if delta != 0
29
+ HeadMusic::Rudiment::Alteration.by(:semitones, delta) if delta != 0
27
30
  end
28
31
 
29
32
  def alteration_semitones
@@ -35,7 +38,7 @@ class HeadMusic::ScaleDegree
35
38
  end
36
39
 
37
40
  def <=>(other)
38
- if other.is_a?(HeadMusic::ScaleDegree)
41
+ if other.is_a?(HeadMusic::Rudiment::ScaleDegree)
39
42
  [degree, alteration_semitones] <=> [other.degree, other.alteration_semitones]
40
43
  else
41
44
  # TODO: Improve this
@@ -53,6 +56,6 @@ class HeadMusic::ScaleDegree
53
56
  private
54
57
 
55
58
  def scale_degree_usual_spelling
56
- HeadMusic::Spelling.get(scale.spellings[degree - 1])
59
+ HeadMusic::Rudiment::Spelling.get(scale.spellings[degree - 1])
57
60
  end
58
61
  end
@@ -1,5 +1,8 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # A ScaleType represents a particular scale pattern, such as major, lydian, or minor pentatonic.
2
- class HeadMusic::ScaleType
5
+ class HeadMusic::Rudiment::ScaleType
3
6
  H = 1 # whole step
4
7
  W = 2 # half step
5
8
 
@@ -1,6 +1,9 @@
1
+ # A module for music rudiments
2
+ module HeadMusic::Rudiment; end
3
+
1
4
  # A scale degree is a number indicating the ordinality of the spelling in the key signature.
2
5
  # TODO: Rewrite to accept a tonal_center and a scale type.
3
- class HeadMusic::Solmization
6
+ class HeadMusic::Rudiment::Solmization
4
7
  include HeadMusic::Named
5
8
 
6
9
  DEFAULT_SOLMIZATION = "solfège"