coltrane 1.0.2 → 1.0.11
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/Gemfile +2 -5
- data/Gemfile.lock +3 -3
- data/coltrane.gemspec +1 -1
- data/db/schema.rb +30 -0
- data/exe/coltrane +21 -27
- data/lib/cli/bass_guitar.rb +1 -4
- data/lib/cli/chord.rb +3 -10
- data/lib/cli/errors.rb +4 -10
- data/lib/cli/guitar.rb +10 -12
- data/lib/cli/notes.rb +4 -7
- data/lib/cli/piano.rb +8 -11
- data/lib/cli/representation.rb +14 -14
- data/lib/cli/scale.rb +11 -15
- data/lib/cli/text.rb +3 -6
- data/lib/cli/ukulele.rb +2 -5
- data/lib/{cli.rb → coltrane-cli.rb} +2 -4
- data/lib/coltrane.rb +1 -4
- data/lib/coltrane/cache.rb +9 -10
- data/lib/coltrane/cadence.rb +1 -3
- data/lib/coltrane/chord.rb +8 -16
- data/lib/coltrane/chord_cache.rb +4 -0
- data/lib/coltrane/chord_quality.rb +8 -7
- data/lib/coltrane/classic_progressions.rb +7 -10
- data/lib/coltrane/classic_scales.rb +43 -30
- data/lib/coltrane/errors.rb +10 -17
- data/lib/coltrane/interval.rb +23 -24
- data/lib/coltrane/interval_sequence.rb +7 -8
- data/lib/coltrane/interval_set.rb +0 -1
- data/lib/coltrane/note.rb +46 -23
- data/lib/coltrane/note_set.rb +14 -12
- data/lib/coltrane/pitch.rb +4 -5
- data/lib/coltrane/progression.rb +20 -9
- data/lib/coltrane/qualities.rb +112 -114
- data/lib/coltrane/roman_chord.rb +5 -8
- data/lib/coltrane/scale.rb +33 -25
- data/lib/coltrane/scale_chord.rb +4 -0
- data/lib/coltrane/version.rb +1 -3
- data/lib/core_ext.rb +4 -7
- metadata +10 -7
- data/.rubocop.yml +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34a17f27f9596847f849a499afada6f8bf46a30b712dc7d9eb0cddb92719eb72
|
4
|
+
data.tar.gz: a18d20bcc0a16271fcbcd219d1d10413153ddedabbcf3ae777614847cd2bfd28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a025c18f84efa7e2a486b02ba9cd94d831ed0334d9ff7a729cdbd22c1b6893a1f79fdfb6f4013e5f1425740734099ce4c13305bcfa5ab0bcbcedb5b236c6c74
|
7
|
+
data.tar.gz: 06a8754da3e82b9da85ee121ed1a71d5c8efbc033fbfb58f1df73dcfa23e03fa218ece40a92d03c9bedfe2997f6ca5de2462a82af096bbb809dc5f06869ba364
|
data/Gemfile
CHANGED
@@ -2,14 +2,11 @@
|
|
2
2
|
|
3
3
|
source 'https://rubygems.org'
|
4
4
|
|
5
|
-
group :test
|
5
|
+
group :test do
|
6
|
+
gem 'rspec'
|
6
7
|
gem 'pry'
|
7
8
|
gem 'pry-byebug'
|
8
9
|
gem 'pry-rescue'
|
9
|
-
end
|
10
|
-
|
11
|
-
group :test do
|
12
|
-
gem 'rspec'
|
13
10
|
gem 'simplecov', require: false
|
14
11
|
gem 'rubocop', require: false
|
15
12
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
coltrane (1.0.
|
5
|
-
|
4
|
+
coltrane (1.0.1)
|
5
|
+
chroma (~> 0.2.0)
|
6
6
|
facets (~> 3.1)
|
7
7
|
mercenary (~> 0.3)
|
8
8
|
paint (~> 2.0)
|
@@ -12,8 +12,8 @@ GEM
|
|
12
12
|
specs:
|
13
13
|
ast (2.3.0)
|
14
14
|
byebug (9.1.0)
|
15
|
+
chroma (0.2.0)
|
15
16
|
coderay (1.1.2)
|
16
|
-
color (1.8)
|
17
17
|
diff-lcs (1.3)
|
18
18
|
docile (1.1.5)
|
19
19
|
facets (3.1.0)
|
data/coltrane.gemspec
CHANGED
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
|
33
33
|
spec.add_runtime_dependency 'facets', '~> 3.1'
|
34
34
|
spec.add_runtime_dependency 'paint', '~> 2.0'
|
35
|
-
spec.add_runtime_dependency '
|
35
|
+
spec.add_runtime_dependency 'chroma', '~> 0.2.0'
|
36
36
|
spec.add_runtime_dependency 'mercenary', '~> 0.3'
|
37
37
|
spec.add_development_dependency "bundler", '~> 1.14'
|
38
38
|
spec.add_development_dependency "rake", '~> 10.0'
|
data/db/schema.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
4
|
+
#
|
5
|
+
# Note that this schema.rb definition is the authoritative source for your
|
6
|
+
# database schema. If you need to create the application database on another
|
7
|
+
# system, you should be using db:schema:load, not running all the migrations
|
8
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
9
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
10
|
+
#
|
11
|
+
# It's strongly recommended that you check this file into your version control system.
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define(version: 20170508162849) do
|
14
|
+
|
15
|
+
create_table "scale_caches", force: :cascade do |t|
|
16
|
+
t.string "interval_sequence"
|
17
|
+
t.string "tone"
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table "chord_caches", force: :cascade do |t|
|
21
|
+
t.string "name"
|
22
|
+
t.integer "size"
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table "scale_chords", force: :cascade do |t|
|
26
|
+
t.integer "scale_cache_id"
|
27
|
+
t.integer "chord_cache_id"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/exe/coltrane
CHANGED
@@ -1,13 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
# rubocop:disable Metrics/BlockLength
|
5
2
|
|
6
3
|
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
7
4
|
|
8
5
|
require 'core_ext'
|
9
6
|
require 'coltrane'
|
10
|
-
require 'cli'
|
7
|
+
require 'coltrane-cli'
|
11
8
|
|
12
9
|
full_color_terminals = %w[iTerm.app]
|
13
10
|
safe_mode_terminals = %w[Unsupported]
|
@@ -74,9 +71,9 @@ Mercenary.program(:Coltrane) do |p|
|
|
74
71
|
c.option :pentads, '--pentads', 'Outputs pentad chords from the scale'
|
75
72
|
c.option :tertians, '--tertians SIZE', 'Outputs all tertian chords from the given size from the scale'
|
76
73
|
c.option :chords, '--chords [SIZE]', 'Outputs all chords from given size from the scale. Leave size empty to retrieve all'
|
77
|
-
c.action do |(scale_str), flavor:
|
74
|
+
c.action do |(scale_str), flavor:'degrees', on:'text', **options|
|
78
75
|
scale = Coltrane::Cli::Scale.parse(scale_str)
|
79
|
-
keyword_args = {
|
76
|
+
keyword_args = {flavor: flavor, on: on }
|
80
77
|
if options.include?(:triads)
|
81
78
|
chords = scale.triads
|
82
79
|
Coltrane::Cli::Chord.new(*chords, **keyword_args)
|
@@ -90,11 +87,11 @@ Mercenary.program(:Coltrane) do |p|
|
|
90
87
|
chords = scale.tertians(options[:tertians].to_i)
|
91
88
|
Coltrane::Cli::Chord.new(*chords, **keyword_args)
|
92
89
|
elsif options.include?(:chords)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
90
|
+
if options[:chords].nil?
|
91
|
+
chords = scale.all_chords
|
92
|
+
else
|
93
|
+
chords = scale.chords(options[:chords])
|
94
|
+
end
|
98
95
|
Coltrane::Cli::Chord.new(*chords, **keyword_args)
|
99
96
|
else
|
100
97
|
Coltrane::Cli::Scale.new(scale, **keyword_args)
|
@@ -107,11 +104,11 @@ Mercenary.program(:Coltrane) do |p|
|
|
107
104
|
list.description 'List information.'
|
108
105
|
list.action do |(arg)|
|
109
106
|
puts case arg
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
107
|
+
when 'scales' then Coltrane::Scale.known_scales
|
108
|
+
when 'flavors' then %w[marks notes intervals degrees]
|
109
|
+
when 'instruments' then %w[guitar bass ukulele piano text]
|
110
|
+
when 'chords', 'chord-qualities' then Coltrane::Qualities::CHORD_QUALITIES.keys.sort.join(' ')
|
111
|
+
end
|
115
112
|
end
|
116
113
|
end
|
117
114
|
|
@@ -120,9 +117,9 @@ Mercenary.program(:Coltrane) do |p|
|
|
120
117
|
c.description 'finds scales with the provided --notes or --chord'
|
121
118
|
c.option :notes, '--notes C-D-E', 'Find scales with those notes'
|
122
119
|
c.option :chords, '--chords Cmaj7-D11', 'find scales with those chords'
|
123
|
-
c.action do |(
|
124
|
-
options[:notes] =
|
125
|
-
options[:chords] =
|
120
|
+
c.action do |(arg), options|
|
121
|
+
options[:notes] = "#{options[:notes]}".split('-')
|
122
|
+
options[:chords] = "#{options[:chords]}".split('-')
|
126
123
|
Coltrane::Cli::Scale.find(**options)
|
127
124
|
end
|
128
125
|
end
|
@@ -148,15 +145,14 @@ Mercenary.program(:Coltrane) do |p|
|
|
148
145
|
p.command(:help) do |c|
|
149
146
|
c.description 'May give you some help.'
|
150
147
|
c.syntax 'help <command> [subcommand, sub-subcommand, ...]'
|
151
|
-
c.action do |(*command_path),
|
148
|
+
c.action do |(*command_path), options|
|
152
149
|
if command_path.empty?
|
153
150
|
puts p
|
151
|
+
return
|
154
152
|
else
|
155
|
-
puts
|
156
|
-
|
157
|
-
|
158
|
-
end || "\n Sorry, command found."
|
159
|
-
end
|
153
|
+
puts (command_path.reduce(p) do |memo, key|
|
154
|
+
memo.commands.delete(key.to_sym)
|
155
|
+
end || "\n Sorry, command found.")
|
160
156
|
end
|
161
157
|
end
|
162
158
|
end
|
@@ -170,5 +166,3 @@ Mercenary.program(:Coltrane) do |p|
|
|
170
166
|
|
171
167
|
p.default_command(:about)
|
172
168
|
end
|
173
|
-
|
174
|
-
# rubocop:enable Metrics/BlockLength
|
data/lib/cli/bass_guitar.rb
CHANGED
data/lib/cli/chord.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Coltrane
|
4
2
|
module Cli
|
5
|
-
# Interfaces chord functionality with the lib
|
6
3
|
class Chord
|
7
4
|
def initialize(*chords, on: :text, flavor: 'intervals', notes: nil)
|
8
5
|
@chords =
|
@@ -13,19 +10,15 @@ module Coltrane
|
|
13
10
|
chords
|
14
11
|
end
|
15
12
|
elsif !notes.nil?
|
16
|
-
|
13
|
+
[Coltrane::Chord.new(notes: notes)]
|
17
14
|
end
|
18
15
|
|
19
16
|
@chords.each do |chord|
|
20
17
|
raise ChordNotFoundError unless chord.named?
|
21
18
|
desc = "#{chord.name} chord:"
|
22
|
-
Coltrane::Cli::Notes.new
|
23
|
-
desc: desc,
|
24
|
-
flavor: flavor
|
19
|
+
Coltrane::Cli::Notes.new(chord.notes, on: on, desc: desc, flavor: flavor)
|
25
20
|
end
|
26
21
|
end
|
27
22
|
end
|
28
23
|
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# rubocop:enable Metrics/MethodLength
|
24
|
+
end
|
data/lib/cli/errors.rb
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# rubocop:disable Style/Documentation
|
4
|
-
|
5
1
|
module Coltrane
|
6
2
|
module Cli
|
7
3
|
class ColtraneCliError < StandardError
|
@@ -11,13 +7,13 @@ module Coltrane
|
|
11
7
|
end
|
12
8
|
|
13
9
|
class WrongFlavorError < ColtraneCliError
|
14
|
-
def initialize(msg
|
10
|
+
def initialize(msg=nil)
|
15
11
|
super msg || 'Wrong flavor. Check possible flavors with `coltrane list flavors`.'
|
16
12
|
end
|
17
13
|
end
|
18
14
|
|
19
15
|
class BadFindScales < ColtraneCliError
|
20
|
-
def initialize(msg
|
16
|
+
def initialize(msg=nil)
|
21
17
|
super msg || 'Provide --notes or --chords. Ex: `coltrane find-scale --notes C E G`.'
|
22
18
|
end
|
23
19
|
end
|
@@ -25,10 +21,8 @@ module Coltrane
|
|
25
21
|
class WrongRepresentationTypeError < ColtraneCliError
|
26
22
|
def initialize(type)
|
27
23
|
super "The provided representation type (#{type}) "\
|
28
|
-
|
24
|
+
"is not available at the moment."
|
29
25
|
end
|
30
26
|
end
|
31
27
|
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# rubocop:enable Style/Documentation
|
28
|
+
end
|
data/lib/cli/guitar.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Coltrane
|
4
2
|
module Cli
|
5
|
-
SPECIAL_FRETS = [3, 5, 7, 9, 12, 15, 17, 19]
|
3
|
+
SPECIAL_FRETS = [3, 5, 7, 9, 12, 15, 17, 19]
|
6
4
|
|
7
|
-
# Renders notes in a common most popular ukulele scheme
|
8
5
|
class Guitar < Representation
|
9
6
|
def initialize(notes, flavor, tuning: %w[E A D G B E], frets: 22)
|
10
7
|
@notes = notes
|
@@ -15,33 +12,34 @@ module Coltrane
|
|
15
12
|
end
|
16
13
|
|
17
14
|
def render
|
18
|
-
[render_notes, render_special_frets, hint].join("\n"
|
15
|
+
[render_notes, render_special_frets, hint].join("\n"*2)
|
19
16
|
end
|
20
17
|
|
21
18
|
def render_notes
|
22
19
|
@tuning.map do |string|
|
23
20
|
string_note = Note[string]
|
24
|
-
|
21
|
+
(@frets+2).times.map do |i|
|
25
22
|
if i.zero?
|
26
23
|
string
|
27
24
|
else
|
28
25
|
fret = i - 1
|
29
26
|
note = string_note + fret
|
30
|
-
m = (@notes.include?(note) ? place_mark(note) :
|
31
|
-
fret.zero? ? (m +
|
27
|
+
m = (@notes.include?(note) ? place_mark(note) : "--")
|
28
|
+
fret.zero? ? (m + " |") : m
|
32
29
|
end
|
33
30
|
end.join(' ')
|
34
31
|
end.join("\n")
|
35
32
|
end
|
36
33
|
|
37
34
|
def render_special_frets
|
38
|
-
|
35
|
+
(@frets+1).times.map do |fret|
|
39
36
|
m = SPECIAL_FRETS.include?(fret) ? fret.to_s.rjust(2, 0.to_s) : ' '
|
40
37
|
"#{m}#{' ' if fret.zero?}"
|
41
38
|
end.join(' ')
|
42
39
|
end
|
43
40
|
|
44
|
-
def render_dotted_frets
|
41
|
+
def render_dotted_frets
|
42
|
+
end
|
45
43
|
|
46
44
|
def place_mark(note)
|
47
45
|
case @flavor
|
@@ -49,9 +47,9 @@ module Coltrane
|
|
49
47
|
when :intervals then (@ref_note - note).name.ljust(2, '-')
|
50
48
|
when :degrees then @notes.degree(note).to_s.rjust(2, '0')
|
51
49
|
when :marks then '◼◼'
|
52
|
-
else raise WrongFlavorError
|
50
|
+
else raise WrongFlavorError.new
|
53
51
|
end
|
54
52
|
end
|
55
53
|
end
|
56
54
|
end
|
57
|
-
end
|
55
|
+
end
|
data/lib/cli/notes.rb
CHANGED
@@ -1,11 +1,8 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Coltrane
|
4
2
|
module Cli
|
5
|
-
# Interfaces notes outputting functionality with the lib
|
6
3
|
class Notes
|
7
|
-
def initialize(notes, on: 'text', desc:
|
8
|
-
@desc = desc
|
4
|
+
def initialize(notes, on: 'text', desc: 'The notes you supplied:', flavor: 'notes')
|
5
|
+
@desc = desc
|
9
6
|
flavor = flavor.underscore.to_sym
|
10
7
|
on = on.to_sym
|
11
8
|
notes = Coltrane::NoteSet.new(notes)
|
@@ -14,8 +11,8 @@ module Coltrane
|
|
14
11
|
end
|
15
12
|
|
16
13
|
def render
|
17
|
-
puts "\n"
|
14
|
+
puts "\n"+[@desc, @representation.render].join("\n"*2)
|
18
15
|
end
|
19
16
|
end
|
20
17
|
end
|
21
|
-
end
|
18
|
+
end
|
data/lib/cli/piano.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Coltrane
|
4
2
|
module Cli
|
5
|
-
# It allows rendering notes in an ASCII piano
|
6
3
|
class Piano < Representation
|
7
4
|
PIANO_TEMPLATE = <<~ASCII
|
8
5
|
┌─┬─┬┬─┬─╥─┬─┬┬─┬┬─┬─╥─┬─┬┬─┬─╥─┬─┬┬─┬┬─┬─┐
|
@@ -27,11 +24,11 @@ module Coltrane
|
|
27
24
|
|
28
25
|
private
|
29
26
|
|
30
|
-
def replace_x(line, notes, size, index
|
31
|
-
line.gsub('X'
|
32
|
-
note = notes[i
|
33
|
-
next ' '
|
34
|
-
Paint[replacer(note)[size == 2 ? 0..2 : index], 'red']
|
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']
|
35
32
|
end
|
36
33
|
end
|
37
34
|
|
@@ -40,7 +37,7 @@ module Coltrane
|
|
40
37
|
case @flavor
|
41
38
|
when :intervals then (@ref_note - note).name
|
42
39
|
when :marks then '◼ '
|
43
|
-
when :degrees then @notes.degree(note).to_s.rjust(2,
|
40
|
+
when :degrees then @notes.degree(note).to_s.rjust(2,'0')
|
44
41
|
when :notes then note.pretty_name.to_s.ljust(2, "\u266E")
|
45
42
|
end
|
46
43
|
end
|
@@ -50,8 +47,8 @@ module Coltrane
|
|
50
47
|
end
|
51
48
|
|
52
49
|
def black_notes
|
53
|
-
Coltrane::Scale.pentatonic_major('C#',
|
50
|
+
Coltrane::Scale.pentatonic_major('C#',4).notes
|
54
51
|
end
|
55
52
|
end
|
56
53
|
end
|
57
|
-
end
|
54
|
+
end
|
data/lib/cli/representation.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Coltrane
|
4
2
|
module Cli
|
5
|
-
# It manages notes representations, most of times instruments
|
6
3
|
class Representation
|
7
|
-
ACCEPTED_FLAVORS = %i[marks notes intervals degrees]
|
4
|
+
ACCEPTED_FLAVORS = %i[marks notes intervals degrees]
|
8
5
|
|
9
6
|
def self.inherited(subclass)
|
10
7
|
@@types ||= {}
|
@@ -12,15 +9,18 @@ module Coltrane
|
|
12
9
|
end
|
13
10
|
|
14
11
|
def self.build(type, notes, flavor)
|
15
|
-
raise WrongFlavorError unless ACCEPTED_FLAVORS.include?(flavor)
|
12
|
+
raise WrongFlavorError.new unless ACCEPTED_FLAVORS.include?(flavor)
|
16
13
|
type = case type
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
when :ukelele then :ukulele
|
15
|
+
when :bass then :bass_guitar
|
16
|
+
else type
|
17
|
+
end
|
21
18
|
|
22
|
-
|
23
|
-
|
19
|
+
if (the_class = @@types[type])
|
20
|
+
the_class.new(notes, flavor)
|
21
|
+
else
|
22
|
+
raise WrongRepresentationTypeError.new(type)
|
23
|
+
end
|
24
24
|
end
|
25
25
|
|
26
26
|
def initialize(notes, flavor)
|
@@ -31,7 +31,7 @@ module Coltrane
|
|
31
31
|
|
32
32
|
def hint
|
33
33
|
case @flavor
|
34
|
-
when :marks then
|
34
|
+
when :marks then ""
|
35
35
|
when :notes then "(\u266E means the note is natural, not flat nor sharp)"
|
36
36
|
when :intervals
|
37
37
|
<<~DESC
|
@@ -39,9 +39,9 @@ module Coltrane
|
|
39
39
|
Ex: 1P = Perfect First / 3m = Minor Third / 4A = Augmented Fourth
|
40
40
|
DESC
|
41
41
|
|
42
|
-
when :degrees then
|
42
|
+
when :degrees then "(The numbers represent the degree of the note in the scale)"
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
47
|
-
end
|
47
|
+
end
|