coltrane 0.0.2

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.
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
@@ -0,0 +1,70 @@
1
+ module Coltrane
2
+ # It describes a sequence of intervals
3
+ class IntervalSequence
4
+ attr_reader :intervals
5
+
6
+ def initialize(arg)
7
+ arg = [arg] if arg.class != Array
8
+ @intervals = arg.reduce([]) do |memo, arg_item|
9
+ case arg_item
10
+ when Numeric then memo << Interval.new(arg_item)
11
+ when Interval then memo << arg_item
12
+ when NoteSet then memo + intervals_from_note_set(arg_item)
13
+ end
14
+ end
15
+ end
16
+
17
+ def intervals_from_note_set(note_set)
18
+ note_numbers = note_set.notes.collect(&:number)
19
+ root = note_numbers.shift
20
+ note_numbers.reduce([Interval.new(0)]) do |memo, number|
21
+ number += 12 if number < root
22
+ memo << Interval.new(number - root)
23
+ end
24
+ end
25
+
26
+ def reordered
27
+ IntervalSequence.new @intervals.sort_by!(&:number)
28
+ end
29
+
30
+ def all
31
+ intervals
32
+ end
33
+
34
+ def [](x)
35
+ intervals[x]
36
+ end
37
+
38
+ def shift(ammount)
39
+ IntervalSequence.new(intervals.map do |i|
40
+ (i.number + ammount) % 12
41
+ end)
42
+ end
43
+
44
+ def zero_it
45
+ self.shift(-intervals.first.number)
46
+ end
47
+
48
+ def next_inversion
49
+ IntervalSequence.new(intervals.rotate(+1))
50
+ end
51
+
52
+ def previous_inversion
53
+ IntervalSequence.new(intervals.rotate(-1))
54
+ end
55
+
56
+ def inversions
57
+ Array.new(intervals.length) do |index|
58
+ IntervalSequence.new(interval.rotate(index))
59
+ end
60
+ end
61
+
62
+ def numbers
63
+ intervals.collect(&:number)
64
+ end
65
+
66
+ def names
67
+ intervals.collect(&:name)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,23 @@
1
+
2
+ # class IntervalSet
3
+ # def initialize(*intervals)
4
+ # @intervals = intervals
5
+ # @number_of_frets = 24
6
+ # sum = @intervals.reduce(:+)
7
+ # sum < 12 && @intervals << 12 - sum
8
+ # end
9
+
10
+ # def to_s
11
+ # @intervals.to_s
12
+ # end
13
+
14
+ # def to_frets(offset = 0)
15
+ # frets = [-offset]
16
+ # i = 0
17
+ # while frets.last < @number_of_frets
18
+ # frets << frets.last + @intervals[i % @intervals.length]
19
+ # i += 1
20
+ # end
21
+ # frets
22
+ # end
23
+ # end
File without changes
@@ -0,0 +1,98 @@
1
+ module Coltrane
2
+ # It describes a musical note, independent of octave
3
+ class Note
4
+ attr_reader :name
5
+
6
+ NOTES = {
7
+ 'C' => 0,
8
+ 'C#' => 1,
9
+ 'Db' => 1,
10
+ 'D' => 2,
11
+ 'D#' => 3,
12
+ 'Eb' => 3,
13
+ 'E' => 4,
14
+ 'F' => 5,
15
+ 'F#' => 6,
16
+ 'Gb' => 6,
17
+ 'G' => 7,
18
+ 'G#' => 8,
19
+ 'Ab' => 8,
20
+ 'A' => 9,
21
+ 'A#' => 10,
22
+ 'Bb' => 10,
23
+ 'B' => 11
24
+ }.freeze
25
+
26
+ def self.all
27
+ NOTES.keys.map {|n| Note.new(n)}
28
+ end
29
+
30
+ def accident?
31
+ [1,3,6,8,10].include?(number)
32
+ end
33
+
34
+ def initialize(arg)
35
+ case arg
36
+ when String
37
+ raise "invalid note: #{arg}" unless valid_note?(arg)
38
+ @name = arg
39
+ when Numeric then @name = name_from_number(arg)
40
+ end
41
+ end
42
+
43
+ def +(n)
44
+ case n
45
+ when Numeric then Note.new(number + n)
46
+ when Note then Chord.new(number + n.number)
47
+ end
48
+ end
49
+
50
+ def -(n)
51
+ case n
52
+ when Numeric then Note.new(number + n)
53
+ when Note then Interval.new((number - n.number) % 12)
54
+ end
55
+ end
56
+
57
+ def valid_note?(note_name)
58
+ NOTES.key?(note_name)
59
+ end
60
+
61
+ def number
62
+ NOTES[name]
63
+ end
64
+
65
+ def interval_to(note_name)
66
+ Interval.new(Note.new(note_name).number - number)
67
+ end
68
+
69
+ def transpose_by(interval_number)
70
+ @name = name_from_number(number + interval_number)
71
+ self
72
+ end
73
+
74
+ def guitar_notes
75
+ Guitar.strings.reduce([]) do |memo, guitar_string|
76
+ memo + in_guitar_string(guitar_string)
77
+ end
78
+ end
79
+
80
+ def on_guitar
81
+ GuitarNoteSet.new(guitar_notes).render
82
+ end
83
+
84
+ def in_guitar_string(guitar_string)
85
+ guitar_string.guitar_notes_for_note(self)
86
+ end
87
+
88
+ def in_guitar_string_region(guitar_string, region)
89
+ guitar_string.guitar_notes_for_note_in_region(self, region)
90
+ end
91
+
92
+ protected
93
+
94
+ def name_from_number(number)
95
+ NOTES.key(number % 12)
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,50 @@
1
+ module Coltrane
2
+ # It describes a set of notes
3
+ class NoteSet
4
+ attr_reader :notes
5
+
6
+ def initialize(arg)
7
+ @notes = case arg
8
+ when String then notes_from_note_string(arg)
9
+ when Array then notes_from_note_array(arg)
10
+ end
11
+ end
12
+
13
+ def root_note
14
+ notes.sort_by(&:number).first
15
+ end
16
+
17
+ def transpose_to(note_name)
18
+ transpose_by(root_note.interval_to(note_name).number)
19
+ end
20
+
21
+ def transpose_by(interval)
22
+ notes.map do |note|
23
+ note.transpose_by(interval)
24
+ end
25
+ end
26
+
27
+ def guitar_notes
28
+ GuitarNoteSet.new(notes.map(&:guitar_notes).flatten)
29
+ end
30
+
31
+ def interval_sequence
32
+ IntervalSequence.new(self)
33
+ end
34
+
35
+ protected
36
+
37
+ def notes_from_note_string(note_string)
38
+ notes_from_note_array(note_string.split(' '))
39
+ end
40
+
41
+ def notes_from_note_array(note_array)
42
+ note_array.collect do |note|
43
+ case note
44
+ when String then Note.new(note)
45
+ when Note then note
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,58 @@
1
+ module Coltrane
2
+ class PianoRepresentation
3
+ PIANO_TEMPLATE = <<~ASCII
4
+ ┌─┬─┬┬─┬─╥─┬─┬┬─┬┬─┬─╥─┬─┬┬─┬─╥─┬─┬┬─┬┬─┬─┐
5
+ │ │ ││ │ ║ │ ││ ││ │ ║ │ ││ │ ║ │ ││ ││ │ │
6
+ │ │X││X│ ║ │X││X││X│ ║ │X││X│ ║ │X││X││X│ │
7
+ │ │X││X│ ║ │X││X││X│ ║ │X││X│ ║ │X││X││X│ │
8
+ │ ┕╥┙┕╥┙ ║ ┕╥┙┕╥┙┕╥┙ ║ ┕╥┙┕╥┙ ║ ┕╥┙┕╥┙┕╥┙ │
9
+ │XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX│
10
+ └──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──┘
11
+ ASCII
12
+
13
+ class << self
14
+ def render_intervals(notes, ref_note)
15
+ output = place_black_notes(Paint[PIANO_TEMPLATE, 'gray'], notes, ref_note)
16
+ place_white_notes(output, notes, ref_note)
17
+ end
18
+
19
+ def place_white_notes(input, notes, ref_note)
20
+ white_notes = Scale.major.notes
21
+ i = 0
22
+ output_array = input.split("\n")
23
+ output_array[5].gsub!('XX') do |match|
24
+ note = white_notes[i%7]
25
+ interval_name = (note - ref_note).name
26
+ i += 1
27
+ notes.map(&:name).include?(note.name) ? Paint[interval_name, 'red'] : ' '
28
+ end
29
+
30
+ output_array.join("\n")
31
+ end
32
+
33
+ def place_black_notes(input, notes, ref_note)
34
+ black_notes = Scale.pentatonic_major('C#',4).notes
35
+ output_array = input.split("\n")
36
+
37
+ i = 0
38
+ output_array[2].gsub!('X') do |match|
39
+ note = black_notes[i%5]
40
+ interval_name = (note - ref_note).name
41
+ i += 1
42
+ notes.map(&:name).include?(note.name) ? Paint[interval_name[0], 'red'] : ' '
43
+ end
44
+
45
+ i = 0
46
+ output_array[3].gsub!('X') do |match|
47
+ note = black_notes[i%5]
48
+ interval_name = (black_notes[i%5] - ref_note).name
49
+ i += 1
50
+ notes.map(&:name).include?(note.name) ? Paint[interval_name[1], 'red'] : ' '
51
+ end
52
+
53
+ output_array.join("\n")
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,27 @@
1
+ module Coltrane
2
+ # It describes a pitch, like E4 or Bb5. It's like a note, but it has an octave
3
+ class Pitch
4
+ attr_reader :number
5
+
6
+ def initialize(pitch)
7
+ case pitch
8
+ when String then @number = number_from_name(pitch)
9
+ when Numeric then @number = pitch
10
+ when Pitch then @number = pitch.number
11
+ end
12
+ end
13
+
14
+ def number_from_name(pitch_string)
15
+ _, note, octaves = pitch_string.match(/(.*)(\d)/).to_a
16
+ Note.new(note).number + 12 * octaves.to_i
17
+ end
18
+
19
+ def name
20
+ "#{note.name}#{number / 12}"
21
+ end
22
+
23
+ def note
24
+ Note.new(number)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ module Coltrane
2
+ class Progression
3
+ end
4
+ end
@@ -0,0 +1,115 @@
1
+ module Qualities
2
+ CHORD_QUALITIES = {
3
+ "5" => [0, 7],
4
+ "Msus2" => [0, 2, 7],
5
+ "dim" => [0, 3, 6],
6
+ "m" => [0, 3, 7],
7
+ "m#5" => [0, 3, 8],
8
+ "Mb5" => [0, 4, 6],
9
+ "M" => [0, 4, 7],
10
+ "" => [0, 4, 7],
11
+ "M#5" => [0, 4, 8],
12
+ "Mb6" => [0, 4, 8],
13
+ "7ndim5" => [0, 4, 10],
14
+ "Msus4" => [0, 5, 7],
15
+ "mb6b9" => [0, 1, 3, 8],
16
+ "addb9" => [0, 1, 4, 7],
17
+ "madd9" => [0, 2, 3, 7],
18
+ "Madd9" => [0, 2, 4, 7],
19
+ "sus24" => [0, 2, 5, 7],
20
+ "M#5add9" => [0, 2, 4, 8],
21
+ "9ndim5" => [0, 2, 4, 10],
22
+ "+add#9" => [0, 3, 4, 8],
23
+ "madd4" => [0, 3, 5, 7],
24
+ "4" => [0, 3, 5, 10],
25
+ "dim7" => [0, 3, 6, 9],
26
+ "m7" => [0, 3, 7, 10],
27
+ "mM7" => [0, 3, 7, 11],
28
+ "m7#5" => [0, 3, 8, 10],
29
+ "7b5" => [0, 4, 6, 10],
30
+ "7" => [0, 4, 7, 10],
31
+ "7#5" => [0, 4, 8, 10],
32
+ "7b13" => [0, 4, 8, 10],
33
+ "M7#5" => [0, 4, 8, 11],
34
+ "M7b6" => [0, 4, 8, 11],
35
+ "M7b5" => [0, 4, 6, 11],
36
+ "M7#5sus4" => [0, 5, 8, 11],
37
+ "7sus4" => [0, 5, 7, 10],
38
+ "7#5sus4" => [0, 5, 8, 10],
39
+ "M7sus4" => [0, 5, 7, 11],
40
+ "M6" => [0, 4, 7, 9],
41
+ "m7b5" => [0, 3, 6, 10],
42
+ "M7" => [0, 4, 7, 11],
43
+ "maj7" => [0, 4, 7, 11],
44
+ "mb6M7" => [0, 3, 8, 11],
45
+ "dimM7" => [0, 3, 6, 11],
46
+ "m6" => [0, 3, 5, 7, 9],
47
+ "m6/9" => [0, 2, 3, 7, 9],
48
+ "M6/9" => [0, 2, 4, 7, 9],
49
+ "M6#11" => [0, 4, 6, 7, 9],
50
+ "m7add11" => [0, 3, 5, 7, 10],
51
+ "dim7M7" => [0, 3, 6, 9, 11],
52
+ "m9" => [0, 2, 3, 7, 10],
53
+ "m9#5" => [0, 2, 3, 8, 10],
54
+ "m9b5" => [0, 2, 3, 6, 10],
55
+ "mM7b6" => [0, 3, 7, 8, 11],
56
+ "mM9" => [0, 2, 3, 7, 11],
57
+ "M7b9" => [0, 1, 4, 7, 11],
58
+ "M9" => [0, 2, 4, 7, 11],
59
+ "M9#5" => [0, 2, 4, 8, 11],
60
+ "M9#5sus4" => [0, 2, 5, 8, 11],
61
+ "M9b5" => [0, 2, 4, 6, 11],
62
+ "M9sus4" => [0, 2, 5, 7, 11],
63
+ "M7#11" => [0, 4, 6, 7, 11],
64
+ "7#9" => [0, 3, 4, 7, 10],
65
+ "7#11" => [0, 4, 6, 7, 10],
66
+ "11" => [0, 2, 5, 7, 10],
67
+ "11b9" => [0, 1, 5, 7, 10],
68
+ "13ndim5" => [0, 2, 4, 9, 10],
69
+ "7#5#9" => [0, 3, 4, 8, 10],
70
+ "7#5b9" => [0, 1, 4, 8, 10],
71
+ "7add6" => [0, 4, 7, 9, 10],
72
+ "7b6" => [0, 4, 7, 8, 10],
73
+ "7b9" => [0, 1, 4, 7, 10],
74
+ "7sus4b9" => [0, 1, 5, 7, 10],
75
+ "9" => [0, 2, 4, 7, 10],
76
+ "9#5" => [0, 2, 4, 8, 10],
77
+ "9b13" => [0, 2, 4, 8, 10],
78
+ "9b5" => [0, 2, 4, 6, 10],
79
+ "9sus4" => [0, 2, 5, 7, 10],
80
+ "M6/9#11" => [0, 2, 4, 6, 7, 9],
81
+ "6/9#11" => [0, 2, 4, 6, 7, 9],
82
+ "9#5#11" => [0, 2, 4, 6, 8, 10],
83
+ "Blues" => [0, 3, 5, 6, 7, 10],
84
+ "m11" => [0, 2, 3, 5, 7, 10],
85
+ "m11#5" => [0, 2, 3, 5, 8, 10],
86
+ "m11b5" => [0, 2, 3, 5, 6, 10],
87
+ "13b5" => [0, 2, 4, 6, 9, 10],
88
+ "13b9" => [0, 1, 4, 7, 9, 10],
89
+ "13sus4" => [0, 2, 5, 7, 9, 10],
90
+ "7#11b13" => [0, 4, 6, 7, 8, 10],
91
+ "7#5b9#11" => [0, 1, 4, 6, 8, 10],
92
+ "7#9#11" => [0, 3, 4, 6, 7, 10],
93
+ "7#9b13" => [0, 3, 4, 7, 8, 10],
94
+ "13#9" => [0, 3, 4, 7, 9, 10],
95
+ "13" => [0, 2, 4, 7, 9, 10],
96
+ "7b9#11" => [0, 1, 4, 6, 7, 10],
97
+ "7b9#9" => [0, 1, 3, 4, 7, 10],
98
+ "7b9b13" => [0, 1, 4, 7, 8, 10],
99
+ "7sus4b9b13" => [0, 1, 5, 7, 8, 10],
100
+ "9#11" => [0, 2, 4, 6, 7, 10],
101
+ "M13" => [0, 2, 4, 7, 9, 11],
102
+ "M7#9#11" => [0, 3, 4, 6, 7, 11],
103
+ "M7add13" => [0, 2, 4, 7, 9, 11],
104
+ "M9#11" => [0, 2, 4, 6, 7, 11],
105
+ "mM9b6" => [0, 2, 3, 7, 8, 11],
106
+ "7b9b13#11" => [0, 1, 4, 6, 7, 8, 10],
107
+ "13b9#11" => [0, 1, 4, 6, 7, 9, 10],
108
+ "9#11b13" => [0, 2, 4, 6, 7, 8, 10],
109
+ "13#11" => [0, 2, 4, 6, 7, 9, 10],
110
+ "M13#11" => [0, 2, 4, 6, 7, 9, 11],
111
+ "m13" => [0, 2, 3, 5, 7, 9, 10],
112
+ "13#9#11" => [0, 3, 4, 6, 7, 9, 10],
113
+ "7#9#11b13" => [0, 3, 4, 6, 7, 8, 10]
114
+ }
115
+ end