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
@@ -1,148 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- #
5
- # Pitch classes, and by classes here we don't mean in the sense of a ruby class
6
- # are all the classes of pitches (frequencies) that are in a whole number of
7
- # octaves apart.
8
- #
9
- # For example, C1, C2, C3 are all pitches from the C pitch class. Take a look into
10
- # Notes description if you somehow feel this is confuse and that it could just be
11
- # called as notes instead.
12
- #
13
- class PitchClass
14
- attr_reader :integer
15
- include Comparable
16
-
17
- NOTATION = %w[C C# D D# E F F# G G# A A# B].freeze
18
-
19
- def self.all_letters
20
- %w[C D E F G A B]
21
- end
22
-
23
- def self.all
24
- NOTATION.map { |n| new(n) }
25
- end
26
-
27
- def initialize(arg = nil, frequency: nil)
28
- @integer = case arg
29
- when String then NOTATION.index(arg)
30
- when Frequency then frequency_to_integer(Frequency.new(arg))
31
- when Numeric then (arg % 12)
32
- when nil then frequency_to_integer(Frequency.new(frequency))
33
- when PitchClass then arg.integer
34
- else raise(WrongArgumentsError)
35
- end
36
- end
37
-
38
- def self.[](arg, frequency: nil)
39
- new(arg, frequency: nil)
40
- end
41
-
42
- def ==(other)
43
- integer == other.integer
44
- end
45
-
46
- alias eql? ==
47
- alias hash integer
48
-
49
- def true_notation
50
- NOTATION[integer]
51
- end
52
-
53
- def letter
54
- name[0]
55
- end
56
-
57
- def ascending_interval_to(other)
58
- Interval.new(self, (other.is_a?(PitchClass) ? other : Note.new(other)))
59
- end
60
-
61
- alias interval_to ascending_interval_to
62
-
63
- def descending_interval_to(other)
64
- Interval.new(
65
- self,
66
- (other.is_a?(PitchClass) ? other : Note.new(other)),
67
- ascending: false
68
- )
69
- end
70
-
71
- alias name true_notation
72
-
73
- def pretty_name
74
- name.tr('b', "\u266D").tr('#', "\u266F")
75
- end
76
-
77
- def accidental?
78
- notation.match? /#|b/
79
- end
80
-
81
- def sharp?
82
- notation.match? /#/
83
- end
84
-
85
- def flat?
86
- notation.match? /b/
87
- end
88
-
89
- alias notation true_notation
90
- alias to_s true_notation
91
-
92
- def +(other)
93
- case other
94
- when Interval then self.class[integer + other.semitones]
95
- when Integer then self.class[integer + other]
96
- when PitchClass then self.class[integer + other.integer]
97
- when Frequency then self.class.new(frequency: frequency + other)
98
- end
99
- end
100
-
101
- def -(other)
102
- case other
103
- when Interval then self.class[integer - other.semitones]
104
- when Integer then self.class[integer - other]
105
- when PitchClass then Interval.new(self, other)
106
- when Frequency then self.class.new(frequency: frequency - other)
107
- end
108
- end
109
-
110
- def <=>(other)
111
- integer <=> other.integer
112
- end
113
-
114
- def fundamental_frequency
115
- @fundamental_frequency ||=
116
- Frequency[
117
- Coltrane.base_tuning *
118
- (2**((integer - Coltrane::BASE_PITCH_INTEGER.to_f) / 12))
119
- ]
120
- end
121
-
122
- alias frequency fundamental_frequency
123
-
124
- def self.size
125
- NOTATION.size
126
- end
127
-
128
- def size
129
- self.class.size
130
- end
131
-
132
- def enharmonic?(other)
133
- case other
134
- when String then integer == Note[other].integer
135
- when Note then integer == other.integer
136
- end
137
- end
138
-
139
- private
140
-
141
- def frequency_to_integer(f)
142
- begin
143
- (Coltrane::BASE_PITCH_INTEGER +
144
- size * Math.log(f.to_f / Coltrane.base_tuning.to_f, 2)) % size
145
- end.round
146
- end
147
- end
148
- end
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # Allows the creation of chord progressions using standard notations.
5
- # Ex: Progression.new('I-IV-V', key: 'Am')
6
- class Progression
7
- extend NotableProgressions
8
- include Changes
9
- attr_reader :scale, :chords, :notation
10
-
11
- def self.find(*chords)
12
- chords.map! { |c| Chord.new(name: c) } if chords[0].is_a?(String)
13
-
14
- root_notes = NoteSet[*chords.map(&:root_note)]
15
-
16
- scales = Scale.having_notes(*root_notes)[:scales]
17
- progressions = scales.reduce([]) do |memo, scale|
18
- memo + [Progression.new(chords: chords, scale: scale)]
19
- end
20
-
21
- progressions.sort_by(&:notes_out_size)
22
- end
23
-
24
- def initialize(notation = nil, chords: nil, roman_chords: nil, key: nil, scale: nil)
25
- if notation.nil? && chords.nil? && roman_chords.nil? || key.nil? && scale.nil?
26
- raise WrongKeywordsError,
27
- '[chords:, [scale: || key:]] '\
28
- '[roman_chords:, [scale: || key:]] '\
29
- '[notation:, [scale: || key:]] '\
30
- end
31
-
32
- @scale = scale || Key[key]
33
- @chords =
34
- if !chords.nil?
35
- chords
36
- elsif !roman_chords.nil?
37
- roman_chords.map(&:chord)
38
- elsif !notation.nil?
39
- @notation = notation
40
- notation.split('-').map { |c| RomanChord.new(c, scale: @scale).chord }
41
- end
42
- end
43
-
44
- def interval_sequence
45
- @interval_sequence ||= IntervalSequence(notes: @root_notes)
46
- end
47
-
48
- def root_notes
49
- @root_notes ||= @chords.map(&:root_note)
50
- end
51
-
52
- def roman_chords
53
- @roman_chords ||= chords.map do |c|
54
- RomanChord.new(chord: c, scale: scale)
55
- end
56
- end
57
-
58
- def notation
59
- roman_chords.map(&:notation).join('-')
60
- end
61
-
62
- def notes
63
- NoteSet[*chords.map(&:notes).map(&:notes).flatten]
64
- end
65
-
66
- def notes_out
67
- notes - scale.notes
68
- end
69
-
70
- def notes_out_size
71
- notes_out.size
72
- end
73
- end
74
- end
@@ -1,112 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # This class deals with chords in roman notation. Ex: IVº.
5
- class RomanChord
6
- DIGITS = %w[I II III IV V VI VII].freeze
7
- NOTATION_REGEX = /(b?[ivIV]*)(.*)/
8
-
9
- NOTATION_SUBSTITUTIONS = [
10
- %w[º dim],
11
- %w[o dim],
12
- %w[ø m7b5]
13
- ].freeze
14
-
15
- def initialize(notation = nil, chord: nil, key: nil, scale: nil)
16
- if notation.nil? && chord.nil? || key.nil? && scale.nil?
17
- raise WrongKeywordsError,
18
- '[notation, [scale: || key:]] '\
19
- '[chord:, [scale: || key:]] '\
20
- end
21
- @scale = scale || Key[key]
22
- if notation
23
- @notation = notation
24
- elsif chord
25
- @chord = chord.is_a?(String) ? Chord.new(name: chord) : chord
26
- end
27
- end
28
-
29
- def degree
30
- return @scale.degree_of_note(root_note) unless @chord.nil?
31
- d = regexed_notation[:degree]
32
- @flats = d.count('b')
33
- d = d.delete('b')
34
- @degree ||= DIGITS.index(d.upcase) + 1
35
- end
36
-
37
- def roman_numeral
38
- r = DIGITS[degree]
39
- minor? ? r.downcase : r
40
- end
41
-
42
- def upcase?
43
- !!(regexed_notation[:degree][0].match /[[:upper:]]/)
44
- end
45
-
46
- def chord
47
- @chord ||= Chord.new root_note: root_note,
48
- quality: quality
49
- end
50
-
51
- def minor?
52
- quality.has_minor_third?
53
- end
54
-
55
- def major?
56
- quality.has_major_third?
57
- end
58
-
59
- def quality_name
60
- return @chord.quality.name unless @chord.nil?
61
- q = normalize_quality_name(regexed_notation[:quality])
62
- minor = 'm' if (!q.match? /dim|m7b5/) && !upcase?
63
- q = [minor, q].join
64
- q.empty? ? 'M' : q
65
- end
66
-
67
- def quality
68
- return @chord.quality unless @chord.nil?
69
- ChordQuality.new(name: quality_name) if quality_name
70
- end
71
-
72
- def root_note
73
- return @chord.root_note unless @chord.nil?
74
- @scale[degree] - @flats
75
- end
76
-
77
- def notation
78
- q = case quality_name
79
- when 'm', 'M' then ''
80
- when 'm7', 'M' then '7'
81
- else quality_name
82
- end
83
-
84
- @notation ||= [
85
- roman_numeral,
86
- q
87
- ].join
88
- end
89
-
90
- def function
91
- return if @scale.name != 'Major'
92
- %w[Tonic Supertonic Mediant Subdominant
93
- Dominant Submediant Leading][degree - 1]
94
- end
95
-
96
- private
97
-
98
- def regexed_notation
99
- @regexed_notation ||= begin
100
- matchdata = @notation.match(NOTATION_REGEX)
101
- { degree: matchdata[1], quality: matchdata[2] }
102
- end
103
- end
104
-
105
- def normalize_quality_name(quality_name)
106
- NOTATION_SUBSTITUTIONS.reduce(quality_name) do |memo, subs|
107
- break memo if memo.empty?
108
- memo.gsub(*subs)
109
- end
110
- end
111
- end
112
- end
@@ -1,154 +0,0 @@
1
- # frozen_string_literal: true
2
- # frozen_string_literal: true
3
-
4
- module Coltrane
5
- # Musical scale creation and manipulation
6
- class Scale
7
- extend ClassicScales
8
- extend Forwardable
9
-
10
- def_delegators :notes, :accidentals, :sharps, :flats
11
-
12
- attr_reader :interval_sequence, :tone
13
-
14
- def initialize(*relative_intervals, tone: 'C',
15
- mode: 1,
16
- name: nil,
17
- notes: nil)
18
- @name = name
19
- if relative_intervals.any? && tone
20
- @tone = Note[tone]
21
- relative_intervals = relative_intervals.rotate(mode - 1)
22
- @interval_sequence = IntervalSequence.new(
23
- relative_intervals: relative_intervals
24
- )
25
- elsif notes
26
- @notes = NoteSet[*notes]
27
- @tone = @notes.first
28
- ds = @notes.interval_sequence.relative_intervals
29
- @interval_sequence = IntervalSequence.new(relative_intervals: ds)
30
- else
31
- raise WrongKeywordsError,
32
- '[*relative_intervals, tone: "C", mode: 1] || [notes:]'
33
- end
34
- end
35
-
36
- def id
37
- [(name || @interval_sequence), tone.number]
38
- end
39
-
40
- def name
41
- @name = begin
42
- is = interval_sequence.relative_intervals
43
- (0...is.size).each do |i|
44
- if (scale_name = Coltrane::ClassicScales::SCALES.key(is.rotate(i)))
45
- return scale_name
46
- end
47
- end
48
- nil
49
- end
50
- end
51
-
52
- def to_s
53
- "#{tone} #{name}"
54
- end
55
-
56
- def pretty_name
57
- "#{tone.name} #{name}"
58
- end
59
-
60
- alias full_name pretty_name
61
-
62
- def degree(d)
63
- raise WrongDegreeError, d if d < 1 || d > size
64
- tone + interval_sequence[d - 1].semitones
65
- end
66
-
67
- alias [] degree
68
-
69
- def degrees
70
- (1..size)
71
- end
72
-
73
- def degree_of_chord(chord)
74
- return if chords(chord.size).map(&:name).include?(chord.name)
75
- degree_of_note(chord.root_note)
76
- end
77
-
78
- def degree_of_note(note)
79
- notes.index(note)
80
- end
81
-
82
- def &(other)
83
- raise HasNoNotesError unless other.respond_to?(:notes)
84
- notes & other
85
- end
86
-
87
- def include_notes?(arg)
88
- noteset = arg.is_a?(Note) ? NoteSet[arg] : arg
89
- (self & noteset).size == noteset.size
90
- end
91
-
92
- alias include? include_notes?
93
-
94
- def notes
95
- @notes ||= NoteSet[*degrees.map { |d| degree(d) }]
96
- end
97
-
98
- def interval(i)
99
- interval_sequence[(i - 1) % size]
100
- end
101
-
102
- def size
103
- interval_sequence.size
104
- end
105
-
106
- def tertians(n = 3)
107
- degrees.size.times.reduce([]) do |memo, d|
108
- ns = NoteSet[ *Array.new(n) { |i| notes[(d + (i * 2)) % size] } ]
109
- begin
110
- chord = Chord.new(notes: ns)
111
- rescue ChordNotFoundError
112
- memo
113
- else
114
- memo + [chord]
115
- end
116
- end
117
- end
118
-
119
- def triads
120
- tertians(3)
121
- end
122
-
123
- def sevenths
124
- tertians(4)
125
- end
126
-
127
- def pentads
128
- tertians(5)
129
- end
130
-
131
- def progression(*degrees)
132
- Progression.new(self, degrees)
133
- end
134
-
135
- def chords(size = 3..12)
136
- size = (size..size) if size.is_a?(Integer)
137
- scale_rotations = interval_sequence.inversions
138
- ChordQuality.intervals_per_name.reduce([]) do |memo1, (qname, qintervals)|
139
- next memo1 unless size.include?(qintervals.size)
140
- memo1 + scale_rotations.each_with_index
141
- .reduce([]) do |memo2, (rot, index)|
142
- if (rot & qintervals).size == qintervals.size
143
- memo2 + [Chord.new(root_note: degree(index + 1),
144
- quality: ChordQuality.new(name: qname))]
145
- else
146
- memo2
147
- end
148
- end
149
- end
150
- end
151
-
152
- alias all_chords chords
153
- end
154
- end