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.
- 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 +24 -9
- 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 +13 -0
- 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)
|