head_music 0.27.0 → 0.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +1 -1
  3. data/.rubocop.yml +23 -33
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +1 -1
  6. data/Gemfile +8 -6
  7. data/Rakefile +5 -4
  8. data/bin/console +3 -3
  9. data/head_music.gemspec +21 -22
  10. data/lib/head_music/chromatic_interval.rb +3 -3
  11. data/lib/head_music/circle.rb +18 -14
  12. data/lib/head_music/clef.rb +3 -3
  13. data/lib/head_music/content/bar.rb +1 -1
  14. data/lib/head_music/content/composition.rb +1 -1
  15. data/lib/head_music/content/note.rb +1 -1
  16. data/lib/head_music/content/placement.rb +5 -5
  17. data/lib/head_music/content/position.rb +3 -3
  18. data/lib/head_music/content/rhythmic_value.rb +8 -8
  19. data/lib/head_music/content/voice.rb +1 -1
  20. data/lib/head_music/diatonic_interval.rb +41 -43
  21. data/lib/head_music/grand_staff.rb +8 -8
  22. data/lib/head_music/instrument.rb +8 -8
  23. data/lib/head_music/interval_cycle.rb +10 -12
  24. data/lib/head_music/key_signature.rb +12 -13
  25. data/lib/head_music/letter_name.rb +7 -7
  26. data/lib/head_music/melodic_interval.rb +1 -1
  27. data/lib/head_music/meter.rb +17 -12
  28. data/lib/head_music/named.rb +3 -3
  29. data/lib/head_music/pitch/enharmonic_equivalence.rb +2 -2
  30. data/lib/head_music/pitch/octave_equivalence.rb +1 -1
  31. data/lib/head_music/pitch.rb +9 -8
  32. data/lib/head_music/pitch_class.rb +18 -4
  33. data/lib/head_music/pitch_class_set.rb +5 -5
  34. data/lib/head_music/pitch_set.rb +9 -6
  35. data/lib/head_music/quality.rb +15 -14
  36. data/lib/head_music/reference_pitch.rb +23 -23
  37. data/lib/head_music/register.rb +2 -2
  38. data/lib/head_music/rhythmic_unit.rb +9 -9
  39. data/lib/head_music/scale.rb +4 -4
  40. data/lib/head_music/scale_degree.rb +2 -2
  41. data/lib/head_music/scale_type.rb +13 -13
  42. data/lib/head_music/sign.rb +12 -11
  43. data/lib/head_music/solmization.rb +3 -3
  44. data/lib/head_music/sonority.rb +2 -2
  45. data/lib/head_music/spelling.rb +4 -6
  46. data/lib/head_music/staff.rb +1 -1
  47. data/lib/head_music/style/analysis.rb +1 -1
  48. data/lib/head_music/style/annotation.rb +7 -7
  49. data/lib/head_music/style/guidelines/always_move.rb +4 -4
  50. data/lib/head_music/style/guidelines/approach_perfection_contrarily.rb +1 -1
  51. data/lib/head_music/style/guidelines/at_least_eight_notes.rb +3 -3
  52. data/lib/head_music/style/guidelines/avoid_crossing_voices.rb +6 -6
  53. data/lib/head_music/style/guidelines/avoid_overlapping_voices.rb +1 -1
  54. data/lib/head_music/style/guidelines/consonant_climax.rb +1 -1
  55. data/lib/head_music/style/guidelines/consonant_downbeats.rb +1 -1
  56. data/lib/head_music/style/guidelines/diatonic.rb +2 -2
  57. data/lib/head_music/style/guidelines/end_on_perfect_consonance.rb +1 -1
  58. data/lib/head_music/style/guidelines/end_on_tonic.rb +1 -1
  59. data/lib/head_music/style/guidelines/frequent_direction_changes.rb +1 -1
  60. data/lib/head_music/style/guidelines/limit_octave_leaps.rb +1 -1
  61. data/lib/head_music/style/guidelines/moderate_direction_changes.rb +1 -1
  62. data/lib/head_music/style/guidelines/mostly_conjunct.rb +4 -4
  63. data/lib/head_music/style/guidelines/no_rests.rb +1 -1
  64. data/lib/head_music/style/guidelines/no_unisons_in_middle.rb +1 -1
  65. data/lib/head_music/style/guidelines/notes_same_length.rb +4 -4
  66. data/lib/head_music/style/guidelines/one_to_one.rb +1 -1
  67. data/lib/head_music/style/guidelines/prefer_contrary_motion.rb +1 -1
  68. data/lib/head_music/style/guidelines/prefer_imperfect.rb +1 -1
  69. data/lib/head_music/style/guidelines/prepare_octave_leaps.rb +1 -1
  70. data/lib/head_music/style/guidelines/recover_large_leaps.rb +1 -1
  71. data/lib/head_music/style/guidelines/singable_intervals.rb +1 -1
  72. data/lib/head_music/style/guidelines/singable_range.rb +1 -1
  73. data/lib/head_music/style/guidelines/single_large_leaps.rb +1 -1
  74. data/lib/head_music/style/guidelines/start_on_perfect_consonance.rb +1 -1
  75. data/lib/head_music/style/guidelines/start_on_tonic.rb +1 -1
  76. data/lib/head_music/style/guidelines/step_down_to_final_note.rb +2 -2
  77. data/lib/head_music/style/guidelines/step_out_of_unison.rb +1 -1
  78. data/lib/head_music/style/guidelines/step_to_final_note.rb +2 -2
  79. data/lib/head_music/style/guidelines/step_up_to_final_note.rb +2 -2
  80. data/lib/head_music/style/guidelines/up_to_fourteen_notes.rb +2 -2
  81. data/lib/head_music/style/guides/first_species_harmony.rb +9 -1
  82. data/lib/head_music/style/guides/first_species_melody.rb +1 -1
  83. data/lib/head_music/style/guides/fux_cantus_firmus.rb +1 -1
  84. data/lib/head_music/style/guides/modern_cantus_firmus.rb +1 -1
  85. data/lib/head_music/style/mark.rb +1 -1
  86. data/lib/head_music/utilities/hash_key.rb +1 -1
  87. data/lib/head_music/version.rb +1 -1
  88. data/lib/head_music.rb +91 -91
  89. metadata +6 -20
