coltrane 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.bundle/config +2 -0
- data/.rspec +2 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +164 -0
- data/Guardfile +71 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/bin/_guard-core +17 -0
- data/bin/bundler +17 -0
- data/bin/coderay +17 -0
- data/bin/coltrane +11 -0
- data/bin/console +14 -0
- data/bin/erubis +17 -0
- data/bin/guard +17 -0
- data/bin/htmldiff +17 -0
- data/bin/kill-pry-rescue +17 -0
- data/bin/ldiff +17 -0
- data/bin/listen +17 -0
- data/bin/nokogiri +17 -0
- data/bin/pry +17 -0
- data/bin/rackup +17 -0
- data/bin/rails +17 -0
- data/bin/rake +17 -0
- data/bin/rdoc +17 -0
- data/bin/rescue +17 -0
- data/bin/ri +17 -0
- data/bin/rspec +17 -0
- data/bin/rubocop +17 -0
- data/bin/ruby-parse +17 -0
- data/bin/ruby-rewrite +17 -0
- data/bin/setup +8 -0
- data/bin/sprockets +17 -0
- data/bin/thor +17 -0
- data/bin/tilt +17 -0
- data/coltrane.gemspec +35 -0
- data/db/cache.sqlite3 +0 -0
- data/db/cache_test.sqlite3 +0 -0
- data/db/config.yml +11 -0
- data/db/schema.rb +30 -0
- data/lib/coltrane.rb +48 -0
- data/lib/coltrane/cadence.rb +4 -0
- data/lib/coltrane/chord.rb +81 -0
- data/lib/coltrane/chord_cache.rb +4 -0
- data/lib/coltrane/chord_quality.rb +48 -0
- data/lib/coltrane/classic_scales.rb +134 -0
- data/lib/coltrane/essential_guitar_chords.rb +82 -0
- data/lib/coltrane/fret_set.rb +0 -0
- data/lib/coltrane/guitar.rb +15 -0
- data/lib/coltrane/guitar_chord.rb +50 -0
- data/lib/coltrane/guitar_chord_finder.rb +98 -0
- data/lib/coltrane/guitar_note.rb +50 -0
- data/lib/coltrane/guitar_note_set.rb +61 -0
- data/lib/coltrane/guitar_representation.rb +96 -0
- data/lib/coltrane/guitar_string.rb +52 -0
- data/lib/coltrane/interval.rb +33 -0
- data/lib/coltrane/interval_sequence.rb +70 -0
- data/lib/coltrane/interval_set.rb +23 -0
- data/lib/coltrane/mode.rb +0 -0
- data/lib/coltrane/note.rb +98 -0
- data/lib/coltrane/note_set.rb +50 -0
- data/lib/coltrane/piano_representation.rb +58 -0
- data/lib/coltrane/pitch.rb +27 -0
- data/lib/coltrane/progression.rb +4 -0
- data/lib/coltrane/qualities.rb +115 -0
- data/lib/coltrane/scale.rb +139 -0
- data/lib/coltrane/scale_cache.rb +4 -0
- data/lib/coltrane/scale_chord.rb +4 -0
- data/lib/coltrane/version.rb +3 -0
- metadata +144 -0
File without changes
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Coltrane
|
2
|
+
# It describes a group of guitar notes
|
3
|
+
class GuitarChord < GuitarNoteSet
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def new_from_notes(guitar_notes)
|
7
|
+
new(*frets_in_sequence(guitar_notes))
|
8
|
+
end
|
9
|
+
|
10
|
+
def new_from_frets(*frets)
|
11
|
+
gns = (0..5).each_with_object([]) do |string_index, memo|
|
12
|
+
fret = frets[5-string_index]
|
13
|
+
unless fret.nil?
|
14
|
+
memo << GuitarNote.new(guitar_string_index: string_index,
|
15
|
+
fret: fret)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
new_from_notes(gns)
|
19
|
+
end
|
20
|
+
|
21
|
+
def frets_in_sequence(guitar_notes)
|
22
|
+
guitar_notes.each_with_object([]) do |gn, memo|
|
23
|
+
memo[gn.guitar_string_index] = gn.fret
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(*frets_in_sequence)
|
29
|
+
arg = frets_in_sequence.each_with_index.map do |fret, i|
|
30
|
+
{ fret: fret, guitar_string_index: i }
|
31
|
+
end
|
32
|
+
super(arg)
|
33
|
+
end
|
34
|
+
|
35
|
+
def chord
|
36
|
+
Chord.new(notes)
|
37
|
+
end
|
38
|
+
|
39
|
+
def frets_in_sequence
|
40
|
+
self.class.frets_in_sequence(guitar_notes)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
fs = frets_in_sequence
|
45
|
+
(0..5).map { |x|
|
46
|
+
fs[5-x].nil? ? 'X ' : fs[5-x].to_s.ljust(2,' ')
|
47
|
+
}.join(' ')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# require 'digest'
|
2
|
+
|
3
|
+
module Coltrane
|
4
|
+
module GuitarChordFinder
|
5
|
+
class << self
|
6
|
+
# TODO Refactor this piece of shit
|
7
|
+
def by_chord_name(chord_name)
|
8
|
+
by_chord(Chord.new(chord_name))
|
9
|
+
end
|
10
|
+
|
11
|
+
def by_chord(chord)
|
12
|
+
gnotes = gnotes_for_chord(chord.notes)
|
13
|
+
frets = gnotes.map(&:fret).uniq.sort
|
14
|
+
frets.delete(0)
|
15
|
+
threads = []
|
16
|
+
frets.each do |fret|
|
17
|
+
threads << Thread.new do
|
18
|
+
gnotes_for_fret = gnotes.dup.delete_if do |gnote|
|
19
|
+
!(gnote.fret-fret).between?(0,4) && !gnote.fret.zero?
|
20
|
+
end
|
21
|
+
|
22
|
+
gnotes_to_start_with = gnotes_for_fret.dup.delete_if do |gnote|
|
23
|
+
gnote.fret != fret
|
24
|
+
end
|
25
|
+
|
26
|
+
gnotes_to_start_with.reduce([]) do |memo2, gnote|
|
27
|
+
gnotes_for_fret.delete(gnote)
|
28
|
+
memo2 + combine_gnotes_into_chords( gnotes_for_fret, [gnote])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
chords = threads.map(&:value).flatten
|
33
|
+
remove_duplicate_chords(chords)
|
34
|
+
end
|
35
|
+
|
36
|
+
def gnotes_for_chord(notes)
|
37
|
+
gnotes = notes.reduce([]) do |memo_1, note|
|
38
|
+
memo_1 + Guitar.strings.reduce([]) do |memo_2, gs|
|
39
|
+
memo_2 + gs.guitar_notes_for_note_in_region(note, 0..12)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def combine_gnotes_into_chords(gnotes_left,
|
45
|
+
chord_notes=[],
|
46
|
+
repeat_notes: false,
|
47
|
+
color_gnotes: [])
|
48
|
+
|
49
|
+
if chord_notes.size == 6
|
50
|
+
return [GuitarChord.new_from_notes(chord_notes)]
|
51
|
+
elsif gnotes_left.empty?
|
52
|
+
if color_gnotes.empty?
|
53
|
+
return [GuitarChord.new_from_notes(chord_notes)]
|
54
|
+
else
|
55
|
+
return combine_gnotes_into_chords color_gnotes.uniq,
|
56
|
+
chord_notes,
|
57
|
+
repeat_notes: true
|
58
|
+
end
|
59
|
+
else
|
60
|
+
|
61
|
+
chords = []
|
62
|
+
|
63
|
+
gnotes_left.each do |gnote|
|
64
|
+
new_chord_notes = chord_notes + [gnote]
|
65
|
+
new_gnl = gnotes_left.dup
|
66
|
+
new_gnl.delete(gnote)
|
67
|
+
new_gnl.delete_if do |g|
|
68
|
+
next(true) if g.guitar_string_index == gnote.guitar_string_index
|
69
|
+
if g.note.name == gnote.note.name && !repeat_notes
|
70
|
+
color_gnotes << g
|
71
|
+
next(true)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
chords += combine_gnotes_into_chords new_gnl,
|
76
|
+
new_chord_notes,
|
77
|
+
repeat_notes: repeat_notes,
|
78
|
+
color_gnotes: color_gnotes.uniq
|
79
|
+
end
|
80
|
+
|
81
|
+
return chords.compact
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def remove_duplicate_chords(chords)
|
86
|
+
chords_to_include = chords.map(&:to_s).uniq
|
87
|
+
chords.each_with_object [] do |chord, new_chords|
|
88
|
+
chord_str = chord.to_s
|
89
|
+
if chords_to_include.include?(chord_str)
|
90
|
+
chords_to_include.delete(chord_str)
|
91
|
+
new_chords << chord
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Coltrane
|
2
|
+
# It describes a guitar note
|
3
|
+
class GuitarNote
|
4
|
+
attr_reader :position
|
5
|
+
|
6
|
+
def initialize(fret:, guitar_string_index:)
|
7
|
+
if fret.nil? || guitar_string_index.nil?
|
8
|
+
raise "invalid guitar note for f:#{fret} and s:#{guitar_string_index}"
|
9
|
+
end
|
10
|
+
@position = { fret: fret, guitar_string_index: guitar_string_index }
|
11
|
+
end
|
12
|
+
|
13
|
+
def new_from_guitar_string_and_note(gsi, note); end
|
14
|
+
|
15
|
+
def note
|
16
|
+
pitch.note
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"string: #{guitar_string_index}/fret: #{fret}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def guitar_string_index
|
24
|
+
position[:guitar_string_index]
|
25
|
+
end
|
26
|
+
|
27
|
+
def fret
|
28
|
+
position[:fret]
|
29
|
+
end
|
30
|
+
|
31
|
+
def guitar_string
|
32
|
+
Guitar.strings[guitar_string_index]
|
33
|
+
end
|
34
|
+
|
35
|
+
def pitch
|
36
|
+
Pitch.new(guitar_string.pitch.number + fret)
|
37
|
+
end
|
38
|
+
|
39
|
+
def guitar_chord(_quality)
|
40
|
+
guitar_strings = (guitar_string_index..0).to_a
|
41
|
+
guitar_note_array = guitar_strings.each_with_object([]) do |gsi, memo|
|
42
|
+
if f < Guitar.frets
|
43
|
+
gn = new_from_guitar_string_and_note(gsi, note)
|
44
|
+
memo << gn unless gn.nil?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
GuitarChord.new(guitar_note_array)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Coltrane
|
2
|
+
class GuitarNoteSet
|
3
|
+
attr_reader :guitar_notes
|
4
|
+
|
5
|
+
def initialize(arg)
|
6
|
+
arg = [arg] unless arg.class == Array
|
7
|
+
@guitar_notes = arg.reduce([]) do |memo, arg_item|
|
8
|
+
case arg_item
|
9
|
+
when Hash
|
10
|
+
if arg_item[:fret].nil?
|
11
|
+
memo
|
12
|
+
else
|
13
|
+
(memo + [GuitarNote.new(arg_item)])
|
14
|
+
end
|
15
|
+
|
16
|
+
when GuitarNote then memo + [arg_item]
|
17
|
+
when Pitch then memo + guitar_notes_for_pitch(arg_item)
|
18
|
+
when String then memo + guitar_notes_for_pitch(Pitch.new(arg_item))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def guitar_notes_for_pitch(pitch)
|
24
|
+
Guitar.strings.each_with_index.each_with_object([]) do |object, memo|
|
25
|
+
guitar_string, index = object
|
26
|
+
fret = guitar_string.fret_by_pitch(pitch)
|
27
|
+
unless fret.nil?
|
28
|
+
memo << GuitarNote.new(guitar_string_index: index, fret: fret)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def root_note
|
34
|
+
lowest_pitch.note
|
35
|
+
end
|
36
|
+
|
37
|
+
def lowest_pitch
|
38
|
+
pitches.sort_by(&:number).first
|
39
|
+
end
|
40
|
+
|
41
|
+
def pitches
|
42
|
+
guitar_notes.collect(&:pitch)
|
43
|
+
end
|
44
|
+
|
45
|
+
def notes
|
46
|
+
guitar_notes.collect(&:note)
|
47
|
+
end
|
48
|
+
|
49
|
+
def note_string
|
50
|
+
notes.collect(&:name).join(' ')
|
51
|
+
end
|
52
|
+
|
53
|
+
def chord_quality
|
54
|
+
ChordQuality.new_from_pitches(*pitches)
|
55
|
+
end
|
56
|
+
|
57
|
+
def render(*args)
|
58
|
+
GuitarRepresentation.render(self, *args)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'paint'
|
2
|
+
module Coltrane
|
3
|
+
module GuitarRepresentation
|
4
|
+
class << self
|
5
|
+
def render(guitar_notes, ref_note=nil)
|
6
|
+
gns = guitar_notes.guitar_notes.map(&:position)
|
7
|
+
tabs = Guitar.strings.map do |string|
|
8
|
+
Guitar.frets.times.map.each do |fret|
|
9
|
+
position = { guitar_string_index: string.index, fret: fret }
|
10
|
+
present = gns.include?(position)
|
11
|
+
x = if present
|
12
|
+
gn = GuitarNote.new(position)
|
13
|
+
mark = ref_note ? (gn.note - ref_note).name : '◼︎◼︎'
|
14
|
+
Paint[mark, :red]
|
15
|
+
else
|
16
|
+
'--'
|
17
|
+
end
|
18
|
+
x + (fret.zero? ? '|' : ' ')
|
19
|
+
end.join('')
|
20
|
+
end.join("\n")
|
21
|
+
|
22
|
+
special_frets = [3,5,7,9,12,15,17,19,21]
|
23
|
+
visual_aids = Guitar.frets.times.map do |fret|
|
24
|
+
x = if special_frets.include?(fret)
|
25
|
+
Paint[fret.to_s.rjust(2, 0.to_s), :yellow]
|
26
|
+
else
|
27
|
+
' '
|
28
|
+
end
|
29
|
+
x + ' '
|
30
|
+
end.join('')
|
31
|
+
|
32
|
+
[tabs, "#{visual_aids}"].join("\n")
|
33
|
+
end
|
34
|
+
|
35
|
+
def render_degrees(guitar_notes, ref_scale)
|
36
|
+
gns = guitar_notes.guitar_notes.map(&:position)
|
37
|
+
tabs = Guitar.strings.map do |string|
|
38
|
+
Guitar.frets.times.map.each do |fret|
|
39
|
+
position = { guitar_string_index: string.index, fret: fret }
|
40
|
+
present = gns.include?(position)
|
41
|
+
x = if present
|
42
|
+
gn = GuitarNote.new(position)
|
43
|
+
mark = ref_scale.degree_of_note(gn.note).to_s.rjust(2, 0.to_s)
|
44
|
+
Paint[mark, :red]
|
45
|
+
else
|
46
|
+
'--'
|
47
|
+
end
|
48
|
+
x + (fret.zero? ? '|' : ' ')
|
49
|
+
end.join('')
|
50
|
+
end.join("\n")
|
51
|
+
|
52
|
+
special_frets = [3,5,7,9,12,15,17,19,21]
|
53
|
+
visual_aids = Guitar.frets.times.map do |fret|
|
54
|
+
x = if special_frets.include?(fret)
|
55
|
+
Paint[fret.to_s.rjust(2, 0.to_s), :yellow]
|
56
|
+
else
|
57
|
+
' '
|
58
|
+
end
|
59
|
+
x + ' '
|
60
|
+
end.join('')
|
61
|
+
|
62
|
+
[tabs, "#{visual_aids}"].join("\n")
|
63
|
+
end
|
64
|
+
|
65
|
+
def render_notes(guitar_notes, ref_scale)
|
66
|
+
gns = guitar_notes.guitar_notes.map(&:position)
|
67
|
+
tabs = Guitar.strings.map do |string|
|
68
|
+
Guitar.frets.times.map.each do |fret|
|
69
|
+
position = { guitar_string_index: string.index, fret: fret }
|
70
|
+
present = gns.include?(position)
|
71
|
+
x = if present
|
72
|
+
gn = GuitarNote.new(position)
|
73
|
+
mark = gn.note.name.to_s.ljust(2, ' ')
|
74
|
+
Paint[mark, :red]
|
75
|
+
else
|
76
|
+
'--'
|
77
|
+
end
|
78
|
+
x + (fret.zero? ? '|' : ' ')
|
79
|
+
end.join('')
|
80
|
+
end.join("\n")
|
81
|
+
|
82
|
+
special_frets = [3,5,7,9,12,15,17,19,21]
|
83
|
+
visual_aids = Guitar.frets.times.map do |fret|
|
84
|
+
x = if special_frets.include?(fret)
|
85
|
+
Paint[fret.to_s.rjust(2, 0.to_s), :yellow]
|
86
|
+
else
|
87
|
+
' '
|
88
|
+
end
|
89
|
+
x + ' '
|
90
|
+
end.join('')
|
91
|
+
|
92
|
+
[tabs, "#{visual_aids}"].join("\n")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Coltrane
|
2
|
+
# It describes a guitar string
|
3
|
+
class GuitarString
|
4
|
+
attr_reader :pitch
|
5
|
+
|
6
|
+
def initialize(pitch)
|
7
|
+
@pitch = Pitch.new(pitch)
|
8
|
+
end
|
9
|
+
|
10
|
+
def fret_by_pitch(pitch)
|
11
|
+
pitch = Pitch.new(pitch) if pitch.class != Pitch
|
12
|
+
fret = pitch.number - @pitch.number
|
13
|
+
fret if fret >= 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def pitch_by_fret(fret)
|
17
|
+
Pitch.new(pitch.number + fret)
|
18
|
+
end
|
19
|
+
|
20
|
+
def guitar_notes_for_note(note)
|
21
|
+
pitches_for_note(note).map do |pitch|
|
22
|
+
GuitarNote.new(fret: fret_by_pitch(pitch),
|
23
|
+
guitar_string_index: index)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def guitar_notes_for_note_in_region(note, region)
|
28
|
+
pitches_for_note(note).each_with_object([]) do |pitch, memo|
|
29
|
+
fret = fret_by_pitch(pitch)
|
30
|
+
if region.include?(fret) || fret == 0
|
31
|
+
memo << GuitarNote.new(fret: fret, guitar_string_index: index)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def index
|
37
|
+
Guitar.strings.index { |string| string.pitch.number == self.pitch.number }
|
38
|
+
end
|
39
|
+
|
40
|
+
def pitches_for_note(note)
|
41
|
+
pitches.each_with_object([]) do |pitch, memo|
|
42
|
+
memo << pitch if pitch.note.name == note.name
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def pitches
|
47
|
+
Guitar.frets.times.map do |fret|
|
48
|
+
pitch_by_fret(fret)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Coltrane
|
2
|
+
# It describes a interval between 2 pitches
|
3
|
+
class Interval
|
4
|
+
attr_reader :number
|
5
|
+
|
6
|
+
NAMES = [
|
7
|
+
'1P',
|
8
|
+
'2m', '2M',
|
9
|
+
'3m', '3M',
|
10
|
+
'4P', '4A',
|
11
|
+
'5P',
|
12
|
+
'6m', '6M',
|
13
|
+
'7m',
|
14
|
+
'7M',
|
15
|
+
'8P',
|
16
|
+
'9m', '9M',
|
17
|
+
'10m', '10M',
|
18
|
+
'11P',
|
19
|
+
'12P',
|
20
|
+
'13m', '13M',
|
21
|
+
'14m', '14M',
|
22
|
+
'15P', '15A'
|
23
|
+
].freeze
|
24
|
+
|
25
|
+
def initialize(number)
|
26
|
+
@number = number
|
27
|
+
end
|
28
|
+
|
29
|
+
def name
|
30
|
+
NAMES[number]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|