head_music 0.26.3 → 0.28.0

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 (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 +24 -9
  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 +13 -0
  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: 3224075c2e9f76d778cdbd71942f5bf1496b25c52c56062169824fc3e916f947
4
- data.tar.gz: e272fa27e163ca5ea97ceaed10f9098cb196f2ceb45008a89da7837452df6c6e
3
+ metadata.gz: 3ea2efb7d874b85dd67a1838102d76409ce6a1bf9989707af68ad4909eccc9e5
4
+ data.tar.gz: c45d1a2683f49adcadb70a0eb29965ad7af7e5fd9f1ec6966c1daacacb1ade95
5
5
  SHA512:
6
- metadata.gz: f007ea93b150e2a2f140fadb68d73c978e9ea1481dc0e7590c99f97b5b0fabb9be935adca85b92c86d48179b751aa745fcb81cfa194c7df3d2d0f4ae8282d60f
7
- data.tar.gz: 3176231f3a293e9b482fcd7a6708159ca76ed73e8efbec6947fe5779d42fc97cb7850ec07be2d2ae5460bb0e8b88b7b70576e839c59d070707b2a7bbf7f491f0
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)