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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +1 -1
- data/.rubocop.yml +11 -37
- data/.ruby-version +1 -1
- data/.travis.yml +1 -1
- data/Gemfile +8 -6
- data/Rakefile +5 -4
- data/bin/console +3 -3
- data/head_music.gemspec +21 -22
- data/lib/head_music/chromatic_interval.rb +3 -3
- data/lib/head_music/circle.rb +18 -14
- data/lib/head_music/clef.rb +3 -3
- data/lib/head_music/content/bar.rb +1 -1
- data/lib/head_music/content/composition.rb +1 -1
- data/lib/head_music/content/note.rb +1 -1
- data/lib/head_music/content/placement.rb +5 -5
- data/lib/head_music/content/position.rb +3 -3
- data/lib/head_music/content/rhythmic_value.rb +8 -8
- data/lib/head_music/content/voice.rb +1 -1
- data/lib/head_music/diatonic_interval.rb +41 -43
- data/lib/head_music/grand_staff.rb +8 -8
- data/lib/head_music/instrument.rb +8 -8
- data/lib/head_music/interval_cycle.rb +10 -12
- data/lib/head_music/key_signature.rb +12 -13
- data/lib/head_music/letter_name.rb +7 -7
- data/lib/head_music/melodic_interval.rb +1 -1
- data/lib/head_music/meter.rb +17 -12
- data/lib/head_music/named.rb +3 -3
- data/lib/head_music/pitch/enharmonic_equivalence.rb +2 -2
- data/lib/head_music/pitch/octave_equivalence.rb +1 -1
- data/lib/head_music/pitch.rb +9 -8
- data/lib/head_music/pitch_class.rb +18 -4
- data/lib/head_music/pitch_class_set.rb +5 -5
- data/lib/head_music/pitch_set.rb +9 -6
- data/lib/head_music/quality.rb +15 -14
- data/lib/head_music/reference_pitch.rb +23 -23
- data/lib/head_music/register.rb +2 -2
- data/lib/head_music/rhythmic_unit.rb +9 -9
- data/lib/head_music/scale.rb +4 -4
- data/lib/head_music/scale_degree.rb +2 -2
- data/lib/head_music/scale_type.rb +13 -13
- data/lib/head_music/sign.rb +12 -11
- data/lib/head_music/solmization.rb +3 -3
- data/lib/head_music/sonority.rb +2 -2
- data/lib/head_music/spelling.rb +4 -6
- data/lib/head_music/staff.rb +1 -1
- data/lib/head_music/style/analysis.rb +1 -1
- data/lib/head_music/style/annotation.rb +7 -7
- data/lib/head_music/style/guidelines/always_move.rb +4 -4
- data/lib/head_music/style/guidelines/approach_perfection_contrarily.rb +1 -1
- data/lib/head_music/style/guidelines/at_least_eight_notes.rb +3 -3
- data/lib/head_music/style/guidelines/avoid_crossing_voices.rb +6 -6
- data/lib/head_music/style/guidelines/avoid_overlapping_voices.rb +1 -1
- data/lib/head_music/style/guidelines/consonant_climax.rb +1 -1
- data/lib/head_music/style/guidelines/consonant_downbeats.rb +1 -1
- data/lib/head_music/style/guidelines/diatonic.rb +2 -2
- data/lib/head_music/style/guidelines/end_on_perfect_consonance.rb +1 -1
- data/lib/head_music/style/guidelines/end_on_tonic.rb +1 -1
- data/lib/head_music/style/guidelines/frequent_direction_changes.rb +1 -1
- data/lib/head_music/style/guidelines/limit_octave_leaps.rb +1 -1
- data/lib/head_music/style/guidelines/moderate_direction_changes.rb +1 -1
- data/lib/head_music/style/guidelines/mostly_conjunct.rb +4 -4
- data/lib/head_music/style/guidelines/no_rests.rb +1 -1
- data/lib/head_music/style/guidelines/no_unisons_in_middle.rb +1 -1
- data/lib/head_music/style/guidelines/notes_same_length.rb +4 -4
- data/lib/head_music/style/guidelines/one_to_one.rb +1 -1
- data/lib/head_music/style/guidelines/prefer_contrary_motion.rb +1 -1
- data/lib/head_music/style/guidelines/prefer_imperfect.rb +1 -1
- data/lib/head_music/style/guidelines/prepare_octave_leaps.rb +1 -1
- data/lib/head_music/style/guidelines/recover_large_leaps.rb +1 -1
- data/lib/head_music/style/guidelines/singable_intervals.rb +1 -1
- data/lib/head_music/style/guidelines/singable_range.rb +1 -1
- data/lib/head_music/style/guidelines/single_large_leaps.rb +1 -1
- data/lib/head_music/style/guidelines/start_on_perfect_consonance.rb +1 -1
- data/lib/head_music/style/guidelines/start_on_tonic.rb +1 -1
- data/lib/head_music/style/guidelines/step_down_to_final_note.rb +2 -2
- data/lib/head_music/style/guidelines/step_out_of_unison.rb +1 -1
- data/lib/head_music/style/guidelines/step_to_final_note.rb +2 -2
- data/lib/head_music/style/guidelines/step_up_to_final_note.rb +2 -2
- data/lib/head_music/style/guidelines/up_to_fourteen_notes.rb +2 -2
- data/lib/head_music/style/guides/first_species_harmony.rb +9 -1
- data/lib/head_music/style/guides/first_species_melody.rb +1 -1
- data/lib/head_music/style/guides/fux_cantus_firmus.rb +1 -1
- data/lib/head_music/style/guides/modern_cantus_firmus.rb +1 -1
- data/lib/head_music/style/mark.rb +1 -1
- data/lib/head_music/utilities/hash_key.rb +1 -1
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +91 -91
- metadata +6 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ea2efb7d874b85dd67a1838102d76409ce6a1bf9989707af68ad4909eccc9e5
|
4
|
+
data.tar.gz: c45d1a2683f49adcadb70a0eb29965ad7af7e5fd9f1ec6966c1daacacb1ade95
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '087c6dc3413b35fd1c1b4cb729630bb2c11b2497a5506aa1a7168df15878cce5732e831f04f165dcee32891b06c31d456bd08a758ba83ffc1e428882b7ac9970'
|
7
|
+
data.tar.gz: 23122c1670d39472bd93059b350520697f31abd5b2738b2738e07079b72abe3c44065d7d74a38568111399f3a646cc78264304c262e6501815f8dab478795773
|
data/.circleci/config.yml
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,42 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require:
|
2
|
+
- standard
|
3
|
+
- rubocop-rspec
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
inherit_gem:
|
6
|
+
standard: config/base.yml
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
Metrics/BlockLength:
|
8
|
+
AllCops:
|
9
|
+
NewCops: enable
|
12
10
|
Exclude:
|
13
|
-
-
|
14
|
-
-
|
15
|
-
|
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
|
-
|
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
|
-
|
1
|
+
3.1.4
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
source
|
3
|
+
source "https://rubygems.org"
|
4
4
|
|
5
|
-
ruby
|
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
|
12
|
-
gem
|
13
|
-
gem
|
14
|
-
gem
|
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
|
4
|
-
require
|
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
|
11
|
+
desc "Open an irb session preloaded with this library"
|
11
12
|
task :console do
|
12
|
-
sh
|
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
|
5
|
-
require
|
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
|
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(
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require
|
5
|
+
require "head_music/version"
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
|
-
spec.name
|
9
|
-
spec.version
|
10
|
-
spec.authors
|
11
|
-
spec.email
|
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
|
14
|
-
spec.description
|
15
|
-
spec.homepage
|
16
|
-
spec.license
|
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
|
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
|
22
|
-
spec.executables
|
23
|
-
spec.require_paths = [
|
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 =
|
25
|
+
spec.required_ruby_version = ">= 2.5"
|
26
26
|
|
27
|
-
spec.add_runtime_dependency
|
28
|
-
spec.add_runtime_dependency
|
29
|
-
spec.add_runtime_dependency
|
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
|
32
|
-
spec.add_development_dependency
|
33
|
-
spec.add_development_dependency
|
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].
|
45
|
+
NAMES[simple.semitones].tr("_", " ")
|
46
46
|
end
|
47
47
|
|
48
48
|
# diatonic set theory
|
49
|
-
|
49
|
+
alias_method :specific_interval, :semitones
|
50
50
|
|
51
51
|
def +(other)
|
52
52
|
HeadMusic::ChromaticInterval.get(to_i + other.to_i)
|
data/lib/head_music/circle.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
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:
|
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(&:
|
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 ||=
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
data/lib/head_music/clef.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
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(
|
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
|
@@ -63,7 +63,7 @@ class HeadMusic::Composition
|
|
63
63
|
private
|
64
64
|
|
65
65
|
def ensure_attributes(name, key_signature, meter)
|
66
|
-
@name = name ||
|
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,
|
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 ||
|
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
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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,
|
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
|
-
|
77
|
+
"dotted"
|
78
78
|
when 2
|
79
|
-
|
79
|
+
"double-dotted"
|
80
80
|
when 3
|
81
|
-
|
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(
|
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
|
@@ -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(
|
13
|
+
NAME_SUFFIXES = Hash.new("th").merge(1 => "st", 2 => "nd", 3 => "rd").freeze
|
14
14
|
|
15
15
|
QUALITY_SEMITONES = {
|
16
|
-
unison: {
|
17
|
-
second: {
|
18
|
-
third: {
|
19
|
-
fourth: {
|
20
|
-
fifth: {
|
21
|
-
sixth: {
|
22
|
-
seventh: {
|
23
|
-
octave: {
|
24
|
-
ninth: {
|
25
|
-
tenth: {
|
26
|
-
eleventh: {
|
27
|
-
twelfth: {
|
28
|
-
thirteenth: {
|
29
|
-
fourteenth: {
|
30
|
-
fifteenth: {
|
31
|
-
sixteenth: {
|
32
|
-
seventeenth: {
|
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:
|
37
|
-
M:
|
38
|
-
m:
|
39
|
-
d:
|
40
|
-
A:
|
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(
|
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,
|
79
|
-
return [quality_for(identifier[0]), number].join(
|
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 ||=
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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 ==
|
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 ?
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
356
|
-
|
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
|
-
{
|
10
|
-
{
|
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
|
-
{
|
17
|
-
{
|
18
|
-
{
|
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)
|