head_music 0.27.0 → 0.28.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 +11 -37
  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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4274cb71a70fcab6904e99afc282bb1db756e2a1cce0c1e1e50c601e0e848edf
4
- data.tar.gz: 47566a6db5b0836686fd789cf2e0b052416a68f8f2ed67b2f6582266a141ea1a
3
+ metadata.gz: 3ea2efb7d874b85dd67a1838102d76409ce6a1bf9989707af68ad4909eccc9e5
4
+ data.tar.gz: c45d1a2683f49adcadb70a0eb29965ad7af7e5fd9f1ec6966c1daacacb1ade95
5
5
  SHA512:
6
- metadata.gz: b85d9dbd114b70bfea1fa9ce8cb964f6780ad9f64325a75baba02df48e416475f33e26a9dab22309740ff6e7f033fca756906a4fd5935af1f22e6fd325a775d4
7
- data.tar.gz: 60a75acc692a1e1014f6042f9b167ecd7a0f6ec8eb98ca5dccb4c16a9bbcc79deff0b56481a713941caad779b7cc17b8487c89f4b4839bb31e3b453cff7558b5
6
+ metadata.gz: '087c6dc3413b35fd1c1b4cb729630bb2c11b2497a5506aa1a7168df15878cce5732e831f04f165dcee32891b06c31d456bd08a758ba83ffc1e428882b7ac9970'
7
+ data.tar.gz: 23122c1670d39472bd93059b350520697f31abd5b2738b2738e07079b72abe3c44065d7d74a38568111399f3a646cc78264304c262e6501815f8dab478795773
data/.circleci/config.yml CHANGED
@@ -2,7 +2,7 @@ version: 2
2
2
  jobs:
3
3
  build:
4
4
  docker:
5
- - image: circleci/ruby:2.6.5
5
+ - image: circleci/ruby:2.6.7
6
6
  steps:
7
7
  - checkout
8
8
  - restore_cache:
data/.rubocop.yml CHANGED
@@ -1,42 +1,16 @@
1
- AllCops:
2
- NewCops: enable
3
- TargetRubyVersion: 2.5
1
+ require:
2
+ - standard
3
+ - rubocop-rspec
4
4
 
5
- Layout/DotPosition:
6
- EnforcedStyle: trailing
5
+ inherit_gem:
6
+ standard: config/base.yml
7
7
 
8
- Layout/LineLength:
9
- Max: 120
10
-
11
- Metrics/BlockLength:
8
+ AllCops:
9
+ NewCops: enable
12
10
  Exclude:
13
- - 'Gemfile'
14
- - 'Rakefile'
15
- - 'spec/**/*.rb'
16
-
17
- Metrics/ClassLength:
18
- Max: 155
19
-
20
- Style/ClassAndModuleChildren:
21
- EnforcedStyle: compact
11
+ - public/**/*
12
+ - vendor/**/*
13
+ TargetRubyVersion: 3.1.4
22
14
 
23
- Style/HashEachMethods:
24
- Enabled: false
25
-
26
- Style/HashTransformKeys:
27
- Enabled: false
28
-
29
- Style/HashTransformValues:
30
- Enabled: false
31
-
32
- Style/SymbolArray:
15
+ RSpec:
33
16
  Enabled: true
34
-
35
- Style/TrailingCommaInArrayLiteral:
36
- EnforcedStyleForMultiline: consistent_comma
37
-
38
- Style/TrailingCommaInHashLiteral:
39
- EnforcedStyleForMultiline: consistent_comma
40
-
41
- Layout/EmptyLineAfterGuardClause:
42
- Enabled: false
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.6.5
1
+ 3.1.4
data/.travis.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.6.5
4
+ - 3.1.4
5
5
  before_install: gem install bundler
data/Gemfile CHANGED
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source 'https://rubygems.org'
3
+ source "https://rubygems.org"
4
4
 
5
- ruby '2.6.5'
5
+ ruby "3.1.4"
6
6
 
7
7
  # Specify your gem's dependencies in head_music.gemspec
8
8
  gemspec
9
9
 
10
+ gem "standard", require: false
11
+
10
12
  group :test do
11
- gem 'codeclimate-test-reporter', '~> 1.0.0'
12
- gem 'rubocop', '>= 0.80.0', require: false
13
- gem 'rubocop-rspec', require: false
14
- gem 'simplecov'
13
+ gem "codeclimate-test-reporter", "~> 1.0.0"
14
+ gem "rubocop", require: false
15
+ gem "rubocop-rspec", require: false
16
+ gem "simplecov"
15
17
  end
