coltrane 2.0.0 → 2.1.0

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -1
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +21 -3
  5. data/README.md +0 -7
  6. data/Rakefile +5 -0
  7. data/bin/console +2 -0
  8. data/bin/opal +29 -0
  9. data/bin/opal-build +29 -0
  10. data/bin/opal-mspec +29 -0
  11. data/bin/opal-repl +29 -0
  12. data/bin/rackup +12 -0
  13. data/bin/sprockets +12 -0
  14. data/bin/tilt +12 -0
  15. data/coltrane.gemspec +1 -1
  16. data/config.ru +10 -0
  17. data/exe/coltrane +44 -13
  18. data/lib/cli.rb +7 -1
  19. data/lib/cli/bass_guitar.rb +1 -1
  20. data/lib/cli/chord.rb +11 -4
  21. data/lib/cli/config.rb +38 -0
  22. data/lib/cli/guitar.rb +8 -10
  23. data/lib/cli/guitar_chords.rb +122 -0
  24. data/lib/cli/notes.rb +3 -4
  25. data/lib/cli/piano.rb +1 -1
  26. data/lib/cli/representation.rb +6 -12
  27. data/lib/cli/scale.rb +4 -4
  28. data/lib/cli/text.rb +1 -1
  29. data/lib/cli/ukulele.rb +1 -1
  30. data/lib/coltrane.rb +35 -32
  31. data/lib/coltrane/chord_quality.rb +4 -3
  32. data/lib/coltrane/circle_of_fifths.rb +29 -0
  33. data/lib/coltrane/classic_scales.rb +36 -43
  34. data/lib/coltrane/diatonic_scale.rb +34 -0
  35. data/lib/coltrane/frequency.rb +1 -1
  36. data/lib/coltrane/interval.rb +14 -0
  37. data/lib/coltrane/interval_class.rb +6 -0
  38. data/lib/coltrane/interval_sequence.rb +0 -1
  39. data/lib/coltrane/key.rb +14 -0
  40. data/lib/coltrane/note.rb +37 -2
  41. data/lib/coltrane/note_set.rb +23 -2
  42. data/lib/coltrane/pitch.rb +65 -31
  43. data/lib/coltrane/pitch_class.rb +25 -12
  44. data/lib/coltrane/progression.rb +2 -2
  45. data/lib/coltrane/qualities.rb +257 -0
  46. data/lib/coltrane/roman_chord.rb +10 -10
  47. data/lib/coltrane/scale.rb +9 -3
  48. data/lib/coltrane/version.rb +1 -1
  49. data/lib/coltrane/voicing.rb +38 -0
  50. data/lib/coltrane_game/question.rb +6 -0
  51. data/lib/coltrane_instruments.rb +3 -0
  52. data/lib/coltrane_instruments/guitar.rb +5 -2
  53. data/lib/coltrane_instruments/guitar/base.rb +18 -3
  54. data/lib/coltrane_instruments/guitar/chord.rb +139 -12
  55. data/lib/coltrane_instruments/guitar/note.rb +16 -0
  56. data/lib/coltrane_instruments/guitar/string.rb +21 -0
  57. data/lib/coltrane_synth.rb +7 -0
  58. data/lib/coltrane_synth/base.rb +47 -0
  59. data/lib/coltrane_synth/synth.rb +24 -0
  60. data/lib/core_ext.rb +25 -7
  61. data/lib/os.rb +19 -0
  62. metadata +24 -9
  63. data/dist/coltrane +0 -0
  64. data/lib/coltrane/piano_representation.rb +0 -0
@@ -12,6 +12,7 @@ module Coltrane
12
12
  #
13
13
  class PitchClass
14
14
  attr_reader :integer
15
+ include Comparable
15
16
 