@@ -61,10 +61,10 @@ class HeadMusic::Register
61
61
  end
62
62
 
63
63
  def helmholtz_marks
64
- return ',' * (2 - number) if number < 2
64
+ return "," * (2 - number) if number < 2
65
65
  return "'" * (number - 3) if number > 3
66
66
 
67
- ''
67
+ ""
68
68
  end
69
69
 
70
70
  private_class_method :new
@@ -4,10 +4,10 @@
4
4
  class HeadMusic::RhythmicUnit
5
5
  include HeadMusic::Named
6
6
 
7
- MULTIPLES = ['whole', 'double whole', 'longa', 'maxima'].freeze
7
+ MULTIPLES = ["whole", "double whole", "longa", "maxima"].freeze
8
8
  FRACTIONS = [
9
- 'whole', 'half', 'quarter', 'eighth', 'sixteenth', 'thirty-second',
10
- 'sixty-fourth', 'hundred twenty-eighth', 'two hundred fifty-sixth',
9
+ "whole", "half", "quarter", "eighth", "sixteenth", "thirty-second",
10
+ "sixty-fourth", "hundred twenty-eighth", "two hundred fifty-sixth"
11
11
  ].freeze
12
12
 
13
13
  BRITISH_MULTIPLE_NAMES = %w[semibreve breve longa maxima].freeze
@@ -80,26 +80,26 @@ class HeadMusic::RhythmicUnit
80
80
  end
81
81
 
82
82
  def numerator_exponent
83
- multiples_keys.index(name.gsub(/\W+/, '_')) || british_multiples_keys.index(name.gsub(/\W+/, '_')) || 0
83
+ multiples_keys.index(name.gsub(/\W+/, "_")) || british_multiples_keys.index(name.gsub(/\W+/, "_")) || 0
84
84
  end
85
85
 
86
86
  def multiples_keys
87
- MULTIPLES.map { |multiple| multiple.gsub(/\W+/, '_') }
87
+ MULTIPLES.map { |multiple| multiple.gsub(/\W+/, "_") }
88
88
  end
89
89
 
90
90
  def british_multiples_keys
91
- BRITISH_MULTIPLE_NAMES.map { |multiple| multiple.gsub(/\W+/, '_') }
91
+ BRITISH_MULTIPLE_NAMES.map { |multiple| multiple.gsub(/\W+/, "_") }
92
92
  end
93
93
 
94
94
  def denominator_exponent
95
- fractions_keys.index(name.gsub(/\W+/, '_')) || british_fractions_keys.index(name.gsub(/\W+/, '_')) || 0
95
+ fractions_keys.index(name.gsub(/\W+/, "_")) || british_fractions_keys.index(name.gsub(/\W+/, "_")) || 0
96
96
  end
97
97
 
