coltrane 2.2.1 → 3.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +16 -1
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +25 -2
- data/README.md +3 -0
- data/bin/console +6 -2
- data/coltrane.gemspec +2 -1
- data/exe/coltrane +14 -224
- data/lib/coltrane.rb +3 -50
- data/lib/coltrane/commands.rb +14 -0
- data/lib/coltrane/commands/chords.rb +77 -0
- data/lib/coltrane/commands/command.rb +45 -0
- data/lib/coltrane/commands/common_chords.rb +33 -0
- data/lib/coltrane/commands/errors.rb +44 -0
- data/lib/coltrane/commands/find_progression.rb +28 -0
- data/lib/coltrane/commands/find_scale.rb +39 -0
- data/lib/coltrane/commands/notes.rb +44 -0
- data/lib/coltrane/commands/progression.rb +27 -0
- data/lib/{cli → coltrane/commands}/representation.rb +0 -0
- data/lib/coltrane/commands/scale.rb +46 -0
- data/lib/coltrane/renderers.rb +6 -0
- data/lib/coltrane/renderers/renderer.rb +42 -0
- data/lib/coltrane/renderers/text_renderer.rb +23 -0
- data/lib/coltrane/renderers/text_renderer/array_drawer.rb +45 -0
- data/lib/coltrane/renderers/text_renderer/base_drawer.rb +20 -0
- data/lib/coltrane/renderers/text_renderer/hash_drawer.rb +16 -0
- data/lib/coltrane/renderers/text_renderer/representation_guitar_chord_drawer.rb +95 -0
- data/lib/coltrane/renderers/text_renderer/representation_guitar_note_set_drawer.rb +76 -0
- data/lib/coltrane/renderers/text_renderer/representation_piano_note_set_drawer.rb +49 -0
- data/lib/coltrane/renderers/text_renderer/theory_chord_drawer.rb +14 -0
- data/lib/coltrane/renderers/text_renderer/theory_note_set_drawer.rb +17 -0
- data/lib/coltrane/renderers/text_renderer/theory_progression_drawer.rb +13 -0
- data/lib/coltrane/renderers/text_renderer/theory_progression_set_drawer.rb +22 -0
- data/lib/coltrane/renderers/text_renderer/theory_scale_drawer.rb +13 -0
- data/lib/coltrane/renderers/text_renderer/theory_scale_set_drawer.rb +27 -0
- data/lib/coltrane/representation.rb +12 -0
- data/lib/coltrane/representation/guitar.rb +34 -0
- data/lib/coltrane/representation/guitar/chord.rb +180 -0
- data/lib/coltrane/representation/guitar/note.rb +26 -0
- data/lib/coltrane/representation/guitar/note_set.rb +35 -0
- data/lib/coltrane/representation/guitar/string.rb +31 -0
- data/lib/coltrane/representation/guitar_like_instruments.rb +21 -0
- data/lib/coltrane/representation/piano.rb +36 -0
- data/lib/coltrane/representation/piano/note_set.rb +22 -0
- data/lib/{coltrane_synth.rb → coltrane/synth.rb.rb} +0 -0
- data/lib/{coltrane_synth → coltrane/synth}/base.rb +0 -0
- data/lib/{coltrane_synth → coltrane/synth}/synth.rb +0 -0
- data/lib/coltrane/theory.rb +54 -0
- data/lib/coltrane/theory/cadence.rb +9 -0
- data/lib/coltrane/{changes.rb → theory/changes.rb} +0 -0
- data/lib/coltrane/theory/chord.rb +101 -0
- data/lib/coltrane/theory/chord_quality.rb +113 -0
- data/lib/coltrane/theory/chord_substitutions.rb +11 -0
- data/lib/coltrane/theory/circle_of_fifths.rb +33 -0
- data/lib/coltrane/theory/classic_scales.rb +113 -0
- data/lib/coltrane/theory/diatonic_scale.rb +38 -0
- data/lib/coltrane/theory/errors.rb +97 -0
- data/lib/coltrane/theory/frequency.rb +52 -0
- data/lib/coltrane/theory/frequency_interval.rb +83 -0
- data/lib/coltrane/theory/interval.rb +209 -0
- data/lib/coltrane/theory/interval_class.rb +212 -0
- data/lib/coltrane/theory/interval_sequence.rb +157 -0
- data/lib/coltrane/theory/key.rb +18 -0
- data/lib/coltrane/{mode.rb → theory/mode.rb} +0 -0
- data/lib/coltrane/theory/notable_progressions.rb +30 -0
- data/lib/coltrane/theory/note.rb +104 -0
- data/lib/coltrane/theory/note_set.rb +101 -0
- data/lib/coltrane/theory/pitch.rb +94 -0
- data/lib/coltrane/theory/pitch_class.rb +154 -0
- data/lib/coltrane/theory/progression.rb +81 -0
- data/lib/coltrane/theory/progression_set.rb +18 -0
- data/lib/coltrane/{qualities.rb → theory/qualities.rb} +0 -0
- data/lib/coltrane/theory/roman_chord.rb +114 -0
- data/lib/coltrane/theory/scale.rb +161 -0
- data/lib/coltrane/theory/scale_search_result.rb +6 -0
- data/lib/coltrane/theory/scale_set.rb +40 -0
- data/lib/coltrane/theory/voicing.rb +41 -0
- data/lib/coltrane/version.rb +1 -1
- metadata +88 -63
- data/bin/rails +0 -17
- data/data/qualities.yml +0 -83
- data/db/cache.sqlite3 +0 -0
- data/db/cache_test.sqlite3 +0 -0
- data/db/config.yml +0 -11
- data/lib/cli.rb +0 -24
- data/lib/cli/bass_guitar.rb +0 -12
- data/lib/cli/chord.rb +0 -39
- data/lib/cli/config.rb +0 -39
- data/lib/cli/errors.rb +0 -46
- data/lib/cli/guitar.rb +0 -67
- data/lib/cli/guitar_chords.rb +0 -122
- data/lib/cli/notes.rb +0 -20
- data/lib/cli/piano.rb +0 -57
- data/lib/cli/scale.rb +0 -53
- data/lib/cli/text.rb +0 -16
- data/lib/cli/ukulele.rb +0 -14
- data/lib/coltrane/cache.rb +0 -43
- data/lib/coltrane/cadence.rb +0 -7
- data/lib/coltrane/chord.rb +0 -89
- data/lib/coltrane/chord_quality.rb +0 -111
- data/lib/coltrane/chord_substitutions.rb +0 -9
- data/lib/coltrane/circle_of_fifths.rb +0 -31
- data/lib/coltrane/classic_scales.rb +0 -94
- data/lib/coltrane/diatonic_scale.rb +0 -36
- data/lib/coltrane/errors.rb +0 -95
- data/lib/coltrane/frequency.rb +0 -50
- data/lib/coltrane/frequency_interval.rb +0 -81
- data/lib/coltrane/interval.rb +0 -208
- data/lib/coltrane/interval_class.rb +0 -210
- data/lib/coltrane/interval_sequence.rb +0 -155
- data/lib/coltrane/key.rb +0 -16
- data/lib/coltrane/notable_progressions.rb +0 -28
- data/lib/coltrane/note.rb +0 -98
- data/lib/coltrane/note_set.rb +0 -89
- data/lib/coltrane/pitch.rb +0 -92
- data/lib/coltrane/pitch_class.rb +0 -148
- data/lib/coltrane/progression.rb +0 -74
- data/lib/coltrane/roman_chord.rb +0 -112
- data/lib/coltrane/scale.rb +0 -154
- data/lib/coltrane/unordered_interval_class.rb +0 -7
- data/lib/coltrane/voicing.rb +0 -39
- data/lib/coltrane_game/question.rb +0 -7
- data/lib/coltrane_instruments.rb +0 -7
- data/lib/coltrane_instruments/guitar.rb +0 -10
- data/lib/coltrane_instruments/guitar/base.rb +0 -29
- data/lib/coltrane_instruments/guitar/chord.rb +0 -170
- data/lib/coltrane_instruments/guitar/note.rb +0 -24
- data/lib/coltrane_instruments/guitar/string.rb +0 -29
- data/lib/os.rb +0 -21
data/lib/coltrane/pitch_class.rb
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Coltrane
|
4
|
-
#
|
5
|
-
# Pitch classes, and by classes here we don't mean in the sense of a ruby class
|
6
|
-
# are all the classes of pitches (frequencies) that are in a whole number of
|
7
|
-
# octaves apart.
|
8
|
-
#
|
9
|
-
# For example, C1, C2, C3 are all pitches from the C pitch class. Take a look into
|
10
|
-
# Notes description if you somehow feel this is confuse and that it could just be
|
11
|
-
# called as notes instead.
|
12
|
-
#
|
13
|
-
class PitchClass
|
14
|
-
attr_reader :integer
|
15
|
-
include Comparable
|
16
|
-
|
17
|
-
NOTATION = %w[C C# D D# E F F# G G# A A# B].freeze
|
18
|
-
|
19
|
-
def self.all_letters
|
20
|
-
%w[C D E F G A B]
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.all
|
24
|
-
NOTATION.map { |n| new(n) }
|
25
|
-
end
|
26
|
-
|
27
|
-
def initialize(arg = nil, frequency: nil)
|
28
|
-
@integer = case arg
|
29
|
-
when String then NOTATION.index(arg)
|
30
|
-
when Frequency then frequency_to_integer(Frequency.new(arg))
|
31
|
-
when Numeric then (arg % 12)
|
32
|
-
when nil then frequency_to_integer(Frequency.new(frequency))
|
33
|
-
when PitchClass then arg.integer
|
34
|
-
else raise(WrongArgumentsError)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.[](arg, frequency: nil)
|
39
|
-
new(arg, frequency: nil)
|
40
|
-
end
|
41
|
-
|
42
|
-
def ==(other)
|
43
|
-
integer == other.integer
|
44
|
-
end
|
45
|
-
|
46
|
-
alias eql? ==
|
47
|
-
alias hash integer
|
48
|
-
|
49
|
-
def true_notation
|
50
|
-
NOTATION[integer]
|
51
|
-
end
|
52
|
-
|
53
|
-
def letter
|
54
|
-
name[0]
|
55
|
-
end
|
56
|
-
|
57
|
-
def ascending_interval_to(other)
|
58
|
-
Interval.new(self, (other.is_a?(PitchClass) ? other : Note.new(other)))
|
59
|
-
end
|
60
|
-
|
61
|
-
alias interval_to ascending_interval_to
|
62
|
-
|
63
|
-
def descending_interval_to(other)
|
64
|
-
Interval.new(
|
65
|
-
self,
|
66
|
-
(other.is_a?(PitchClass) ? other : Note.new(other)),
|
67
|
-
ascending: false
|
68
|
-
)
|
69
|
-
end
|
70
|
-
|
71
|
-
alias name true_notation
|
72
|
-
|
73
|
-
def pretty_name
|
74
|
-
name.tr('b', "\u266D").tr('#', "\u266F")
|
75
|
-
end
|
76
|
-
|
77
|
-
def accidental?
|
78
|
-
notation.match? /#|b/
|
79
|
-
end
|
80
|
-
|
81
|
-
def sharp?
|
82
|
-
notation.match? /#/
|
83
|
-
end
|
84
|
-
|
85
|
-
def flat?
|
86
|
-
notation.match? /b/
|
87
|
-
end
|
88
|
-
|
89
|
-
alias notation true_notation
|
90
|
-
alias to_s true_notation
|
91
|
-
|
92
|
-
def +(other)
|
93
|
-
case other
|
94
|
-
when Interval then self.class[integer + other.semitones]
|
95
|
-
when Integer then self.class[integer + other]
|
96
|
-
when PitchClass then self.class[integer + other.integer]
|
97
|
-
when Frequency then self.class.new(frequency: frequency + other)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def -(other)
|
102
|
-
case other
|
103
|
-
when Interval then self.class[integer - other.semitones]
|
104
|
-
when Integer then self.class[integer - other]
|
105
|
-
when PitchClass then Interval.new(self, other)
|
106
|
-
when Frequency then self.class.new(frequency: frequency - other)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def <=>(other)
|
111
|
-
integer <=> other.integer
|
112
|
-
end
|
113
|
-
|
114
|
-
def fundamental_frequency
|
115
|
-
@fundamental_frequency ||=
|
116
|
-
Frequency[
|
117
|
-
Coltrane.base_tuning *
|
118
|
-
(2**((integer - Coltrane::BASE_PITCH_INTEGER.to_f) / 12))
|
119
|
-
]
|
120
|
-
end
|
121
|
-
|
122
|
-
alias frequency fundamental_frequency
|
123
|
-
|
124
|
-
def self.size
|
125
|
-
NOTATION.size
|
126
|
-
end
|
127
|
-
|
128
|
-
def size
|
129
|
-
self.class.size
|
130
|
-
end
|
131
|
-
|
132
|
-
def enharmonic?(other)
|
133
|
-
case other
|
134
|
-
when String then integer == Note[other].integer
|
135
|
-
when Note then integer == other.integer
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
private
|
140
|
-
|
141
|
-
def frequency_to_integer(f)
|
142
|
-
begin
|
143
|
-
(Coltrane::BASE_PITCH_INTEGER +
|
144
|
-
size * Math.log(f.to_f / Coltrane.base_tuning.to_f, 2)) % size
|
145
|
-
end.round
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
data/lib/coltrane/progression.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Coltrane
|
4
|
-
# Allows the creation of chord progressions using standard notations.
|
5
|
-
# Ex: Progression.new('I-IV-V', key: 'Am')
|
6
|
-
class Progression
|
7
|
-
extend NotableProgressions
|
8
|
-
include Changes
|
9
|
-
attr_reader :scale, :chords, :notation
|
10
|
-
|
11
|
-
def self.find(*chords)
|
12
|
-
chords.map! { |c| Chord.new(name: c) } if chords[0].is_a?(String)
|
13
|
-
|
14
|
-
root_notes = NoteSet[*chords.map(&:root_note)]
|
15
|
-
|
16
|
-
scales = Scale.having_notes(*root_notes)[:scales]
|
17
|
-
progressions = scales.reduce([]) do |memo, scale|
|
18
|
-
memo + [Progression.new(chords: chords, scale: scale)]
|
19
|
-
end
|
20
|
-
|
21
|
-
progressions.sort_by(&:notes_out_size)
|
22
|
-
end
|
23
|
-
|
24
|
-
def initialize(notation = nil, chords: nil, roman_chords: nil, key: nil, scale: nil)
|
25
|
-
if notation.nil? && chords.nil? && roman_chords.nil? || key.nil? && scale.nil?
|
26
|
-
raise WrongKeywordsError,
|
27
|
-
'[chords:, [scale: || key:]] '\
|
28
|
-
'[roman_chords:, [scale: || key:]] '\
|
29
|
-
'[notation:, [scale: || key:]] '\
|
30
|
-
end
|
31
|
-
|
32
|
-
@scale = scale || Key[key]
|
33
|
-
@chords =
|
34
|
-
if !chords.nil?
|
35
|
-
chords
|
36
|
-
elsif !roman_chords.nil?
|
37
|
-
roman_chords.map(&:chord)
|
38
|
-
elsif !notation.nil?
|
39
|
-
@notation = notation
|
40
|
-
notation.split('-').map { |c| RomanChord.new(c, scale: @scale).chord }
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def interval_sequence
|
45
|
-
@interval_sequence ||= IntervalSequence(notes: @root_notes)
|
46
|
-
end
|
47
|
-
|
48
|
-
def root_notes
|
49
|
-
@root_notes ||= @chords.map(&:root_note)
|
50
|
-
end
|
51
|
-
|
52
|
-
def roman_chords
|
53
|
-
@roman_chords ||= chords.map do |c|
|
54
|
-
RomanChord.new(chord: c, scale: scale)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def notation
|
59
|
-
roman_chords.map(&:notation).join('-')
|
60
|
-
end
|
61
|
-
|
62
|
-
def notes
|
63
|
-
NoteSet[*chords.map(&:notes).map(&:notes).flatten]
|
64
|
-
end
|
65
|
-
|
66
|
-
def notes_out
|
67
|
-
notes - scale.notes
|
68
|
-
end
|
69
|
-
|
70
|
-
def notes_out_size
|
71
|
-
notes_out.size
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
data/lib/coltrane/roman_chord.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Coltrane
|
4
|
-
# This class deals with chords in roman notation. Ex: IVº.
|
5
|
-
class RomanChord
|
6
|
-
DIGITS = %w[I II III IV V VI VII].freeze
|
7
|
-
NOTATION_REGEX = /(b?[ivIV]*)(.*)/
|
8
|
-
|
9
|
-
NOTATION_SUBSTITUTIONS = [
|
10
|
-
%w[º dim],
|
11
|
-
%w[o dim],
|
12
|
-
%w[ø m7b5]
|
13
|
-
].freeze
|
14
|
-
|
15
|
-
def initialize(notation = nil, chord: nil, key: nil, scale: nil)
|
16
|
-
if notation.nil? && chord.nil? || key.nil? && scale.nil?
|
17
|
-
raise WrongKeywordsError,
|
18
|
-
'[notation, [scale: || key:]] '\
|
19
|
-
'[chord:, [scale: || key:]] '\
|
20
|
-
end
|
21
|
-
@scale = scale || Key[key]
|
22
|
-
if notation
|
23
|
-
@notation = notation
|
24
|
-
elsif chord
|
25
|
-
@chord = chord.is_a?(String) ? Chord.new(name: chord) : chord
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def degree
|
30
|
-
return @scale.degree_of_note(root_note) unless @chord.nil?
|
31
|
-
d = regexed_notation[:degree]
|
32
|
-
@flats = d.count('b')
|
33
|
-
d = d.delete('b')
|
34
|
-
@degree ||= DIGITS.index(d.upcase) + 1
|
35
|
-
end
|
36
|
-
|
37
|
-
def roman_numeral
|
38
|
-
r = DIGITS[degree]
|
39
|
-
minor? ? r.downcase : r
|
40
|
-
end
|
41
|
-
|
42
|
-
def upcase?
|
43
|
-
!!(regexed_notation[:degree][0].match /[[:upper:]]/)
|
44
|
-
end
|
45
|
-
|
46
|
-
def chord
|
47
|
-
@chord ||= Chord.new root_note: root_note,
|
48
|
-
quality: quality
|
49
|
-
end
|
50
|
-
|
51
|
-
def minor?
|
52
|
-
quality.has_minor_third?
|
53
|
-
end
|
54
|
-
|
55
|
-
def major?
|
56
|
-
quality.has_major_third?
|
57
|
-
end
|
58
|
-
|
59
|
-
def quality_name
|
60
|
-
return @chord.quality.name unless @chord.nil?
|
61
|
-
q = normalize_quality_name(regexed_notation[:quality])
|
62
|
-
minor = 'm' if (!q.match? /dim|m7b5/) && !upcase?
|
63
|
-
q = [minor, q].join
|
64
|
-
q.empty? ? 'M' : q
|
65
|
-
end
|
66
|
-
|
67
|
-
def quality
|
68
|
-
return @chord.quality unless @chord.nil?
|
69
|
-
ChordQuality.new(name: quality_name) if quality_name
|
70
|
-
end
|
71
|
-
|
72
|
-
def root_note
|
73
|
-
return @chord.root_note unless @chord.nil?
|
74
|
-
@scale[degree] - @flats
|
75
|
-
end
|
76
|
-
|
77
|
-
def notation
|
78
|
-
q = case quality_name
|
79
|
-
when 'm', 'M' then ''
|
80
|
-
when 'm7', 'M' then '7'
|
81
|
-
else quality_name
|
82
|
-
end
|
83
|
-
|
84
|
-
@notation ||= [
|
85
|
-
roman_numeral,
|
86
|
-
q
|
87
|
-
].join
|
88
|
-
end
|
89
|
-
|
90
|
-
def function
|
91
|
-
return if @scale.name != 'Major'
|
92
|
-
%w[Tonic Supertonic Mediant Subdominant
|
93
|
-
Dominant Submediant Leading][degree - 1]
|
94
|
-
end
|
95
|
-
|
96
|
-
private
|
97
|
-
|
98
|
-
def regexed_notation
|
99
|
-
@regexed_notation ||= begin
|
100
|
-
matchdata = @notation.match(NOTATION_REGEX)
|
101
|
-
{ degree: matchdata[1], quality: matchdata[2] }
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def normalize_quality_name(quality_name)
|
106
|
-
NOTATION_SUBSTITUTIONS.reduce(quality_name) do |memo, subs|
|
107
|
-
break memo if memo.empty?
|
108
|
-
memo.gsub(*subs)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
data/lib/coltrane/scale.rb
DELETED
@@ -1,154 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
module Coltrane
|
5
|
-
# Musical scale creation and manipulation
|
6
|
-
class Scale
|
7
|
-
extend ClassicScales
|
8
|
-
extend Forwardable
|
9
|
-
|
10
|
-
def_delegators :notes, :accidentals, :sharps, :flats
|
11
|
-
|
12
|
-
attr_reader :interval_sequence, :tone
|
13
|
-
|
14
|
-
def initialize(*relative_intervals, tone: 'C',
|
15
|
-
mode: 1,
|
16
|
-
name: nil,
|
17
|
-
notes: nil)
|
18
|
-
@name = name
|
19
|
-
if relative_intervals.any? && tone
|
20
|
-
@tone = Note[tone]
|
21
|
-
relative_intervals = relative_intervals.rotate(mode - 1)
|
22
|
-
@interval_sequence = IntervalSequence.new(
|
23
|
-
relative_intervals: relative_intervals
|
24
|
-
)
|
25
|
-
elsif notes
|
26
|
-
@notes = NoteSet[*notes]
|
27
|
-
@tone = @notes.first
|
28
|
-
ds = @notes.interval_sequence.relative_intervals
|
29
|
-
@interval_sequence = IntervalSequence.new(relative_intervals: ds)
|
30
|
-
else
|
31
|
-
raise WrongKeywordsError,
|
32
|
-
'[*relative_intervals, tone: "C", mode: 1] || [notes:]'
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def id
|
37
|
-
[(name || @interval_sequence), tone.number]
|
38
|
-
end
|
39
|
-
|
40
|
-
def name
|
41
|
-
@name = begin
|
42
|
-
is = interval_sequence.relative_intervals
|
43
|
-
(0...is.size).each do |i|
|
44
|
-
if (scale_name = Coltrane::ClassicScales::SCALES.key(is.rotate(i)))
|
45
|
-
return scale_name
|
46
|
-
end
|
47
|
-
end
|
48
|
-
nil
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def to_s
|
53
|
-
"#{tone} #{name}"
|
54
|
-
end
|
55
|
-
|
56
|
-
def pretty_name
|
57
|
-
"#{tone.name} #{name}"
|
58
|
-
end
|
59
|
-
|
60
|
-
alias full_name pretty_name
|
61
|
-
|
62
|
-
def degree(d)
|
63
|
-
raise WrongDegreeError, d if d < 1 || d > size
|
64
|
-
tone + interval_sequence[d - 1].semitones
|
65
|
-
end
|
66
|
-
|
67
|
-
alias [] degree
|
68
|
-
|
69
|
-
def degrees
|
70
|
-
(1..size)
|
71
|
-
end
|
72
|
-
|
73
|
-
def degree_of_chord(chord)
|
74
|
-
return if chords(chord.size).map(&:name).include?(chord.name)
|
75
|
-
degree_of_note(chord.root_note)
|
76
|
-
end
|
77
|
-
|
78
|
-
def degree_of_note(note)
|
79
|
-
notes.index(note)
|
80
|
-
end
|
81
|
-
|
82
|
-
def &(other)
|
83
|
-
raise HasNoNotesError unless other.respond_to?(:notes)
|
84
|
-
notes & other
|
85
|
-
end
|
86
|
-
|
87
|
-
def include_notes?(arg)
|
88
|
-
noteset = arg.is_a?(Note) ? NoteSet[arg] : arg
|
89
|
-
(self & noteset).size == noteset.size
|
90
|
-
end
|
91
|
-
|
92
|
-
alias include? include_notes?
|
93
|
-
|
94
|
-
def notes
|
95
|
-
@notes ||= NoteSet[*degrees.map { |d| degree(d) }]
|
96
|
-
end
|
97
|
-
|
98
|
-
def interval(i)
|
99
|
-
interval_sequence[(i - 1) % size]
|
100
|
-
end
|
101
|
-
|
102
|
-
def size
|
103
|
-
interval_sequence.size
|
104
|
-
end
|
105
|
-
|
106
|
-
def tertians(n = 3)
|
107
|
-
degrees.size.times.reduce([]) do |memo, d|
|
108
|
-
ns = NoteSet[ *Array.new(n) { |i| notes[(d + (i * 2)) % size] } ]
|
109
|
-
begin
|
110
|
-
chord = Chord.new(notes: ns)
|
111
|
-
rescue ChordNotFoundError
|
112
|
-
memo
|
113
|
-
else
|
114
|
-
memo + [chord]
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def triads
|
120
|
-
tertians(3)
|
121
|
-
end
|
122
|
-
|
123
|
-
def sevenths
|
124
|
-
tertians(4)
|
125
|
-
end
|
126
|
-
|
127
|
-
def pentads
|
128
|
-
tertians(5)
|
129
|
-
end
|
130
|
-
|
131
|
-
def progression(*degrees)
|
132
|
-
Progression.new(self, degrees)
|
133
|
-
end
|
134
|
-
|
135
|
-
def chords(size = 3..12)
|
136
|
-
size = (size..size) if size.is_a?(Integer)
|
137
|
-
scale_rotations = interval_sequence.inversions
|
138
|
-
ChordQuality.intervals_per_name.reduce([]) do |memo1, (qname, qintervals)|
|
139
|
-
next memo1 unless size.include?(qintervals.size)
|
140
|
-
memo1 + scale_rotations.each_with_index
|
141
|
-
.reduce([]) do |memo2, (rot, index)|
|
142
|
-
if (rot & qintervals).size == qintervals.size
|
143
|
-
memo2 + [Chord.new(root_note: degree(index + 1),
|
144
|
-
quality: ChordQuality.new(name: qname))]
|
145
|
-
else
|
146
|
-
memo2
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
alias all_chords chords
|
153
|
-
end
|
154
|
-
end
|