coltrane 2.2.1 → 3.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- module Cli
5
- # Renders notes in a bass guitar scheme
6
- class BassGuitar < Guitar
7
- def initialize(notes, tuning: %w[E A D G])
8
- super
9
- end
10
- end
11
- end
12
- end
data/lib/cli/chord.rb DELETED
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- module Cli
5
- # Interfaces chord functionality with the lib
6
- class Chord
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
-
16
- @chords =
17
- if !chords.empty?
18
- if chords[0].is_a?(String)
19
- chords.map { |c| Coltrane::Chord.new(name: c) }
20
- else
21
- chords
22
- end
23
- elsif !notes.nil?
24
- [Coltrane::Chord.new(notes: notes)]
25
- end
26
-
27
- raise BadChordError unless @chords
28
-
29
- @chords.each do |chord|
30
- desc = "#{chord.name} chord:"
31
- Coltrane::Cli::Notes.new chord.notes, desc: desc
32
- # ColtraneSynth::Base.play(chord, 1) if Cli.config.sound
33
- end
34
- end
35
- end
36
- end
37
- end
38
-
39
- # rubocop:enable Metrics/MethodLength
data/lib/cli/config.rb DELETED
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- module Cli
5
- class Config
6
- DEFAULTS = {
7
- flavor: :notes,
8
- sound: false,
9
- on: :text
10
- }.freeze
11
-
12
- attr_accessor :flavor, :degrees, :sound
13
- attr_reader :on
14
-
15
- def initialize(defaults = DEFAULTS)
16
- defaults.each do |key, val|
17
- instance_variable_set(:"@#{key}", val)
18
- end
19
- end
20
-
21
- def on=(instrument)
22
- @on = case instrument
23
- when :ukelele then :ukulele
24
- when :bass then :bass_guitar
25
- else instrument
26
- end
27
- end
28
- end
29
-
30
- def self.config
31
- @config = Config.new if @config.nil?
32
- block_given? ? yield(@config) : @config
33
- end
34
-
35
- def self.erase_config
36
- @config = nil
37
- end
38
- end
39
- end
data/lib/cli/errors.rb DELETED
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Style/Documentation
4
-
5
- module Coltrane
6
- module Cli
7
- class ColtraneCliError < StandardError
8
- def initialize(msg)
9
- super msg
10
- end
11
- end
12
-
13
- class WrongFlavorError < ColtraneCliError
14
- def initialize(msg = nil)
15
- super msg || 'Wrong flavor. Check possible flavors with `coltrane list flavors`.'
16
- end
17
- end
18
-
19
- class BadFindScales < ColtraneCliError
20
- def initialize(msg = nil)
21
- super msg || 'Provide --notes or --chords. Ex: `coltrane find-scale --notes C-E-G`.'
22
- end
23
- end
24
-
25
- class WrongRepresentationTypeError < ColtraneCliError
26
- def initialize(type)
27
- super "The provided representation type (#{type}) "\
28
- 'is not available at the moment.'
29
- end
30
- end
31
-
32
- class BadScaleError < ColtraneCliError
33
- def initialize(msg = nil)
34
- super msg || 'Incorrect scale, please specify scale and root separated by `-`. Ex: `coltrane scale major-C'
35
- end
36
- end
37
-
38
- class BadChordError < ColtraneCliError
39
- def initialize(msg = nil)
40
- super msg || 'Incorrect chord, please specify a set of chords separated by `-`. Ex: coltrane chord CM7'
41
- end
42
- end
43
- end
44
- end
45
-
46
- # rubocop:enable Style/Documentation
data/lib/cli/guitar.rb DELETED
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- module Cli
5
- # Renders notes in a common most popular ukulele scheme
6
- class Guitar < Representation
7
- SPECIAL_FRETS = [3, 5, 7, 9, 12, 15, 17, 19].freeze
8
-
9
- include Color
10
- def initialize(notes, tuning: %w[E A D G B E], frets: 22)
11
- @notes = notes
12
- @tuning = tuning.reverse
13
- @frets = frets
14
- @ref_note = @notes.first
15
- end
16
-
17
- def render
18
- [render_notes, render_special_frets, hint].join("\n" * 2)
19
- end
20
-
21
- def render_notes
22
- @tuning.each_with_index.map do |string, str_i|
23
- string_note = Note[string]
24
- Array.new(@frets + 2) do |i|
25
- if i.zero?
26
- Paint[string, HSL.new(140 + str_i * 20, 50, 50).html]
27
- else
28
- fret = i - 1
29
- note = string_note + fret
30
- m = (@notes.include?(note) ? place_mark(note) : place_empty(str_i))
31
- fret.zero? ? (m + ' |') : m
32
- end
33
- end.join(' ')
34
- end.join("\n")
35
- end
36
-
37
- def render_special_frets
38
- ' ' +
39
- Array.new(@frets + 2) do |fret|
40
- m = SPECIAL_FRETS.include?(fret) ? fret.to_s.rjust(2, 0.to_s) : ' '
41
- "#{m}#{' ' if fret.zero?}"
42
- end.join(' ')
43
- end
44
-
45
- def place_empty(str_i)
46
- Paint['--', HSL.new(180 + str_i * 3, 50, 30).html]
47
- end
48
-
49
- def place_mark(note)
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
57
-
58
- base_hue = (180 + note.integer * 10) % 360 # + 260
59
- Paint[
60
- mark,
61
- HSL.new(0, 0, 100).html,
62
- HSL.new(base_hue, 100, 30).html
63
- ]
64
- end
65
- end
66
- end
67
- end
@@ -1,122 +0,0 @@
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]).cover?(x)
74
- y_on_middle = (0...edge[1]).cover?(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 DELETED
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- module Cli
5
- # Interfaces notes outputting functionality with the lib
6
- class Notes
7
- def initialize(notes, desc: nil)
8
- @desc = desc || 'The notes you supplied:'
9
- notes = Coltrane::NoteSet.new(notes)
10
- @representation = Representation.build(notes)
11
- render
12
- # notes.each {|n| ColtraneSynth::Base.play(n, 0.1) } if Cli.config.sound
13
- end
14
-
15
- def render
16
- puts "\n" + [@desc, @representation.render].join("\n" * 2)
17
- end
18
- end
19
- end
20
- end
data/lib/cli/piano.rb DELETED
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- module Cli
5
- # It allows rendering notes in an ASCII piano
6
- class Piano < Representation
7
- PIANO_TEMPLATE = <<~ASCII
8
- ┌─┬─┬┬─┬─╥─┬─┬┬─┬┬─┬─╥─┬─┬┬─┬─╥─┬─┬┬─┬┬─┬─┐
9
- │ │ ││ │ ║ │ ││ ││ │ ║ │ ││ │ ║ │ ││ ││ │ │
10
- │ │X││X│ ║ │X││X││X│ ║ │X││X│ ║ │X││X││X│ │
11
- │ │X││X│ ║ │X││X││X│ ║ │X││X│ ║ │X││X││X│ │
12
- │ ┕╥┙┕╥┙ ║ ┕╥┙┕╥┙┕╥┙ ║ ┕╥┙┕╥┙ ║ ┕╥┙┕╥┙┕╥┙ │
13
- │ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ │
14
- │XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX║XX│
15
- └──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──╨──┘
16
- ASCII
17
-
18
- def render
19
- PIANO_TEMPLATE.each_line.map.each_with_index do |l, ln|
20
- case ln
21
- when 2, 3 then replace_x(l, black_notes, 1, ln - 2)
22
- when 6 then replace_x(l, white_notes, 2)
23
- else l
24
- end
25
- end.join + "\n#{hint}"
26
- end
27
-
28
- private
29
-
30
- def replace_x(line, notes, size, index = 0)
31
- line.gsub('X' * size).with_index do |_match, i|
32
- note = notes[i % notes.size]
33
- next ' ' * size unless @notes.include?(note)
34
- Paint[replacer(note)[size == 2 ? 0..2 : index], 'red']
35
- end
36
- end
37
-
38
- def replacer(note)
39
- # TODO: Maybe extract this method into its own class/module
40
- case Cli.config.flavor
41
- when :intervals then (@ref_note - note).name
42
- when :marks then '◼ '
43
- when :degrees then @notes.degree(note).to_s.rjust(2, '0')
44
- when :notes then note.pretty_name.to_s.ljust(2, "\u266E")
45
- end
46
- end
47
-
48
- def white_notes
49
- Coltrane::Scale.major.notes
50
- end
51
-
52
- def black_notes
53
- Coltrane::Scale.pentatonic_major('C#', 4).notes
54
- end
55
- end
56
- end
57
- end
data/lib/cli/scale.rb DELETED
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- module Cli
5
- # Interfaces commands with the scales functionality
6
- class Scale
7
- def self.parse(str)
8
- raise BadScaleError unless str&.include?('-')
9
- *scale_name, tone = str.split('-')
10
- Coltrane::Scale.fetch(scale_name.join('_'), tone)
11
- end
12
-
13
- def self.find(notes: [], chords: [])
14
- if notes.any?
15
- puts "\nSearching for scales containing #{notes.join(', ')}:\n\n"
16
- notes = NoteSet[*notes]
17
- elsif chords.any?
18
- puts "\nSearching for scales containing #{chords.join(', ')}:\n\n"
19
- notes = chords.reduce(NoteSet[]) do |memo, c|
20
- memo + Coltrane::Chord.new(name: c).notes
21
- end
22
- else raise BadFindScales
23
- end
24
- render_search(notes)
25
- puts "\nUnderlined means the scale has all notes"
26
- end
27
-
28
- def self.render_search(searched_notes)
29
- search = Coltrane::Scale.having_notes(searched_notes)
30
- output = []
31
- scale_width = search[:results].keys.map(&:size).max
32
- search[:results].each do |name, scales_by_tone|
33
- output << name.ljust(scale_width + 1, ' ')
34
- scales_by_tone.each do |tone_number, notes|
35
- p = (notes.size.to_f / searched_notes.size) * 100
36
- l = p == 100 ? p : (p + 20) * 0.4
37
- und = p == 100 ? :underline : nil
38
- color = Color::HSL.new(30, p, l / 2).html
39
- output << Paint["#{Note[tone_number].name}(#{notes.size})", color, und]
40
- output << ' '
41
- end
42
- output << "\n"
43
- end
44
- puts output.join
45
- end
46
-
47
- def initialize(scale)
48
- desc = "This is the #{scale.tone.name} #{scale.name} scale:"
49
- Coltrane::Cli::Notes.new(scale.notes)
50
- end
51
- end
52
- end
53
- end