coltrane 2.0.0 → 2.1.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -1
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +21 -3
  5. data/README.md +0 -7
  6. data/Rakefile +5 -0
  7. data/bin/console +2 -0
  8. data/bin/opal +29 -0
  9. data/bin/opal-build +29 -0
  10. data/bin/opal-mspec +29 -0
  11. data/bin/opal-repl +29 -0
  12. data/bin/rackup +12 -0
  13. data/bin/sprockets +12 -0
  14. data/bin/tilt +12 -0
  15. data/coltrane.gemspec +1 -1
  16. data/config.ru +10 -0
  17. data/exe/coltrane +44 -13
  18. data/lib/cli.rb +7 -1
  19. data/lib/cli/bass_guitar.rb +1 -1
  20. data/lib/cli/chord.rb +11 -4
  21. data/lib/cli/config.rb +38 -0
  22. data/lib/cli/guitar.rb +8 -10
  23. data/lib/cli/guitar_chords.rb +122 -0
  24. data/lib/cli/notes.rb +3 -4
  25. data/lib/cli/piano.rb +1 -1
  26. data/lib/cli/representation.rb +6 -12
  27. data/lib/cli/scale.rb +4 -4
  28. data/lib/cli/text.rb +1 -1
  29. data/lib/cli/ukulele.rb +1 -1
  30. data/lib/coltrane.rb +35 -32
  31. data/lib/coltrane/chord_quality.rb +4 -3
  32. data/lib/coltrane/circle_of_fifths.rb +29 -0
  33. data/lib/coltrane/classic_scales.rb +36 -43
  34. data/lib/coltrane/diatonic_scale.rb +34 -0
  35. data/lib/coltrane/frequency.rb +1 -1
  36. data/lib/coltrane/interval.rb +14 -0
  37. data/lib/coltrane/interval_class.rb +6 -0
  38. data/lib/coltrane/interval_sequence.rb +0 -1
  39. data/lib/coltrane/key.rb +14 -0
  40. data/lib/coltrane/note.rb +37 -2
  41. data/lib/coltrane/note_set.rb +23 -2
  42. data/lib/coltrane/pitch.rb +65 -31
  43. data/lib/coltrane/pitch_class.rb +25 -12
  44. data/lib/coltrane/progression.rb +2 -2
  45. data/lib/coltrane/qualities.rb +257 -0
  46. data/lib/coltrane/roman_chord.rb +10 -10
  47. data/lib/coltrane/scale.rb +9 -3
  48. data/lib/coltrane/version.rb +1 -1
  49. data/lib/coltrane/voicing.rb +38 -0
  50. data/lib/coltrane_game/question.rb +6 -0
  51. data/lib/coltrane_instruments.rb +3 -0
  52. data/lib/coltrane_instruments/guitar.rb +5 -2
  53. data/lib/coltrane_instruments/guitar/base.rb +18 -3
  54. data/lib/coltrane_instruments/guitar/chord.rb +139 -12
  55. data/lib/coltrane_instruments/guitar/note.rb +16 -0
  56. data/lib/coltrane_instruments/guitar/string.rb +21 -0
  57. data/lib/coltrane_synth.rb +7 -0
  58. data/lib/coltrane_synth/base.rb +47 -0
  59. data/lib/coltrane_synth/synth.rb +24 -0
  60. data/lib/core_ext.rb +25 -7
  61. data/lib/os.rb +19 -0
  62. metadata +24 -9
  63. data/dist/coltrane +0 -0
  64. data/lib/coltrane/piano_representation.rb +0 -0
data/lib/cli.rb CHANGED
@@ -1,17 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'os'
3
4
  require 'mercenary'
4
5
  require 'paint'
5
6
  require 'color'
6
7
 
7
- require 'cli/errors'
8
+ require 'coltrane'
9
+ require 'coltrane_instruments'
10
+ require 'coltrane_synth'
8
11
 
12
+ require 'cli/config'
13
+ require 'cli/errors'
9
14
  require 'cli/representation'
10
15
  require 'cli/text'
11
16
  require 'cli/piano'
