coltrane 2.2.1 → 3.0.0.pre

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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +16 -1
  3. data/CHANGELOG.md +11 -0
  4. data/Gemfile.lock +25 -2
  5. data/README.md +3 -0
  6. data/bin/console +6 -2
  7. data/coltrane.gemspec +2 -1
  8. data/exe/coltrane +14 -224
  9. data/lib/coltrane.rb +3 -50
  10. data/lib/coltrane/commands.rb +14 -0
  11. data/lib/coltrane/commands/chords.rb +77 -0
  12. data/lib/coltrane/commands/command.rb +45 -0
  13. data/lib/coltrane/commands/common_chords.rb +33 -0
  14. data/lib/coltrane/commands/errors.rb +44 -0
  15. data/lib/coltrane/commands/find_progression.rb +28 -0
  16. data/lib/coltrane/commands/find_scale.rb +39 -0
  17. data/lib/coltrane/commands/notes.rb +44 -0
  18. data/lib/coltrane/commands/progression.rb +27 -0
  19. data/lib/{cli → coltrane/commands}/representation.rb +0 -0
  20. data/lib/coltrane/commands/scale.rb +46 -0
  21. data/lib/coltrane/renderers.rb +6 -0
  22. data/lib/coltrane/renderers/renderer.rb +42 -0
  23. data/lib/coltrane/renderers/text_renderer.rb +23 -0
  24. data/lib/coltrane/renderers/text_renderer/array_drawer.rb +45 -0
  25. data/lib/coltrane/renderers/text_renderer/base_drawer.rb +20 -0
  26. data/lib/coltrane/renderers/text_renderer/hash_drawer.rb +16 -0
  27. data/lib/coltrane/renderers/text_renderer/representation_guitar_chord_drawer.rb +95 -0
  28. data/lib/coltrane/renderers/text_renderer/representation_guitar_note_set_drawer.rb +76 -0
  29. data/lib/coltrane/renderers/text_renderer/representation_piano_note_set_drawer.rb +49 -0
  30. data/lib/coltrane/renderers/text_renderer/theory_chord_drawer.rb +14 -0
  31. data/lib/coltrane/renderers/text_renderer/theory_note_set_drawer.rb +17 -0
  32. data/lib/coltrane/renderers/text_renderer/theory_progression_drawer.rb +13 -0
  33. data/lib/coltrane/renderers/text_renderer/theory_progression_set_drawer.rb +22 -0
  34. data/lib/coltrane/renderers/text_renderer/theory_scale_drawer.rb +13 -0
  35. data/lib/coltrane/renderers/text_renderer/theory_scale_set_drawer.rb +27 -0
  36. data/lib/coltrane/representation.rb +12 -0
  37. data/lib/coltrane/representation/guitar.rb +34 -0
  38. data/lib/coltrane/representation/guitar/chord.rb +180 -0
  39. data/lib/coltrane/representation/guitar/note.rb +26 -0
  40. data/lib/coltrane/representation/guitar/note_set.rb +35 -0
  41. data/lib/coltrane/representation/guitar/string.rb +31 -0
  42. data/lib/coltrane/representation/guitar_like_instruments.rb +21 -0
  43. data/lib/coltrane/representation/piano.rb +36 -0
  44. data/lib/coltrane/representation/piano/note_set.rb +22 -0
  45. data/lib/{coltrane_synth.rb → coltrane/synth.rb.rb} +0 -0
  46. data/lib/{coltrane_synth → coltrane/synth}/base.rb +0 -0
  47. data/lib/{coltrane_synth → coltrane/synth}/synth.rb +0 -0
  48. data/lib/coltrane/theory.rb +54 -0
  49. data/lib/coltrane/theory/cadence.rb +9 -0
  50. data/lib/coltrane/{changes.rb → theory/changes.rb} +0 -0
  51. data/lib/coltrane/theory/chord.rb +101 -0
  52. data/lib/coltrane/theory/chord_quality.rb +113 -0
  53. data/lib/coltrane/theory/chord_substitutions.rb +11 -0
  54. data/lib/coltrane/theory/circle_of_fifths.rb +33 -0
  55. data/lib/coltrane/theory/classic_scales.rb +113 -0
  56. data/lib/coltrane/theory/diatonic_scale.rb +38 -0
  57. data/lib/coltrane/theory/errors.rb +97 -0
  58. data/lib/coltrane/theory/frequency.rb +52 -0
  59. data/lib/coltrane/theory/frequency_interval.rb +83 -0
  60. data/lib/coltrane/theory/interval.rb +209 -0
  61. data/lib/coltrane/theory/interval_class.rb +212 -0
  62. data/lib/coltrane/theory/interval_sequence.rb +157 -0
  63. data/lib/coltrane/theory/key.rb +18 -0
  64. data/lib/coltrane/{mode.rb → theory/mode.rb} +0 -0
  65. data/lib/coltrane/theory/notable_progressions.rb +30 -0
  66. data/lib/coltrane/theory/note.rb +104 -0
  67. data/lib/coltrane/theory/note_set.rb +101 -0
  68. data/lib/coltrane/theory/pitch.rb +94 -0
  69. data/lib/coltrane/theory/pitch_class.rb +154 -0
  70. data/lib/coltrane/theory/progression.rb +81 -0
  71. data/lib/coltrane/theory/progression_set.rb +18 -0
  72. data/lib/coltrane/{qualities.rb → theory/qualities.rb} +0 -0
  73. data/lib/coltrane/theory/roman_chord.rb +114 -0
  74. data/lib/coltrane/theory/scale.rb +161 -0
  75. data/lib/coltrane/theory/scale_search_result.rb +6 -0
  76. data/lib/coltrane/theory/scale_set.rb +40 -0
  77. data/lib/coltrane/theory/voicing.rb +41 -0
  78. data/lib/coltrane/version.rb +1 -1
  79. metadata +88 -63
  80. data/bin/rails +0 -17
  81. data/data/qualities.yml +0 -83
  82. data/db/cache.sqlite3 +0 -0
  83. data/db/cache_test.sqlite3 +0 -0
  84. data/db/config.yml +0 -11
  85. data/lib/cli.rb +0 -24
  86. data/lib/cli/bass_guitar.rb +0 -12
  87. data/lib/cli/chord.rb +0 -39
  88. data/lib/cli/config.rb +0 -39
  89. data/lib/cli/errors.rb +0 -46
  90. data/lib/cli/guitar.rb +0 -67
  91. data/lib/cli/guitar_chords.rb +0 -122
  92. data/lib/cli/notes.rb +0 -20
  93. data/lib/cli/piano.rb +0 -57
  94. data/lib/cli/scale.rb +0 -53
  95. data/lib/cli/text.rb +0 -16
  96. data/lib/cli/ukulele.rb +0 -14
  97. data/lib/coltrane/cache.rb +0 -43
  98. data/lib/coltrane/cadence.rb +0 -7
  99. data/lib/coltrane/chord.rb +0 -89
  100. data/lib/coltrane/chord_quality.rb +0 -111
  101. data/lib/coltrane/chord_substitutions.rb +0 -9
  102. data/lib/coltrane/circle_of_fifths.rb +0 -31
  103. data/lib/coltrane/classic_scales.rb +0 -94
  104. data/lib/coltrane/diatonic_scale.rb +0 -36
  105. data/lib/coltrane/errors.rb +0 -95
  106. data/lib/coltrane/frequency.rb +0 -50
  107. data/lib/coltrane/frequency_interval.rb +0 -81
  108. data/lib/coltrane/interval.rb +0 -208
  109. data/lib/coltrane/interval_class.rb +0 -210
  110. data/lib/coltrane/interval_sequence.rb +0 -155
  111. data/lib/coltrane/key.rb +0 -16
  112. data/lib/coltrane/notable_progressions.rb +0 -28
  113. data/lib/coltrane/note.rb +0 -98
  114. data/lib/coltrane/note_set.rb +0 -89
  115. data/lib/coltrane/pitch.rb +0 -92
  116. data/lib/coltrane/pitch_class.rb +0 -148
  117. data/lib/coltrane/progression.rb +0 -74
  118. data/lib/coltrane/roman_chord.rb +0 -112
  119. data/lib/coltrane/scale.rb +0 -154
  120. data/lib/coltrane/unordered_interval_class.rb +0 -7
  121. data/lib/coltrane/voicing.rb +0 -39
  122. data/lib/coltrane_game/question.rb +0 -7
  123. data/lib/coltrane_instruments.rb +0 -7
  124. data/lib/coltrane_instruments/guitar.rb +0 -10
  125. data/lib/coltrane_instruments/guitar/base.rb +0 -29
  126. data/lib/coltrane_instruments/guitar/chord.rb +0 -170
  127. data/lib/coltrane_instruments/guitar/note.rb +0 -24
  128. data/lib/coltrane_instruments/guitar/string.rb +0 -29
  129. data/lib/os.rb +0 -21
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'coltrane'
4
+ require 'coltrane/representation'
5
+ require 'coltrane/renderers'
6
+
7
+ require 'coltrane/commands/command'
8
+ require 'coltrane/commands/notes'
9
+ require 'coltrane/commands/chords'
10
+ require 'coltrane/commands/scale'
11
+ require 'coltrane/commands/progression'
12
+ require 'coltrane/commands/common_chords'
13
+ require 'coltrane/commands/find_scale'
14
+ require 'coltrane/commands/find_progression'
@@ -0,0 +1,77 @@
1
+ module Coltrane
2
+ module Commands
3
+ class Chords < Command
4
+ attr_reader :chord, :flavor, :on, :preface
5
+
6
+ def initialize(chord = nil,
7
+ flavor: :notes,
8
+ on: :text,
9
+ notes: nil,
10
+ preface: nil,
11
+ **options)
12
+ if chord
13
+ @chord = chord.is_a?(Theory::Chord) ? chord : Theory::Chord.new(name: chord)
14
+ elsif notes
15
+ @chord = Theory::Chord.new(notes: notes&.split('-'))
16
+ else
17
+ raise 'Provide chord names or notes. Ex: coltrane chords Cm7-Db7, ' \
18
+ 'or coltrane chords --notes C-E-G'
19
+ end
20
+
21
+ @flavor = flavor.to_sym
22
+ @preface = preface || @chord.name
23
+ @on = on.to_sym
24
+ end
25
+
26
+ def representation
27
+ return on_model if on == :text
28
+ { preface => on_model }
29
+ end
30
+
31
+ def on_model
32
+ case on
33
+ when :text then chord
34
+ when :guitar then Representation::Guitar.find_chords(chord).first(6)
35
+ when :ukulele, :ukelele then Representation::Ukulele.find_chords(chord).first(6)
36
+ when :bass then Representation::Bass.find_chords(chord).first(6)
37
+ when :piano then Representation::Piano.find_notes(chord.notes)
38
+ end
39
+ end
40
+
41
+ def layout_horizontal?
42
+ [:guitar, :ukulele, :ukelele, :bass, :piano].include?(on)
43
+ end
44
+
45
+ def renderer_options
46
+ [
47
+ { flavor: flavor },
48
+ (
49
+ {
50
+ layout: :horizontal,
51
+ per_row: 6,
52
+ } if layout_horizontal?
53
+ )
54
+ ]
55
+ .compact
56
+ .reduce({}, :merge)
57
+ end
58
+
59
+ def self.mercenary_init(program)
60
+ program.command(:chords) do |c|
61
+ c.alias(:chord)
62
+ c.syntax 'chords [<chord-name>] [--on <instrument>]'
63
+ c.description 'Shows the given chord. Ex: coltrane chord Cmaj7 --on piano'
64
+ c.alias(:chords)
65
+ c.option :notes, '--notes C-D-E', 'finds chords with those notes, ' \
66
+ 'provided they are separated by dashes'
67
+
68
+ add_shared_option(:flavor, c)
69
+ add_shared_option(:on, c)
70
+ c.action { |(chords), **options|
71
+ (chords&.split('-') || [nil]).each { |chord| new(chord, **options).render }
72
+ }
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,45 @@
1
+ module Coltrane
2
+ module Commands
3
+ class Command
4
+ COMMON_OPTIONS = {
5
+ on: [
6
+ '--on guitar INSTRUMENT',
7
+ 'Shows the notes on the given instrument/representation type. ' \
8
+ 'Can be piano, guitar, ukulele, bass or text'
9
+ ],
10
+
11
+ flavor: [
12
+ '--flavor FLAVOR',
13
+ 'Chooses which additional information to display: ' \
14
+ 'marks, notes, intervals or degrees'
15
+ ]
16
+ }
17
+
18
+ def render
19
+ puts "\n" + Renderers::TextRenderer.render(
20
+ representation, **renderer_options
21
+ )
22
+ end
23
+
24
+ def renderer_options
25
+ {}
26
+ end
27
+
28
+ class << self
29
+
30
+ def subclasses
31
+ @subclasses ||= []
32
+ end
33
+
34
+ def inherited(base)
35
+ subclasses << base
36
+ super(base)
37
+ end
38
+
39
+ def add_shared_option(option_name, mercenary_command)
40
+ mercenary_command.option(option_name, *COMMON_OPTIONS[option_name])
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,33 @@
1
+ module Coltrane
2
+ module Commands
3
+ class CommonChords < Command
4
+ def self.parse(scale_notation)
5
+ scale_notation
6
+ .split(' ', 2)
7
+ .yield_self { |(tone, scale_name)|
8
+ Theory::Scale.fetch(scale_name.gsub(' ', '_'), tone)
9
+ }
10
+ end
11
+
12
+ def self.mercenary_init(program)
13
+ program.command(:'common-chords') do |c|
14
+ c.alias(:'common-chord')
15
+ add_shared_option(:flavor, c)
16
+ add_shared_option(:on, c)
17
+ c.syntax 'common-chords [TONE_1 SCALE_NAME_1], [TONE_2 SCALE_NAME_2], [...]'
18
+ c.description 'Finds chords that are shared between the given scales'
19
+ c.action do |(*scale_strings), **options|
20
+ scale_strings
21
+ .join(' ')
22
+ .split(',')
23
+ .map { |scale_notation|
24
+ Commands::Scale.parse(scale_notation).all_chords
25
+ }
26
+ .reduce(:&)
27
+ .each { |chord| Commands::Chords.new(chord, **options).render }
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/Documentation
4
+
5
+ module Coltrane
6
+ class CommandError < StandardError
7
+ def initialize(msg)
8
+ super msg
9
+ end
10
+ end
11
+
12
+ class WrongFlavorError < CommandError
13
+ def initialize(msg = nil)
14
+ super msg || 'Wrong flavor. Check possible flavors with `coltrane list flavors`.'
15
+ end
16
+ end
17
+
18
+ class BadFindScales < CommandError
19
+ def initialize(msg = nil)
20
+ super msg || 'Provide --notes or --chords. Ex: `coltrane find-scale --notes C-E-G`.'
21
+ end
22
+ end
23
+
24
+ class WrongRepresentationTypeError < CommandError
25
+ def initialize(type)
26
+ super "The provided representation type (#{type}) "\
27
+ 'is not available at the moment.'
28
+ end
29
+ end
30
+
31
+ class BadScaleError < CommandError
32
+ def initialize(msg = nil)
33
+ super msg || 'Incorrect scale, please specify scale and root separated by `-`. Ex: `coltrane scale major-C'
34
+ end
35
+ end
36
+
37
+ class BadChordError < CommandError
38
+ def initialize(msg = nil)
39
+ super msg || 'Incorrect chord, please specify a set of chords separated by `-`. Ex: coltrane chord CM7'
40
+ end
41
+ end
42
+ end
43
+
44
+ # rubocop:enable Style/Documentation
@@ -0,0 +1,28 @@
1
+ module Coltrane
2
+ module Commands
3
+ class FindProgression < Command
4
+ attr_reader :progression_set
5
+
6
+ def initialize(progression_set, **options)
7
+ @progression_set = progression_set
8
+ end
9
+
10
+ def representation
11
+ progression_set
12
+ end
13
+
14
+ def self.mercenary_init(program)
15
+ program.command(:'find-progression') do |c|
16
+ c.syntax 'find-progression <list of chords>'
17
+ c.description 'Find progressions in scales. Ex: coltrane find-progression AM-DM-F#m-EM'
18
+ c.action do |(chord_notation)|
19
+ chord_notation
20
+ .split('-')
21
+ .yield_self { |chords| Theory::Progression.find(*chords) }
22
+ .yield_self { |progression_set| new(progression_set).render }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ module Coltrane
2
+ module Commands
3
+ class FindScale < Command
4
+ attr_reader :scale_set
5
+
6
+ def initialize(scale_set)
7
+ @scale_set = scale_set
8
+ end
9
+
10
+ def representation
11
+ scale_set
12
+ end
13
+
14
+ def self.mercenary_init(program)
15
+ program.command(:'find-scale') do |c|
16
+ c.syntax 'find-scale --notes C-D-E-...] OR --chord Cmaj7-Db7'
17
+ c.description 'finds scales with the provided --notes or --chord'
18
+ c.option :notes, '--notes C-D-E', 'Find scales with those notes'
19
+ c.option :chords, '--chords Cmaj7-D11', 'find scales with those chords'
20
+ c.action do |(_), notes: nil, chords: nil|
21
+ begin
22
+ if notes
23
+ Theory::Scale.having_notes(
24
+ Theory::NoteSet[*notes.to_s.split('-')]
25
+ )
26
+ elsif chords
27
+ Theory::Scale.having_chords(*chords.to_s.split('-'))
28
+ else
29
+ raise 'Provide --notes or --chords separated by dashes.' \
30
+ 'For example coltrane find-scale --notes C-E-F#'
31
+ end
32
+ end
33
+ .yield_self { |scale_set| new(scale_set).render }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,44 @@
1
+ module Coltrane
2
+ module Commands
3
+ class Notes < Command
4
+ attr_reader :notes, :flavor, :on, :preface
5
+
6
+ def initialize(notes, flavor: :notes, on: :text, preface: nil)
7
+ raise 'Provide some notes. Ex: coltrane notes C-D-Gb' if notes.nil?
8
+ @notes = notes.is_a?(Theory::NoteSet) ? notes : Theory::NoteSet[*notes.split('-')]
9
+ @flavor = flavor.to_sym
10
+ @preface = preface || 'Here are the notes you asked for'
11
+ @on = on
12
+ end
13
+
14
+ def representation
15
+ { preface => on_model }
16
+ end
17
+
18
+ def renderer_options
19
+ { flavor: flavor }
20
+ end
21
+
22
+ def on_model
23
+ case on.to_sym
24
+ when :text then notes
25
+ when :guitar then Representation::Guitar.find_notes(notes)
26
+ when :ukulele, :ukelele then Representation::Ukulele.find_notes(notes)
27
+ when :bass then Representation::Bass.find_notes(notes)
28
+ when :piano then Representation::Piano.find_notes(notes)
29
+ end
30
+ end
31
+
32
+ def self.mercenary_init(program)
33
+ program.command(:notes) do |c|
34
+ c.alias(:note)
35
+ c.syntax 'notes <notes separated by space> [--on <instrument>]'
36
+ c.description 'Shows the given notes.'
37
+ add_shared_option(:flavor, c)
38
+ add_shared_option(:on, c)
39
+ c.action { |(notes), **options| new(notes, **options).render }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,27 @@
1
+ module Coltrane
2
+ module Commands
3
+ class Progression < Command
4
+ def self.mercenary_init(program)
5
+ program.command(:progression) do |c|
6
+ c.syntax 'progression <roman numeral notation> in <key> [--on <instrument>]'
7
+ c.description 'Gives you chords of a progression in a key. Ex: coltrane progression I-IV-iv-V in Am --on guitar'
8
+ add_shared_option(:flavor, c)
9
+ add_shared_option(:on, c)
10
+ c.action do |(prog, _, key), **options|
11
+ prog
12
+ .tr('-', '_')
13
+ .yield_self { |possible_method|
14
+ if Theory::Progression.respond_to?(possible_method)
15
+ Theory::Progression.send(possible_method, key)
16
+ else
17
+ Theory::Progression.new(prog, key: key)
18
+ end
19
+ }
20
+ .chords
21
+ .each { |chord| Commands::Chords.new(chord, **options).render }
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
File without changes
@@ -0,0 +1,46 @@
1
+ module Coltrane
2
+ module Commands
3
+ class Scale < Command
4
+ def representation
5
+ return on_model if on == :text
6
+ { preface => on_model }
7
+ end
8
+
9
+ def self.parse(scale_notation)
10
+ scale_notation
11
+ .split(' ', 2)
12
+ .yield_self { |(tone, scale_name)|
13
+ Theory::Scale.fetch(scale_name.gsub(' ', '_'), tone)
14
+ }
15
+ end
16
+
17
+ def self.mercenary_init(program)
18
+ program.command(:scale) do |c|
19
+ c.syntax 'scale <root_note> <scale name> [--on <instrument>]'
20
+ c.description 'Gives you information about a scale. Ex: coltrane scale D Natural Minor --on guitar'
21
+ c.option :tertians, '--tertians SIZE', 'Outputs all tertian chords from the given size from the scale'
22
+ c.option :chords, '--chords [SIZE]', 'Outputs all chords from given size from the scale. Leave size empty to retrieve all'
23
+ add_shared_option(:flavor, c)
24
+ add_shared_option(:on, c)
25
+
26
+ c.action { |scale_notation, **options|
27
+ parse(scale_notation.join(' '))
28
+ .yield_self { |scale|
29
+ if options[:tertians]
30
+ scale
31
+ .tertians(options[:tertians].to_i)
32
+ .each { |chord| Commands::Chords.new(chord, **options).render }
33
+ elsif options[:chords]
34
+ scale.chords(options[:chords].to_i)
35
+ .each { |chord| Commands::Chords.new(chord, **options).render }
36
+ else
37
+ scale.notes
38
+ .yield_self {|notes| Commands::Notes.new(notes, **options.merge(preface: scale.full_name)).render }
39
+ end
40
+ }
41
+ }
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'coltrane'
4
+ require 'coltrane/representation'
5
+ require 'coltrane/renderers/renderer'
6
+ require 'coltrane/renderers/text_renderer'
@@ -0,0 +1,42 @@
1
+ module Coltrane
2
+ module Renderers
3
+ module Renderer
4
+ include Dry::Monads::Try::Mixin
5
+
6
+ def render(model, **options)
7
+ model
8
+ .yield_self { |model| model_class_list(model) }
9
+ .yield_self { |model_classes| renderer_class(*model_classes) }
10
+ .value_or { raise("Renderer doesn't implements #{model.class}") }
11
+ .new(model, **options)
12
+ .render
13
+ end
14
+
15
+ private
16
+
17
+ def model_class_list(model)
18
+ model
19
+ .class
20
+ .ancestors
21
+ .yield_self { |classes| classes[0...classes.index(Object)] }
22
+ end
23
+
24
+ def renderer_class(*classes)
25
+ return if classes.empty?
26
+ Try() { classes }
27
+ .fmap { |classes|
28
+ classes
29
+ .first
30
+ .to_s
31
+ .gsub('Coltrane::', '')
32
+ .gsub('::', '')
33
+ .prepend("#{self.name}::")
34
+ .concat('Drawer')
35
+ .yield_self {|class_name| Object.const_get(class_name) }
36
+ }
37
+ .to_maybe
38
+ .or(renderer_class(*classes[1..-1]))
39
+ end
40
+ end
41
+ end
42
+ end