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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/Gemfile +1 -1
- data/lib/head_music/{circle.rb → analysis/circle.rb} +9 -6
- data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/category.rb +1 -1
- data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/naming.rb +6 -6
- data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/parser.rb +4 -4
- data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/semitones.rb +4 -4
- data/lib/head_music/{diatonic_interval → analysis/diatonic_interval}/size.rb +1 -1
- data/lib/head_music/{diatonic_interval.rb → analysis/diatonic_interval.rb} +19 -16
- data/lib/head_music/{harmonic_interval.rb → analysis/harmonic_interval.rb} +5 -2
- data/lib/head_music/{interval_cycle.rb → analysis/interval_cycle.rb} +12 -9
- data/lib/head_music/{melodic_interval.rb → analysis/melodic_interval.rb} +7 -4
- data/lib/head_music/{motion.rb → analysis/motion.rb} +6 -3
- data/lib/head_music/{pitch_class_set.rb → analysis/pitch_class_set.rb} +5 -2
- data/lib/head_music/{pitch_set.rb → analysis/pitch_set.rb} +14 -11
- data/lib/head_music/{sonority.rb → analysis/sonority.rb} +7 -4
- data/lib/head_music/content/bar.rb +2 -2
- data/lib/head_music/content/composition.rb +3 -3
- data/lib/head_music/content/note.rb +1 -1
- data/lib/head_music/content/placement.rb +1 -1
- data/lib/head_music/content/position.rb +1 -1
- data/lib/head_music/content/rhythmic_value.rb +3 -3
- data/lib/head_music/{staff.rb → content/staff.rb} +6 -3
- data/lib/head_music/content/voice.rb +2 -2
- data/lib/head_music/{instrument.rb → instruments/instrument.rb} +10 -7
- data/lib/head_music/{data → instruments}/instrument_families.yml +49 -0
- data/lib/head_music/{instrument_family.rb → instruments/instrument_family.rb} +5 -2
- data/lib/head_music/{data → instruments}/instruments.yml +116 -0
- data/lib/head_music/{instrument → instruments}/staff.rb +5 -2
- data/lib/head_music/{instrument → instruments}/staff_scheme.rb +7 -2
- data/lib/head_music/{instrument → instruments}/variant.rb +6 -3
- data/lib/head_music/locales/en.yml +22 -0
- data/lib/head_music/{alteration.rb → rudiment/alteration.rb} +9 -6
- data/lib/head_music/{chromatic_interval.rb → rudiment/chromatic_interval.rb} +7 -4
- data/lib/head_music/{clef.rb → rudiment/clef.rb} +7 -4
- data/lib/head_music/{consonance.rb → rudiment/consonance.rb} +4 -1
- data/lib/head_music/{key_signature → rudiment/key_signature}/enharmonic_equivalence.rb +4 -4
- data/lib/head_music/{key_signature.rb → rudiment/key_signature.rb} +10 -7
- data/lib/head_music/{letter_name.rb → rudiment/letter_name.rb} +9 -6
- data/lib/head_music/{meter.rb → rudiment/meter.rb} +6 -3
- data/lib/head_music/{musical_symbol.rb → rudiment/musical_symbol.rb} +4 -1
- data/lib/head_music/{pitch → rudiment/pitch}/enharmonic_equivalence.rb +4 -4
- data/lib/head_music/{pitch → rudiment/pitch}/octave_equivalence.rb +2 -2
- data/lib/head_music/{pitch.rb → rudiment/pitch.rb} +30 -27
- data/lib/head_music/{pitch_class.rb → rudiment/pitch_class.rb} +14 -11
- data/lib/head_music/{quality.rb → rudiment/quality.rb} +4 -1
- data/lib/head_music/{reference_pitch.rb → rudiment/reference_pitch.rb} +5 -2
- data/lib/head_music/{register.rb → rudiment/register.rb} +6 -3
- data/lib/head_music/rudiment/rhythm.rb +6 -0
- data/lib/head_music/{rhythmic_unit.rb → rudiment/rhythmic_unit.rb} +5 -2
- data/lib/head_music/{scale.rb → rudiment/scale.rb} +12 -9
- data/lib/head_music/{scale_degree.rb → rudiment/scale_degree.rb} +8 -5
- data/lib/head_music/{scale_type.rb → rudiment/scale_type.rb} +4 -1
- data/lib/head_music/{solmization.rb → rudiment/solmization.rb} +4 -1
- data/lib/head_music/{spelling.rb → rudiment/spelling.rb} +20 -17
- data/lib/head_music/{tuning.rb → rudiment/tuning.rb} +6 -3
- data/lib/head_music/style/annotation.rb +6 -6
- data/lib/head_music/style/guidelines/consonant_climax.rb +4 -4
- data/lib/head_music/style/guidelines/diatonic.rb +1 -1
- data/lib/head_music/style/guidelines/step_out_of_unison.rb +1 -1
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +50 -46
- metadata +51 -51
- data/lib/head_music/rhythm.rb +0 -3
- /data/lib/head_music/{data → rudiment}/clefs.yml +0 -0
- /data/lib/head_music/{solmizations.yml → rudiment/solmizations.yml} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e3d6ab19fbe0d2fb36ef83bd77eb6e029af7e1ce7e0c78b40bdf0945c114a64
|
4
|
+
data.tar.gz: 7392a202e8e2beeb1582116592d99ec2b61ca108227812c335b145ee1664d4d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c53c0f8a7bb12f7891aa331ce14a8746d89732b6f04dc92aa3793ec22f1d0ac4bcba0662d423f5bc82a553f5c97548b4d951e0a369e8bf809f8b162919211359
|
7
|
+
data.tar.gz: 3a92eb3616549a5efbbea45f239241262cb82f704dc78bfe64e9a4943e488389f6f9c95d5b169d813615ad8678e5bf11c44c1bf62b95d67d996a80801ef4a4fe
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.3.0
|
data/Gemfile
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
-
require "head_music/interval_cycle"
|
1
|
+
require "head_music/analysis/interval_cycle"
|
2
|
+
|
3
|
+
# A module for musical analysis
|
4
|
+
module HeadMusic::Analysis; end
|
2
5
|
|
3
6
|
# A Circle of Fifths or Fourths shows relationships between pitch classes
|
4
|
-
class HeadMusic::Circle < HeadMusic::IntervalCycle
|
7
|
+
class HeadMusic::Analysis::Circle < HeadMusic::Analysis::IntervalCycle
|
5
8
|
def self.of_fifths
|
6
9
|
get(:perfect_fifth)
|
7
10
|
end
|
@@ -12,20 +15,20 @@ class HeadMusic::Circle < HeadMusic::IntervalCycle
|
|
12
15
|
|
13
16
|
def self.get(interval = :perfect_fifth)
|
14
17
|
@circles ||= {}
|
15
|
-
diatonic_interval = HeadMusic::DiatonicInterval.get(interval)
|
18
|
+
diatonic_interval = HeadMusic::Analysis::DiatonicInterval.get(interval)
|
16
19
|
@circles[interval] ||= new(interval: diatonic_interval, starting_pitch: "C4")
|
17
20
|
end
|
18
21
|
|
19
22
|
def index(pitch_class)
|
20
|
-
pitch_classes.index(HeadMusic::Spelling.get(pitch_class).pitch_class)
|
23
|
+
pitch_classes.index(HeadMusic::Rudiment::Spelling.get(pitch_class).pitch_class)
|
21
24
|
end
|
22
25
|
|
23
26
|
def key_signatures_up
|
24
|
-
spellings_up.map { |spelling| HeadMusic::KeySignature.new(spelling) }
|
27
|
+
spellings_up.map { |spelling| HeadMusic::Rudiment::KeySignature.new(spelling) }
|
25
28
|
end
|
26
29
|
|
27
30
|
def key_signatures_down
|
28
|
-
spellings_down.map { |spelling| HeadMusic::KeySignature.new(spelling) }
|
31
|
+
spellings_down.map { |spelling| HeadMusic::Rudiment::KeySignature.new(spelling) }
|
29
32
|
end
|
30
33
|
|
31
34
|
def spellings_up
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# Accepts a number and number of semitones and privides the naming methods.
|
2
|
-
class HeadMusic::DiatonicInterval::Naming
|
3
|
-
QUALITY_SEMITONES = HeadMusic::DiatonicInterval::QUALITY_SEMITONES
|
4
|
-
NUMBER_NAMES = HeadMusic::DiatonicInterval::NUMBER_NAMES
|
5
|
-
NAME_SUFFIXES = HeadMusic::DiatonicInterval::NAME_SUFFIXES
|
2
|
+
class HeadMusic::Analysis::DiatonicInterval::Naming
|
3
|
+
QUALITY_SEMITONES = HeadMusic::Analysis::DiatonicInterval::QUALITY_SEMITONES
|
4
|
+
NUMBER_NAMES = HeadMusic::Analysis::DiatonicInterval::NUMBER_NAMES
|
5
|
+
NAME_SUFFIXES = HeadMusic::Analysis::DiatonicInterval::NAME_SUFFIXES
|
6
6
|
|
7
7
|
attr_reader :number, :semitones
|
8
8
|
|
@@ -23,7 +23,7 @@ class HeadMusic::DiatonicInterval::Naming
|
|
23
23
|
starting_quality = QUALITY_SEMITONES[simple_number_name.to_sym].keys.first
|
24
24
|
delta = simple_semitones - (QUALITY_SEMITONES[simple_number_name.to_sym][starting_quality] % 12)
|
25
25
|
delta -= 12 while delta >= 6
|
26
|
-
HeadMusic::Quality.from(starting_quality, delta)
|
26
|
+
HeadMusic::Rudiment::Quality.from(starting_quality, delta)
|
27
27
|
end
|
28
28
|
|
29
29
|
def simple_number_name
|
@@ -60,7 +60,7 @@ class HeadMusic::DiatonicInterval::Naming
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def quality
|
63
|
-
@quality ||= HeadMusic::Quality.get(quality_name)
|
63
|
+
@quality ||= HeadMusic::Rudiment::Quality.get(quality_name)
|
64
64
|
end
|
65
65
|
|
66
66
|
def octaves
|
@@ -1,5 +1,5 @@
|
|
1
|
-
class HeadMusic::DiatonicInterval::Parser
|
2
|
-
NUMBER_NAMES = HeadMusic::DiatonicInterval::NUMBER_NAMES
|
1
|
+
class HeadMusic::Analysis::DiatonicInterval::Parser
|
2
|
+
NUMBER_NAMES = HeadMusic::Analysis::DiatonicInterval::NUMBER_NAMES
|
3
3
|
|
4
4
|
attr_reader :identifier
|
5
5
|
|
@@ -24,7 +24,7 @@ class HeadMusic::DiatonicInterval::Parser
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def higher_letter
|
27
|
-
HeadMusic::Pitch.middle_c.letter_name.steps_up(steps)
|
27
|
+
HeadMusic::Rudiment::Pitch.middle_c.letter_name.steps_up(steps)
|
28
28
|
end
|
29
29
|
|
30
30
|
def expand(identifier)
|
@@ -36,7 +36,7 @@ class HeadMusic::DiatonicInterval::Parser
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def quality_abbreviations
|
39
|
-
HeadMusic::DiatonicInterval::QUALITY_ABBREVIATIONS
|
39
|
+
HeadMusic::Analysis::DiatonicInterval::QUALITY_ABBREVIATIONS
|
40
40
|
end
|
41
41
|
|
42
42
|
def quality_for(abbreviation)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Accepts a name and a quality and returns the number of semitones
|
2
|
-
class HeadMusic::DiatonicInterval::Semitones
|
3
|
-
QUALITY_SEMITONES = HeadMusic::DiatonicInterval::QUALITY_SEMITONES
|
2
|
+
class HeadMusic::Analysis::DiatonicInterval::Semitones
|
3
|
+
QUALITY_SEMITONES = HeadMusic::Analysis::DiatonicInterval::QUALITY_SEMITONES
|
4
4
|
|
5
5
|
attr_reader :count
|
6
6
|
|
@@ -28,9 +28,9 @@ class HeadMusic::DiatonicInterval::Semitones
|
|
28
28
|
|
29
29
|
def self._degree_quality_modifications(quality)
|
30
30
|
if quality == :perfect
|
31
|
-
HeadMusic::Quality::PERFECT_INTERVAL_MODIFICATION.invert
|
31
|
+
HeadMusic::Rudiment::Quality::PERFECT_INTERVAL_MODIFICATION.invert
|
32
32
|
else
|
33
|
-
HeadMusic::Quality::MAJOR_INTERVAL_MODIFICATION.invert
|
33
|
+
HeadMusic::Rudiment::Quality::MAJOR_INTERVAL_MODIFICATION.invert
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# A module for musical analysis
|
2
|
+
module HeadMusic::Analysis; end
|
3
|
+
|
1
4
|
# A diatonic interval is the distance between two spelled pitches.
|
2
|
-
class HeadMusic::DiatonicInterval
|
5
|
+
class HeadMusic::Analysis::DiatonicInterval
|
3
6
|
include Comparable
|
4
7
|
|
5
8
|
# TODO: include Named module
|
@@ -59,33 +62,33 @@ class HeadMusic::DiatonicInterval
|
|
59
62
|
def self.get(identifier)
|
60
63
|
name = Parser.new(identifier)
|
61
64
|
semitones = Semitones.new(name.degree_name.to_sym, name.quality_name).count
|
62
|
-
higher_pitch = HeadMusic::Pitch.from_number_and_letter(HeadMusic::Pitch.middle_c + semitones, name.higher_letter)
|
63
|
-
new(HeadMusic::Pitch.middle_c, higher_pitch)
|
65
|
+
higher_pitch = HeadMusic::Rudiment::Pitch.from_number_and_letter(HeadMusic::Rudiment::Pitch.middle_c + semitones, name.higher_letter)
|
66
|
+
new(HeadMusic::Rudiment::Pitch.middle_c, higher_pitch)
|
64
67
|
end
|
65
68
|
|
66
69
|
def initialize(pitch1, pitch2)
|
67
|
-
pitch1 = HeadMusic::Pitch.get(pitch1)
|
68
|
-
pitch2 = HeadMusic::Pitch.get(pitch2)
|
70
|
+
pitch1 = HeadMusic::Rudiment::Pitch.get(pitch1)
|
71
|
+
pitch2 = HeadMusic::Rudiment::Pitch.get(pitch2)
|
69
72
|
@lower_pitch, @higher_pitch = [pitch1, pitch2].sort
|
70
73
|
end
|
71
74
|
|
72
75
|
def quality
|
73
|
-
HeadMusic::Quality.get(quality_name)
|
76
|
+
HeadMusic::Rudiment::Quality.get(quality_name)
|
74
77
|
end
|
75
78
|
|
76
79
|
def inversion
|
77
80
|
inverted_low_pitch = lower_pitch
|
78
81
|
while inverted_low_pitch < higher_pitch
|
79
|
-
inverted_low_pitch = HeadMusic::Pitch.fetch_or_create(lower_pitch.spelling, inverted_low_pitch.register + 1)
|
82
|
+
inverted_low_pitch = HeadMusic::Rudiment::Pitch.fetch_or_create(lower_pitch.spelling, inverted_low_pitch.register + 1)
|
80
83
|
end
|
81
|
-
HeadMusic::DiatonicInterval.new(higher_pitch, inverted_low_pitch)
|
84
|
+
HeadMusic::Analysis::DiatonicInterval.new(higher_pitch, inverted_low_pitch)
|
82
85
|
end
|
83
86
|
alias_method :invert, :inversion
|
84
87
|
|
85
88
|
def consonance(style = :standard_practice)
|
86
89
|
consonance_for_perfect(style) ||
|
87
90
|
consonance_for_major_and_minor ||
|
88
|
-
HeadMusic::Consonance.get(:dissonant)
|
91
|
+
HeadMusic::Rudiment::Consonance.get(:dissonant)
|
89
92
|
end
|
90
93
|
|
91
94
|
def consonance?(style = :standard_practice)
|
@@ -106,13 +109,13 @@ class HeadMusic::DiatonicInterval
|
|
106
109
|
end
|
107
110
|
|
108
111
|
def above(pitch)
|
109
|
-
pitch = HeadMusic::Pitch.get(pitch)
|
110
|
-
HeadMusic::Pitch.from_number_and_letter(pitch + semitones, pitch.letter_name.steps_up(number - 1))
|
112
|
+
pitch = HeadMusic::Rudiment::Pitch.get(pitch)
|
113
|
+
HeadMusic::Rudiment::Pitch.from_number_and_letter(pitch + semitones, pitch.letter_name.steps_up(number - 1))
|
111
114
|
end
|
112
115
|
|
113
116
|
def below(pitch)
|
114
|
-
pitch = HeadMusic::Pitch.get(pitch)
|
115
|
-
HeadMusic::Pitch.from_number_and_letter(pitch - semitones, pitch.letter_name.steps_down(number - 1))
|
117
|
+
pitch = HeadMusic::Rudiment::Pitch.get(pitch)
|
118
|
+
HeadMusic::Rudiment::Pitch.from_number_and_letter(pitch - semitones, pitch.letter_name.steps_down(number - 1))
|
116
119
|
end
|
117
120
|
|
118
121
|
def interval_class
|
@@ -128,7 +131,7 @@ class HeadMusic::DiatonicInterval
|
|
128
131
|
alias_method :diatonic_generic_interval, :simple_steps
|
129
132
|
|
130
133
|
def <=>(other)
|
131
|
-
other = self.class.get(other) unless other.is_a?(HeadMusic::DiatonicInterval)
|
134
|
+
other = self.class.get(other) unless other.is_a?(HeadMusic::Analysis::DiatonicInterval)
|
132
135
|
semitones <=> other.semitones
|
133
136
|
end
|
134
137
|
|
@@ -155,11 +158,11 @@ class HeadMusic::DiatonicInterval
|
|
155
158
|
end
|
156
159
|
|
157
160
|
def consonance_for_perfect(style = :standard_practice)
|
158
|
-
HeadMusic::Consonance.get(dissonant_fourth?(style) ? :dissonant : :perfect) if perfect?
|
161
|
+
HeadMusic::Rudiment::Consonance.get(dissonant_fourth?(style) ? :dissonant : :perfect) if perfect?
|
159
162
|
end
|
160
163
|
|
161
164
|
def consonance_for_major_and_minor
|
162
|
-
HeadMusic::Consonance.get((third_or_compound? || sixth_or_compound?) ? :imperfect : :dissonant) if major? || minor?
|
165
|
+
HeadMusic::Rudiment::Consonance.get((third_or_compound? || sixth_or_compound?) ? :imperfect : :dissonant) if major? || minor?
|
163
166
|
end
|
164
167
|
|
165
168
|
def dissonant_fourth?(style = :standard_practice)
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# A module for musical analysis
|
2
|
+
module HeadMusic::Analysis; end
|
3
|
+
|
1
4
|
# A harmonic interval is the diatonic interval between two notes sounding together.
|
2
|
-
class HeadMusic::HarmonicInterval
|
5
|
+
class HeadMusic::Analysis::HarmonicInterval
|
3
6
|
attr_reader :voice1, :voice2, :position
|
4
7
|
|
5
8
|
def initialize(voice1, voice2, position)
|
@@ -9,7 +12,7 @@ class HeadMusic::HarmonicInterval
|
|
9
12
|
end
|
10
13
|
|
11
14
|
def diatonic_interval
|
12
|
-
@diatonic_interval ||= HeadMusic::DiatonicInterval.new(lower_pitch, upper_pitch)
|
15
|
+
@diatonic_interval ||= HeadMusic::Analysis::DiatonicInterval.new(lower_pitch, upper_pitch)
|
13
16
|
end
|
14
17
|
|
15
18
|
def voices
|
@@ -1,20 +1,23 @@
|
|
1
|
+
# A module for musical analysis
|
2
|
+
module HeadMusic::Analysis; end
|
3
|
+
|
1
4
|
# An Interval Cycle is a collection of pitch classes created from a sequence of the same interval class.
|
2
|
-
class HeadMusic::IntervalCycle
|
5
|
+
class HeadMusic::Analysis::IntervalCycle
|
3
6
|
attr_reader :interval, :starting_pitch
|
4
7
|
|
5
8
|
def self.get(interval = 7)
|
6
9
|
@interval_cycles ||= {}
|
7
10
|
interval = interval.to_s.gsub(/^C/i, "").to_i
|
8
|
-
interval = HeadMusic::ChromaticInterval.get(interval)
|
11
|
+
interval = HeadMusic::Rudiment::ChromaticInterval.get(interval)
|
9
12
|
@interval_cycles[interval.to_i] ||= new(interval: interval, starting_pitch: "C4")
|
10
13
|
end
|
11
14
|
|
12
15
|
def initialize(interval:, starting_pitch: "C4")
|
13
|
-
@interval = interval if interval.is_a?(HeadMusic::DiatonicInterval)
|
14
|
-
@interval ||= interval if interval.is_a?(HeadMusic::ChromaticInterval)
|
15
|
-
@interval ||= HeadMusic::ChromaticInterval.get(interval) if interval.to_s.match?(/\d/)
|
16
|
-
@interval ||= HeadMusic::DiatonicInterval.get(interval)
|
17
|
-
@starting_pitch = HeadMusic::Pitch.get(starting_pitch)
|
16
|
+
@interval = interval if interval.is_a?(HeadMusic::Analysis::DiatonicInterval)
|
17
|
+
@interval ||= interval if interval.is_a?(HeadMusic::Rudiment::ChromaticInterval)
|
18
|
+
@interval ||= HeadMusic::Rudiment::ChromaticInterval.get(interval) if interval.to_s.match?(/\d/)
|
19
|
+
@interval ||= HeadMusic::Analysis::DiatonicInterval.get(interval)
|
20
|
+
@starting_pitch = HeadMusic::Rudiment::Pitch.get(starting_pitch)
|
18
21
|
end
|
19
22
|
|
20
23
|
def pitches
|
@@ -26,7 +29,7 @@ class HeadMusic::IntervalCycle
|
|
26
29
|
end
|
27
30
|
|
28
31
|
def pitch_class_set
|
29
|
-
@pitch_class_set ||= HeadMusic::PitchClassSet.new(pitches)
|
32
|
+
@pitch_class_set ||= HeadMusic::Analysis::PitchClassSet.new(pitches)
|
30
33
|
end
|
31
34
|
|
32
35
|
def spellings
|
@@ -48,6 +51,6 @@ class HeadMusic::IntervalCycle
|
|
48
51
|
end
|
49
52
|
|
50
53
|
def octave
|
51
|
-
@octave ||= HeadMusic::DiatonicInterval.get(:perfect_octave)
|
54
|
+
@octave ||= HeadMusic::Analysis::DiatonicInterval.get(:perfect_octave)
|
52
55
|
end
|
53
56
|
end
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# A module for musical analysis
|
2
|
+
module HeadMusic::Analysis; end
|
3
|
+
|
1
4
|
# A melodic interval is the distance between one note and the next.
|
2
|
-
class HeadMusic::MelodicInterval
|
5
|
+
class HeadMusic::Analysis::MelodicInterval
|
3
6
|
attr_reader :first_note, :second_note
|
4
7
|
|
5
8
|
def initialize(note1, note2)
|
@@ -8,7 +11,7 @@ class HeadMusic::MelodicInterval
|
|
8
11
|
end
|
9
12
|
|
10
13
|
def diatonic_interval
|
11
|
-
@diatonic_interval ||= HeadMusic::DiatonicInterval.new(first_pitch, second_pitch)
|
14
|
+
@diatonic_interval ||= HeadMusic::Analysis::DiatonicInterval.new(first_pitch, second_pitch)
|
12
15
|
end
|
13
16
|
|
14
17
|
def position_start
|
@@ -56,7 +59,7 @@ class HeadMusic::MelodicInterval
|
|
56
59
|
end
|
57
60
|
|
58
61
|
def spans?(pitch)
|
59
|
-
pitch
|
62
|
+
pitch.between?(low_pitch, high_pitch)
|
60
63
|
end
|
61
64
|
|
62
65
|
def high_pitch
|
@@ -84,7 +87,7 @@ class HeadMusic::MelodicInterval
|
|
84
87
|
combined_pitches = (pitches + other_interval.pitches).uniq
|
85
88
|
return false if combined_pitches.length < 3
|
86
89
|
|
87
|
-
HeadMusic::PitchSet.new(combined_pitches).consonant_triad?
|
90
|
+
HeadMusic::Analysis::PitchSet.new(combined_pitches).consonant_triad?
|
88
91
|
end
|
89
92
|
|
90
93
|
def method_missing(method_name, *args, &block)
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# A module for musical analysis
|
2
|
+
module HeadMusic::Analysis; end
|
3
|
+
|
1
4
|
# Motion defines the relative pitch direction of the upper and lower voices of subsequence intervals.
|
2
|
-
class HeadMusic::Motion
|
5
|
+
class HeadMusic::Analysis::Motion
|
3
6
|
attr_reader :first_harmonic_interval, :second_harmonic_interval
|
4
7
|
|
5
8
|
def initialize(first_harmonic_interval, second_harmonic_interval)
|
@@ -56,11 +59,11 @@ class HeadMusic::Motion
|
|
56
59
|
private
|
57
60
|
|
58
61
|
def upper_melodic_interval
|
59
|
-
HeadMusic::MelodicInterval.new(upper_notes.first, upper_notes.last)
|
62
|
+
HeadMusic::Analysis::MelodicInterval.new(upper_notes.first, upper_notes.last)
|
60
63
|
end
|
61
64
|
|
62
65
|
def lower_melodic_interval
|
63
|
-
HeadMusic::MelodicInterval.new(lower_notes.first, lower_notes.last)
|
66
|
+
HeadMusic::Analysis::MelodicInterval.new(lower_notes.first, lower_notes.last)
|
64
67
|
end
|
65
68
|
|
66
69
|
def upper_notes
|
@@ -1,13 +1,16 @@
|
|
1
|
+
# A module for musical analysis
|
2
|
+
module HeadMusic::Analysis; end
|
3
|
+
|
1
4
|
# A PitchClassSet represents a pitch-class set or pitch collection.
|
2
5
|
# See also: PitchSet, PitchClass
|
3
|
-
class HeadMusic::PitchClassSet
|
6
|
+
class HeadMusic::Analysis::PitchClassSet
|
4
7
|
attr_reader :pitch_classes
|
5
8
|
|
6
9
|
delegate :empty?, to: :pitch_classes
|
7
10
|
alias_method :empty_set?, :empty?
|
8
11
|
|
9
12
|
def initialize(identifiers)
|
10
|
-
@pitch_classes = identifiers.map { |identifier| HeadMusic::PitchClass.get(identifier) }.uniq.sort
|
13
|
+
@pitch_classes = identifiers.map { |identifier| HeadMusic::Rudiment::PitchClass.get(identifier) }.uniq.sort
|
11
14
|
end
|
12
15
|
|
13
16
|
def to_s
|
@@ -1,6 +1,9 @@
|
|
1
|
+
# A module for musical analysis
|
2
|
+
module HeadMusic::Analysis; end
|
3
|
+
|
1
4
|
# A PitchSet is a collection of one or more pitches.
|
2
5
|
# See also: PitchClassSet
|
3
|
-
class HeadMusic::PitchSet
|
6
|
+
class HeadMusic::Analysis::PitchSet
|
4
7
|
TERTIAN_SONORITIES = {
|
5
8
|
implied_triad: [3],
|
6
9
|
triad: [3, 5],
|
@@ -20,7 +23,7 @@ class HeadMusic::PitchSet
|
|
20
23
|
delegate :size, to: :pitch_class_set, prefix: true
|
21
24
|
|
22
25
|
def initialize(pitches)
|
23
|
-
@pitches = pitches.map { |pitch| HeadMusic::Pitch.get(pitch) }.sort.uniq
|
26
|
+
@pitches = pitches.map { |pitch| HeadMusic::Rudiment::Pitch.get(pitch) }.sort.uniq
|
24
27
|
end
|
25
28
|
|
26
29
|
def pitch_classes
|
@@ -28,22 +31,22 @@ class HeadMusic::PitchSet
|
|
28
31
|
end
|
29
32
|
|
30
33
|
def pitch_class_set
|
31
|
-
@pitch_class_set ||= HeadMusic::PitchClassSet.new(pitch_classes)
|
34
|
+
@pitch_class_set ||= HeadMusic::Analysis::PitchClassSet.new(pitch_classes)
|
32
35
|
end
|
33
36
|
|
34
37
|
def reduction
|
35
|
-
@reduction ||= HeadMusic::PitchSet.new(reduction_pitches)
|
38
|
+
@reduction ||= HeadMusic::Analysis::PitchSet.new(reduction_pitches)
|
36
39
|
end
|
37
40
|
|
38
41
|
def diatonic_intervals
|
39
42
|
@diatonic_intervals ||= pitches.each_cons(2).map do |pitch_pair|
|
40
|
-
HeadMusic::DiatonicInterval.new(*pitch_pair)
|
43
|
+
HeadMusic::Analysis::DiatonicInterval.new(*pitch_pair)
|
41
44
|
end
|
42
45
|
end
|
43
46
|
|
44
47
|
def diatonic_intervals_above_bass_pitch
|
45
48
|
@diatonic_intervals_above_bass_pitch ||= pitches_above_bass_pitch.map do |pitch|
|
46
|
-
HeadMusic::DiatonicInterval.new(bass_pitch, pitch)
|
49
|
+
HeadMusic::Analysis::DiatonicInterval.new(bass_pitch, pitch)
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
@@ -62,15 +65,15 @@ class HeadMusic::PitchSet
|
|
62
65
|
end
|
63
66
|
|
64
67
|
def invert
|
65
|
-
inverted_pitch = pitches[0] + HeadMusic::DiatonicInterval.get("perfect octave")
|
68
|
+
inverted_pitch = pitches[0] + HeadMusic::Analysis::DiatonicInterval.get("perfect octave")
|
66
69
|
new_pitches = pitches.drop(1) + [inverted_pitch]
|
67
|
-
HeadMusic::PitchSet.new(new_pitches)
|
70
|
+
HeadMusic::Analysis::PitchSet.new(new_pitches)
|
68
71
|
end
|
69
72
|
|
70
73
|
def uninvert
|
71
|
-
inverted_pitch = pitches[-1] - HeadMusic::DiatonicInterval.get("perfect octave")
|
74
|
+
inverted_pitch = pitches[-1] - HeadMusic::Analysis::DiatonicInterval.get("perfect octave")
|
72
75
|
new_pitches = [inverted_pitch] + pitches[0..-2]
|
73
|
-
HeadMusic::PitchSet.new(new_pitches)
|
76
|
+
HeadMusic::Analysis::PitchSet.new(new_pitches)
|
74
77
|
end
|
75
78
|
|
76
79
|
def bass_pitch
|
@@ -188,7 +191,7 @@ class HeadMusic::PitchSet
|
|
188
191
|
|
189
192
|
def reduction_pitches
|
190
193
|
pitches.map do |pitch|
|
191
|
-
pitch = HeadMusic::Pitch.fetch_or_create(pitch.spelling, pitch.register - 1) while pitch > bass_pitch + 12
|
194
|
+
pitch = HeadMusic::Rudiment::Pitch.fetch_or_create(pitch.spelling, pitch.register - 1) while pitch > bass_pitch + 12
|
192
195
|
pitch
|
193
196
|
end.sort
|
194
197
|
end
|
@@ -1,7 +1,10 @@
|
|
1
|
+
# A module for musical analysis
|
2
|
+
module HeadMusic::Analysis; end
|
3
|
+
|
1
4
|
# A Sonority describes a set of pitch class intervalic relationships.
|
2
5
|
# For example, a minor triad, or a major-minor seventh chord.
|
3
6
|
# The Sonority class is a factory for returning one of its subclasses.
|
4
|
-
class HeadMusic::Sonority
|
7
|
+
class HeadMusic::Analysis::Sonority
|
5
8
|
SONORITIES = {
|
6
9
|
major_triad: %w[M3 P5],
|
7
10
|
minor_triad: %w[m3 P5],
|
@@ -110,12 +113,12 @@ class HeadMusic::Sonority
|
|
110
113
|
return nil unless identifier
|
111
114
|
|
112
115
|
@diatonic_intervals_above_bass_pitch ||=
|
113
|
-
SONORITIES[identifier].map { |shorthand| HeadMusic::DiatonicInterval.get(shorthand) }
|
116
|
+
SONORITIES[identifier].map { |shorthand| HeadMusic::Analysis::DiatonicInterval.get(shorthand) }
|
114
117
|
end
|
115
118
|
|
116
119
|
def ==(other)
|
117
|
-
other = HeadMusic::PitchSet.new(other) if other.is_a?(Array)
|
118
|
-
other = self.class.new(other) if other.is_a?(HeadMusic::PitchSet)
|
120
|
+
other = HeadMusic::Analysis::PitchSet.new(other) if other.is_a?(Array)
|
121
|
+
other = self.class.new(other) if other.is_a?(HeadMusic::Analysis::PitchSet)
|
119
122
|
identifier == other.identifier
|
120
123
|
end
|
121
124
|
end
|
@@ -9,8 +9,8 @@ class HeadMusic::Content::Bar
|
|
9
9
|
|
10
10
|
def initialize(composition, key_signature: nil, meter: nil)
|
11
11
|
@composition = composition
|
12
|
-
@key_signature = HeadMusic::KeySignature.get(key_signature) if key_signature
|
13
|
-
@meter = HeadMusic::Meter.get(meter) if meter
|
12
|
+
@key_signature = HeadMusic::Rudiment::KeySignature.get(key_signature) if key_signature
|
13
|
+
@meter = HeadMusic::Rudiment::Meter.get(meter) if meter
|
14
14
|
end
|
15
15
|
|
16
16
|
def to_s
|
@@ -65,9 +65,9 @@ class HeadMusic::Content::Composition
|
|
65
65
|
|
66
66
|
def ensure_attributes(name, key_signature, meter)
|
67
67
|
@name = name || "Composition"
|
68
|
-
@key_signature = HeadMusic::KeySignature.get(key_signature) if key_signature
|
69
|
-
@key_signature ||= HeadMusic::KeySignature.default
|
70
|
-
@meter = meter ? HeadMusic::Meter.get(meter) : HeadMusic::Meter.default
|
68
|
+
@key_signature = HeadMusic::Rudiment::KeySignature.get(key_signature) if key_signature
|
69
|
+
@key_signature ||= HeadMusic::Rudiment::KeySignature.default
|
70
|
+
@meter = meter ? HeadMusic::Rudiment::Meter.get(meter) : HeadMusic::Rudiment::Meter.default
|
71
71
|
end
|
72
72
|
|
73
73
|
def last_meter_change(bar_number)
|
@@ -10,7 +10,7 @@ class HeadMusic::Content::Note
|
|
10
10
|
attr_accessor :pitch, :rhythmic_value, :voice, :position
|
11
11
|
|
12
12
|
def initialize(pitch, rhythmic_value, voice = nil, position = nil)
|
13
|
-
@pitch = HeadMusic::Pitch.get(pitch)
|
13
|
+
@pitch = HeadMusic::Rudiment::Pitch.get(pitch)
|
14
14
|
@rhythmic_value = HeadMusic::Content::RhythmicValue.get(rhythmic_value)
|
15
15
|
@voice = voice || HeadMusic::Content::Voice.new
|
16
16
|
@position = position || HeadMusic::Content::Position.new(@voice.composition, "1:1")
|
@@ -56,7 +56,7 @@ class HeadMusic::Content::Placement
|
|
56
56
|
@voice = voice
|
57
57
|
ensure_position(position)
|
58
58
|
@rhythmic_value = HeadMusic::Content::RhythmicValue.get(rhythmic_value)
|
59
|
-
@pitch = HeadMusic::Pitch.get(pitch)
|
59
|
+
@pitch = HeadMusic::Rudiment::Pitch.get(pitch)
|
60
60
|
end
|
61
61
|
|
62
62
|
def ensure_position(position)
|
@@ -53,7 +53,7 @@ class HeadMusic::Content::Position
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def +(other)
|
56
|
-
other = HeadMusic::Content::RhythmicValue.new(other) if [HeadMusic::RhythmicUnit, Symbol, String].include?(other.class)
|
56
|
+
other = HeadMusic::Content::RhythmicValue.new(other) if [HeadMusic::Rudiment::RhythmicUnit, Symbol, String].include?(other.class)
|
57
57
|
self.class.new(composition, bar_number, count, tick + other.ticks)
|
58
58
|
end
|
59
59
|
|
@@ -12,7 +12,7 @@ class HeadMusic::Content::RhythmicValue
|
|
12
12
|
case identifier
|
13
13
|
when HeadMusic::Content::RhythmicValue
|
14
14
|
identifier
|
15
|
-
when HeadMusic::RhythmicUnit
|
15
|
+
when HeadMusic::Rudiment::RhythmicUnit
|
16
16
|
new(identifier)
|
17
17
|
when Symbol, String
|
18
18
|
identifier = identifier.to_s.downcase.strip.gsub(/\W+/, "_")
|
@@ -43,7 +43,7 @@ class HeadMusic::Content::RhythmicValue
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def initialize(unit, dots: nil, tied_value: nil)
|
46
|
-
@unit = HeadMusic::RhythmicUnit.get(unit)
|
46
|
+
@unit = HeadMusic::Rudiment::RhythmicUnit.get(unit)
|
47
47
|
@dots = [0, 1, 2, 3].include?(dots) ? dots : 0
|
48
48
|
@tied_value = tied_value
|
49
49
|
end
|
@@ -65,7 +65,7 @@ class HeadMusic::Content::RhythmicValue
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def ticks
|
68
|
-
HeadMusic::Rhythm::PPQN * 4 * total_value
|
68
|
+
HeadMusic::Rudiment::Rhythm::PPQN * 4 * total_value
|
69
69
|
end
|
70
70
|
|
71
71
|
def per_whole
|
@@ -1,13 +1,16 @@
|
|
1
|
+
# A module for musical content
|
2
|
+
module HeadMusic::Content; end
|
3
|
+
|
1
4
|
# A staff is a set of lines and spaces that provides context for a pitch
|
2
|
-
class HeadMusic::Staff
|
5
|
+
class HeadMusic::Content::Staff
|
3
6
|
DEFAULT_LINE_COUNT = 5
|
4
7
|
|
5
8
|
attr_reader :default_clef, :line_count, :instrument
|
6
9
|
|
7
10
|
def initialize(default_clef_key, instrument: nil, line_count: nil)
|
8
|
-
@default_clef = HeadMusic::Clef.get(default_clef_key)
|
11
|
+
@default_clef = HeadMusic::Rudiment::Clef.get(default_clef_key)
|
9
12
|
@line_count = line_count || DEFAULT_LINE_COUNT
|
10
|
-
@instrument = HeadMusic::Instrument.get(instrument) if instrument
|
13
|
+
@instrument = HeadMusic::Instruments::Instrument.get(instrument) if instrument
|
11
14
|
end
|
12
15
|
|
13
16
|
def clef
|
@@ -56,12 +56,12 @@ class HeadMusic::Content::Voice
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def range
|
59
|
-
HeadMusic::DiatonicInterval.new(lowest_pitch, highest_pitch)
|
59
|
+
HeadMusic::Analysis::DiatonicInterval.new(lowest_pitch, highest_pitch)
|
60
60
|
end
|
61
61
|
|
62
62
|
def melodic_intervals
|
63
63
|
@melodic_intervals ||=
|
64
|
-
notes.each_cons(2).map { |note_pair| HeadMusic::MelodicInterval.new(*note_pair) }
|
64
|
+
notes.each_cons(2).map { |note_pair| HeadMusic::Analysis::MelodicInterval.new(*note_pair) }
|
65
65
|
end
|
66
66
|
|
67
67
|
def leaps
|