data/Rakefile CHANGED
@@ -1,13 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ require "standard/rake"
5
6
 
6
7
  RSpec::Core::RakeTask.new(:spec)
7
8
 
8
9
  task default: :spec
9
10
 
10
- desc 'Open an irb session preloaded with this library'
11
+ desc "Open an irb session preloaded with this library"
11
12
  task :console do
12
- sh 'irb -I lib -r head_music.rb'
13
+ sh "irb -I lib -r head_music.rb"
13
14
  end
data/bin/console CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'bundler/setup'
5
- require 'head_music'
4
+ require "bundler/setup"
5
+ require "head_music"
6
6
 
7
7
  # You can add fixtures and/or initialization code here to make experimenting
8
8
  # with your gem easier. You can also use a different console, if you like.
@@ -11,5 +11,5 @@ require 'head_music'
11
11
  # require "pry"
12
12
  # Pry.start
13
13
 
14
- require 'irb'
14
+ require "irb"
15
15
  IRB.start
data/head_music.gemspec CHANGED
@@ -1,35 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('lib', __dir__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'head_music/version'
5
+ require "head_music/version"
6
6
 
7
7
  Gem::Specification.new do |spec|
8
- spec.name = 'head_music'
9
- spec.version = HeadMusic::VERSION
10
- spec.authors = ['Rob Head']
11
- spec.email = ['robert.head@gmail.com']
8
+ spec.name = "head_music"
9
+ spec.version = HeadMusic::VERSION
10
+ spec.authors = ["Rob Head"]
11
+ spec.email = ["robert.head@gmail.com"]
12
12
 
13
- spec.summary = 'The rudiments of western music theory.'
14
- spec.description = 'Work with the elements of western music theory, such as pitches, scales, intervals, and chords.'
15
- spec.homepage = 'https://github.com/roberthead/head_music'
16
- spec.license = 'MIT'
13
+ spec.summary = "The rudiments of western music theory."
14
+ spec.description = "Work with the elements of western music theory, such as pitches, scales, intervals, and chords."
15
+ spec.homepage = "https://github.com/roberthead/head_music"
16
+ spec.license = "MIT"
17
17
 
18
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
19
  f.match(%r{^(test|spec|features)/})
20
20
  end
21
- spec.bindir = 'exe'
22
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
- spec.require_paths = ['lib']
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
24
 
25
- spec.required_ruby_version = '>= 2.5'
25
+ spec.required_ruby_version = ">= 2.5"
26
26
 
27
- spec.add_runtime_dependency 'activesupport', '> 5.0'
28
- spec.add_runtime_dependency 'humanize', '~> 1.3'
29
- spec.add_runtime_dependency 'i18n', '~> 1.8'
27
+ spec.add_runtime_dependency "activesupport", "> 5.0"
28
+ spec.add_runtime_dependency "humanize", "~> 1.3"
29
+ spec.add_runtime_dependency "i18n", "~> 1.8"
30
30
 
31
- spec.add_development_dependency 'bundler', '~> 1.13'
32
- spec.add_development_dependency 'rake', '>= 12.3.3'
33
- spec.add_development_dependency 'rspec', '~> 3.0'
34
- spec.add_development_dependency 'rspec-its', '~> 1.2'
31
+ spec.add_development_dependency "rake", ">= 12.3.3"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ spec.add_development_dependency "rspec-its", "~> 1.2"
35
34
  end
@@ -16,7 +16,7 @@ class HeadMusic::ChromaticInterval
16
16
 
17
17
  def self.get(identifier)
18
18
  @intervals ||= {}
19
- candidate = identifier.to_s.downcase.gsub(/\W+/, '_')
19
+ candidate = identifier.to_s.downcase.gsub(/\W+/, "_")
20
20
  semitones = NAMES.index(candidate) || identifier.to_i
21
21
  @intervals[semitones] ||= new(semitones.to_i)
22
22
  end
@@ -42,11 +42,11 @@ class HeadMusic::ChromaticInterval
42
42
  end
43
43
 
44
44
  def diatonic_name
45
- NAMES[simple.semitones].gsub(/_/, ' ')
45
+ NAMES[simple.semitones].tr("_", " ")
46
46
  end
47
47
 
48
48
  # diatonic set theory
49
- alias specific_interval semitones
49
+ alias_method :specific_interval, :semitones
50
50
 
51
51
  def +(other)
52
52
  HeadMusic::ChromaticInterval.get(to_i + other.to_i)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'head_music/interval_cycle'
3
+ require "head_music/interval_cycle"
4
4
 
5
5
  # A Circle of Fifths or Fourths shows relationships between pitch classes
6
6
  class HeadMusic::Circle < HeadMusic::IntervalCycle
@@ -15,15 +15,13 @@ class HeadMusic::Circle < HeadMusic::IntervalCycle
15
15
  def self.get(interval = :perfect_fifth)
16
16
  @circles ||= {}
17
17
  diatonic_interval = HeadMusic::DiatonicInterval.get(interval)
18
- @circles[interval] ||= new(interval: diatonic_interval, starting_pitch: 'C4')
18
+ @circles[interval] ||= new(interval: diatonic_interval, starting_pitch: "C4")
19
19
  end
20
20
 
21
21
  def index(pitch_class)
22
22
  pitch_classes.index(HeadMusic::Spelling.get(pitch_class).pitch_class)
23
23
  end
24
24
 
25
- alias spellings_up spellings
26
-
27
25
  def key_signatures_up
28
26
  spellings_up.map { |spelling| HeadMusic::KeySignature.new(spelling) }
29
27
  end
@@ -32,20 +30,26 @@ class HeadMusic::Circle < HeadMusic::IntervalCycle
32
30
  spellings_down.map { |spelling| HeadMusic::KeySignature.new(spelling) }
33
31
  end
34
32
 
33
+ def spellings_up
34
+ pitches_up.map(&:pitch_class).map do |pitch_class|
35
+ pitch_class.smart_spelling(max_sharps_in_major_key_signature: 7)
36
+ end
37
+ end
38
+
35
39
  def spellings_down
36
- pitches_down.map(&:spelling)
40
+ pitches_down.map(&:pitch_class).map do |pitch_class|
41
+ pitch_class.smart_spelling(max_sharps_in_major_key_signature: 4)
42
+ end
37
43
  end
38
44
 
39
45
  def pitches_down
40
- @pitches_down ||= begin
41
- [starting_pitch].tap do |list|
42
- loop do
43
- next_pitch = list.last - interval
44
- next_pitch += octave while starting_pitch - next_pitch > 12
45
- break if next_pitch.pitch_class == list.first.pitch_class
46
-
47
- list << next_pitch
48
- end
46
+ @pitches_down ||= [starting_pitch].tap do |list|
47
+ loop do
48
+ next_pitch = list.last - interval
49
+ next_pitch += octave while starting_pitch - next_pitch > 12
50
+ break if next_pitch.pitch_class == list.first.pitch_class
51
+
52
+ list << next_pitch
49
53
  end
50
54
  end
51
55
  end
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yaml'
3
+ require "yaml"
4
4
 
5
5
  # A clef assigns pitches to the lines and spaces of a staff.
6
6
  class HeadMusic::Clef
7
7
  include HeadMusic::Named
8
8
 
9
- RECORDS = YAML.load_file(File.expand_path('data/clefs.yml', __dir__)).freeze
9
+ RECORDS = YAML.load_file(File.expand_path("data/clefs.yml", __dir__)).freeze
10
10
 
11
11
  def self.get(name)
12
12
  get_by_name(name)
@@ -97,7 +97,7 @@ class HeadMusic::Clef
97
97
 
98
98
  def initialize_musical_symbols(list)
99
99
  @musical_symbols = (list || []).map do |symbol_data|
100
- HeadMusic::MusicalSymbol.new(symbol_data.slice(:ascii, :html_entity, :unicode))
100
+ HeadMusic::MusicalSymbol.new(**symbol_data.slice(:ascii, :html_entity, :unicode))
101
101
  end
102
102
  end
103
103
  end
@@ -13,6 +13,6 @@ class HeadMusic::Bar
13
13
  end
14
14
 
15
15
  def to_s
16
- ['Bar', key_signature, meter].compact.join(' ')
16
+ ["Bar", key_signature, meter].compact.join(" ")
17
17
  end
18
18
  end
@@ -63,7 +63,7 @@ class HeadMusic::Composition
63
63
  private
64
64
 
65
65
  def ensure_attributes(name, key_signature, meter)
66
- @name = name || 'Composition'
66
+ @name = name || "Composition"
67
67
  @key_signature = HeadMusic::KeySignature.get(key_signature) if key_signature
68
68
  @key_signature ||= HeadMusic::KeySignature.default
69
69
  @meter = meter ? HeadMusic::Meter.get(meter) : HeadMusic::Meter.default
@@ -12,7 +12,7 @@ class HeadMusic::Note
12
12
  @pitch = HeadMusic::Pitch.get(pitch)
13
13
  @rhythmic_value = HeadMusic::RhythmicValue.get(rhythmic_value)
14
14
  @voice = voice || HeadMusic::Voice.new
15
- @position = position || HeadMusic::Position.new(@voice.composition, '1:1')
15
+ @position = position || HeadMusic::Position.new(@voice.composition, "1:1")
16
16
  end
17
17
 
18
18
  def placement
@@ -34,7 +34,7 @@ class HeadMusic::Placement
34
34
  end
35
35
 
36
36
  def to_s
37
- "#{rhythmic_value} #{pitch || 'rest'} at #{position}"
37
+ "#{rhythmic_value} #{pitch || "rest"} at #{position}"
38
38
  end
39
39
 
40
40
  private
@@ -60,9 +60,9 @@ class HeadMusic::Placement
60
60
 
61
61
  def ensure_position(position)
62
62
  @position = if position.is_a?(HeadMusic::Position)
63
- position
64
- else
65
- HeadMusic::Position.new(composition, position)
66
- end
63
+ position
64
+ else
65
+ HeadMusic::Position.new(composition, position)
66
+ end
67
67
  end
68
68
  end
@@ -22,12 +22,12 @@ class HeadMusic::Position
22
22
  end
23
23
 
24
24
  def code
25
- tick_string = tick.to_s.rjust(3, '0')
26
- [bar_number, count, tick_string].join(':')
25
+ tick_string = tick.to_s.rjust(3, "0")
26
+ [bar_number, count, tick_string].join(":")
27
27
  end
28
28
 
29
29
  def state
30
- [composition.name, code].join(' ')
30
+ [composition.name, code].join(" ")
31
31
  end
32
32
 
33
33
  def values
@@ -14,7 +14,7 @@ class HeadMusic::RhythmicValue
14
14
  when HeadMusic::RhythmicUnit
15
15
  new(identifier)
16
16
  when Symbol, String
17
- identifier = identifier.to_s.downcase.strip.gsub(/\W+/, '_')
17
+ identifier = identifier.to_s.downcase.strip.gsub(/\W+/, "_")
18
18
  from_words(identifier)
19
19
  end
20
20
  end
@@ -24,7 +24,7 @@ class HeadMusic::RhythmicValue
24
24
  end
25
25
 
26
26
  def self.unit_from_words(identifier)
27
- identifier.gsub(/^\w*dotted_/, '')
27
+ identifier.gsub(/^\w*dotted_/, "")
28
28
  end
29
29
 
30
30
  def self.dots_from_words(identifier)
@@ -74,21 +74,21 @@ class HeadMusic::RhythmicValue
74
74
  def name_modifier_prefix
75
75
  case dots
76
76
  when 1
77
- 'dotted'
77
+ "dotted"
78
78
  when 2
79
- 'double-dotted'
79
+ "double-dotted"
80
80
  when 3
81
- 'triple-dotted'
81
+ "triple-dotted"
82
82
  end
83
83
  end
84
84
 
85
85
  def single_value_name
86
- [name_modifier_prefix, unit_name].compact.join(' ')
86
+ [name_modifier_prefix, unit_name].compact.join(" ")
87
87
  end
88
88
 
89
89
  def name
90
90
  if tied_value
91
- [single_value_name, tied_value.name].compact.join(' tied to ')
91
+ [single_value_name, tied_value.name].compact.join(" tied to ")
92
92
  else
93
93
  single_value_name
94
94
  end
@@ -99,6 +99,6 @@ class HeadMusic::RhythmicValue
99
99
  end
100
100
 
101
101
  def to_s
102
- name.tr('_', '-')
102
+ name.tr("_", "-")
103
103
  end
104
104
  end
@@ -84,7 +84,7 @@ class HeadMusic::Voice
84
84
  end
85
85
 
86
86
  def note_preceding(position)
87
- notes.select { |note| note.position < position }.last
87
+ notes.reverse.find { |note| note.position < position }
88
88
  end
89
89
 
90
90
  def note_following(position)
@@ -10,34 +10,34 @@ class HeadMusic::DiatonicInterval
10
10
  ninth tenth eleventh twelfth thirteenth fourteenth fifteenth
11
11
  sixteenth seventeenth
12
12
  ].freeze