12
17
  require 'cli/guitar'
13
18
  require 'cli/bass_guitar'
14
19
  require 'cli/ukulele'
20
+ require 'cli/guitar_chords'
15
21
 
16
22
  require 'cli/notes'
17
23
  require 'cli/chord'
@@ -4,7 +4,7 @@ module Coltrane
4
4
  module Cli
5
5
  # Renders notes in a bass guitar scheme
6
6
  class BassGuitar < Guitar
7
- def initialize(notes, flavor, tuning: %w[E A D G])
7
+ def initialize(notes, tuning: %w[E A D G])
8
8
  super
9
9
  end
10
10
  end
data/lib/cli/chord.rb CHANGED
@@ -4,7 +4,15 @@ module Coltrane
4
4
  module Cli
5
5
  # Interfaces chord functionality with the lib
6
6
  class Chord
7
- def initialize(*chords, on: :text, flavor: 'intervals', notes: nil)
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
+
8
16
  @chords =
9
17
  if !chords.empty?
10
18
  if chords[0].is_a?(String)
@@ -18,9 +26,8 @@ module Coltrane
18
26
 
19
27
  @chords.each do |chord|
20
28
  desc = "#{chord.name} chord:"
21
- Coltrane::Cli::Notes.new chord.notes, on: on,
22
- desc: desc,
23
- flavor: flavor
29
+ Coltrane::Cli::Notes.new chord.notes, desc: desc
30
+ ColtraneSynth::Base.play(chord, 1) if Cli.config.sound
24
31
  end
25
32
  end
26
33
  end
data/lib/cli/config.rb ADDED
@@ -0,0 +1,38 @@
1
+ module Coltrane
2
+ module Cli
3
+ class Config
4
+ DEFAULTS = {
5
+ flavor: :notes,
6
+ sound: false,
7
+ on: :text
8
+ }
9
+
10
+ attr_accessor :flavor, :degrees, :sound
11
+ attr_reader :on
12
+
13
+ def initialize(defaults = DEFAULTS)
14
+ defaults.each do |key, val|
15
+ instance_variable_set(:"@#{key}", val)
16
+ end
17
+ end
18
+
19
+ def on=(instrument)
20
+ @on = case instrument
21
+ when :ukelele then :ukulele
22
+ when :bass then :bass_guitar
23
+ else instrument
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ def self.config
30
+ @config = Config.new if @config.nil?
31
+ block_given? ? yield(@config) : @config
32
+ end
33
+
34
+ def self.erase_config
35
+ @config = nil
36
+ end
37
+ end
38
+ end
data/lib/cli/guitar.rb CHANGED
@@ -7,11 +7,10 @@ module Coltrane
7
7
  SPECIAL_FRETS = [3, 5, 7, 9, 12, 15, 17, 19].freeze
8
8
 
9
9
  include Color
10
- def initialize(notes, flavor, tuning: %w[E A D G B E], frets: 22)
10
+ def initialize(notes, tuning: %w[E A D G B E], frets: 22)
11
11
  @notes = notes
12
12
  @tuning = tuning.reverse
13
13
  @frets = frets
14
- @flavor = flavor
15
14
  @ref_note = @notes.first
16
15
  end
17
16
 
@@ -48,14 +47,13 @@ module Coltrane
48
47
  end
49
48
 
50
49
  def place_mark(note)
51
- mark =
52
- case @flavor
53
- when :notes then note.pretty_name.ljust(2, ' ')
54
- when :intervals then (@ref_note - note).name.ljust(2, '-')
55
- when :degrees then @notes.degree(note).to_s.rjust(2, '0')
56
- when :marks then ' ' # '◼◼'
57
- else raise WrongFlavorError
58
- end
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
59
57
 
60
58
  base_hue = (180 + note.integer * 10) % 360 # + 260
