coltrane 0.0.2 → 1.0.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 +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
|