13
- NAME_SUFFIXES = Hash.new('th').merge(1 => 'st', 2 => 'nd', 3 => 'rd').freeze
13
+ NAME_SUFFIXES = Hash.new("th").merge(1 => "st", 2 => "nd", 3 => "rd").freeze
14
14
 
15
15
  QUALITY_SEMITONES = {
16
- unison: { perfect: 0 },
17
- second: { major: 2 },
18
- third: { major: 4 },
19
- fourth: { perfect: 5 },
20
- fifth: { perfect: 7 },
21
- sixth: { major: 9 },
22
- seventh: { major: 11 },
23
- octave: { perfect: 12 },
24
- ninth: { major: 14 },
25
- tenth: { major: 16 },
26
- eleventh: { perfect: 17 },
27
- twelfth: { perfect: 19 },
28
- thirteenth: { major: 21 },
29
- fourteenth: { major: 23 },
30
- fifteenth: { perfect: 24 },
31
- sixteenth: { major: 26 },
32
- seventeenth: { major: 28 },
16
+ unison: {perfect: 0},
17
+ second: {major: 2},
18
+ third: {major: 4},
19
+ fourth: {perfect: 5},
20
+ fifth: {perfect: 7},
21
+ sixth: {major: 9},
22
+ seventh: {major: 11},
23
+ octave: {perfect: 12},
24
+ ninth: {major: 14},
25
+ tenth: {major: 16},
26
+ eleventh: {perfect: 17},
27
+ twelfth: {perfect: 19},
28
+ thirteenth: {major: 21},
29
+ fourteenth: {major: 23},
30
+ fifteenth: {perfect: 24},
31
+ sixteenth: {major: 26},
32
+ seventeenth: {major: 28}
33
33
  }.freeze