61
59
  Paint[
@@ -0,0 +1,122 @@
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]).include?(x)
74
+ y_on_middle = (0...edge[1]).include?(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 CHANGED
@@ -4,13 +4,12 @@ module Coltrane
4
4
  module Cli
5
5
  # Interfaces notes outputting functionality with the lib
6
6
  class Notes
7
- def initialize(notes, on: 'text', desc: nil, flavor: 'notes')
7
+ def initialize(notes, desc: nil)
8
8
  @desc = desc || 'The notes you supplied:'
9
- flavor = flavor.underscore.to_sym
10
- on = on.to_sym
11
9
  notes = Coltrane::NoteSet.new(notes)
12
- @representation = Representation.build(on, notes, flavor)
10
+ @representation = Representation.build(notes)
13
11
  render
12
+ # notes.each {|n| ColtraneSynth::Base.play(n, 0.1) } if Cli.config.sound
14
13
  end
15
14
 
16
15
  def render
data/lib/cli/piano.rb CHANGED
@@ -37,7 +37,7 @@ module Coltrane
37
37
 
38
38
  def replacer(note)
39
39
  # TODO: Maybe extract this method into its own class/module
40
- case @flavor
40
+ case Cli.config.flavor
41
41
  when :intervals then (@ref_note - note).name
42
42
  when :marks then '◼ '
43
43
  when :degrees then @notes.degree(note).to_s.rjust(2, '0')
@@ -11,26 +11,20 @@ module Coltrane
11
11
  @@types[subclass.to_s.split('::').last.underscore.to_sym] = subclass
12
12
  end
13
13
 
14
- def self.build(type, notes, flavor)
15
- raise WrongFlavorError unless ACCEPTED_FLAVORS.include?(flavor)
16
- type = case type
17
- when :ukelele then :ukulele
18
- when :bass then :bass_guitar
19
- else type
20
- end
21
-
14
+ def self.build(notes)
15
+ type = Cli.config.on
16
+ raise WrongFlavorError unless ACCEPTED_FLAVORS.include?(Cli.config.flavor)
22
17
  raise(WrongRepresentationTypeError, type) unless @@types.include?(type)
23
- @@types[type].new(notes, flavor)
18
+ @@types[type].new(notes)
24
19
  end
25
20
 
26
- def initialize(notes, flavor)
21
+ def initialize(notes)
27
22
  @notes = notes
28
- @flavor = flavor
29
23
  @ref_note = notes.first
30
24
  end
31
25
 
32
26
  def hint
33
- case @flavor
27
+ case Cli.config.flavor
34
28
  when :marks then ''
35
29
  # when :notes then "(\u266E means the note is natural, not flat nor sharp)"
36
30
  when :intervals
data/lib/cli/scale.rb CHANGED
@@ -27,8 +27,8 @@ module Coltrane
27
27
  def self.render_search(searched_notes)
28
28
  search = Coltrane::Scale.having_notes(searched_notes)
29
29
  output = []
30
- scale_width = search.results.keys.map(&:size).max
31
- search.results.each do |name, scales_by_tone|
30
+ scale_width = search[:results].keys.map(&:size).max
31
+ search[:results].each do |name, scales_by_tone|
32
32
  output << name.ljust(scale_width + 1, ' ')
33
33
  scales_by_tone.each do |tone_number, notes|
34
34
  p = (notes.size.to_f / searched_notes.size) * 100
@@ -43,9 +43,9 @@ module Coltrane
43
43
  puts output.join
44
44
  end
45
45
 
46
- def initialize(scale, on: :text, flavor: 'degrees')
46
+ def initialize(scale)
47
47
  desc = "This is the #{scale.tone.name} #{scale.name} scale:"
48
- Coltrane::Cli::Notes.new(scale.notes, on: on, desc: desc, flavor: flavor)
48
+ Coltrane::Cli::Notes.new(scale.notes)
49
49
  end
50
50
  end
51
51
  end
data/lib/cli/text.rb CHANGED
@@ -5,7 +5,7 @@ module Coltrane
5
5
  # A text representation
6
6
  class Text < Representation
7
7
  def render
8
- case @flavor
8
+ case Cli.config.flavor
9
9
  when :marks, :notes, :degrees then @notes.pretty_names.join(' ')
10
10
  when :intervals then @notes.map { |n| (@notes.first - n).name }.join(' ')
11
11
  else raise WrongFlavorError
data/lib/cli/ukulele.rb CHANGED
@@ -6,7 +6,7 @@ module Coltrane
6
6
  class Ukulele < Guitar
7
7
  SPECIAL_FRETS = [5, 7, 9, 12].freeze
8
8
 
9
- def initialize(notes, flavor, tuning: %w[G C E A], frets: 12)
9
+ def initialize(notes, tuning: %w[G C E A], frets: 12)
10
10
  super
11
11
  end
12
12
  end
data/lib/coltrane.rb CHANGED
@@ -1,44 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yaml'
4
-
3
+ require 'json'
5
4
  require 'forwardable'
6
- require 'facets/multiton'
7
5
  require 'core_ext'
8
6
  require 'ostruct'
9
7
 
10
- require 'coltrane/version'
11
- require 'coltrane/errors'
12
- require 'coltrane/cadence'
13
-
14
- require 'coltrane/frequency'
15
- require 'coltrane/pitch'
16
- require 'coltrane/pitch_class'
17
- require 'coltrane/note'
18
- require 'coltrane/note_set'
19
-
20
- require 'coltrane/interval'
21
- require 'coltrane/interval_class'
22
- require 'coltrane/unordered_interval_class'
23
- require 'coltrane/interval_sequence'
24
-
25
- require 'coltrane/chord_quality'
26
-
27
- require 'coltrane/chord_substitutions'
28
- require 'coltrane/chord'
29
- require 'coltrane/roman_chord'
30
-
31
- require 'coltrane/classic_scales'
32
- require 'coltrane/scale'
33
-
34
- require 'coltrane/notable_progressions'
35
- require 'coltrane/changes'
36
- require 'coltrane/progression'
37
-
38
- require 'coltrane/mode'
39
8
 
40
9
  # The main module for working with Music Theory
41
10
  module Coltrane
11
+ autoload :Frequency, 'coltrane/frequency'
12
+
42
13
  BASE_OCTAVE = 4
43
14
  BASE_PITCH_INTEGER = 9
44
15
 
@@ -51,4 +22,36 @@ module Coltrane
51
22
  end
52
23
 
53
24
  @base_tuning = Frequency[440].octave(-4)
25
+
26
+ require 'coltrane/version'
27
+ require 'coltrane/errors'
28
+
29
+ autoload :Pitch, 'coltrane/pitch'
30
+ autoload :Voicing, 'coltrane/voicing'
31
+
32
+ autoload :PitchClass, 'coltrane/pitch_class'
33
+ autoload :Note, 'coltrane/note'
34
+ autoload :NoteSet, 'coltrane/note_set'
35
+
36
+ autoload :Interval, 'coltrane/interval'
37
+ autoload :IntervalClass, 'coltrane/interval_class'
38
+ autoload :UnorderedIntervalClass, 'coltrane/unordered_interval_class'
39
+ autoload :IntervalSequence, 'coltrane/interval_sequence'
40
+ autoload :Qualities, 'coltrane/qualities'
41
+ autoload :ChordQuality, 'coltrane/chord_quality'
42
+ autoload :Chord, 'coltrane/chord'
43
+ autoload :ChordSubstitutions, 'coltrane/chord_substitutions'
44
+ autoload :RomanChord, 'coltrane/roman_chord'
45
+
46
+ autoload :ClassicScales, 'coltrane/classic_scales'
47
+ autoload :Scale, 'coltrane/scale'
48
+ autoload :CircleOfFifths, 'coltrane/circle_of_fifths'
49
+ autoload :DiatonicScale, 'coltrane/diatonic_scale'
50
+ autoload :Key, 'coltrane/key'
51
+
52
+ autoload :NotableProgressions, 'coltrane/notable_progressions'
53
+ autoload :Changes, 'coltrane/changes'
54
+ autoload :Cadence, 'coltrane/cadence'
55
+ autoload :Progression, 'coltrane/progression'
56
+ autoload :Mode, 'coltrane/mode'
54
57
  end