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.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +16 -1
  3. data/CHANGELOG.md +11 -0
  4. data/Gemfile.lock +25 -2
  5. data/README.md +3 -0
  6. data/bin/console +6 -2
  7. data/coltrane.gemspec +2 -1
  8. data/exe/coltrane +14 -224
  9. data/lib/coltrane.rb +3 -50
  10. data/lib/coltrane/commands.rb +14 -0
  11. data/lib/coltrane/commands/chords.rb +77 -0
  12. data/lib/coltrane/commands/command.rb +45 -0
  13. data/lib/coltrane/commands/common_chords.rb +33 -0
  14. data/lib/coltrane/commands/errors.rb +44 -0
  15. data/lib/coltrane/commands/find_progression.rb +28 -0
  16. data/lib/coltrane/commands/find_scale.rb +39 -0
  17. data/lib/coltrane/commands/notes.rb +44 -0
  18. data/lib/coltrane/commands/progression.rb +27 -0
  19. data/lib/{cli → coltrane/commands}/representation.rb +0 -0
  20. data/lib/coltrane/commands/scale.rb +46 -0
  21. data/lib/coltrane/renderers.rb +6 -0
  22. data/lib/coltrane/renderers/renderer.rb +42 -0
  23. data/lib/coltrane/renderers/text_renderer.rb +23 -0
  24. data/lib/coltrane/renderers/text_renderer/array_drawer.rb +45 -0
  25. data/lib/coltrane/renderers/text_renderer/base_drawer.rb +20 -0
  26. data/lib/coltrane/renderers/text_renderer/hash_drawer.rb +16 -0
  27. data/lib/coltrane/renderers/text_renderer/representation_guitar_chord_drawer.rb +95 -0
  28. data/lib/coltrane/renderers/text_renderer/representation_guitar_note_set_drawer.rb +76 -0
  29. data/lib/coltrane/renderers/text_renderer/representation_piano_note_set_drawer.rb +49 -0
  30. data/lib/coltrane/renderers/text_renderer/theory_chord_drawer.rb +14 -0
  31. data/lib/coltrane/renderers/text_renderer/theory_note_set_drawer.rb +17 -0
  32. data/lib/coltrane/renderers/text_renderer/theory_progression_drawer.rb +13 -0
  33. data/lib/coltrane/renderers/text_renderer/theory_progression_set_drawer.rb +22 -0
  34. data/lib/coltrane/renderers/text_renderer/theory_scale_drawer.rb +13 -0
  35. data/lib/coltrane/renderers/text_renderer/theory_scale_set_drawer.rb +27 -0
  36. data/lib/coltrane/representation.rb +12 -0
  37. data/lib/coltrane/representation/guitar.rb +34 -0
  38. data/lib/coltrane/representation/guitar/chord.rb +180 -0
  39. data/lib/coltrane/representation/guitar/note.rb +26 -0
  40. data/lib/coltrane/representation/guitar/note_set.rb +35 -0
  41. data/lib/coltrane/representation/guitar/string.rb +31 -0
  42. data/lib/coltrane/representation/guitar_like_instruments.rb +21 -0
  43. data/lib/coltrane/representation/piano.rb +36 -0
  44. data/lib/coltrane/representation/piano/note_set.rb +22 -0
  45. data/lib/{coltrane_synth.rb → coltrane/synth.rb.rb} +0 -0
  46. data/lib/{coltrane_synth → coltrane/synth}/base.rb +0 -0
  47. data/lib/{coltrane_synth → coltrane/synth}/synth.rb +0 -0
  48. data/lib/coltrane/theory.rb +54 -0
  49. data/lib/coltrane/theory/cadence.rb +9 -0
  50. data/lib/coltrane/{changes.rb → theory/changes.rb} +0 -0
  51. data/lib/coltrane/theory/chord.rb +101 -0
  52. data/lib/coltrane/theory/chord_quality.rb +113 -0
  53. data/lib/coltrane/theory/chord_substitutions.rb +11 -0
  54. data/lib/coltrane/theory/circle_of_fifths.rb +33 -0
  55. data/lib/coltrane/theory/classic_scales.rb +113 -0
  56. data/lib/coltrane/theory/diatonic_scale.rb +38 -0
  57. data/lib/coltrane/theory/errors.rb +97 -0
  58. data/lib/coltrane/theory/frequency.rb +52 -0
  59. data/lib/coltrane/theory/frequency_interval.rb +83 -0
  60. data/lib/coltrane/theory/interval.rb +209 -0
  61. data/lib/coltrane/theory/interval_class.rb +212 -0
  62. data/lib/coltrane/theory/interval_sequence.rb +157 -0
  63. data/lib/coltrane/theory/key.rb +18 -0
  64. data/lib/coltrane/{mode.rb → theory/mode.rb} +0 -0
  65. data/lib/coltrane/theory/notable_progressions.rb +30 -0
  66. data/lib/coltrane/theory/note.rb +104 -0
  67. data/lib/coltrane/theory/note_set.rb +101 -0
  68. data/lib/coltrane/theory/pitch.rb +94 -0
  69. data/lib/coltrane/theory/pitch_class.rb +154 -0
  70. data/lib/coltrane/theory/progression.rb +81 -0
  71. data/lib/coltrane/theory/progression_set.rb +18 -0
  72. data/lib/coltrane/{qualities.rb → theory/qualities.rb} +0 -0
  73. data/lib/coltrane/theory/roman_chord.rb +114 -0
  74. data/lib/coltrane/theory/scale.rb +161 -0
  75. data/lib/coltrane/theory/scale_search_result.rb +6 -0
  76. data/lib/coltrane/theory/scale_set.rb +40 -0
  77. data/lib/coltrane/theory/voicing.rb +41 -0
  78. data/lib/coltrane/version.rb +1 -1
  79. metadata +88 -63
  80. data/bin/rails +0 -17
  81. data/data/qualities.yml +0 -83
  82. data/db/cache.sqlite3 +0 -0
  83. data/db/cache_test.sqlite3 +0 -0
  84. data/db/config.yml +0 -11
  85. data/lib/cli.rb +0 -24
  86. data/lib/cli/bass_guitar.rb +0 -12
  87. data/lib/cli/chord.rb +0 -39
  88. data/lib/cli/config.rb +0 -39
  89. data/lib/cli/errors.rb +0 -46
  90. data/lib/cli/guitar.rb +0 -67
  91. data/lib/cli/guitar_chords.rb +0 -122
  92. data/lib/cli/notes.rb +0 -20
  93. data/lib/cli/piano.rb +0 -57
  94. data/lib/cli/scale.rb +0 -53
  95. data/lib/cli/text.rb +0 -16
  96. data/lib/cli/ukulele.rb +0 -14
  97. data/lib/coltrane/cache.rb +0 -43
  98. data/lib/coltrane/cadence.rb +0 -7
  99. data/lib/coltrane/chord.rb +0 -89
  100. data/lib/coltrane/chord_quality.rb +0 -111
  101. data/lib/coltrane/chord_substitutions.rb +0 -9
  102. data/lib/coltrane/circle_of_fifths.rb +0 -31
  103. data/lib/coltrane/classic_scales.rb +0 -94
  104. data/lib/coltrane/diatonic_scale.rb +0 -36
  105. data/lib/coltrane/errors.rb +0 -95
  106. data/lib/coltrane/frequency.rb +0 -50
  107. data/lib/coltrane/frequency_interval.rb +0 -81
  108. data/lib/coltrane/interval.rb +0 -208
  109. data/lib/coltrane/interval_class.rb +0 -210
  110. data/lib/coltrane/interval_sequence.rb +0 -155
  111. data/lib/coltrane/key.rb +0 -16
  112. data/lib/coltrane/notable_progressions.rb +0 -28
  113. data/lib/coltrane/note.rb +0 -98
  114. data/lib/coltrane/note_set.rb +0 -89
  115. data/lib/coltrane/pitch.rb +0 -92
  116. data/lib/coltrane/pitch_class.rb +0 -148
  117. data/lib/coltrane/progression.rb +0 -74
  118. data/lib/coltrane/roman_chord.rb +0 -112
  119. data/lib/coltrane/scale.rb +0 -154
  120. data/lib/coltrane/unordered_interval_class.rb +0 -7
  121. data/lib/coltrane/voicing.rb +0 -39
  122. data/lib/coltrane_game/question.rb +0 -7
  123. data/lib/coltrane_instruments.rb +0 -7
  124. data/lib/coltrane_instruments/guitar.rb +0 -10
  125. data/lib/coltrane_instruments/guitar/base.rb +0 -29
  126. data/lib/coltrane_instruments/guitar/chord.rb +0 -170
  127. data/lib/coltrane_instruments/guitar/note.rb +0 -24
  128. data/lib/coltrane_instruments/guitar/string.rb +0 -29
  129. data/lib/os.rb +0 -21
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'coltrane'
4
+
5
+ module Coltrane
6
+ module Representation
7
+ autoload :Guitar, 'coltrane/representation/guitar'
8
+ autoload :Ukulele, 'coltrane/representation/guitar_like_instruments'
9
+ autoload :Bass, 'coltrane/representation/guitar_like_instruments'
10
+ autoload :Piano, 'coltrane/representation/piano'
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ require 'coltrane/representation/guitar/chord'
3
+ require 'coltrane/representation/guitar/note'
4
+ require 'coltrane/representation/guitar/note_set'
5
+ require 'coltrane/representation/guitar/string'
6
+
7
+ module Coltrane
8
+ module Representation
9
+ class Guitar
10
+ # A base class for operations involving Guitars
11
+ attr_reader :strings, :tuning, :frets, :special_frets
12
+
13
+ def self.find_chords(target_chord)
14
+ unless target_chord.is_a?(Theory::Chord)
15
+ target_chord = Theory::Chord.new(name: target_chord)
16
+ end
17
+
18
+ Guitar::Chord.find(target_chord, guitar: new)
19
+ end
20
+
21
+ def self.find_notes(notes)
22
+ Guitar::NoteSet.new(notes, guitar: new)
23
+ end
24
+
25
+ def initialize(tuning: nil, frets: nil, special_frets: nil)
26
+ @tuning = tuning || %w[E2 A2 D3 G3 B3 E4]
27
+ @special_frets = special_frets || [3, 5, 7, 9, 12, 15, 17, 19]
28
+ @frets = frets || 23
29
+
30
+ @strings = @tuning.map { |p| String.new(Theory::Pitch[p], guitar: self) }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coltrane
4
+ module Representation
5
+ class Guitar
6
+ # This class represents a group of guitar notes, strummed at the same time
7
+ class Chord
8
+ include Comparable
9
+ attr_reader :guitar_notes, :guitar, :free_fingers, :target_chord, :barre
10
+
11
+ MAX_FRET_SPAN = 3
12
+
13
+ def self.find(chord, guitar:)
14
+ new(chord, guitar: guitar)
15
+ .fetch_descendant_chords
16
+ .sort
17
+ .reverse
18
+ end
19
+
20
+ def initialize(target_chord,
21
+ guitar_notes: [],
22
+ free_fingers: 4,
23
+ barre: nil,
24
+ guitar:)
25
+
26
+ @target_chord = target_chord
27
+ @guitar_notes = guitar_notes
28
+ @free_fingers = free_fingers
29
+ @guitar = guitar
30
+ @barre = barre
31
+ end
32
+
33
+ def max_fret_span
34
+ MAX_FRET_SPAN
35
+ end
36
+
37
+ def <=>(other)
38
+ rank <=> other.rank
39
+ end
40
+
41
+ def rank
42
+ +completeness * 10_000 +
43
+ +fullness * 1000 +
44
+ -spreadness * 100 +
45
+ +easyness * 1
46
+ end
47
+
48
+ def analysis
49
+ {
50
+ completeness: completeness,
51
+ fullness: fullness,
52
+ easyness: easyness,
53
+ spreadness: spreadness
54
+ }
55
+ end
56
+
57
+ def spreadness
58
+ fret_range.size.to_f / MAX_FRET_SPAN
59
+ end
60
+
61
+ def completeness
62
+ (target_chord.notes.size.to_f - notes_left.size) / target_chord.notes.size
63
+ end
64
+
65
+ def easyness
66
+ frets.count(0).to_f / guitar_notes.size
67
+ end
68
+
69
+ def fullness
70
+ (guitar.strings.size.to_f - frets.count(nil)) / guitar.strings.size
71
+ end
72
+
73
+ def barre?
74
+ !@barre.nil?
75
+ end
76
+
77
+ def fetch_descendant_chords
78
+ return [self] if guitar_notes.size >= guitar.strings.size
79
+ possible_new_notes(notes_available.positive?).reduce([]) do |memo, n|
80
+ barre = n.fret if guitar_notes.last == n.fret
81
+ fingers_change = n.fret == barre || n.fret.zero? ? 0 : 1
82
+ next memo if (free_fingers - fingers_change).negative?
83
+ self.class.new(target_chord,
84
+ guitar_notes: guitar_notes + [n],
85
+ free_fingers: free_fingers - fingers_change,
86
+ guitar: guitar,
87
+ barre: barre
88
+ ).fetch_descendant_chords + memo
89
+ end
90
+ end
91
+
92
+ def notes_available
93
+ strings_available - notes_left.size
94
+ end
95
+
96
+ def strings_available
97
+ guitar.strings.size - guitar_notes.size
98
+ end
99
+
100
+ def notes_left
101
+ @notes_left ||= begin
102
+ target_chord.notes - Theory::NoteSet[
103
+ *guitar_notes.map do |n|
104
+ next if n.pitch.nil?
105
+ n.pitch.pitch_class
106
+ end
107
+ ]
108
+ end
109
+ end
110
+
111
+ def target_notes
112
+ notes_left.any? ? notes_left : target_chord.notes
113
+ end
114
+
115
+ def notes
116
+ guitar_notes.map(&:pitch)
117
+ end
118
+
119
+ def frets
120
+ @frets ||= guitar_notes.map(&:fret)
121
+ end
122
+
123
+ def non_zero_frets
124
+ frets.reject { |f| f.nil? || f.zero? }
125
+ end
126
+
127
+ def lowest_fret
128
+ non_zero_frets.any? ? non_zero_frets.min : 0
129
+ end
130
+
131
+ def highest_fret
132
+ non_zero_frets.max || 0
133
+ end
134
+
135
+ def fret_range
136
+ (lowest_fret..highest_fret)
137
+ end
138
+
139
+ def lowest_possible_fret
140
+ lowest_fret.zero? ? 0 : [(lowest_fret - possible_span), 0].max
141
+ end
142
+
143
+ def highest_possible_fret
144
+ [(possible_span + (highest_fret == 0 ? guitar.frets : highest_fret)), guitar.frets].min
145
+ end
146
+
147
+ def possible_span
148
+ MAX_FRET_SPAN - fret_range.size
149
+ end
150
+
151
+ def fret_expansion_range
152
+ (lowest_possible_fret..highest_possible_fret).to_a +
153
+ [(0 unless barre?)].compact
154
+ end
155
+
156
+ def to_s
157
+ guitar_notes.map { |n| n.fret.nil? ? 'x' : n.fret }.join('-')
158
+ end
159
+
160
+ def voicing
161
+ Coltrane::Voicing.new(pitches: guitar_notes.map(&:pitch).compact)
162
+ end
163
+
164
+ private
165
+
166
+ attr_writer :barre
167
+
168
+ def next_string
169
+ guitar.strings[guitar_notes.size]
170
+ end
171
+
172
+ def possible_new_notes(include_mute_note = false)
173
+ target_notes.notes.map do |note|
174
+ next_string.find(note, possible_frets: fret_expansion_range)
175
+ end.flatten + [(Guitar::Note.new(next_string, nil) if include_mute_note)].compact
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coltrane
4
+ module Representation
5
+ class Guitar
6
+ class Note
7
+ attr_reader :string, :fret
8
+
9
+ def initialize(string, fret = nil)
10
+ @string = string
11
+ @fret = fret
12
+ end
13
+
14
+ def pitch
15
+ string + fret unless fret.nil?
16
+ end
17
+
18
+ def pitch_class
19
+ pitch.pitch_class unless fret.nil?
20
+ end
21
+
22
+ alias note pitch_class
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coltrane
4
+ module Representation
5
+ class Guitar
6
+ class NoteSet
7
+ extend Forwardable
8
+ include Enumerable
9
+
10
+ attr_reader :notes, :guitar
11
+ def_delegators :notes, :each, :root
12
+
13
+ def initialize(note_set, guitar:)
14
+ @notes = note_set
15
+ @guitar = guitar
16
+ end
17
+
18
+ instance_eval { alias [] new }
19
+
20
+ def results
21
+ @strings ||= begin
22
+ guitar.strings.map do |string|
23
+ {
24
+ pitch: string.pitch,
25
+ notes: notes.map { |note| string.find(note) }
26
+ }
27
+ end
28
+ end
29
+ end
30
+
31
+ alias strings results
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coltrane
4
+ module Representation
5
+ class Guitar
6
+ class String
7
+ attr_reader :pitch, :guitar
8
+
9
+ def initialize(pitch, guitar:)
10
+ @guitar = guitar
11
+ @pitch = pitch
12
+ end
13
+
14
+ def find(pitch_class, possible_frets: (0..guitar.frets).to_a)
15
+ output = []
16
+ n = 0
17
+ loop do
18
+ f = (pitch_class.integer - pitch.integer) % 12 + 12 * n
19
+ possible_frets.include?(f) ? output << Note.new(self, f) : break
20
+ n += 1
21
+ end
22
+ output
23
+ end
24
+
25
+ def +(fret)
26
+ pitch + fret
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'coltrane/representation/guitar'
4
+
5
+ module Coltrane
6
+ module Representation
7
+ class Ukulele < Guitar
8
+ def initialize(tuning: %w[G3 C4 E4 A4],
9
+ frets: 12,
10
+ special_frets: [5, 7, 9, 12])
11
+ super
12
+ end
13
+ end
14
+
15
+ class Bass < Guitar
16
+ def initialize(tuning: %w[E1 A1 D2 G2])
17
+ super
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ require 'coltrane/representation/piano/note_set'
2
+
3
+ module Coltrane
4
+ module Representation
5
+ class Piano
6
+ attr_reader :octaves
7
+
8
+ class << self
9
+
10
+ def find_notes(notes)
11
+ Piano::NoteSet.new(notes, piano: new)
12
+ end
13
+
14
+ def chord(chord)
15
+ find_notes(
16
+ chord.is_a?(Chord) ? chord.notes : Chord.new(name: chord).notes
17
+ )
18
+ end
19
+
20
+ def black_notes
21
+ Theory::Scale.pentatonic_major('C#', 4).notes
22
+ end
23
+
24
+ def white_notes
25
+ Theory::Scale.major.notes
26
+ end
27
+
28
+ end
29
+
30
+ def initialize
31
+ @octaves
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coltrane
4
+ module Representation
5
+ class Piano
6
+ class NoteSet
7
+ extend Forwardable
8
+ include Enumerable
9
+
10
+ attr_reader :notes, :piano
11
+ def_delegators :notes, :each, :root
12
+
13
+ def initialize(note_set, piano:)
14
+ @notes = note_set
15
+ @piano = piano
16
+ end
17
+
18
+ instance_eval { alias [] new }
19
+ end
20
+ end
21
+ end
22
+ end
File without changes
File without changes
File without changes
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coltrane
4
+ # The main module for working with Music Theory
5
+ module Theory
6
+ require 'coltrane/theory/errors'
7
+ autoload :Frequency, 'coltrane/theory/frequency'
8
+
9
+ BASE_OCTAVE = 4
10
+ BASE_PITCH_INTEGER = 9
11
+
12
+ def self.tuning=(f)
13
+ @base_tuning = Frequency[f].octave(-4)
14
+ end
15
+
16
+ def self.base_tuning
17
+ @base_tuning
18
+ end
19
+
20
+ @base_tuning = Frequency[440].octave(-4)
21
+
22
+ autoload :Pitch, 'coltrane/theory/pitch'
23
+ autoload :Voicing, 'coltrane/theory/voicing'
24
+
25
+ autoload :PitchClass, 'coltrane/theory/pitch_class'
26
+ autoload :Note, 'coltrane/theory/note'
27
+ autoload :NoteSet, 'coltrane/theory/note_set'
28
+
29
+ autoload :FrequencyInterval, 'coltrane/theory/frequency_interval'
30
+ autoload :IntervalClass, 'coltrane/theory/interval_class'
31
+ autoload :Interval, 'coltrane/theory/interval'
32
+ autoload :UnorderedIntervalClass, 'coltrane/theory/unordered_interval_class'
33
+ autoload :IntervalSequence, 'coltrane/theory/interval_sequence'
34
+ autoload :Qualities, 'coltrane/theory/qualities'
35
+ autoload :ChordQuality, 'coltrane/theory/chord_quality'
36
+ autoload :Chord, 'coltrane/theory/chord'
37
+ autoload :ChordSubstitutions, 'coltrane/theory/chord_substitutions'
38
+ autoload :RomanChord, 'coltrane/theory/roman_chord'
39
+
40
+ autoload :ClassicScales, 'coltrane/theory/classic_scales'
41
+ autoload :Scale, 'coltrane/theory/scale'
42
+ autoload :ScaleSet, 'coltrane/theory/scale_set'
43
+ autoload :CircleOfFifths, 'coltrane/theory/circle_of_fifths'
44
+ autoload :DiatonicScale, 'coltrane/theory/diatonic_scale'
45
+ autoload :Key, 'coltrane/theory/key'
46
+
47
+ autoload :NotableProgressions, 'coltrane/theory/notable_progressions'
48
+ autoload :Changes, 'coltrane/theory/changes'
49
+ autoload :Cadence, 'coltrane/theory/cadence'
50
+ autoload :ProgressionSet, 'coltrane/theory/progression_set'
51
+ autoload :Progression, 'coltrane/theory/progression'
52
+ autoload :Mode, 'coltrane/theory/mode'
53
+ end
54
+ end