34
34
 
35
35
  QUALITY_ABBREVIATIONS = {
36
- P: 'perfect',
37
- M: 'major',
38
- m: 'minor',
39
- d: 'diminished',
40
- A: 'augmented',
36
+ P: "perfect",
37
+ M: "major",
38
+ m: "minor",
39
+ d: "diminished",
40
+ A: "augmented"
41
41
  }.freeze
42
42
 
43
43
  attr_reader :lower_pitch, :higher_pitch
@@ -58,7 +58,7 @@ class HeadMusic::DiatonicInterval
58
58
  end
59
59
 
60
60
  def quality_name
61
- words[0..-2].join(' ').to_sym
61
+ words[0..-2].join(" ").to_sym
62
62
  end
63
63
 
64
64
  def degree_name
@@ -75,8 +75,8 @@ class HeadMusic::DiatonicInterval
75
75
 
76
76
  def expand(identifier)
77
77
  if /[A-Z]\d{1,2}/i.match?(identifier)
78
- number = NUMBER_NAMES[identifier.gsub(/[A-Z]/i, '').to_i - 1]
79
- return [quality_for(identifier[0]), number].join('_').to_sym
78
+ number = NUMBER_NAMES[identifier.gsub(/[A-Z]/i, "").to_i - 1]
79
+ return [quality_for(identifier[0]), number].join("_").to_sym
80
80
  end
