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
@@ -1,155 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # It describes a sequence of intervals
5
- class IntervalSequence
6
- extend Forwardable
7
- attr_reader :intervals
8
-
9
- def_delegators :@intervals, :map, :each, :[], :size,
10
- :reduce, :delete, :reject!, :delete_if, :detect, :to_a
11
-
12
- def initialize(*intervals, notes: nil, relative_intervals: nil)
13
- if intervals.any?
14
- @intervals = if intervals.first.is_a?(Interval)
15
- intervals
16
- else
17
- intervals.map { |i| Interval[i] }
18
- end
19
-
20
- elsif notes
21
- notes = NoteSet[*notes] if notes.is_a?(Array)
22
- @intervals = intervals_from_notes(notes)
23
- elsif relative_intervals
24
- @relative_intervals = relative_intervals
25
- @intervals = intervals_from_relative_intervals(relative_intervals)
26
- else
27
- raise WrongKeywordsError,
28
- 'Provide: [notes:] || [intervals:] || [relative_intervals:]'
29
- end
30
- end
31
-
32
- Interval.all_including_compound_and_altered.each do |interval|
33
- # Creates methods such as major_third, returning it if it finds
34
- define_method(interval.full_name.underscore.to_s) { find(interval) }
35
- # Creates methods such as has_major_third?, returning a boolean
36
- define_method("has_#{interval.full_name.underscore}?") { has?(interval) }
37
- end
38
-
39
- Interval.distances_names.map(&:underscore).each_with_index do |distance, i|
40
- # Creates methods such as has_third?, returning a boolean
41
- define_method("has_#{distance}?") { !!find_by_distance(i + 1) }
42
- # Creates methods such third, returning any third it finds
43
- define_method(distance.to_s) { find_by_distance(i + 1) }
44
- # Creates methods such third!, returning thirds that arent aug or dim
45
- define_method("#{distance}!") { find_by_distance(i + 1, false) }
46
- end
47
-
48
- instance_eval { alias [] new }
49
-
50
- def relative_intervals
51
- intervals_semitones[1..-1].each_with_index.map do |n, i|
52
- if i.zero?
53
- n
54
- elsif i < intervals_semitones.size
55
- n - intervals_semitones[i]
56
- end
57
- end + [12 - intervals_semitones.last]
58
- end
59
-
60
- def names
61
- intervals.map(&:name)
62
- end
63
-
64
- def find(interval)
65
- interval.clone if detect { |i| interval == i }
66
- end
67
-
68
- def has?(interval)
69
- !!find(interval)
70
- end
71
-
72
- def find_by_distance(n, accept_altered = true)
73
- strategy = (accept_altered ? :as : :as!)
74
- map { |interval| interval.send(strategy, n) }
75
- .compact
76
- .sort_by { |i| i.alteration.abs }
77
- .first
78
- end
79
-
80
- alias interval_names names
81
-
82
- def all
83
- intervals
84
- end
85
-
86
- def [](x)
87
- intervals[x]
88
- end
89
-
90
- def shift(ammount)
91
- self.class.new(*intervals.map do |i|
92
- (i.semitones + ammount) % 12
93
- end)
94
- end
95
-
96
- def zero_it
97
- shift(-intervals.first.semitones)
98
- end
99
-
100
- def inversion(index)
101
- self.class.new(*intervals.rotate(index)).zero_it
102
- end
103
-
104
- def next_inversion
105
- inversion(index + 1)
106
- end
107
-
108
- def previous_inversion
109
- inversion(index - 1)
110
- end
111
-
112
- def inversions
113
- Array.new(intervals.length) { |i| inversion(i) }
114
- end
115
-
116
- def intervals_semitones
117
- map(&:semitones)
118
- end
119
-
120
- def names
121
- map(&:name)
122
- end
123
-
124
- def full_names
125
- map(&:full_name)
126
- end
127
-
128
- def notes_for(root_note)
129
- NoteSet[
130
- *intervals.reduce([]) do |memo, interval|
131
- memo + [root_note + interval]
132
- end
133
- ]
134
- end
135
-
136
- def &(other)
137
- case other
138
- when Array then intervals & other
139
- when IntervalSequence then intervals & other.semitones
140
- end
141
- end
142
-
143
- private
144
-
145
- def intervals_from_relative_intervals(relative_intervals)
146
- relative_intervals[0..-2].reduce([Interval[0]]) do |memo, d|
147
- memo + [memo.last + d]
148
- end
149
- end
150
-
151
- def intervals_from_notes(notes)
152
- notes.map { |n| notes.root - n }.sort_by(&:semitones)
153
- end
154
- end
155
- end
data/lib/coltrane/key.rb DELETED
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- class Key < DiatonicScale
5
- KEY_REGEX = /([A-G][#b]?)([mM]?)/
6
-
7
- def initialize(notation)
8
- _, note, s = *notation.match(KEY_REGEX)
9
- super(note, major: s != 'm')
10
- end
11
-
12
- def self.[](notation)
13
- new(notation)
14
- end
15
- end
16
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # This module takes care of adding to progressions knowledge that is more
5
- # based on common standards and practices.
6
- module NotableProgressions
7
- PROGRESSIONS = {
8
- 'Pop' => ['I-V-vi-IV', :major],
9
- 'Jazzy Pop' => ['IM7-V7-vi7-IVM7', :major],
10
- 'Jazz' => ['ii7-V7-I7', :major],
11
- 'Jazz Minor' => ['ii7-V7-i7', :major],
12
- 'Blues' => ['IM7-IV7-I7-V7-IV7-I7', :major],
13
- 'Jazz Blues' => ['I7-IV7-I7-V7-IV7-I7', :major],
14
- 'Fifties' => ['I-vi-IV-V', :major],
15
- 'Circle' => ['vi-ii-V-I', :major],
16
- 'Tune Up' => ['ii7-V7-IM7-i7-IV7-IVM7-VIIM7', :minor]
17
- }.freeze
18
-
19
- PROGRESSIONS.each do |name, values|
20
- notation, scale_name = values
21
- define_method name.underscore do |note|
22
- note = note.is_a?(Note) ? note : Note[note]
23
- scale = Scale.public_send(scale_name, note)
24
- new(notation, scale: scale)
25
- end
26
- end
27
- end
28
- end
data/lib/coltrane/note.rb DELETED
@@ -1,98 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # Notes are different ways of calling pitch classes. In the context of equal
5
- # tempered scales, they're more like a conceptual subject for
6
- # matters of convention than an actual thing.
7
- #
8
- # Take for example A# and Bb. Those are different notes. Nevertheless, in the
9
- # context of equal tempered scales they represent pretty much the same
10
- # frequency.
11
- #
12
- # The theory of notes have changed too much in the course of time, which
13
- # lead us with a lot of conventions and strategies when dealing with music.
14
- # That's what this class is for.
15
- class Note < PitchClass
16
- attr_reader :alteration
17
-
18
- ALTERATIONS = {
19
- 'b' => -1,
20
- '#' => 1
21
- }.freeze
22
-
23
- def initialize(arg)
24
- note_name = case arg
25
- when String then arg
26
- when PitchClass then arg.true_notation
27
- when Numeric, Frequency then PitchClass.new(arg).true_notation
28
- else raise(WrongArgumentsError, arg)
29
- end
30
-
31
- chars = note_name.chars
32
- letter = chars.shift
33
- raise InvalidNoteLetterError, arg unless ('A'..'G').cover?(letter)
34
- @alteration = chars.reduce(0) do |alt, symbol|
35
- raise InvalidNoteLetterError, arg unless ALTERATIONS.include?(symbol)
36
- alt + ALTERATIONS[symbol]
37
- end
38
- super((PitchClass[letter].integer + @alteration) % PitchClass.size)
39
- end
40
-
41
- def self.[](arg)
42
- new(arg)
43
- end
44
-
45
- def name
46
- "#{base_pitch_class}#{accidents}".gsub(/#b|b#/, '')
47
- end
48
-
49
- def base_pitch_class
50
- PitchClass[integer - alteration]
51
- end
52
-
53
- def alteration=(a)
54
- @alteration = a unless PitchClass[integer - a].accidental?
55
- end
56
-
57
- def alter(x)
58
- Note.new(name).tap { |n| n.alteration = x }
59
- end
60
-
61
- def sharp?
62
- alteration == 1
63
- end
64
-
65
- def double_sharp?
66
- alteration == 2
67
- end
68
-
69
- def flat?
70
- alteration == -1
71
- end
72
-
73
- def double_flat?
74
- alteration == -2
75
- end
76
-
77
- def natural?
78
- alteration == 0
79
- end
80
-
81
- def accidents
82
- (@alteration > 0 ? '#' : 'b') * alteration.abs
83
- end
84
-
85
- def -(other)
86
- super(other).yield_self { |r| r.is_a?(Note) ? r.alter(alteration) : r }
87
- end
88
-
89
- def +(other)
90
- super(other).yield_self { |r| r.is_a?(Note) ? r.alter(alteration) : r }
91
- end
92
-
93
- def as(letter)
94
- a = (Note[letter] - self)
95
- alter([a.semitones, -(-a).semitones].min_by(&:abs))
96
- end
97
- end
98
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # It describes a set of notes
5
- class NoteSet
6
- extend Forwardable
7
-
8
- def_delegators :@notes, :first, :each, :size, :map, :reduce, :index,
9
- :[], :index, :empty?, :permutation, :include?, :<<, :any?,
10
- :count, :rotate
11
-
12
- attr_reader :notes
13
-
14
- alias root first
15
- alias all notes
16
-
17
- def self.[](*notes)
18
- new(notes)
19
- end
20
-
21
- def initialize(arg)
22
- @notes =
23
- case arg
24
- when NoteSet then arg.notes
25
- when Array then arg.compact.map { |n| n.is_a?(PitchClass) ? n : Note[n] }.uniq
26
- else raise InvalidNotesError, arg
27
- end
28
- end
29
-
30
- def &(other)
31
- NoteSet[*(notes & other.notes)]
32
- end
33
-
34
- def degree(note)
35
- index(note) + 1
36
- end
37
-
38
- def +(other)
39
- case other
40
- when Note then NoteSet[*(notes + [other])]
41
- when NoteSet then NoteSet[*notes, *other.notes]
42
- when Interval then NoteSet[*notes.map { |n| n + other }]
43
- end
44
- end
45
-
46
- def -(other)
47
- case other
48
- when NoteSet then NoteSet[*(notes - other.notes)]
49
- when Interval then NoteSet[*notes.map { |n| n - other }]
50
- end
51
- end
52
-
53
- def pretty_names
54
- map(&:pretty_name)
55
- end
56
-
57
- def names
58
- map(&:name)
59
- end
60
-
61
- def integers
62
- map(&:integer)
63
- end
64
-
65
- def accidentals
66
- count(&:accidental?)
67
- end
68
-
69
- def sharps
70
- count(&:sharp?)
71
- end
72
-
73
- def flats
74
- count(&:flat?)
75
- end
76
-
77
- def alter(alteration)
78
- NoteSet[*map { |n| n.alter(alteration) }]
79
- end
80
-
81
- def alter_accidentals(alteration)
82
- NoteSet[*map { |n| n.alter(alteration) if n.accidental? }]
83
- end
84
-
85
- def interval_sequence
86
- IntervalSequence.new(notes: self)
87
- end
88
- end
89
- end
@@ -1,92 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # It describes a pitch, like E4 or Bb5. It's like a note, but it has an octave
5
- class Pitch
6
- attr_reader :integer
7
-
8
- def initialize(notation_arg = nil,
9
- note: nil,
10
- octave: nil,
11
- notation: nil,
12
- frequency: nil)
13
-
14
- @integer = begin
15
- if (n = notation_arg || notation)
16
- n.is_a?(Integer) ? n : integer_from_notation(n)
17
- elsif note && octave
18
- integer_from_note_and_octave(Note[note], octave)
19
- elsif frequency
20
- integer_from_frequency(frequency)
21
- else
22
- raise(InvalidArgumentsError)
23
- end
24
- end
25
- end
26
-
27
- def self.[](*args)
28
- new *args
29
- end
30
-
31
- def scientific_notation
32
- "#{pitch_class}#{octave}"
33
- end
34
-
35
- def pitch_class
36
- PitchClass[integer]
37
- end
38
-
39
- def octave
40
- (integer / 12) - 1
41
- end
42
-
43
- alias notation scientific_notation
44
- alias name scientific_notation
45
- alias to_s scientific_notation
46
-
47
- alias hash integer
48
- alias midi integer
49
-
50
- def ==(other)
51
- integer == other.integer
52
- end
53
-
54
- def frequency
55
- pitch_class.frequency.octave(octave)
56
- end
57
-
58
- alias eql? ==
59
- alias eq ==
60
-
61
- def +(other)
62
- case other
63
- when Integer then Pitch[integer + other]
64
- end
65
- end
66
-
67
- def -(other)
68
- case other
69
- when Integer then Pitch[integer - other]
70
- end
71
- end
72
-
73
- private
74
-
75
- def integer_from_notation(the_notation)
76
- _, n, o = *the_notation.match(/(\D+)(\d+)/)
77
- integer_from_note_and_octave(Note[n], o)
78
- end
79
-
80
- def integer_from_note_and_octave(p, o)
81
- 12 * (o.to_i + 1) + p.integer
82
- end
83
-
84
- def integer_from_frequency(f)
85
- octave_from_frequency(f) * 12 + PitchClass.new(frequency: f).integer
86
- end
87
-
88
- def octave_from_frequency(f)
89
- Math.log(f.to_f / PitchClass['C'].frequency.to_f, 2).ceil
90
- end
91
- end
92
- end