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
data/lib/cli/text.rb DELETED
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- module Cli
5
- # A text representation
6
- class Text < Representation
7
- def render
8
- case Cli.config.flavor
9
- when :marks, :notes, :degrees then @notes.pretty_names.join(' ')
10
- when :intervals then @notes.map { |n| (@notes.first - n).name }.join(' ')
11
- else raise WrongFlavorError
12
- end
13
- end
14
- end
15
- end
16
- end
data/lib/cli/ukulele.rb DELETED
@@ -1,14 +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 Ukulele < Guitar
7
- SPECIAL_FRETS = [5, 7, 9, 12].freeze
8
-
9
- def initialize(notes, tuning: %w[G C E A], frets: 12)
10
- super
11
- end
12
- end
13
- end
14
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # A simple caching based on serializing objects into files
5
- # maybe this should changed to save in a single json file
6
- class Cache
7
- class << self
8
- def find_or_record(key, &block)
9
- if @disabled || !(cached = fetch(key))
10
- cached = yield block
11
- record(key, cached)
12
- end
13
- cached
14
- end
15
-
16
- def disable
17
- @disabled = true
18
- end
19
-
20
- def enable
21
- @disabled = false
22
- end
23
-
24
- private
25
-
26
- def dir
27
- dir = File.expand_path('../../../', __FILE__) + '/cache/'
28
- Dir.mkdir(dir) unless Dir.exist?(dir)
29
- dir
30
- end
31
-
32
- def fetch(key)
33
- Marshal.load File.read(dir + key) if File.file?(dir + key)
34
- end
35
-
36
- def record(key, contents)
37
- File.open(dir + key, 'w') do |f|
38
- f.write Marshal.dump(contents)
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # Not done yet
5
- class Cadence
6
- end
7
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # It describe a chord
5
-
6
- class Chord
7
- attr_reader :root_note, :quality, :notes
8
- include ChordSubstitutions
9
-
10
- def initialize(notes: nil, root_note: nil, quality: nil, name: nil)
11
- if notes
12
- notes = NoteSet[*notes] if notes.is_a?(Array)
13
- @notes = notes
14
- @root_note = notes.first
15
- @quality = ChordQuality.new(notes: notes)
16
- elsif root_note && quality
17
- @notes = quality.notes_for(root_note)
18
- @root_note = root_note
19
- @quality = quality
20
- elsif name
21
- @root_note, @quality, @notes = parse_from_name(name)
22
- else
23
- raise WrongKeywordsError,
24
- '[notes:] || [root_note:, quality:] || [name:]'
25
- end
26
- end
27
-
28
- def name
29
- "#{root_note}#{quality}"
30
- end
31
-
32
- alias to_s name
33
-
34
- def pretty_name
35
- "#{root_note.pretty_name}#{quality.name}"
36
- end
37
-
38
- def intervals
39
- IntervalSequence.new(NoteSet.new(notes))
40
- end
41
-
42
- def size
43
- notes.size
44
- end
45
-
46
- def scales
47
- Scale.having_chord(name)
48
- end
49
-
50
- def next_inversion
51
- Chord.new(notes: notes.rotate(1))
52
- end
53
-
54
- def invert(n = 1)
55
- Chord.new(notes: notes.rotate(n))
56
- end
57
-
58
- def previous_inversion
59
- Chord.new(notes: notes.rotate(-1))
60
- end
61
-
62
- def +(other)
63
- case other
64
- when Note, NoteSet, Interval then Chord.new(notes: notes + other)
65
- when Chord then Chord.new(notes: notes + other.notes)
66
- end
67
- end
68
-
69
- def -(other)
70
- case other
71
- when Note, NoteSet, Interval, Numeric then Chord.new(notes: notes - other)
72
- when Chord then Chord.new(notes: notes - other.notes)
73
- end
74
- end
75
-
76
- protected
77
-
78
- def parse_from_name(name)
79
- chord_name, bass = name.match?(/\/9/) ? [name, nil] : name.split('/')
80
- chord_regex = /([A-Z](?:#|b)?)(.*)/
81
- _, root_name, quality_name = chord_name.match(chord_regex).to_a
82
- root = Note[root_name]
83
- quality = ChordQuality.new(name: quality_name, bass: bass)
84
- notes = quality.notes_for(root)
85
- notes << Note[bass] unless bass.nil?
86
- [root, quality, notes]
87
- end
88
- end
89
- end
@@ -1,111 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # It describe the quality of a chord, like maj7 or dim.
5
- class ChordQuality < IntervalSequence
6
- include Qualities
7
- attr_reader :name
8
- # QUALITIES_FILE = File.expand_path("#{'../' * 3}data/qualities.json", __FILE__)
9
-
10
- private
11
-
12
- def self.chord_trie
13
- File.expand_path("#{'../' * 3}data/qualities.json", __FILE__)
14
- trie = QUALITIES
15
-
16
- trie.clone_values from_keys: ['Perfect Unison', 'Major Third'],
17
- to_keys: ['Perfect Unison', 'Major Second'],
18
- suffix: 'sus2'
19
-
20
- trie.clone_values from_keys: ['Perfect Unison', 'Major Third'],
21
- to_keys: ['Perfect Unison', 'Perfect Fourth'],
22
- suffix: 'sus4'
23
- trie.deep_dup
24
- end
25
-
26
- def self.intervals_per_name(quality_names: {}, intervals: [], hash: nil)
27
- hash ||= chord_trie
28
- return quality_names if hash.empty?
29
- if hash['name']
30
- quality_names[hash.delete('name')] = intervals.map { |n| Interval.public_send(n.underscore) }
31
- end
32
- hash.reduce(quality_names) do |memo, (interval, values)|
33
- memo.merge intervals_per_name(hash: values,
34
- quality_names: quality_names,
35
- intervals: intervals + [interval])
36
- end
37
- end
38
-
39
- NAMES = intervals_per_name
40
-
41
- def find_chord(given_interval_names, trie: self.class.chord_trie, last_name: nil)
42
- return if trie.nil?
43
- if given_interval_names.empty?
44
- @found = true
45
- return trie['name']
46
- end
47
- interval = given_interval_names.shift
48
- new_trie = trie[interval.full_name]
49
- find_chord given_interval_names, last_name: (trie['name'] || last_name),
50
- trie: new_trie
51
- end
52
-
53
- def normal_sequence
54
- %i[unison third! fifth sixth! seventh ninth eleventh! thirteenth]
55
- end
56
-
57
- def sus2_sequence
58
- %i[unison second! fifth sixth! seventh ninth eleventh! thirteenth]
59
- end
60
-
61
- def sus4_sequence
62
- %i[unison fourth! fifth sixth! seventh ninth eleventh! thirteenth]
63
- end
64
-
65
- def retrieve_chord_intervals(chord_sequence = normal_sequence)
66
- ints = IntervalSequence.new(*self)
67
- chord_sequence.map do |int_sym|
68
- next unless interval_name = ints.public_send(int_sym)
69
- ints.delete_if { |i| i.cents == IntervalClass.new(interval_name).cents }
70
- interval_name
71
- end
72
- end
73
-
74
- public
75
-
76
- def get_name
77
- find_chord(retrieve_chord_intervals.compact) ||
78
- find_chord(retrieve_chord_intervals(sus2_sequence).compact) ||
79
- find_chord(retrieve_chord_intervals(sus4_sequence).compact) ||
80
- raise(ChordNotFoundError)
81
- end
82
-
83
- def suspension_type
84
- if has_major_second?
85
- 'sus2'
86
- else has_fourth?
87
- 'sus4'
88
- end
89
- end
90
-
91
- def initialize(name: nil, notes: nil, bass: nil)
92
- if name
93
- @name = bass.nil? ? name : [name, bass].join('/')
94
- super(*intervals_from_name(name))
95
- elsif notes
96
- super(notes: notes)
97
- @name = get_name
98
- else
99
- raise WrongKeywordsError, '[name:] || [notes:]'
100
- end
101
- end
102
-
103
- alias to_s name
104
-
105
- private
106
-
107
- def intervals_from_name(name)
108
- NAMES[name] || NAMES["M#{name}"] || raise(ChordNotFoundError)
109
- end
110
- end
111
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- module ChordSubstitutions
5
- def tritone_substitution
6
- self + Interval.augmented_fourth
7
- end
8
- end
9
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- class CircleOfFifths
5
- attr_reader :notes
6
-
7
- LETTER_SEQUENCE = %w[C G D A E B F].freeze
8
-
9
- def initialize(start_note = Note['C'], size = Float::INFINITY)
10
- index = letters.index(start_note.name[0])
11
- @notes = fifths(note: start_note - Interval.perfect_fifth,
12
- size: size,
13
- index: (index - 1) % LETTER_SEQUENCE.size)
14
- end
15
-
16
- private
17
-
18
- def fifths(note:, index: nil, notes: [], size:)
19
- return notes if size == 0
20
- return notes if notes.any? && note.as(letters(index)).name == notes.first.name
21
- fifths note: note + Interval.perfect_fifth,
22
- notes: notes + [note.as(letters(index))],
23
- index: index + 1,
24
- size: size - 1
25
- end
26
-
27
- def letters(i = nil)
28
- i.nil? ? LETTER_SEQUENCE : LETTER_SEQUENCE[i % LETTER_SEQUENCE.size]
29
- end
30
- end
31
- end
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- # This module deals with well known scales on music
5
- module ClassicScales
6
- SCALES = {
7
- 'Pentatonic Major' => [2, 2, 3, 2, 3],
8
- 'Blues Major' => [2, 1, 1, 3, 2, 3],
9
- 'Harmonic Minor' => [2, 1, 2, 2, 1, 3, 1],
10
- 'Hungarian Minor' => [2, 1, 2, 1, 1, 3, 1],
11
- 'Pentatonic Minor' => [3, 2, 2, 3, 2],
12
- 'Blues Minor' => [3, 2, 1, 1, 3, 2],
13
- 'Whole Tone' => [2, 2, 2, 2, 2, 2],
14
- 'Flamenco' => [1, 3, 1, 2, 1, 2, 2],
15
- 'Chromatic' => [1] * 12
16
- }.freeze
17
-
18
- GREEK_MODES = %w[
19
- Ionian
20
- Dorian
21
- Phrygian
22
- Lydian
23
- Mixolydian
24
- Aeolian
25
- Locrian
26
- ].freeze
27
-
28
- # Creates factories for scales
29
- SCALES.each do |name, distances|
30
- define_method name.underscore do |tone = 'C', mode = 1|
31
- new(*distances, tone: tone, mode: mode, name: name)
32
- end
33
- end
34
-
35
- # Creates factories for Greek Modes
36
- GREEK_MODES.each_with_index do |mode, index|
37
- mode_name = mode
38
- mode_n = index + 1
39
- define_method mode.underscore do |tone = 'C'|
40
- new(notes: DiatonicScale.new(tone).notes.rotate(index))
41
- end
42
- end
43
-
44
- # Factories for the diatonic scale
45
- def major(note = 'C')
46
- DiatonicScale.new(note)
47
- end
48
-
49
- def minor(note = 'A')
50
- DiatonicScale.new(note, major: false)
51
- end
52
-
53
- alias diatonic major
54
- alias natural_minor minor
55
- alias pentatonic pentatonic_major
56
- alias blues blues_major
57
-
58
- def known_scales
59
- SCALES.keys + ['Major', 'Natural Minor']
60
- end
61
-
62
- # List of scales appropriate for search
63
- def standard_scales
64
- known_scales - ['Chromatic']
65
- end
66
-
67
- def fetch(name, tone = nil)
68
- Coltrane::Scale.public_send(name, tone)
69
- end
70
-
71
- def having_notes(notes)
72
- format = { scales: [], results: {} }
73
- standard_scales.each_with_object(format) do |name, output|
74
- PitchClass.all.each.map do |tone|
75
- scale = send(name.underscore, tone)
76
- output[:results][name] ||= {}
77
- next if output[:results][name].key?(tone.integer)
78
- output[:scales] << scale if scale.include?(notes)
79
- output[:results][name][tone.integer] = scale.notes & notes
80
- end
81
- end
82
- end
83
-
84
- def having_chords(*chords)
85
- should_create = !chords.first.is_a?(Chord)
86
- notes = chords.reduce(NoteSet[]) do |memo, c|
87
- memo + (should_create ? Chord.new(name: c) : c).notes
88
- end
89
- having_notes(notes)
90
- end
91
-
92
- alias having_chord having_chords
93
- end
94
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Coltrane
4
- class DiatonicScale < Scale
5
- def initialize(tone, major: true)
6
- @major = major
7
- tone = Note[tone]
8
- notes = CircleOfFifths.new(tone - (major? ? 0 : 9), 7).notes.sort
9
- super notes: notes.rotate(notes.index(tone))
10
- end
11
-
12
- def name
13
- major? ? 'Major' : 'Natural Minor'
14
- end
15
-
16
- def relative_minor
17
- minor? ? self : self.class.new(@tone + 9, major: false)
18
- end
19
-
20
- def relative_major
21
- major? ? self : self.class.new(@tone - 9, major: true)
22
- end
23
-
24
- def relative
25
- major? ? relative_minor : relative_major
26
- end
27
-
28
- def major?
29
- !!@major
30
- end
31
-
32
- def minor?
33
- !@major
34
- end
35
- end
36
- end