coltrane 2.2.1 → 3.0.0.pre

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 (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