coltrane 0.0.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +15 -10
- data/Gemfile.lock +47 -128
- data/README.md +30 -8
- data/Rakefile +0 -4
- data/bin/bundle +105 -0
- data/bin/byebug +29 -0
- data/bin/coderay +12 -0
- data/bin/coltrane +25 -7
- data/bin/htmldiff +12 -0
- data/bin/kill-pry-rescue +12 -0
- data/bin/ldiff +12 -0
- data/bin/pry +12 -0
- data/bin/rake +12 -0
- data/bin/rescue +12 -0
- data/bin/rspec +12 -0
- data/bin/rubocop +12 -0
- data/bin/ruby-parse +12 -0
- data/bin/ruby-rewrite +12 -0
- data/exe/coltrane +173 -0
- data/lib/cli/bass_guitar.rb +9 -0
- data/lib/cli/chord.rb +24 -0
- data/lib/cli/errors.rb +28 -0
- data/lib/cli/guitar.rb +55 -0
- data/lib/cli/notes.rb +18 -0
- data/lib/cli/piano.rb +54 -0
- data/lib/cli/representation.rb +47 -0
- data/lib/cli/scale.rb +48 -0
- data/lib/cli/text.rb +13 -0
- data/lib/cli/ukulele.rb +11 -0
- data/lib/coltrane-cli.rb +12 -0
- data/lib/coltrane.rb +16 -31
- data/lib/coltrane/cache.rb +34 -0
- data/lib/coltrane/chord.rb +28 -41
- data/lib/coltrane/chord_quality.rb +14 -39
- data/lib/coltrane/classic_progressions.rb +37 -0
- data/lib/coltrane/classic_scales.rb +92 -118
- data/lib/coltrane/errors.rb +54 -0
- data/lib/coltrane/interval.rb +25 -17
- data/lib/coltrane/interval_sequence.rb +57 -27
- data/lib/coltrane/note.rb +44 -30
- data/lib/coltrane/note_set.rb +37 -22
- data/lib/coltrane/piano_representation.rb +0 -58
- data/lib/coltrane/pitch.rb +12 -3
- data/lib/coltrane/progression.rb +31 -0
- data/lib/coltrane/qualities.rb +115 -114
- data/lib/coltrane/roman_chord.rb +36 -0
- data/lib/coltrane/scale.rb +85 -77
- data/lib/coltrane/version.rb +1 -1
- data/lib/core_ext.rb +12 -0
- data/pkg/coltrane-0.0.2.gem +0 -0
- metadata +27 -14
- data/lib/coltrane/essential_guitar_chords.rb +0 -82
- data/lib/coltrane/fret_set.rb +0 -0
- data/lib/coltrane/guitar.rb +0 -15
- data/lib/coltrane/guitar_chord.rb +0 -50
- data/lib/coltrane/guitar_chord_finder.rb +0 -98
- data/lib/coltrane/guitar_note.rb +0 -50
- data/lib/coltrane/guitar_note_set.rb +0 -61
- data/lib/coltrane/guitar_representation.rb +0 -96
- data/lib/coltrane/guitar_string.rb +0 -52
- data/lib/coltrane/scale_cache.rb +0 -4
data/lib/cli/chord.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Coltrane
|
2
|
+
module Cli
|
3
|
+
class Chord
|
4
|
+
def initialize(*chords, on: :text, flavor: 'intervals', notes: nil)
|
5
|
+
@chords =
|
6
|
+
if !chords.empty?
|
7
|
+
if chords[0].is_a?(String)
|
8
|
+
chords.map { |c| Coltrane::Chord.new(name: c) }
|
9
|
+
else
|
10
|
+
chords
|
11
|
+
end
|
12
|
+
elsif !notes.nil?
|
13
|
+
[Coltrane::Chord.new(notes: notes)]
|
14
|
+
end
|
15
|
+
|
16
|
+
@chords.each do |chord|
|
17
|
+
raise ChordNotFoundError unless chord.named?
|
18
|
+
desc = "#{chord.name} chord:"
|
19
|
+
Coltrane::Cli::Notes.new(chord.notes, on: on, desc: desc, flavor: flavor)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/cli/errors.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Coltrane
|
2
|
+
module Cli
|
3
|
+
class ColtraneCliError < StandardError
|
4
|
+
def initialize(msg)
|
5
|
+
super msg
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class WrongFlavorError < ColtraneCliError
|
10
|
+
def initialize(msg=nil)
|
11
|
+
super msg || 'Wrong flavor. Check possible flavors with `coltrane list flavors`.'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class BadFindScales < ColtraneCliError
|
16
|
+
def initialize(msg=nil)
|
17
|
+
super msg || 'Provide --notes or --chords. Ex: `coltrane find-scale --notes C E G`.'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class WrongRepresentationTypeError < ColtraneCliError
|
22
|
+
def initialize(type)
|
23
|
+
super "The provided representation type (#{type}) "\
|
24
|
+
"is not available at the moment."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/cli/guitar.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Coltrane
|
2
|
+
module Cli
|
3
|
+
SPECIAL_FRETS = [3, 5, 7, 9, 12, 15, 17, 19]
|
4
|
+
|
5
|
+
class Guitar < Representation
|
6
|
+
def initialize(notes, flavor, tuning: %w[E A D G B E], frets: 22)
|
7
|
+
@notes = notes
|
8
|
+
@tuning = tuning.reverse
|
9
|
+
@frets = frets
|
10
|
+
@flavor = flavor
|
11
|
+
@ref_note = @notes.first
|
12
|
+
end
|
13
|
+
|
14
|
+
def render
|
15
|
+
[render_notes, render_special_frets, hint].join("\n"*2)
|
16
|
+
end
|
17
|
+
|
18
|
+
def render_notes
|
19
|
+
@tuning.map do |string|
|
20
|
+
string_note = Note[string]
|
21
|
+
(@frets+2).times.map do |i|
|
22
|
+
if i.zero?
|
23
|
+
string
|
24
|
+
else
|
25
|
+
fret = i - 1
|
26
|
+
note = string_note + fret
|
27
|
+
m = (@notes.include?(note) ? place_mark(note) : "--")
|
28
|
+
fret.zero? ? (m + " |") : m
|
29
|
+
end
|
30
|
+
end.join(' ')
|
31
|
+
end.join("\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
def render_special_frets
|
35
|
+
(@frets+1).times.map do |fret|
|
36
|
+
m = SPECIAL_FRETS.include?(fret) ? fret.to_s.rjust(2, 0.to_s) : ' '
|
37
|
+
"#{m}#{' ' if fret.zero?}"
|
38
|
+
end.join(' ')
|
39
|
+
end
|
40
|
+
|
41
|
+
def render_dotted_frets
|
42
|
+
end
|
43
|
+
|
44
|
+
def place_mark(note)
|
45
|
+
case @flavor
|
46
|
+
when :notes then note.pretty_name.ljust(2, "\u266E")
|
47
|
+
when :intervals then (@ref_note - note).name.ljust(2, '-')
|
48
|
+
when :degrees then @notes.degree(note).to_s.rjust(2, '0')
|
49
|
+
when :marks then '◼◼'
|
50
|
+
else raise WrongFlavorError.new
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/cli/notes.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Coltrane
|
2
|
+
module Cli
|
3
|
+
class Notes
|
4
|
+
def initialize(notes, on: 'text', desc: 'The notes you supplied:', flavor: 'notes')
|
5
|
+
@desc = desc
|
6
|
+
flavor = flavor.underscore.to_sym
|
7
|
+
on = on.to_sym
|
8
|
+
notes = Coltrane::NoteSet.new(notes)
|
9
|
+
@representation = Representation.build(on, notes, flavor)
|
10
|
+
render
|
11
|
+
end
|
12
|
+
|
13
|
+
def render
|
14
|
+
puts "\n"+[@desc, @representation.render].join("\n"*2)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/cli/piano.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Coltrane
|
2
|
+
module Cli
|
3
|
+
class Piano < Representation
|
4
|
+
PIANO_TEMPLATE = <<~ASCII
|
5
|
+
┌─┬─┬┬─┬─╥─┬─┬┬─┬┬─┬─╥─┬─┬┬─┬─╥─┬─┬┬─┬┬─┬─┐
|
6
|
+
│ │ ││ │ ║ │ ││ ││ │ ║ │ ││ │ ║ │ ││ ││ │ │
|
7
|
+
│ │X││X│ ║ │X││X││X│ ║ │X││X│ ║ │X││X││X│ │
|
8
|
+
│ │X││X│ ║ │X││X││X│ ║ │X││X│ ║ │X││X││X│ │
|
9
|
+
│ ┕╥┙┕╥┙ ║ ┕╥┙┕╥┙┕╥┙ ║ ┕╥┙┕╥┙ ║ ┕╥┙┕╥┙┕╥┙ │
|
10
|
+
│ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ │
|
11
|
+
│XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX│
|
12
|
+
└──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──┘
|
13
|
+
ASCII
|
14
|
+
|
15
|
+
def render
|
16
|
+
PIANO_TEMPLATE.each_line.map.each_with_index do |l, ln|
|
17
|
+
case ln
|
18
|
+
when 2, 3 then replace_x(l, black_notes, 1, ln - 2)
|
19
|
+
when 6 then replace_x(l, white_notes, 2)
|
20
|
+
else l
|
21
|
+
end
|
22
|
+
end.join + "\n" + hint
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def replace_x(line, notes, size, index=0)
|
28
|
+
line.gsub('X'*size).with_index do |match, i|
|
29
|
+
note = notes[i%notes.size]
|
30
|
+
next ' '*size unless @notes.include?(note)
|
31
|
+
Paint[replacer(note)[size == 2 ? 0..2 : index ], 'red']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def replacer(note)
|
36
|
+
# TODO: Maybe extract this method into its own class/module
|
37
|
+
case @flavor
|
38
|
+
when :intervals then (@ref_note - note).name
|
39
|
+
when :marks then '◼ '
|
40
|
+
when :degrees then @notes.degree(note).to_s.rjust(2,'0')
|
41
|
+
when :notes then note.pretty_name.to_s.ljust(2, "\u266E")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def white_notes
|
46
|
+
Coltrane::Scale.major.notes
|
47
|
+
end
|
48
|
+
|
49
|
+
def black_notes
|
50
|
+
Coltrane::Scale.pentatonic_major('C#',4).notes
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Coltrane
|
2
|
+
module Cli
|
3
|
+
class Representation
|
4
|
+
ACCEPTED_FLAVORS = %i[marks notes intervals degrees]
|
5
|
+
|
6
|
+
def self.inherited(subclass)
|
7
|
+
@@types ||= {}
|
8
|
+
@@types[subclass.to_s.split('::').last.underscore.to_sym] = subclass
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.build(type, notes, flavor)
|
12
|
+
raise WrongFlavorError.new unless ACCEPTED_FLAVORS.include?(flavor)
|
13
|
+
type = case type
|
14
|
+
when :ukelele then :ukulele
|
15
|
+
when :bass then :bass_guitar
|
16
|
+
else type
|
17
|
+
end
|
18
|
+
|
19
|
+
if (the_class = @@types[type])
|
20
|
+
the_class.new(notes, flavor)
|
21
|
+
else
|
22
|
+
raise WrongRepresentationTypeError.new(type)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(notes, flavor)
|
27
|
+
@notes = notes
|
28
|
+
@flavor = flavor
|
29
|
+
@ref_note = notes.first
|
30
|
+
end
|
31
|
+
|
32
|
+
def hint
|
33
|
+
case @flavor
|
34
|
+
when :marks then ""
|
35
|
+
when :notes then "(\u266E means the note is natural, not flat nor sharp)"
|
36
|
+
when :intervals
|
37
|
+
<<~DESC
|
38
|
+
The letters represent the intervals relative to the root tone
|
39
|
+
Ex: 1P = Perfect First / 3m = Minor Third / 4A = Augmented Fourth
|
40
|
+
DESC
|
41
|
+
|
42
|
+
when :degrees then "(The numbers represent the degree of the note in the scale)"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/cli/scale.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Coltrane
|
2
|
+
module Cli
|
3
|
+
class Scale
|
4
|
+
def self.parse(str)
|
5
|
+
*scale_name, tone = str.split('-')
|
6
|
+
Coltrane::Scale.fetch(scale_name.join('_'), tone)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.find(notes: [], chords: [])
|
10
|
+
if notes.any?
|
11
|
+
puts "\nSearching for scales containing #{notes.join(', ')}:\n\n"
|
12
|
+
notes = NoteSet[*notes]
|
13
|
+
elsif chords.any?
|
14
|
+
puts "\nSearching for scales containing #{chords.join(', ')}:\n\n"
|
15
|
+
notes = chords.reduce(NoteSet[]) {|memo, c| memo + Coltrane::Chord.new(name: c).notes }
|
16
|
+
else raise BadFindScales.new
|
17
|
+
end
|
18
|
+
render_search(notes)
|
19
|
+
puts "\nUnderlined means the scale has all notes"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.render_search(searched_notes)
|
23
|
+
search = Coltrane::Scale.having_notes(searched_notes)
|
24
|
+
output = []
|
25
|
+
scale_width = search.results.keys.map(&:size).max
|
26
|
+
search.results.each do |name, scales_by_tone|
|
27
|
+
output << name.ljust(scale_width+1, ' ')
|
28
|
+
scales_by_tone.each do |tone_number, notes|
|
29
|
+
p = notes.size.to_f / searched_notes.size
|
30
|
+
l = p == 1 ? p : (p + 0.2) * 0.4
|
31
|
+
hue, val, sat = 30, val = (l * 100).round, sat = p
|
32
|
+
und = p == 1 ? :underline : nil
|
33
|
+
color = "hsv(#{hue},#{sat},#{val})".paint.to_hex
|
34
|
+
output << Paint["#{Note[tone_number].name}(#{notes.size})", color, und]
|
35
|
+
output << " "
|
36
|
+
end
|
37
|
+
output << "\n"
|
38
|
+
end
|
39
|
+
puts output.join
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(scale, on: :text, flavor: 'degrees', notes: [], chords: [])
|
43
|
+
desc = "This is the #{scale.tone.name} #{scale.name} scale:"
|
44
|
+
Coltrane::Cli::Notes.new(scale.notes, on: on, desc: desc, flavor: flavor)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/cli/text.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Coltrane
|
2
|
+
module Cli
|
3
|
+
class Text < Representation
|
4
|
+
def render
|
5
|
+
case @flavor
|
6
|
+
when :marks, :notes, :degrees then @notes.pretty_names.join(' ')
|
7
|
+
when :intervals then @notes.map {|n| (@notes.first - n).name}.join(' ')
|
8
|
+
else raise WrongFlavorError.new
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/cli/ukulele.rb
ADDED
data/lib/coltrane-cli.rb
ADDED
data/lib/coltrane.rb
CHANGED
@@ -1,48 +1,33 @@
|
|
1
|
-
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require
|
2
3
|
|
3
|
-
|
4
|
+
require 'facets/multiton'
|
5
|
+
require 'ostruct'
|
6
|
+
require 'paint'
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
# ActiveSupport::Inflector.inflections do |inflect|
|
8
|
-
# inflect.irregular 'cache', 'caches'
|
9
|
-
# end
|
10
|
-
|
11
|
-
# ActiveRecord::Base.establish_connection YAML.load_file("#{__dir__}/../db/config.yml")['production']
|
12
|
-
# require "#{__dir__}/../db/schema.rb"
|
8
|
+
require 'coltrane/version'
|
9
|
+
require 'coltrane/errors'
|
13
10
|
|
11
|
+
require 'coltrane/cache'
|
14
12
|
require 'coltrane/cadence'
|
15
13
|
|
14
|
+
require 'coltrane/interval_sequence'
|
15
|
+
require 'coltrane/interval_set'
|
16
|
+
require 'coltrane/interval'
|
16
17
|
require 'coltrane/qualities'
|
17
18
|
require 'coltrane/chord_quality'
|
19
|
+
require 'coltrane/note_set'
|
18
20
|
require 'coltrane/chord'
|
21
|
+
require 'coltrane/roman_chord'
|
19
22
|
|
20
23
|
require 'coltrane/classic_scales'
|
21
|
-
require 'coltrane/
|
22
|
-
|
23
|
-
# require 'coltrane/piano_representation'
|
24
|
-
# require 'coltrane/guitar_representation'
|
25
|
-
require 'coltrane/essential_guitar_chords'
|
26
|
-
require 'coltrane/guitar_chord_finder'
|
27
|
-
require 'coltrane/guitar_note_set'
|
28
|
-
require 'coltrane/guitar_chord'
|
29
|
-
require 'coltrane/guitar_note'
|
30
|
-
require 'coltrane/guitar_string'
|
31
|
-
require 'coltrane/guitar'
|
24
|
+
require 'coltrane/classic_progressions'
|
32
25
|
|
33
|
-
require 'coltrane/
|
34
|
-
require 'coltrane/interval_set'
|
35
|
-
require 'coltrane/interval'
|
26
|
+
require 'coltrane/piano_representation'
|
36
27
|
|
37
|
-
require 'coltrane/note_set'
|
38
28
|
require 'coltrane/note'
|
39
29
|
|
40
30
|
require 'coltrane/pitch'
|
41
31
|
require 'coltrane/progression'
|
42
32
|
require 'coltrane/scale'
|
43
|
-
require 'coltrane/mode'
|
44
|
-
|
45
|
-
# require 'coltrane/scale_chord'
|
46
|
-
# require 'coltrane/chord_cache'
|
47
|
-
# require 'coltrane/scale_cache'
|
48
|
-
# require 'terminal_input'
|
33
|
+
require 'coltrane/mode'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Coltrane
|
2
|
+
class Cache
|
3
|
+
class << self
|
4
|
+
def find_or_record(key, &block)
|
5
|
+
if (cached = fetch(key))
|
6
|
+
return cached
|
7
|
+
else
|
8
|
+
cached = yield block
|
9
|
+
record(key, cached)
|
10
|
+
cached
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def dir
|
17
|
+
dir = File.expand_path('../../../', __FILE__) + "/cache/"
|
18
|
+
Dir.mkdir(dir) unless Dir.exist?(dir)
|
19
|
+
dir
|
20
|
+
end
|
21
|
+
|
22
|
+
def fetch(key)
|
23
|
+
return unless File.file?(dir+key)
|
24
|
+
Marshal.load File.read(dir+key)
|
25
|
+
end
|
26
|
+
|
27
|
+
def record(key, contents)
|
28
|
+
File.open(dir+key, "w") do |f|
|
29
|
+
f.write Marshal.dump(contents)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/coltrane/chord.rb
CHANGED
@@ -1,52 +1,40 @@
|
|
1
1
|
module Coltrane
|
2
2
|
# It describe a chord
|
3
|
-
class Chord
|
4
|
-
attr_reader :root_note, :quality
|
5
|
-
|
6
|
-
def initialize(
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
class Chord < NoteSet
|
4
|
+
attr_reader :root_note, :quality, :notes
|
5
|
+
|
6
|
+
def initialize(notes: nil, root_note: nil, quality: nil, name: nil)
|
7
|
+
if !notes.nil?
|
8
|
+
notes = NoteSet[*notes] if notes.is_a?(Array)
|
9
|
+
@notes = notes
|
10
|
+
@root_note = notes.first
|
11
|
+
@quality = ChordQuality.new(notes: notes)
|
12
|
+
elsif !root_note.nil? && !quality.nil?
|
13
|
+
@notes = quality.notes_for(root_note)
|
14
|
+
@root_note = root_note
|
15
|
+
@quality = quality
|
16
|
+
elsif !name.nil?
|
17
|
+
@root_note, @quality, @notes = parse_from_name(name)
|
18
|
+
else
|
19
|
+
raise WrongKeywords.new('[notes:] || [root_note:, quality:] || [name:]')
|
15
20
|
end
|
16
21
|
end
|
17
22
|
|
18
|
-
def guitar_chords
|
19
|
-
GuitarChordFinder.by_chord(self)
|
20
|
-
end
|
21
|
-
|
22
|
-
def guitar_notes_for_root
|
23
|
-
root_note.guitar_notes
|
24
|
-
end
|
25
|
-
|
26
23
|
def name
|
27
|
-
return '
|
24
|
+
return @notes.names.join('/') if !named?
|
28
25
|
"#{root_note.name}#{quality.name}"
|
29
26
|
end
|
30
27
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
def named?
|
29
|
+
notes.size >= 3 &&
|
30
|
+
!root_note.nil? &&
|
31
|
+
!quality&.name.nil?
|
35
32
|
end
|
36
33
|
|
37
34
|
def intervals
|
38
35
|
IntervalSequence.new(NoteSet.new(notes))
|
39
36
|
end
|
40
37
|
|
41
|
-
def on_guitar
|
42
|
-
name + "\n" +
|
43
|
-
NoteSet.new(notes).guitar_notes.render(root_note) + "\n\n"
|
44
|
-
end
|
45
|
-
|
46
|
-
def on_piano
|
47
|
-
PianoRepresentation.render_intervals(notes, root_note)
|
48
|
-
end
|
49
|
-
|
50
38
|
def size
|
51
39
|
notes.size
|
52
40
|
end
|
@@ -69,13 +57,12 @@ module Coltrane
|
|
69
57
|
|
70
58
|
protected
|
71
59
|
|
72
|
-
def
|
73
|
-
_, name,
|
74
|
-
[
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
[notes.first, ChordQuality.new_from_notes(notes)]
|
60
|
+
def parse_from_name(name)
|
61
|
+
_, name, quality_name = name.match(/([A-Z]#?)(.*)/).to_a
|
62
|
+
root = Note[name]
|
63
|
+
quality = ChordQuality.new(name: quality_name)
|
64
|
+
notes = quality.notes_for(root)
|
65
|
+
[root, quality, notes]
|
79
66
|
end
|
80
67
|
end
|
81
68
|
end
|