16
17
  NOTATION = %w[C C# D D# E F F# G G# A A# B].freeze
17
18
 
@@ -19,14 +20,14 @@ module Coltrane
19
20
  NOTATION.map { |n| new(n) }
20
21
  end
21
22
 
22
- def initialize(arg, frequency: nil)
23
+ def initialize(arg=nil, frequency: nil)
23
24
  @integer = case arg
24
25
  when String then NOTATION.index(arg)
25
- when Frequency, Float then frequency_to_integer(Frequency.new(arg))
26
- when Integer then (arg % 12)
27
- when NilClass then frequency_to_integer(Frequency.new(frequency))
26
+ when Frequency then frequency_to_integer(Frequency.new(arg))
27
+ when Numeric then (arg % 12)
28
+ when nil then frequency_to_integer(Frequency.new(frequency))
28
29
  when PitchClass then arg.integer
29
- else raise(InvalidArgumentError)
30
+ else raise(WrongArgumentsError)
30
31
  end
31
32
  end
32
33
 
@@ -55,27 +56,39 @@ module Coltrane
55
56
  notation.match? /#|b/
56
57
  end
57
58
 
59
+ def sharp?
60
+ notation.match? /#/
61
+ end
62
+
63
+ def flat?
64
+ notation.match? /b/
65
+ end
66
+
58
67
  alias notation true_notation
59
68
  alias to_s true_notation
60
69
 
61
70
  def +(other)
62
71
  case other
63
- when Interval then PitchClass[integer + other.semitones]
64
- when Integer then PitchClass[integer + other]
65
- when PitchClass then Note.new(integer + other.integer)
66
- when Frequency then PitchClass.new(frequency: frequency + other)
72
+ when Interval then self.class[integer + other.semitones]
73
+ when Integer then self.class[integer + other]
74
+ when PitchClass then self.class[integer + other.integer]
75
+ when Frequency then self.class.new(frequency: frequency + other)
67
76
  end
68
77
  end
69
78
 
70
79
  def -(other)
71
80
  case other
72
- when Interval then PitchClass[integer - other.semitones]
73
- when Integer then PitchClass[integer - other]
81
+ when Interval then self.class[integer - other.semitones]
82
+ when Integer then self.class[integer - other]
74
83
  when PitchClass then IntervalClass[frequency / other.frequency]
75
- when Frequency then PitchClass.new(frequency: frequency - other)
84
+ when Frequency then self.class.new(frequency: frequency - other)
76
85
  end
77
86
  end
78
87
 
88
+ def <=>(other)
89
+ integer <=> other.integer
90
+ end
91
+
79
92
  def fundamental_frequency
80
93
  @fundamental_frequency ||=
81
94
  Frequency[
@@ -13,7 +13,7 @@ module Coltrane
13
13
 
14
14
  root_notes = NoteSet[*chords.map(&:root_note)]
15
15
 
16
- scales = Scale.having_notes(*root_notes).scales
16
+ scales = Scale.having_notes(*root_notes)[:scales]
17
17
  progressions = scales.reduce([]) do |memo, scale|
18
18
  memo + [Progression.new(chords: chords, scale: scale)]
19
19
  end
@@ -29,7 +29,7 @@ module Coltrane
29
29
  '[notation:, [scale: || key:]] '\
30
30
  end
31
31
 
32
- @scale = scale || Scale.from_key(key)
32
+ @scale = scale || Key[key]
33
33
  @chords =
34
34
  if !chords.nil?
35
35
  chords
@@ -0,0 +1,257 @@
1
+ module Qualities
2
+ QUALITIES = {
3
+ "Perfect Unison" => {
4
+ "Minor Third" => {
5
+ "Diminished Fifth" => {
6
+ "name" => "dim",
7
+ "Diminished Seventh" => {
8
+ "name" => "dim7",
9
+ "Minor Ninth" => {
10
+ "name" => "dim9"
11
+ },
12
+ "Major Ninth" => {
13
+ "name" => "dim(b9)"
14
+ }
15
+ },
16
+ "Minor Seventh" => {
17
+ "name" => "m7b5",
18
+ "Minor Ninth" => {
19
+ "name" => "m7b5b9",
20
+ "Perfect Eleventh" => {
21
+ "name" => "m7b5b11",
22
+ "Major Thirteenth" => {
23
+ "name" => "m7b5b13"
24
+ }
25
+ }
26
+ },
27
+ "Major Ninth" => {
28
+ "name" => "m7b5(9)"
29
+ }
30
+ }
31
+ },
32
+ "Perfect Fifth" => {
33
+ "name" => "m",
34
+ "Major Sixth" => {
35
+ "name" => "m6"
36
+ },
37
+ "Minor Seventh" => {
38
+ "name" => "m7",
39
+ "Minor Ninth" => {
40
+ "name" => "m9",
41
+ "Perfect Eleventh" => {
42
+ "name" => "m11"
43
+ }
44
+ }
45
+ },
46
+ "Major Seventh" => {
47
+ "name" => "m(M7)",
48
+ "Major Ninth" => {
49
+ "name" => "m(M9)",
50
+ "Perfect Eleventh" => {
51
+ "name" => "m(M11)",
52
+ "Major Thirteenth" => {
53
+ "name" => "m(M13)"
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ },
60
+ "Major Third" => {
61
+ "Perfect Fifth" => {
62
+ "name" => "M",
63
+ "Major Sixth" => {
64
+ "name" => "M6",
65
+ "Major Ninth" => {
66
+ "name" => "6/9",
67
+ "Perfect Eleventh" => {
68
+ "name" => "6/9(add11)"
69
+ }
70
+ }
71
+ },
72
+ "Minor Seventh" => {
73
+ "name" => "7",
74
+ "Major Ninth" => {
75
+ "name" => "9",
76
+ "Perfect Eleventh" => {
77
+ "name" => "11",
78
+ "Major Thirteenth" => {
79
+ "name" => "13"
80
+ }
81
+ }
82
+ }
83
+ },
84
+ "Major Seventh" => {
85
+ "name" => "M7",
86
+ "Major Ninth" => {
87
+ "name" => "M9",
88
+ "Perfect Eleventh" => {
89
+ "name" => "M11",
90
+ "Major Thirteenth" => {
91
+ "name" => "M13"
92
+ }
93
+ }
94
+ }
95
+ }
96
+ },
97
+ "Augmented Fifth" => {
98
+ "name" => "+",
99
+ "Minor Seventh" => {
100
+ "name" => "+7",
101
+ "Major Ninth" => {
102
+ "name" => "+9",
103
+ "Perfect Eleventh" => {
104
+ "name" => "+11",
105
+ "Major Thirteenth" => {
106
+ "name" => "+13"
107
+ }
108
+ }
109
+ }
110
+ },
111
+ "Major Seventh" => {
112
+ "name" => "+M7",
113
+ "Major Ninth" => {
114
+ "name" => "+M9",
115
+ "Perfect Eleventh" => {
116
+ "name" => "+M11",
117
+ "Major Thirteenth" => {
118
+ "name" => "+M13"
119
+ }
120
+ }
121
+ }
122
+ }
123
+ }
124
+ },
125
+ "Major Second" => {
126
+ "Perfect Fifth" => {
127
+ "name" => "Msus2",
128
+ "Major Sixth" => {
129
+ "name" => "M6sus2",
130
+ "Major Ninth" => {
131
+ "name" => "6/9sus2",
132
+ "Perfect Eleventh" => {
133
+ "name" => "6/9(add11)sus2"
134
+ }
135
+ }
136
+ },
137
+ "Minor Seventh" => {
138
+ "name" => "7sus2",
139
+ "Major Ninth" => {
140
+ "name" => "9sus2",
141
+ "Perfect Eleventh" => {
142
+ "name" => "11sus2",
143
+ "Major Thirteenth" => {
144
+ "name" => "13sus2"
145
+ }
146
+ }
147
+ }
148
+ },
149
+ "Major Seventh" => {
150
+ "name" => "M7sus2",
151
+ "Major Ninth" => {
152
+ "name" => "M9sus2",
153
+ "Perfect Eleventh" => {
154
+ "name" => "M11sus2",
155
+ "Major Thirteenth" => {
156
+ "name" => "M13sus2"
157
+ }
158
+ }
159
+ }
160
+ }
161
+ },
162
+ "Augmented Fifth" => {
163
+ "name" => "+sus2",
164
+ "Minor Seventh" => {
165
+ "name" => "+7sus2",
166
+ "Major Ninth" => {
167
+ "name" => "+9sus2",
168
+ "Perfect Eleventh" => {
169
+ "name" => "+11sus2",
170
+ "Major Thirteenth" => {
171
+ "name" => "+13sus2"
172
+ }
173
+ }
174
+ }
175
+ },
176
+ "Major Seventh" => {
177
+ "name" => "+M7sus2",
178
+ "Major Ninth" => {
179
+ "name" => "+M9sus2",
180
+ "Perfect Eleventh" => {
181
+ "name" => "+M11sus2",
182
+ "Major Thirteenth" => {
183
+ "name" => "+M13sus2"
184
+ }
185
+ }
186
+ }
187
+ }
188
+ }
189
+ },
190
+ "Perfect Fourth" => {
191
+ "Perfect Fifth" => {
192
+ "name" => "Msus4",
193
+ "Major Sixth" => {
194
+ "name" => "M6sus4",
195
+ "Major Ninth" => {
196
+ "name" => "6/9sus4",
197
+ "Perfect Eleventh" => {
198
+ "name" => "6/9(add11)sus4"
199
+ }
200
+ }
201
+ },
202
+ "Minor Seventh" => {
203
+ "name" => "7sus4",
204
+ "Major Ninth" => {
205
+ "name" => "9sus4",
206
+ "Perfect Eleventh" => {
207
+ "name" => "11sus4",
208
+ "Major Thirteenth" => {
209
+ "name" => "13sus4"
210
+ }
211
+ }
212
+ }
213
+ },
214
+ "Major Seventh" => {
215
+ "name" => "M7sus4",
216
+ "Major Ninth" => {
217
+ "name" => "M9sus4",
218
+ "Perfect Eleventh" => {
219
+ "name" => "M11sus4",
220
+ "Major Thirteenth" => {
221
+ "name" => "M13sus4"
222
+ }
223
+ }
224
+ }
225
+ }
226
+ },
227
+ "Augmented Fifth" => {
228
+ "name" => "+sus4",
229
+ "Minor Seventh" => {
230
+ "name" => "+7sus4",
231
+ "Major Ninth" => {
232
+ "name" => "+9sus4",
233
+ "Perfect Eleventh" => {
234
+ "name" => "+11sus4",
235
+ "Major Thirteenth" => {
236
+ "name" => "+13sus4"
237
+ }
238
+ }
239
+ }
240
+ },
241
+ "Major Seventh" => {
242
+ "name" => "+M7sus4",
243
+ "Major Ninth" => {
244
+ "name" => "+M9sus4",
245
+ "Perfect Eleventh" => {
246
+ "name" => "+M11sus4",
247
+ "Major Thirteenth" => {
248
+ "name" => "+M13sus4"
249
+ }
250
+ }
251
+ }
252
+ }
253
+ }
254
+ }
255
+ }
256
+ }
257
+ end
@@ -4,10 +4,7 @@ module Coltrane
4
4
  # This class deals with chords in roman notation. Ex: IVº.
5
5
  class RomanChord
6
6
  DIGITS = %w[I II III IV V VI VII].freeze
7
- NOTATION_REGEX = /
8
- (?<degree>b?[ivIV]*)
9
- (?<quality>.*)
10
- /x
7
+ NOTATION_REGEX = /(b?[ivIV]*)(.*)/
11
8
 
12
9
  NOTATION_SUBSTITUTIONS = [
13
10
  %w[º dim],
@@ -21,7 +18,7 @@ module Coltrane
21
18
  '[notation, [scale: || key:]] '\
22
19
  '[chord:, [scale: || key:]] '\
23
20
  end
24
- @scale = scale || Scale.from_key(key)
21
+ @scale = scale || Key[key]
25
22
  if notation
26
23
  @notation = notation
27
24
  elsif chord
@@ -31,7 +28,7 @@ module Coltrane
31
28
 
32
29
  def degree
33
30
  return @scale.degree_of_note(root_note) unless @chord.nil?
34
- d = regexed_notation['degree']
31
+ d = regexed_notation[:degree]
35
32
  @flats = d.count('b')
36
33
  d = d.delete('b')
37
34
  @degree ||= DIGITS.index(d.upcase) + 1
@@ -43,7 +40,7 @@ module Coltrane
43
40
  end
44
41
 
45
42
  def upcase?
46
- !!(regexed_notation['degree'][0].match /[[:upper:]]/)
43
+ !!(regexed_notation[:degree][0].match /[[:upper:]]/)
47
44
  end
48
45
 
49
46
  def chord
@@ -61,8 +58,8 @@ module Coltrane
61
58
 
62
59
  def quality_name
63
60
  return @chord.quality.name unless @chord.nil?
64
- q = normalize_quality_name(regexed_notation['quality'])
65
- minor = 'm' if !q.match?(/dim|m7b5/) && !upcase?
61
+ q = normalize_quality_name(regexed_notation[:quality])
62
+ minor = 'm' if (!q.match? /dim|m7b5/) && !upcase?
66
63
  q = [minor, q].join
67
64
  q.empty? ? 'M' : q
68
65
  end
@@ -99,7 +96,10 @@ module Coltrane
99
96
  private
100
97
 
101
98
  def regexed_notation
102
- @regexed_notation ||= @notation.match(NOTATION_REGEX).named_captures
99
+ @regexed_notation ||= begin
100
+ matchdata = @notation.match(NOTATION_REGEX)
101
+ { degree: matchdata[1], quality: matchdata[2] }
102
+ end
103
103
  end
104
104
 
105
105
  def normalize_quality_name(quality_name)
@@ -4,6 +4,10 @@ module Coltrane
4
4
  # Musical scale creation and manipulation
5
5
  class Scale
6
6
  extend ClassicScales
7
+ extend Forwardable
8
+
9
+ def_delegators :notes, :accidentals, :sharps, :flats
10
+
7
11
  attr_reader :interval_sequence, :tone
8
12
 
9
13
  def initialize(*distances, tone: 'C', mode: 1, name: nil, notes: nil)
@@ -13,8 +17,10 @@ module Coltrane
13
17
  distances = distances.rotate(mode - 1)
14
18
  @interval_sequence = IntervalSequence.new(distances: distances)
15
19
  elsif notes
16
- ds = NoteSet[*notes].interval_sequence.distances
17
- new(*ds, tone: notes.first)
20
+ @notes = NoteSet[*notes]
21
+ @tone = @notes.first
22
+ ds = @notes.interval_sequence.distances
23
+ @interval_sequence = IntervalSequence.new(distances: ds)
18
24
  else
19
25
  raise WrongKeywordsError, '[*distances, tone: "C", mode: 1] || [notes:]'
20
26
  end
@@ -79,7 +85,7 @@ module Coltrane
79
85
  alias include? include_notes?
80
86
 
81
87
  def notes
82
- NoteSet[*degrees.map { |d| degree(d) }]
88
+ @notes ||= NoteSet[*degrees.map { |d| degree(d) }]
83
89
  end
84
90
 
85
91
  def interval(i)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Coltrane
4
- VERSION = '2.0.0'
4
+ VERSION = '2.1.0'
5
5
  end