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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +21 -3
- data/README.md +0 -7
- data/Rakefile +5 -0
- data/bin/console +2 -0
- data/bin/opal +29 -0
- data/bin/opal-build +29 -0
- data/bin/opal-mspec +29 -0
- data/bin/opal-repl +29 -0
- data/bin/rackup +12 -0
- data/bin/sprockets +12 -0
- data/bin/tilt +12 -0
- data/coltrane.gemspec +1 -1
- data/config.ru +10 -0
- data/exe/coltrane +44 -13
- data/lib/cli.rb +7 -1
- data/lib/cli/bass_guitar.rb +1 -1
- data/lib/cli/chord.rb +11 -4
- data/lib/cli/config.rb +38 -0
- data/lib/cli/guitar.rb +8 -10
- data/lib/cli/guitar_chords.rb +122 -0
- data/lib/cli/notes.rb +3 -4
- data/lib/cli/piano.rb +1 -1
- data/lib/cli/representation.rb +6 -12
- data/lib/cli/scale.rb +4 -4
- data/lib/cli/text.rb +1 -1
- data/lib/cli/ukulele.rb +1 -1
- data/lib/coltrane.rb +35 -32
- data/lib/coltrane/chord_quality.rb +4 -3
- data/lib/coltrane/circle_of_fifths.rb +29 -0
- data/lib/coltrane/classic_scales.rb +36 -43
- data/lib/coltrane/diatonic_scale.rb +34 -0
- data/lib/coltrane/frequency.rb +1 -1
- data/lib/coltrane/interval.rb +14 -0
- data/lib/coltrane/interval_class.rb +6 -0
- data/lib/coltrane/interval_sequence.rb +0 -1
- data/lib/coltrane/key.rb +14 -0
- data/lib/coltrane/note.rb +37 -2
- data/lib/coltrane/note_set.rb +23 -2
- data/lib/coltrane/pitch.rb +65 -31
- data/lib/coltrane/pitch_class.rb +25 -12
- data/lib/coltrane/progression.rb +2 -2
- data/lib/coltrane/qualities.rb +257 -0
- data/lib/coltrane/roman_chord.rb +10 -10
- data/lib/coltrane/scale.rb +9 -3
- data/lib/coltrane/version.rb +1 -1
- data/lib/coltrane/voicing.rb +38 -0
- data/lib/coltrane_game/question.rb +6 -0
- data/lib/coltrane_instruments.rb +3 -0
- data/lib/coltrane_instruments/guitar.rb +5 -2
- data/lib/coltrane_instruments/guitar/base.rb +18 -3
- data/lib/coltrane_instruments/guitar/chord.rb +139 -12
- data/lib/coltrane_instruments/guitar/note.rb +16 -0
- data/lib/coltrane_instruments/guitar/string.rb +21 -0
- data/lib/coltrane_synth.rb +7 -0
- data/lib/coltrane_synth/base.rb +47 -0
- data/lib/coltrane_synth/synth.rb +24 -0
- data/lib/core_ext.rb +25 -7
- data/lib/os.rb +19 -0
- metadata +24 -9
- data/dist/coltrane +0 -0
- data/lib/coltrane/piano_representation.rb +0 -0
data/lib/cli.rb
CHANGED
@@ -1,17 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'os'
|
3
4
|
require 'mercenary'
|
4
5
|
require 'paint'
|
5
6
|
require 'color'
|
6
7
|
|
7
|
-
require '
|
8
|
+
require 'coltrane'
|
9
|
+
require 'coltrane_instruments'
|
10
|
+
require 'coltrane_synth'
|
8
11
|
|
12
|
+
require 'cli/config'
|
13
|
+
require 'cli/errors'
|
9
14
|
require 'cli/representation'
|
10
15
|
require 'cli/text'
|
11
16
|
require 'cli/piano'
|
12
17
|
require 'cli/guitar'
|
13
18
|
require 'cli/bass_guitar'
|
14
19
|
require 'cli/ukulele'
|
20
|
+
require 'cli/guitar_chords'
|
15
21
|
|
16
22
|
require 'cli/notes'
|
17
23
|
require 'cli/chord'
|
data/lib/cli/bass_guitar.rb
CHANGED
data/lib/cli/chord.rb
CHANGED
@@ -4,7 +4,15 @@ module Coltrane
|
|
4
4
|
module Cli
|
5
5
|
# Interfaces chord functionality with the lib
|
6
6
|
class Chord
|
7
|
-
def initialize(*chords,
|
7
|
+
def initialize(*chords, notes: nil)
|
8
|
+
Cli.config do |c|
|
9
|
+
if c.on == :guitar
|
10
|
+
c.on = :guitar_chords
|
11
|
+
elsif c.on == :guitar_arm
|
12
|
+
c.on = :guitar
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
8
16
|
@chords =
|
9
17
|
if !chords.empty?
|
10
18
|
if chords[0].is_a?(String)
|
@@ -18,9 +26,8 @@ module Coltrane
|
|
18
26
|
|
19
27
|
@chords.each do |chord|
|
20
28
|
desc = "#{chord.name} chord:"
|
21
|
-
Coltrane::Cli::Notes.new chord.notes,
|
22
|
-
|
23
|
-
flavor: flavor
|
29
|
+
Coltrane::Cli::Notes.new chord.notes, desc: desc
|
30
|
+
ColtraneSynth::Base.play(chord, 1) if Cli.config.sound
|
24
31
|
end
|
25
32
|
end
|
26
33
|
end
|
data/lib/cli/config.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Coltrane
|
2
|
+
module Cli
|
3
|
+
class Config
|
4
|
+
DEFAULTS = {
|
5
|
+
flavor: :notes,
|
6
|
+
sound: false,
|
7
|
+
on: :text
|
8
|
+
}
|
9
|
+
|
10
|
+
attr_accessor :flavor, :degrees, :sound
|
11
|
+
attr_reader :on
|
12
|
+
|
13
|
+
def initialize(defaults = DEFAULTS)
|
14
|
+
defaults.each do |key, val|
|
15
|
+
instance_variable_set(:"@#{key}", val)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def on=(instrument)
|
20
|
+
@on = case instrument
|
21
|
+
when :ukelele then :ukulele
|
22
|
+
when :bass then :bass_guitar
|
23
|
+
else instrument
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.config
|
30
|
+
@config = Config.new if @config.nil?
|
31
|
+
block_given? ? yield(@config) : @config
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.erase_config
|
35
|
+
@config = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/cli/guitar.rb
CHANGED
@@ -7,11 +7,10 @@ module Coltrane
|
|
7
7
|
SPECIAL_FRETS = [3, 5, 7, 9, 12, 15, 17, 19].freeze
|
8
8
|
|
9
9
|
include Color
|
10
|
-
def initialize(notes,
|
10
|
+
def initialize(notes, tuning: %w[E A D G B E], frets: 22)
|
11
11
|
@notes = notes
|
12
12
|
@tuning = tuning.reverse
|
13
13
|
@frets = frets
|
14
|
-
@flavor = flavor
|
15
14
|
@ref_note = @notes.first
|
16
15
|
end
|
17
16
|
|
@@ -48,14 +47,13 @@ module Coltrane
|
|
48
47
|
end
|
49
48
|
|
50
49
|
def place_mark(note)
|
51
|
-
mark =
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
50
|
+
mark = case Cli.config.flavor
|
51
|
+
when :notes then note.pretty_name.ljust(2, ' ')
|
52
|
+
when :intervals then (@ref_note - note).name.ljust(2, '-')
|
53
|
+
when :degrees then @notes.degree(note).to_s.rjust(2, '0')
|
54
|
+
when :marks then ' ' # '◼◼'
|
55
|
+
else raise WrongFlavorError
|
56
|
+
end
|
59
57
|
|
60
58
|
base_hue = (180 + note.integer * 10) % 360 # + 260
|
61
59
|
Paint[
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Coltrane
|
4
|
+
module Cli
|
5
|
+
# Renders notes in a common most popular ukulele scheme
|
6
|
+
class GuitarChords < Representation
|
7
|
+
include Color
|
8
|
+
|
9
|
+
def initialize(notes, tuning: %w[E2 A2 D3 G3 B3 E4], frets: 23)
|
10
|
+
@notes = notes
|
11
|
+
@tuning = tuning.reverse
|
12
|
+
@frets = frets
|
13
|
+
|
14
|
+
target_chord = Coltrane::Chord.new(notes: @notes)
|
15
|
+
chords = ColtraneInstruments::Guitar::Base.find_chords(target_chord)
|
16
|
+
@chords = chords.sort[-6..-1].reverse
|
17
|
+
@chords.each { |c| ColtraneSynth::Base.play(c.voicing) } if false
|
18
|
+
end
|
19
|
+
|
20
|
+
def chord_height
|
21
|
+
ColtraneInstruments::Guitar::Chord::MAX_FRET_SPAN
|
22
|
+
end
|
23
|
+
|
24
|
+
def render
|
25
|
+
render_horizontally
|
26
|
+
# render_vertically
|
27
|
+
end
|
28
|
+
|
29
|
+
def render_vertically
|
30
|
+
@chords.map { |c| render_chord(c) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def render_horizontally
|
34
|
+
rchords = @chords.map { |c| render_chord(c).split("\n") }
|
35
|
+
rchords.max_by(&:size).size.times.reduce('') do |memo1, l|
|
36
|
+
memo1 +
|
37
|
+
rchords.reduce('') do |memo2, c|
|
38
|
+
memo2 +
|
39
|
+
(c[l].nil? ? " " * (c.map(&:size).max) + ' ' : "#{c[l]} ")
|
40
|
+
end +
|
41
|
+
"\n"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def render_chord(chord)
|
46
|
+
render_top(chord) +
|
47
|
+
(chord.lowest_fret..chord.lowest_fret+chord_height)
|
48
|
+
.each_with_index.reduce('') do |memo1, (f, y)|
|
49
|
+
memo1 +
|
50
|
+
' ' +
|
51
|
+
(chord.guitar.strings.size*2).times.map {|x| base_color border(x,y) }.join('') +
|
52
|
+
"\n" +
|
53
|
+
chord.guitar.strings.reduce(base_color f.to_s.ljust(2, ' ')) do |memo2, s|
|
54
|
+
memo2 +
|
55
|
+
' ' +
|
56
|
+
render_note(chord.guitar_notes.select do |n|
|
57
|
+
n.string == s && n.fret == f
|
58
|
+
end.any?)
|
59
|
+
end +
|
60
|
+
" \n"
|
61
|
+
end + ' ' +
|
62
|
+
(chord.guitar.strings.size*2).times.map do |x|
|
63
|
+
base_color border(x, chord_height + 1)
|
64
|
+
end.join('')
|
65
|
+
end
|
66
|
+
|
67
|
+
def border(x, y)
|
68
|
+
edge = [@tuning.size * 2 - 2, chord_height + 1]
|
69
|
+
x_on_start = x == 0
|
70
|
+
y_on_start = y == 0
|
71
|
+
x_on_edge = x == edge[0]
|
72
|
+
y_on_edge = y == edge[1]
|
73
|
+
x_on_middle = (0...edge[0]).include?(x)
|
74
|
+
y_on_middle = (0...edge[1]).include?(y)
|
75
|
+
|
76
|
+
if x_on_start && y_on_start then '┍'
|
77
|
+
elsif x_on_middle && y_on_start then x.odd? ? '━' : '┯'
|
78
|
+
elsif x_on_edge && y_on_start then '┑'
|
79
|
+
elsif x_on_start && y_on_middle then '┝'
|
80
|
+
elsif x_on_middle && y_on_middle then x.odd? ? '━' : '┿'
|
81
|
+
elsif x_on_edge && y_on_middle then '┥'
|
82
|
+
elsif x_on_start && y_on_edge then '┕'
|
83
|
+
elsif x_on_middle && y_on_edge then x.odd? ? '━' : '┷'
|
84
|
+
elsif x_on_edge && y_on_edge then '┙'
|
85
|
+
else ' '
|
86
|
+
end
|
87
|
+
# puts edge.inspect
|
88
|
+
# "#{x},#{y} "
|
89
|
+
end
|
90
|
+
|
91
|
+
def base_color(str)
|
92
|
+
Paint[str, '#6A4AC2']
|
93
|
+
end
|
94
|
+
|
95
|
+
def highlight(str)
|
96
|
+
Paint[str, '#E67831']
|
97
|
+
end
|
98
|
+
|
99
|
+
def alt(str)
|
100
|
+
Paint[str, '#2DD380']
|
101
|
+
end
|
102
|
+
|
103
|
+
def render_top(chord)
|
104
|
+
chord.guitar.strings.reduce(' ') do |memo, s|
|
105
|
+
memo +
|
106
|
+
if chord.guitar_notes.select {|n|
|
107
|
+
n.string == s && n.fret == 0 }.any?
|
108
|
+
highlight '⬤ '
|
109
|
+
elsif chord.guitar_notes.select {|n| n.string == s && n.fret == nil }.any?
|
110
|
+
alt 'x '
|
111
|
+
else
|
112
|
+
' '
|
113
|
+
end
|
114
|
+
end + "\n"
|
115
|
+
end
|
116
|
+
|
117
|
+
def render_note(found)
|
118
|
+
found ? highlight('⬤') : base_color('│')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/cli/notes.rb
CHANGED
@@ -4,13 +4,12 @@ module Coltrane
|
|
4
4
|
module Cli
|
5
5
|
# Interfaces notes outputting functionality with the lib
|
6
6
|
class Notes
|
7
|
-
def initialize(notes,
|
7
|
+
def initialize(notes, desc: nil)
|
8
8
|
@desc = desc || 'The notes you supplied:'
|
9
|
-
flavor = flavor.underscore.to_sym
|
10
|
-
on = on.to_sym
|
11
9
|
notes = Coltrane::NoteSet.new(notes)
|
12
|
-
@representation = Representation.build(
|
10
|
+
@representation = Representation.build(notes)
|
13
11
|
render
|
12
|
+
# notes.each {|n| ColtraneSynth::Base.play(n, 0.1) } if Cli.config.sound
|
14
13
|
end
|
15
14
|
|
16
15
|
def render
|
data/lib/cli/piano.rb
CHANGED
@@ -37,7 +37,7 @@ module Coltrane
|
|
37
37
|
|
38
38
|
def replacer(note)
|
39
39
|
# TODO: Maybe extract this method into its own class/module
|
40
|
-
case
|
40
|
+
case Cli.config.flavor
|
41
41
|
when :intervals then (@ref_note - note).name
|
42
42
|
when :marks then '◼ '
|
43
43
|
when :degrees then @notes.degree(note).to_s.rjust(2, '0')
|
data/lib/cli/representation.rb
CHANGED
@@ -11,26 +11,20 @@ module Coltrane
|
|
11
11
|
@@types[subclass.to_s.split('::').last.underscore.to_sym] = subclass
|
12
12
|
end
|
13
13
|
|
14
|
-
def self.build(
|
15
|
-
|
16
|
-
|
17
|
-
when :ukelele then :ukulele
|
18
|
-
when :bass then :bass_guitar
|
19
|
-
else type
|
20
|
-
end
|
21
|
-
|
14
|
+
def self.build(notes)
|
15
|
+
type = Cli.config.on
|
16
|
+
raise WrongFlavorError unless ACCEPTED_FLAVORS.include?(Cli.config.flavor)
|
22
17
|
raise(WrongRepresentationTypeError, type) unless @@types.include?(type)
|
23
|
-
@@types[type].new(notes
|
18
|
+
@@types[type].new(notes)
|
24
19
|
end
|
25
20
|
|
26
|
-
def initialize(notes
|
21
|
+
def initialize(notes)
|
27
22
|
@notes = notes
|
28
|
-
@flavor = flavor
|
29
23
|
@ref_note = notes.first
|
30
24
|
end
|
31
25
|
|
32
26
|
def hint
|
33
|
-
case
|
27
|
+
case Cli.config.flavor
|
34
28
|
when :marks then ''
|
35
29
|
# when :notes then "(\u266E means the note is natural, not flat nor sharp)"
|
36
30
|
when :intervals
|
data/lib/cli/scale.rb
CHANGED
@@ -27,8 +27,8 @@ module Coltrane
|
|
27
27
|
def self.render_search(searched_notes)
|
28
28
|
search = Coltrane::Scale.having_notes(searched_notes)
|
29
29
|
output = []
|
30
|
-
scale_width = search
|
31
|
-
search
|
30
|
+
scale_width = search[:results].keys.map(&:size).max
|
31
|
+
search[:results].each do |name, scales_by_tone|
|
32
32
|
output << name.ljust(scale_width + 1, ' ')
|
33
33
|
scales_by_tone.each do |tone_number, notes|
|
34
34
|
p = (notes.size.to_f / searched_notes.size) * 100
|
@@ -43,9 +43,9 @@ module Coltrane
|
|
43
43
|
puts output.join
|
44
44
|
end
|
45
45
|
|
46
|
-
def initialize(scale
|
46
|
+
def initialize(scale)
|
47
47
|
desc = "This is the #{scale.tone.name} #{scale.name} scale:"
|
48
|
-
Coltrane::Cli::Notes.new(scale.notes
|
48
|
+
Coltrane::Cli::Notes.new(scale.notes)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
data/lib/cli/text.rb
CHANGED
@@ -5,7 +5,7 @@ module Coltrane
|
|
5
5
|
# A text representation
|
6
6
|
class Text < Representation
|
7
7
|
def render
|
8
|
-
case
|
8
|
+
case Cli.config.flavor
|
9
9
|
when :marks, :notes, :degrees then @notes.pretty_names.join(' ')
|
10
10
|
when :intervals then @notes.map { |n| (@notes.first - n).name }.join(' ')
|
11
11
|
else raise WrongFlavorError
|
data/lib/cli/ukulele.rb
CHANGED
data/lib/coltrane.rb
CHANGED
@@ -1,44 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
|
3
|
+
require 'json'
|
5
4
|
require 'forwardable'
|
6
|
-
require 'facets/multiton'
|
7
5
|
require 'core_ext'
|
8
6
|
require 'ostruct'
|
9
7
|
|
10
|
-
require 'coltrane/version'
|
11
|
-
require 'coltrane/errors'
|
12
|
-
require 'coltrane/cadence'
|
13
|
-
|
14
|
-
require 'coltrane/frequency'
|
15
|
-
require 'coltrane/pitch'
|
16
|
-
require 'coltrane/pitch_class'
|
17
|
-
require 'coltrane/note'
|
18
|
-
require 'coltrane/note_set'
|
19
|
-
|
20
|
-
require 'coltrane/interval'
|
21
|
-
require 'coltrane/interval_class'
|
22
|
-
require 'coltrane/unordered_interval_class'
|
23
|
-
require 'coltrane/interval_sequence'
|
24
|
-
|
25
|
-
require 'coltrane/chord_quality'
|
26
|
-
|
27
|
-
require 'coltrane/chord_substitutions'
|
28
|
-
require 'coltrane/chord'
|
29
|
-
require 'coltrane/roman_chord'
|
30
|
-
|
31
|
-
require 'coltrane/classic_scales'
|
32
|
-
require 'coltrane/scale'
|
33
|
-
|
34
|
-
require 'coltrane/notable_progressions'
|
35
|
-
require 'coltrane/changes'
|
36
|
-
require 'coltrane/progression'
|
37
|
-
|
38
|
-
require 'coltrane/mode'
|
39
8
|
|
40
9
|
# The main module for working with Music Theory
|
41
10
|
module Coltrane
|
11
|
+
autoload :Frequency, 'coltrane/frequency'
|
12
|
+
|
42
13
|
BASE_OCTAVE = 4
|
43
14
|
BASE_PITCH_INTEGER = 9
|
44
15
|
|
@@ -51,4 +22,36 @@ module Coltrane
|
|
51
22
|
end
|
52
23
|
|
53
24
|
@base_tuning = Frequency[440].octave(-4)
|
25
|
+
|
26
|
+
require 'coltrane/version'
|
27
|
+
require 'coltrane/errors'
|
28
|
+
|
29
|
+
autoload :Pitch, 'coltrane/pitch'
|
30
|
+
autoload :Voicing, 'coltrane/voicing'
|
31
|
+
|
32
|
+
autoload :PitchClass, 'coltrane/pitch_class'
|
33
|
+
autoload :Note, 'coltrane/note'
|
34
|
+
autoload :NoteSet, 'coltrane/note_set'
|
35
|
+
|
36
|
+
autoload :Interval, 'coltrane/interval'
|
37
|
+
autoload :IntervalClass, 'coltrane/interval_class'
|
38
|
+
autoload :UnorderedIntervalClass, 'coltrane/unordered_interval_class'
|
39
|
+
autoload :IntervalSequence, 'coltrane/interval_sequence'
|
40
|
+
autoload :Qualities, 'coltrane/qualities'
|
41
|
+
autoload :ChordQuality, 'coltrane/chord_quality'
|
42
|
+
autoload :Chord, 'coltrane/chord'
|
43
|
+
autoload :ChordSubstitutions, 'coltrane/chord_substitutions'
|
44
|
+
autoload :RomanChord, 'coltrane/roman_chord'
|
45
|
+
|
46
|
+
autoload :ClassicScales, 'coltrane/classic_scales'
|
47
|
+
autoload :Scale, 'coltrane/scale'
|
48
|
+
autoload :CircleOfFifths, 'coltrane/circle_of_fifths'
|
49
|
+
autoload :DiatonicScale, 'coltrane/diatonic_scale'
|
50
|
+
autoload :Key, 'coltrane/key'
|
51
|
+
|
52
|
+
autoload :NotableProgressions, 'coltrane/notable_progressions'
|
53
|
+
autoload :Changes, 'coltrane/changes'
|
54
|
+
autoload :Cadence, 'coltrane/cadence'
|
55
|
+
autoload :Progression, 'coltrane/progression'
|
56
|
+
autoload :Mode, 'coltrane/mode'
|
54
57
|
end
|