98
98
  def fractions_keys
99
- FRACTIONS.map { |fraction| fraction.gsub(/\W+/, '_') }
99
+ FRACTIONS.map { |fraction| fraction.gsub(/\W+/, "_") }
100
100
  end
101
101
 
102
102
  def british_fractions_keys
103
- BRITISH_DIVISION_NAMES.map { |fraction| fraction.gsub(/\W+/, '_') }
103
+ BRITISH_DIVISION_NAMES.map { |fraction| fraction.gsub(/\W+/, "_") }
104
104
  end
105
105
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  # A scale contains ordered pitches starting at a tonal center.
4
4
  class HeadMusic::Scale
5
- SCALE_REGEX = /^[A-G][#b]?\s+\w+$/.freeze
5
+ SCALE_REGEX = /^[A-G][#b]?\s+\w+$/
6
6
 
7
7
  def self.get(root_pitch, scale_type = nil)
8
8
  root_pitch, scale_type = root_pitch.split(/\s+/) if root_pitch.is_a?(String) && scale_type =~ SCALE_REGEX
@@ -10,7 +10,7 @@ class HeadMusic::Scale
10
10
  scale_type = HeadMusic::ScaleType.get(scale_type || :major)
11
11
  @scales ||= {}
12
12
  hash_key = HeadMusic::Utilities::HashKey.for(
13
- [root_pitch, scale_type].join(' ').gsub(/#|♯/, 'sharp').gsub(/(\w)[b♭]/, '\\1flat')
13
+ [root_pitch, scale_type].join(" ").gsub(/#|♯/, "sharp").gsub(/(\w)[b♭]/, '\\1flat')
14
14
  )
15
15
  @scales[hash_key] ||= new(root_pitch, scale_type)
16
16
  end
@@ -70,7 +70,7 @@ class HeadMusic::Scale
70
70
  end
71
71
 
72
72
  def direction_sign(direction)
73
- direction == :descending ? -1 : 1
73
+ (direction == :descending) ? -1 : 1
74
74
  end
75
75
 
76
76
  def direction_intervals(direction)
@@ -103,7 +103,7 @@ class HeadMusic::Scale
103
103
  def diatonic_letter_for_step(direction, step)
104
104
  return unless scale_type.diatonic?
105
105
 
106
- direction == :ascending ? letter_name_series_ascending[step % 7] : letter_name_series_descending[step % 7]
106
+ (direction == :ascending) ? letter_name_series_ascending[step % 7] : letter_name_series_descending[step % 7]
107
107
  end
108
108
 
109
109
  def child_scale_letter_for_step(semitones_from_root)
@@ -5,7 +5,7 @@
5
5
  class HeadMusic::ScaleDegree
6
6
  include Comparable
7
7
 
8
- NAME_FOR_DIATONIC_DEGREE = [nil, 'tonic', 'supertonic', 'mediant', 'subdominant', 'dominant', 'submediant'].freeze
8
+ NAME_FOR_DIATONIC_DEGREE = [nil, "tonic", "supertonic", "mediant", "subdominant", "dominant", "submediant"].freeze
9
9
 
10
10
  attr_reader :key_signature, :spelling
11
11
 
@@ -44,7 +44,7 @@ class HeadMusic::ScaleDegree
44
44
  return unless scale_type.diatonic?
45
45
 
46
46
  NAME_FOR_DIATONIC_DEGREE[degree] ||
47
- (scale_type.intervals.last == 1 || sign == '#' ? 'leading tone' : 'subtonic')
47
+ ((scale_type.intervals.last == 1 || sign == "#") ? "leading tone" : "subtonic")
48
48
  end
49
49
 
50
50
  private
@@ -25,7 +25,7 @@ class HeadMusic::ScaleType
25
25
  iv: [:lydian],
26
26
  v: [:mixolydian],
27
27
  vi: %i[aeolian minor natural_minor],
28
- vii: [:locrian],
28
+ vii: [:locrian]
29
29
  }.freeze
30
30
 
31
31
  CHROMATIC = ([H] * 12)
@@ -35,7 +35,7 @@ class HeadMusic::ScaleType
35
35
  def self._modes
36
36
  {}.tap do |modes|
37
37
  MODE_NAMES.each do |roman_numeral, aliases|
38
- intervals = { ascending: const_get(roman_numeral.upcase) }
38
+ intervals = {ascending: const_get(roman_numeral.upcase)}
39
39
  modes[roman_numeral] = intervals
40
40
  aliases.each { |name| modes[name] = intervals }
41
41
  end
@@ -44,30 +44,30 @@ class HeadMusic::ScaleType
44
44
 
45
45
  def self._minor_scales
46
46
  {
47
- harmonic_minor: { ascending: HARMONIC_MINOR },
48
- melodic_minor: { ascending: MELODIC_MINOR_ASCENDING, descending: VI.reverse },
47
+ harmonic_minor: {ascending: HARMONIC_MINOR},
48
+ melodic_minor: {ascending: MELODIC_MINOR_ASCENDING, descending: VI.reverse}
49
49
  }
50
50
  end
51
51
 
52
52
  def self._chromatic_scales
53
- { chromatic: { ascending: CHROMATIC } }
53
+ {chromatic: {ascending: CHROMATIC}}
54
54
  end
55
55
 
56
56
  def self._pentatonic_scales
57
57
  {
58
- minor_pentatonic: { ascending: MINOR_PENTATONIC, parent_name: :minor },
59
- major_pentatonic: { ascending: MINOR_PENTATONIC.rotate, parent_name: :major },
60
- egyptian_pentatonic: { ascending: MINOR_PENTATONIC.rotate(2), parent_name: :minor },
61
- blues_minor_pentatonic: { ascending: MINOR_PENTATONIC.rotate(3), parent_name: :minor },
62
- blues_major_pentatonic: { ascending: MINOR_PENTATONIC.rotate(4), parent_name: :major },
58
+ minor_pentatonic: {ascending: MINOR_PENTATONIC, parent_name: :minor},
59
+ major_pentatonic: {ascending: MINOR_PENTATONIC.rotate, parent_name: :major},
60
+ egyptian_pentatonic: {ascending: MINOR_PENTATONIC.rotate(2), parent_name: :minor},
61
+ blues_minor_pentatonic: {ascending: MINOR_PENTATONIC.rotate(3), parent_name: :minor},
62
+ blues_major_pentatonic: {ascending: MINOR_PENTATONIC.rotate(4), parent_name: :major}
63
63
  }
64
64
  end
65
65
 
66
66
  def self._exotic_scales
67
67
  # 'octatonic' is also called the 'whole-half diminished scale'
68
68
  {
69
- octatonic: { ascending: [W, H, W, H, W, H, W, H] },
70
- whole_tone: { ascending: [W, W, W, W, W, W] },
69
+ octatonic: {ascending: [W, H, W, H, W, H, W, H]},
70
+ whole_tone: {ascending: [W, W, W, W, W, W]}
71
71
  }
72
72
  end
73
73
 
@@ -99,7 +99,7 @@ class HeadMusic::ScaleType
99
99
  end
100
100
 
101
101
  attr_reader :name, :ascending_intervals, :descending_intervals, :parent_name
102
- alias intervals ascending_intervals
102
+ alias_method :intervals, :ascending_intervals
103
103
 
104
104
  delegate :to_s, to: :name
105
105
 
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'head_music/musical_symbol'
3
+ require "head_music/musical_symbol"
4
4
 
5
5
  # A Sign is a symbol that modifies pitch, such as a sharp, flat, or natural.
6
+ # In French, sharps and flats in the key signature are called "altérations".
6
7
  class HeadMusic::Sign
7
8
  include Comparable
8
9
 
@@ -13,24 +14,24 @@ class HeadMusic::Sign
13
14
  SIGN_RECORDS = [
14
15
  {
15
16
  identifier: :sharp, cents: 100,
16
- symbols: [{ ascii: '#', unicode: '', html_entity: '&#9839;' }],
17
+ symbols: [{ascii: "#", unicode: "", html_entity: "&#9839;"}]
17
18
  },
18
19
  {
19
20
  identifier: :flat, cents: -100,
20
- symbols: [{ ascii: 'b', unicode: '', html_entity: '&#9837;' }],
21
+ symbols: [{ascii: "b", unicode: "", html_entity: "&#9837;"}]
21
22
  },
22
23
  {
23
24
  identifier: :natural, cents: 0,
24
- symbols: [{ ascii: '', unicode: '', html_entity: '&#9838;' }],
25
+ symbols: [{ascii: "", unicode: "", html_entity: "&#9838;"}]
25
26
  },
26
27
  {
27
28
  identifier: :double_sharp, cents: 200,
28
- symbols: [{ ascii: 'x', unicode: '𝄪', html_entity: '&#119082;' }],
29
+ symbols: [{ascii: "x", unicode: "𝄪", html_entity: "&#119082;"}]
29
30
  },
30
31
  {
31
32
  identifier: :double_flat, cents: -200,
32
- symbols: [{ ascii: 'bb', unicode: '𝄫', html_entity: '&#119083;' }],
33
- },
33
+ symbols: [{ascii: "bb", unicode: "𝄫", html_entity: "&#119083;"}]
34
+ }
34
35
  ].freeze
35
36
 
36
37
  SIGN_IDENTIFIERS = SIGN_RECORDS.map { |attributes| attributes[:identifier] }.freeze
@@ -44,7 +45,7 @@ class HeadMusic::Sign
44
45
  end
45
46
 
46
47
  def self.matcher
47
- @matcher ||= Regexp.new symbols.join('|')
48
+ @matcher ||= Regexp.new symbols.join("|")
48
49
  end
49
50
 
50
51
  def self.symbol?(candidate)
@@ -66,12 +67,12 @@ class HeadMusic::Sign
66
67
  end
67
68
 
68
69
  def name
69
- identifier.to_s.tr('_', ' ')
70
+ identifier.to_s.tr("_", " ")
70
71
  end
71
72
 
72
73
  def representions
73
- [identifier, identifier.to_s, name, ascii, unicode, html_entity].
74
- reject { |representation| representation.to_s.strip == '' }
74
+ [identifier, identifier.to_s, name, ascii, unicode, html_entity]
75
+ .reject { |representation| representation.to_s.strip == "" }
75
76
  end
76
77
 
77
78
  def semitones
@@ -5,9 +5,9 @@
5
5
  class HeadMusic::Solmization
6
6
  include HeadMusic::Named
7
7
 
8
- DEFAULT_SOLMIZATION = 'solfège'
8
+ DEFAULT_SOLMIZATION = "solfège"
9
9
 
10
- RECORDS = YAML.load_file(File.expand_path('solmizations.yml', __dir__)).freeze
10
+ RECORDS = YAML.load_file(File.expand_path("solmizations.yml", __dir__)).freeze
11
11
 
12
12
  attr_reader :syllables
13
13
 
@@ -46,7 +46,7 @@ class HeadMusic::Solmization
46
46
 
47
47
  def initialize_localized_names(list)
48
48
  @localized_names = (list || []).map do |name_attributes|
49
- HeadMusic::Named::LocalizedName.new(name_attributes.slice(:name, :locale_code, :abbreviation))
49
+ HeadMusic::Named::LocalizedName.new(**name_attributes.slice(:name, :locale_code, :abbreviation))
50
50
  end
51
51
  end
52
52
  end
@@ -23,7 +23,7 @@ class HeadMusic::Sonority
23
23
  minor_six_nine_chord: %w[M2 m3 P5 M6],
24
24
  suspended_four_chord: %w[P4 P5],
25
25
  suspended_two_chord: %w[M2 P5],
26
- quartal_chord: %w[P4 m7],
26
+ quartal_chord: %w[P4 m7]
27
27
  }.freeze
28
28
 
29
29
  attr_reader :pitch_set
@@ -106,7 +106,7 @@ class HeadMusic::Sonority
106
106
  end.to_f / inversion.diatonic_intervals.length > 0.5
107
107
  end
108
108
  end
109
- alias quintal? quartal?
109
+ alias_method :quintal?, :quartal?
110
110
 
111
111
  def diatonic_intervals_above_bass_pitch
112
112
  return nil unless identifier
@@ -4,11 +4,9 @@
4
4
  # Composite of a LetterName and an optional Sign.
5
5
  # Does not include the octave. See Pitch for that.
6
6
  class HeadMusic::Spelling
7
- MATCHER = /^\s*([A-G])(#{HeadMusic::Sign.matcher}?)(-?\d+)?\s*$/i.freeze
7
+ MATCHER = /^\s*([A-G])(#{HeadMusic::Sign.matcher}?)(-?\d+)?\s*$/i
8
8
 
9
- attr_reader :pitch_class
10
- attr_reader :letter_name
11
- attr_reader :sign
9
+ attr_reader :pitch_class, :letter_name, :sign
12
10
 
13
11
  delegate :number, to: :pitch_class, prefix: true
14
12
  delegate :to_i, to: :pitch_class_number
@@ -114,8 +112,8 @@ class HeadMusic::Spelling
114
112
  spelling != other && spelling.pitch_class_number == other.pitch_class_number
115
113
  end
116
114
 
117
- alias enharmonic? enharmonic_equivalent?
118
- alias equivalent? enharmonic_equivalent?
115
+ alias_method :enharmonic?, :enharmonic_equivalent?
116
+ alias_method :equivalent?, :enharmonic_equivalent?
119
117
 
120
118
  private_class_method :new
121
119
  end
@@ -5,7 +5,7 @@ class HeadMusic::Staff
5
5
  DEFAULT_LINE_COUNT = 5
6
6
 
7
7
  attr_reader :default_clef, :line_count, :instrument
8
- alias clef default_clef
8
+ alias_method :clef, :default_clef
9
9
 
10
10
  def initialize(default_clef, instrument: nil, line_count: nil)
11
11
  @default_clef = HeadMusic::Clef.get(default_clef)
@@ -15,7 +15,7 @@ class HeadMusic::Style::Analysis
15
15
  def messages
16
16
  annotations.reject(&:adherent?).map(&:message)
17
17
  end
18
- alias annotation_messages messages
18
+ alias_method :annotation_messages, :messages
19
19
 
20
20
  def annotations
21
21
  @annotations ||= @guide.analyze(voice)
@@ -2,7 +2,7 @@
2
2
 
3
3
  # An Annotation encapsulates an issue with or comment on a voice
4
4
  class HeadMusic::Style::Annotation
5
- MESSAGE = 'Write music.'
5
+ MESSAGE = "Write music."
6
6
 
7
7
  attr_reader :voice
8
8
 
@@ -104,16 +104,16 @@ class HeadMusic::Style::Annotation
104
104
 
105
105
  def downbeat_harmonic_intervals
106
106
  @downbeat_harmonic_intervals ||=
107
- cantus_firmus.notes.
108
- map { |note| HeadMusic::HarmonicInterval.new(note.voice, voice, note.position) }.
109
- reject { |interval| interval.notes.length < 2 }
107
+ cantus_firmus.notes
108
+ .map { |note| HeadMusic::HarmonicInterval.new(note.voice, voice, note.position) }
109
+ .reject { |interval| interval.notes.length < 2 }
110
110
  end
111
111
 
112
112
  def harmonic_intervals
113
113
  @harmonic_intervals ||=
114
- positions.
115
- map { |position| HeadMusic::HarmonicInterval.new(cantus_firmus, voice, position) }.
116
- reject { |harmonic_interval| harmonic_interval.notes.length < 2 }
114
+ positions
115
+ .map { |position| HeadMusic::HarmonicInterval.new(cantus_firmus, voice, position) }
116
+ .reject { |harmonic_interval| harmonic_interval.notes.length < 2 }
117
117
  end
118
118
 
119
119
  def positions
@@ -5,11 +5,11 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::AlwaysMove < HeadMusic::Style::Annotation
8
- MESSAGE = 'Always move to a different note.'
8
+ MESSAGE = "Always move to a different note."
9
9
 
10
10
  def marks
11
- melodic_intervals.
12
- select { |interval| interval.perfect? && interval.unison? }.
13
- map { |interval| HeadMusic::Style::Mark.for_all(interval.notes) }
11
+ melodic_intervals
12
+ .select { |interval| interval.perfect? && interval.unison? }
13
+ .map { |interval| HeadMusic::Style::Mark.for_all(interval.notes) }
14
14
  end
15
15
  end
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::ApproachPerfectionContrarily < HeadMusic::Style::Annotation
8
- MESSAGE = 'Approach perfect consonances by contrary motion.'
8
+ MESSAGE = "Approach perfect consonances by contrary motion."
9
9
 
10
10
  def marks
11
11
  motions_to_perfect_consonance_approached_directly.map do |bad_motion|
@@ -7,7 +7,7 @@ module HeadMusic::Style::Guidelines; end
7
7
  class HeadMusic::Style::Guidelines::AtLeastEightNotes < HeadMusic::Style::Annotation
8
8
  MINIMUM_NOTES = 8
9
9
 
10
- MESSAGE = 'Write at least eight notes.'
10
+ MESSAGE = "Write at least eight notes."
11
11
 
12
12
  def marks
13
13
  placements.empty? ? no_placements_mark : deficiency_mark
@@ -17,8 +17,8 @@ class HeadMusic::Style::Guidelines::AtLeastEightNotes < HeadMusic::Style::Annota
17
17
 
18
18
  def no_placements_mark
19
19
  HeadMusic::Style::Mark.new(
20
- HeadMusic::Position.new(composition, '1:1'),
21
- HeadMusic::Position.new(composition, '2:1'),
20
+ HeadMusic::Position.new(composition, "1:1"),
21
+ HeadMusic::Position.new(composition, "2:1"),
22
22
  fitness: 0
23
23
  )
24
24
  end
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::AvoidCrossingVoices < HeadMusic::Style::Annotation
8
- MESSAGE = 'Avoid crossing voices. Maintain the high-low relationship between voices.'
8
+ MESSAGE = "Avoid crossing voices. Maintain the high-low relationship between voices."
9
9
 
10
10
  def marks
11
11
  crossings.map do |crossing|
@@ -22,11 +22,11 @@ class HeadMusic::Style::Guidelines::AvoidCrossingVoices < HeadMusic::Style::Anno
22
22
  end
23
23
 
24
24
  def predominant_pitch_orientation
25
- pitch_orientations.
26
- compact.
27
- group_by { |orientation| orientation }.
28
- max { |a, b| a[1].length <=> b[1].length }.
29
- first
25
+ pitch_orientations
26
+ .compact
27
+ .group_by { |orientation| orientation }
28
+ .max { |a, b| a[1].length <=> b[1].length }
29
+ .first
30
30
  end
31
31
 
32
32
  def pitch_orientations
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::AvoidOverlappingVoices < HeadMusic::Style::Annotation
8
- MESSAGE = 'Avoid overlapping voices. Maintain the high-low relationship between voices even for adjacent notes.'
8
+ MESSAGE = "Avoid overlapping voices. Maintain the high-low relationship between voices even for adjacent notes."
9
9
 
10
10
  def marks
11
11
  overlappings
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::ConsonantClimax < HeadMusic::Style::Annotation
8
- MESSAGE = 'Peak on a consonant high or low note one time or twice with a step between.'
8
+ MESSAGE = "Peak on a consonant high or low note one time or twice with a step between."
9
9
 
10
10
  def marks
11
11
  HeadMusic::Style::Mark.for_each(highest_notes) unless adherent_climax?
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::ConsonantDownbeats < HeadMusic::Style::Annotation
8
- MESSAGE = 'Use consonant harmonic intervals on every downbeat.'
8
+ MESSAGE = "Use consonant harmonic intervals on every downbeat."
9
9
 
10
10
  def marks
11
11
  dissonant_pairs.map do |dissonant_pair|
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::Diatonic < HeadMusic::Style::Annotation
8
- MESSAGE = 'Use only notes in the key signature.'
8
+ MESSAGE = "Use only notes in the key signature."
9
9
 
10
10
  def marks
11
11
  HeadMusic::Style::Mark.for_each(notes_not_in_key_excluding_penultimate_leading_tone)
@@ -17,7 +17,7 @@ class HeadMusic::Style::Guidelines::Diatonic < HeadMusic::Style::Annotation
17
17
  notes_not_in_key.reject do |note|
18
18
  penultimate_note &&
19
19
  note == penultimate_note &&
20
- HeadMusic::ScaleDegree.new(key_signature, note.pitch.spelling).sign == '#'
20
+ HeadMusic::ScaleDegree.new(key_signature, note.pitch.spelling).sign == "#"
21
21
  end
22
22
  end
23
23
 
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # marks the voice if the first note is not the first or fifth scale degree of the key.
7
7
  class HeadMusic::Style::Guidelines::EndOnPerfectConsonance < HeadMusic::Style::Annotation
8
- MESSAGE = 'End on the first or the fifth scale degree.'
8
+ MESSAGE = "End on the first or the fifth scale degree."
9
9
 
10
10
  def marks
11
11
  HeadMusic::Style::Mark.for(last_note) if last_note && !ends_on_perfect_consonance?
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::EndOnTonic < HeadMusic::Style::Annotation
8
- MESSAGE = 'End on the first scale degree.'
8
+ MESSAGE = "End on the first scale degree."
9
9
 
10
10
  def marks
11
11
  HeadMusic::Style::Mark.for(notes.last) if notes.any? && !ends_on_tonic?
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::FrequentDirectionChanges < HeadMusic::Style::Guidelines::DirectionChanges
8
- MESSAGE = 'Change melodic direction frequently.'
8
+ MESSAGE = "Change melodic direction frequently."
9
9
 
10
10
  def self.maximum_notes_per_direction
11
11
  3
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline: Use a maximum of one octave leap.
7
7
  class HeadMusic::Style::Guidelines::LimitOctaveLeaps < HeadMusic::Style::Annotation
8
- MESSAGE = 'Use a maximum of one octave leap.'
8
+ MESSAGE = "Use a maximum of one octave leap."
9
9
 
10
10
  def marks
11
11
  return if octave_leaps.length <= 1
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::ModerateDirectionChanges < HeadMusic::Style::Guidelines::DirectionChanges
8
- MESSAGE = 'Change melodic direction occasionally.'
8
+ MESSAGE = "Change melodic direction occasionally."
9
9
 
10
10
  def self.maximum_notes_per_direction
11
11
  5
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::MostlyConjunct < HeadMusic::Style::Annotation
8
- MESSAGE = 'Use mostly conjunct motion.'
8
+ MESSAGE = "Use mostly conjunct motion."
9
9
 
10
10
  MINIMUM_CONJUNCT_PORTION = HeadMusic::GOLDEN_RATIO_INVERSE**2
11
11
  # ~38%
@@ -18,9 +18,9 @@ class HeadMusic::Style::Guidelines::MostlyConjunct < HeadMusic::Style::Annotatio
18
18
  private
19
19
 
20
20
  def marks_for_skips_and_leaps
21
- melodic_intervals.
22
- reject(&:step?).
23
- map { |interval| HeadMusic::Style::Mark.for_all(interval.notes, fitness: HeadMusic::SMALL_PENALTY_FACTOR) }
21
+ melodic_intervals
22
+ .reject(&:step?)
23
+ .map { |interval| HeadMusic::Style::Mark.for_all(interval.notes, fitness: HeadMusic::SMALL_PENALTY_FACTOR) }
24
24
  end
25
25
 
26
26
  def conjunct_ratio
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::NoRests < HeadMusic::Style::Annotation
8
- MESSAGE = 'Place a note in each measure.'
8
+ MESSAGE = "Place a note in each measure."
9
9
 
10
10
  def marks
11
11
  HeadMusic::Style::Mark.for_each(rests)
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::NoUnisonsInMiddle < HeadMusic::Style::Annotation
8
- MESSAGE = 'Unisons may only be used in the first and last note.'
8
+ MESSAGE = "Unisons may only be used in the first and last note."
9
9
 
10
10
  def marks
11
11
  unison_pairs.map do |notes|
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::NotesSameLength < HeadMusic::Style::Annotation
8
- MESSAGE = 'Use consistent rhythmic unit.'
8
+ MESSAGE = "Use consistent rhythmic unit."
9
9
 
10
10
  def marks
11
11
  HeadMusic::Style::Mark.for_each(all_wrong_length_notes)
@@ -31,7 +31,7 @@ class HeadMusic::Style::Guidelines::NotesSameLength < HeadMusic::Style::Annotati
31
31
  last_note.nil? ||
32
32
  [
33
33
  first_most_common_rhythmic_value.total_value,
34
- first_most_common_rhythmic_value.total_value * 2,
34
+ first_most_common_rhythmic_value.total_value * 2
35
35
  ].include?(last_note.rhythmic_value.total_value)
36
36
  end
37
37
 
@@ -47,7 +47,7 @@ class HeadMusic::Style::Guidelines::NotesSameLength < HeadMusic::Style::Annotati
47
47
  @first_most_common_rhythmic_value ||= begin
48
48
  candidates = most_common_rhythmic_values
49
49
  first_match = notes.detect { |note| candidates.include?(note.rhythmic_value) }
50
- first_match ? first_match.rhythmic_value : nil
50
+ first_match&.rhythmic_value
51
51
  end
52
52
  end
53
53
 
@@ -60,7 +60,7 @@ class HeadMusic::Style::Guidelines::NotesSameLength < HeadMusic::Style::Annotati
60
60
  end
61
61
 
62
62
  def occurrences_by_rhythmic_value
63
- rhythmic_values.each_with_object(Hash.new(0)) { |value, hash| hash[value] += 1; }
63
+ rhythmic_values.each_with_object(Hash.new(0)) { |value, hash| hash[value] += 1 }
64
64
  end
65
65
 
66
66
  def rhythmic_values
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::OneToOne < HeadMusic::Style::Annotation
8
- MESSAGE = 'Place a note for each note in the other voice.'
8
+ MESSAGE = "Place a note for each note in the other voice."
9
9
 
10
10
  def marks
11
11
  return unless cantus_firmus&.notes
@@ -5,7 +5,7 @@ module HeadMusic::Style::Guidelines; end
5
5
 
6
6
  # A counterpoint guideline
7
7
  class HeadMusic::Style::Guidelines::PreferContraryMotion < HeadMusic::Style::Annotation
8
- MESSAGE = 'Prefer contrary motion. Move voices in different melodic directions.'
8
+ MESSAGE = "Prefer contrary motion. Move voices in different melodic directions."
9
9
 
10
10
  def marks
11
11
  return nil if notes.length < 2