coltrane 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/.bundle/config +2 -0
  3. data/.rspec +2 -0
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/Gemfile +17 -0
  6. data/Gemfile.lock +164 -0
  7. data/Guardfile +71 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +41 -0
  10. data/Rakefile +6 -0
  11. data/bin/_guard-core +17 -0
  12. data/bin/bundler +17 -0
  13. data/bin/coderay +17 -0
  14. data/bin/coltrane +11 -0
  15. data/bin/console +14 -0
  16. data/bin/erubis +17 -0
  17. data/bin/guard +17 -0
  18. data/bin/htmldiff +17 -0
  19. data/bin/kill-pry-rescue +17 -0
  20. data/bin/ldiff +17 -0
  21. data/bin/listen +17 -0
  22. data/bin/nokogiri +17 -0
  23. data/bin/pry +17 -0
  24. data/bin/rackup +17 -0
  25. data/bin/rails +17 -0
  26. data/bin/rake +17 -0
  27. data/bin/rdoc +17 -0
  28. data/bin/rescue +17 -0
  29. data/bin/ri +17 -0
  30. data/bin/rspec +17 -0
  31. data/bin/rubocop +17 -0
  32. data/bin/ruby-parse +17 -0
  33. data/bin/ruby-rewrite +17 -0
  34. data/bin/setup +8 -0
  35. data/bin/sprockets +17 -0
  36. data/bin/thor +17 -0
  37. data/bin/tilt +17 -0
  38. data/coltrane.gemspec +35 -0
  39. data/db/cache.sqlite3 +0 -0
  40. data/db/cache_test.sqlite3 +0 -0
  41. data/db/config.yml +11 -0
  42. data/db/schema.rb +30 -0
  43. data/lib/coltrane.rb +48 -0
  44. data/lib/coltrane/cadence.rb +4 -0
  45. data/lib/coltrane/chord.rb +81 -0
  46. data/lib/coltrane/chord_cache.rb +4 -0
  47. data/lib/coltrane/chord_quality.rb +48 -0
  48. data/lib/coltrane/classic_scales.rb +134 -0
  49. data/lib/coltrane/essential_guitar_chords.rb +82 -0
  50. data/lib/coltrane/fret_set.rb +0 -0
  51. data/lib/coltrane/guitar.rb +15 -0
  52. data/lib/coltrane/guitar_chord.rb +50 -0
  53. data/lib/coltrane/guitar_chord_finder.rb +98 -0
  54. data/lib/coltrane/guitar_note.rb +50 -0
  55. data/lib/coltrane/guitar_note_set.rb +61 -0
  56. data/lib/coltrane/guitar_representation.rb +96 -0
  57. data/lib/coltrane/guitar_string.rb +52 -0
  58. data/lib/coltrane/interval.rb +33 -0
  59. data/lib/coltrane/interval_sequence.rb +70 -0
  60. data/lib/coltrane/interval_set.rb +23 -0
  61. data/lib/coltrane/mode.rb +0 -0
  62. data/lib/coltrane/note.rb +98 -0
  63. data/lib/coltrane/note_set.rb +50 -0
  64. data/lib/coltrane/piano_representation.rb +58 -0
  65. data/lib/coltrane/pitch.rb +27 -0
  66. data/lib/coltrane/progression.rb +4 -0
  67. data/lib/coltrane/qualities.rb +115 -0
  68. data/lib/coltrane/scale.rb +139 -0
  69. data/lib/coltrane/scale_cache.rb +4 -0
  70. data/lib/coltrane/scale_chord.rb +4 -0
  71. data/lib/coltrane/version.rb +3 -0
  72. metadata +144 -0
File without changes
@@ -0,0 +1,15 @@
1
+ module Coltrane
2
+ # This holds default guitar features
3
+ module Guitar
4
+ extend EssentialGuitarChords
5
+ def self.strings
6
+ %w[E4 B3 G3 D3 A2 E2].map do |pitch|
7
+ GuitarString.new(pitch)
8
+ end
9
+ end
10
+
11
+ def self.frets
12
+ 23
13
+ end
14
+ end
15
+ end
@@ -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