81
81
  identifier
82
82
  end
@@ -97,13 +97,11 @@ class HeadMusic::DiatonicInterval
97
97
  end
98
98
 
99
99
  def self.degree_quality_semitones
100
- @degree_quality_semitones ||= begin
101
- {}.tap do |degree_quality_semitones|
102
- QUALITY_SEMITONES.each do |degree_name, qualities|
103
- default_quality = qualities.keys.first
104
- default_semitones = qualities[default_quality]
105
- degree_quality_semitones[degree_name] = _semitones_for_degree(default_quality, default_semitones)
106
- end
100
+ @degree_quality_semitones ||= {}.tap do |degree_quality_semitones|
101
+ QUALITY_SEMITONES.each do |degree_name, qualities|
102
+ default_quality = qualities.keys.first
103
+ default_semitones = qualities[default_quality]
104
+ degree_quality_semitones[degree_name] = _semitones_for_degree(default_quality, default_semitones)
107
105
  end
108
106
  end
109
107
  end
@@ -219,7 +217,7 @@ class HeadMusic::DiatonicInterval
219
217
  end
220
218
 
221
219
  def simple_name
222
- [quality_name, simple_number_name].join(' ')
220
+ [quality_name, simple_number_name].join(" ")
223
221
  end
224
222
 
225
223
  def quality_name
@@ -239,8 +237,8 @@ class HeadMusic::DiatonicInterval
239
237
 
240
238
  def name
241
239
  if named_number?
242
- [quality_name, number_name].join(' ')
243
- elsif simple_name == 'perfect octave'
240
+ [quality_name, number_name].join(" ")
241
+ elsif simple_name == "perfect octave"
244
242
  "#{octaves.humanize} octaves"
245
243
  else
246
244
  "#{octaves.humanize} octaves and #{quality.article} #{simple_name}"
@@ -248,7 +246,7 @@ class HeadMusic::DiatonicInterval
248
246
  end
249
247
 
250
248
  def shorthand
251
- step_shorthand = number == 1 ? 'U' : number
249
+ step_shorthand = (number == 1) ? "U" : number
252
250
  [quality.shorthand, step_shorthand].join
253
251
  end
254
252
 
@@ -281,7 +279,7 @@ class HeadMusic::DiatonicInterval
281
279
  to: :naming
282
280
  )
283
281
 
284
- alias to_i semitones
282
+ alias_method :to_i, :semitones
285
283
 
286
284
  # Accepts a name and returns the interval with middle c on the bottom
287
285
  def self.get(identifier)
@@ -308,7 +306,7 @@ class HeadMusic::DiatonicInterval
308
306
  end
309
307
  HeadMusic::DiatonicInterval.new(higher_pitch, inverted_low_pitch)
310
308
  end
311
- alias invert inversion
309
+ alias_method :invert, :inversion
312
310
 
313
311
  def consonance(style = :standard_practice)
314
312
  consonance_for_perfect(style) ||
@@ -319,7 +317,7 @@ class HeadMusic::DiatonicInterval
319
317
  def consonance?(style = :standard_practice)
320
318
  consonance(style).perfect? || consonance(style).imperfect?
321
319
  end
322
- alias consonant? consonance?
320
+ alias_method :consonant?, :consonance?
323
321
 
324
322
  def perfect_consonance?(style = :standard_practice)
325
323
  consonance(style).perfect?
@@ -352,8 +350,8 @@ class HeadMusic::DiatonicInterval
352
350
  end
353
351
 
354
352
  # diatonic set theory
355
- alias specific_interval simple_semitones
356
- alias diatonic_generic_interval simple_steps
353
+ alias_method :specific_interval, :simple_semitones
354
+ alias_method :diatonic_generic_interval, :simple_steps
357
355
 
358
356
  def <=>(other)
359
357
  other = self.class.get(other) unless other.is_a?(HeadMusic::DiatonicInterval)
@@ -391,7 +389,7 @@ class HeadMusic::DiatonicInterval
391
389
  end
392
390
 
393
391
  def consonance_for_major_and_minor
394
- HeadMusic::Consonance.get(third_or_compound? || sixth_or_compound? ? :imperfect : :dissonant) if major? || minor?
392
+ HeadMusic::Consonance.get((third_or_compound? || sixth_or_compound?) ? :imperfect : :dissonant) if major? || minor?
395
393
  end
396
394
 
397
395
  def dissonant_fourth?(style = :standard_practice)
@@ -6,18 +6,18 @@ class HeadMusic::GrandStaff
6
6
  piano: {
7
7
  instrument: :piano,
8
8
  staves: [
9
- { clef: :treble_clef, for: :right_hand },
10
- { clef: :bass_clef, for: :left_hand },
11
- ],
9
+ {clef: :treble_clef, for: :right_hand},
10
+ {clef: :bass_clef, for: :left_hand}
11
+ ]
12
12
  },
13
13
  organ: {
14
14
  instrument: :organ,
15
15
  staves: [
16
- { clef: :treble_clef, for: :right_hand },
17
- { clef: :bass_clef, for: :left_hand },
18
- { clef: :bass_clef, for: :pedals },
19
- ],
20
- },
16
+ {clef: :treble_clef, for: :right_hand},
17
+ {clef: :bass_clef, for: :left_hand},
18
+ {clef: :bass_clef, for: :pedals}
19
+ ]
20
+ }
21
21
  }.freeze
22
22
 
23
23
  def self.get(name)