coltrane 0.0.2

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 (72) hide show
  1. checksums.yaml +7 -0
  2. data/.bundle/config +2 -0
  3. data/.rspec +2 -0
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/Gemfile +17 -0
  6. data/Gemfile.lock +164 -0
  7. data/Guardfile +71 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +41 -0
  10. data/Rakefile +6 -0
  11. data/bin/_guard-core +17 -0
  12. data/bin/bundler +17 -0
  13. data/bin/coderay +17 -0
  14. data/bin/coltrane +11 -0
  15. data/bin/console +14 -0
  16. data/bin/erubis +17 -0
  17. data/bin/guard +17 -0
  18. data/bin/htmldiff +17 -0
  19. data/bin/kill-pry-rescue +17 -0
  20. data/bin/ldiff +17 -0
  21. data/bin/listen +17 -0
  22. data/bin/nokogiri +17 -0
  23. data/bin/pry +17 -0
  24. data/bin/rackup +17 -0
  25. data/bin/rails +17 -0
  26. data/bin/rake +17 -0
  27. data/bin/rdoc +17 -0
  28. data/bin/rescue +17 -0
  29. data/bin/ri +17 -0
  30. data/bin/rspec +17 -0
  31. data/bin/rubocop +17 -0
  32. data/bin/ruby-parse +17 -0
  33. data/bin/ruby-rewrite +17 -0
  34. data/bin/setup +8 -0
  35. data/bin/sprockets +17 -0
  36. data/bin/thor +17 -0
  37. data/bin/tilt +17 -0
  38. data/coltrane.gemspec +35 -0
  39. data/db/cache.sqlite3 +0 -0
  40. data/db/cache_test.sqlite3 +0 -0
  41. data/db/config.yml +11 -0
  42. data/db/schema.rb +30 -0
  43. data/lib/coltrane.rb +48 -0
  44. data/lib/coltrane/cadence.rb +4 -0
  45. data/lib/coltrane/chord.rb +81 -0
  46. data/lib/coltrane/chord_cache.rb +4 -0
  47. data/lib/coltrane/chord_quality.rb +48 -0
  48. data/lib/coltrane/classic_scales.rb +134 -0
  49. data/lib/coltrane/essential_guitar_chords.rb +82 -0
  50. data/lib/coltrane/fret_set.rb +0 -0
  51. data/lib/coltrane/guitar.rb +15 -0
  52. data/lib/coltrane/guitar_chord.rb +50 -0
  53. data/lib/coltrane/guitar_chord_finder.rb +98 -0
  54. data/lib/coltrane/guitar_note.rb +50 -0
  55. data/lib/coltrane/guitar_note_set.rb +61 -0
  56. data/lib/coltrane/guitar_representation.rb +96 -0
  57. data/lib/coltrane/guitar_string.rb +52 -0
  58. data/lib/coltrane/interval.rb +33 -0
  59. data/lib/coltrane/interval_sequence.rb +70 -0
  60. data/lib/coltrane/interval_set.rb +23 -0
  61. data/lib/coltrane/mode.rb +0 -0
  62. data/lib/coltrane/note.rb +98 -0
  63. data/lib/coltrane/note_set.rb +50 -0
  64. data/lib/coltrane/piano_representation.rb +58 -0
  65. data/lib/coltrane/pitch.rb +27 -0
  66. data/lib/coltrane/progression.rb +4 -0
  67. data/lib/coltrane/qualities.rb +115 -0
  68. data/lib/coltrane/scale.rb +139 -0
  69. data/lib/coltrane/scale_cache.rb +4 -0
  70. data/lib/coltrane/scale_chord.rb +4 -0
  71. data/lib/coltrane/version.rb +3 -0
  72. metadata +144 -0
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'coltrane/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "coltrane"
8
+ spec.version = Coltrane::VERSION
9
+ spec.authors = ["Pedro Maciel"]
10
+ spec.email = ["pedro@pedromaciel.com"]
11
+
12
+ spec.summary = %q{It deals with all sorts of calculations around music theory
13
+ and allows for graphical representations of it}
14
+ spec.homepage = "http://github.com/pedrozath/coltrane"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against " \
23
+ "public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.14"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ end
Binary file
Binary file
@@ -0,0 +1,11 @@
1
+ production:
2
+ adapter: sqlite3
3
+ database: db/cache.sqlite3
4
+ pool: 5
5
+ timeout: 5000
6
+
7
+ test:
8
+ adapter: sqlite3
9
+ database: db/cache_test.sqlite3
10
+ pool: 5
11
+ timeout: 5000
@@ -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
@@ -0,0 +1,48 @@
1
+ # $LOAD_PATH << __dir__
2
+
3
+ # require 'bundler'
4
+
5
+ # Bundler.require
6
+
7
+ # ActiveSupport::Inflector.inflections do |inflect|
8
+ # inflect.irregular 'cache', 'caches'
9
+ # end
10
+
11
+ # ActiveRecord::Base.establish_connection YAML.load_file("#{__dir__}/../db/config.yml")['production']
12
+ # require "#{__dir__}/../db/schema.rb"
13
+
14
+ require 'coltrane/cadence'
15
+
16
+ require 'coltrane/qualities'
17
+ require 'coltrane/chord_quality'
18
+ require 'coltrane/chord'
19
+
20
+ require 'coltrane/classic_scales'
21
+ require 'coltrane/fret_set'
22
+
23
+ # require 'coltrane/piano_representation'
24
+ # require 'coltrane/guitar_representation'
25
+ require 'coltrane/essential_guitar_chords'
26
+ require 'coltrane/guitar_chord_finder'
27
+ require 'coltrane/guitar_note_set'
28
+ require 'coltrane/guitar_chord'
29
+ require 'coltrane/guitar_note'
30
+ require 'coltrane/guitar_string'
31
+ require 'coltrane/guitar'
32
+
33
+ require 'coltrane/interval_sequence'
34
+ require 'coltrane/interval_set'
35
+ require 'coltrane/interval'
36
+
37
+ require 'coltrane/note_set'
38
+ require 'coltrane/note'
39
+
40
+ require 'coltrane/pitch'
41
+ require 'coltrane/progression'
42
+ require 'coltrane/scale'
43
+ require 'coltrane/mode'
44
+
45
+ # require 'coltrane/scale_chord'
46
+ # require 'coltrane/chord_cache'
47
+ # require 'coltrane/scale_cache'
48
+ # require 'terminal_input'
@@ -0,0 +1,4 @@
1
+ module Coltrane
2
+ class Cadence
3
+ end
4
+ end
@@ -0,0 +1,81 @@
1
+ module Coltrane
2
+ # It describe a chord
3
+ class Chord
4
+ attr_reader :root_note, :quality
5
+
6
+ def initialize(arg)
7
+ @root_note, @quality = case arg
8
+ when String then note_and_quality_from_name(arg)
9
+ when GuitarNoteSet then [arg.root_note, arg.chord_quality]
10
+ when Array
11
+ case arg[0]
12
+ when String then note_and_quality_from_notes(*arg.map{|a| Note.new(a)})
13
+ when Note then note_and_quality_from_notes(*arg)
14
+ end
15
+ end
16
+ end
17
+
18
+ def guitar_chords
19
+ GuitarChordFinder.by_chord(self)
20
+ end
21
+
22
+ def guitar_notes_for_root
23
+ root_note.guitar_notes
24
+ end
25
+
26
+ def name
27
+ return '—' if root_note.nil? || quality.nil?
28
+ "#{root_note.name}#{quality.name}"
29
+ end
30
+
31
+ def notes
32
+ quality.intervals.each_with_object([]) do |interval, notes|
33
+ notes << Note.new(root_note.number + interval)
34
+ end
35
+ end
36
+
37
+ def intervals
38
+ IntervalSequence.new(NoteSet.new(notes))
39
+ end
40
+
41
+ def on_guitar
42
+ name + "\n" +
43
+ NoteSet.new(notes).guitar_notes.render(root_note) + "\n\n"
44
+ end
45
+
46
+ def on_piano
47
+ PianoRepresentation.render_intervals(notes, root_note)
48
+ end
49
+
50
+ def size
51
+ notes.size
52
+ end
53
+
54
+ def scales
55
+ Scale.having_chord(self.name)
56
+ end
57
+
58
+ def next_inversion
59
+ Chord.new(notes.rotate(1))
60
+ end
61
+
62
+ def invert(n=1)
63
+ Chord.new(notes.rotate(n))
64
+ end
65
+
66
+ def previous_inversion
67
+ Chord.new(notes.rotate(-1))
68
+ end
69
+
70
+ protected
71
+
72
+ def note_and_quality_from_name(chord_name)
73
+ _, name, quality = chord_name.match(/([A-Z]#?)(.*)/).to_a
74
+ [Note.new(name), ChordQuality.new_from_string(quality)]
75
+ end
76
+
77
+ def note_and_quality_from_notes(*notes)
78
+ [notes.first, ChordQuality.new_from_notes(notes)]
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,4 @@
1
+ class ChordCache < ActiveRecord::Base
2
+ has_many :scale_chords
3
+ has_many :scale_caches, through: :scale_chords
4
+ end
@@ -0,0 +1,48 @@
1
+
2
+
3
+ module Coltrane
4
+ # It describe the quality of a chord, like maj7 or dim.
5
+ class ChordQuality
6
+ attr_reader :name
7
+ include Qualities
8
+
9
+ def initialize(interval_sequence)
10
+ if interval_sequence.class != IntervalSequence
11
+ interval_sequence = IntervalSequence.new(interval_sequence)
12
+ end
13
+ @name = find_chord_name(interval_sequence)
14
+ end
15
+
16
+ def self.new_from_notes(notes)
17
+ note_set = NoteSet.new(notes) unless notes.class == NoteSet
18
+ new(note_set.interval_sequence.reordered)
19
+ end
20
+
21
+ def self.new_from_pitches(*pitches)
22
+ notes = pitches.sort_by(&:number)
23
+ .collect(&:note)
24
+ .collect(&:name)
25
+ .uniq
26
+
27
+ new_from_notes(notes)
28
+ end
29
+
30
+ def self.new_from_string(quality_string)
31
+ new(CHORD_QUALITIES[quality_string])
32
+ end
33
+
34
+ def intervals
35
+ CHORD_QUALITIES[name]
36
+ end
37
+
38
+ protected
39
+
40
+ def find_chord_name(note_intervals, inversion = 0)
41
+ inversion >= note_intervals.all.size && return
42
+ CHORD_QUALITIES.key(note_intervals.numbers) ||
43
+ CHORD_QUALITIES.key(note_intervals.numbers.sort) ||
44
+ find_chord_name(note_intervals.next_inversion, inversion + 1) ||
45
+ nil
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,134 @@
1
+ require 'ostruct'
2
+
3
+ module ClassicScales
4
+
5
+ SCALES = {
6
+ 'Major' => [2,2,1,2,2,2,1],
7
+ 'Natural Minor' => [2,1,2,2,1,2,2],
8
+ 'Harmonic Minor' => [2,1,2,2,1,3,1],
9
+ 'Hungarian Minor' => [2,1,2,1,1,3,1],
10
+ 'Pentatonic Major' => [2,2,3,2,3],
11
+ 'Pentatonic Minor' => [3,2,2,3,2],
12
+ 'Blues Major' => [2,1,1,3,2,3],
13
+ 'Blues Minor' => [3,2,1,1,3,2],
14
+ 'Whole Tone' => [2,2,2,2,2,2]
15
+ }
16
+
17
+ def major(tone='C', mode=1)
18
+ new(*SCALES['Major'], tone: tone, mode: mode)
19
+ end
20
+
21
+ def natural_minor(tone='C', mode=1)
22
+ new(*SCALES['Natural Minor'], tone: tone, mode: mode)
23
+ end
24
+
25
+ def harmonic_minor(tone='C', mode=1)
26
+ new(*SCALES['Harmonic Minor'], tone: tone, mode: mode)
27
+ end
28
+
29
+ def hungarian_minor(tone='C', mode=1)
30
+ new(*SCALES['Hungarian Minor'], tone: tone, mode: mode)
31
+ end
32
+
33
+ def pentatonic_major(tone='C', mode=1)
34
+ new(*SCALES['Pentatonic Major'], tone: tone, mode: mode)
35
+ end
36
+
37
+ def pentatonic_minor(tone='C', mode=1)
38
+ new(*SCALES['Pentatonic Minor'], tone: tone, mode: mode)
39
+ end
40
+
41
+ def blues_major(tone='C', mode=1)
42
+ new(*SCALES['Blues Major'], tone: tone, mode: mode)
43
+ end
44
+
45
+ def blues_minor(tone='C', mode=1)
46
+ new(*SCALES['Blues Minor'], tone: tone, mode: mode)
47
+ end
48
+
49
+ def whole_tone(tone='C', mode=1)
50
+ new(*SCALES['Blues Minor'], tone: tone, mode: mode)
51
+ end
52
+
53
+ def chromatic(tone='C', mode=1)
54
+ new(*([1]*12), tone: tone, mode: mode)
55
+ end
56
+
57
+ def ionian(tone='C')
58
+ new(*SCALES['Major'], tone: tone, mode: 1)
59
+ end
60
+
61
+ def dorian(tone='C')
62
+ new(*SCALES['Major'], tone: tone, mode: 2)
63
+ end
64
+
65
+ def phrygian(tone='C')
66
+ new(*SCALES['Major'], tone: tone, mode: 3)
67
+ end
68
+
69
+ def lydian(tone='C')
70
+ new(*SCALES['Major'], tone: tone, mode: 4)
71
+ end
72
+
73
+ def mixolydian(tone='C')
74
+ new(*SCALES['Major'], tone: tone, mode: 5)
75
+ end
76
+
77
+ def aeolian(tone='C')
78
+ new(*SCALES['Major'], tone: tone, mode: 6)
79
+ end
80
+
81
+ def locrian(tone='C')
82
+ new(*SCALES['Major'], tone: tone, mode: 7)
83
+ end
84
+
85
+ def having_chord(chord)
86
+ puts "\nSearching #{chord} in scales:\n\n"
87
+ scale_name_size = SCALES.keys.map(&:size).max
88
+ chord = Chord.new(chord)
89
+ SCALES.each_with_object([]) do |scale_obj, results|
90
+ scale_name, intervals = scale_obj
91
+ print Paint[scale_name.ljust(scale_name_size), 'yellow']
92
+ Note.all.each do |note|
93
+ found_sth = false
94
+ print ' '
95
+ scale = Scale.new(*intervals, tone: note.name)
96
+ degree = scale.degree_of_chord(chord)
97
+ unless degree.nil?
98
+ found_sth = true
99
+ results << OpenStruct.new({ name: "#{note.name} #{scale_name}",
100
+ degree: degree,
101
+ scale: scale})
102
+ end
103
+ print Paint[note.name, found_sth ? 'yellow' : 'black']
104
+ end
105
+ print "\n"
106
+ end
107
+ end
108
+
109
+ def having_notes(*notes)
110
+ puts "\nSearching #{notes.join(', ')} in scales:\n\n"
111
+ scale_name_size = SCALES.keys.map(&:size).max
112
+ notes = notes.map { |n| Note.new(n) }
113
+ SCALES.each_with_object([]) do |scale_obj, results|
114
+ scale_name, intervals = scale_obj
115
+ print Paint[scale_name.ljust(scale_name_size), 'yellow']
116
+ Note.all.each do |note|
117
+ found_sth = false
118
+ print ' '
119
+ scale = Scale.new(*intervals, tone: note.name)
120
+ notes_included = scale.include_notes?(*notes)
121
+ if notes_included == notes
122
+ found_sth = true
123
+ results << OpenStruct.new({
124
+ name: "#{note.name} #{scale_name}",
125
+ notes: notes_included
126
+ })
127
+ end
128
+ print Paint[note.name, found_sth ? 'yellow' : 'black']
129
+ print Paint["(#{notes_included.count || 0})", found_sth ? 'gray' : 'black']
130
+ end
131
+ print "\n"
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,82 @@
1
+ module Coltrane
2
+ module EssentialGuitarChords
3
+ CHORDS = {
4
+ 'C' => [nil, 3, 2, 0, 1, 0],
5
+ 'Cm' => [nil, 10, 10, 8, 6, 4],
6
+ 'Cmaj7' => [nil, 3, 5, 4, 5, nil],
7
+ 'C7' => [nil, 10, 10, 9, 11, nil],
8
+ 'Cm7' => [6,6,5,5,nil,nil],
9
+ 'Cm7b5' => [nil, 3,4,3,4, nil],
10
+ 'C#' => [nil,8,6,6,6,nil],
11
+ 'C#m' => [nil,5,6,6,5,nil],
12
+ 'C#maj7' => [4,4,3,5,nil,nil],
13
+ 'C#7' => [nil, nil, 6,6,6,7],
14
+ 'C#m7' => [nil, 2,2,1,2],
15
+ 'C#m7b5' => [nil, 4,5,4,5, nil],
16
+ 'D' => [5,5,4,nil,nil,nil],
17
+ 'Dm' => [10,nil,nil,10,10,nil],
18
+ 'Dmaj7' => [5,5,4,6,nil,nil],
19
+ 'D7' => [5,5,4,5,nil,nil],
20
+ 'Dm7' => [nil,3,3,2,3,nil],
21
+ 'Dm7b5' => [],
22
+ 'D#' => [],
23
+ 'D#m' => [],
24
+ 'D#maj7' => [],
25
+ 'D#7' => [],
26
+ 'D#m7' => [],
27
+ 'D#m7b5' => [],
28
+ 'E' => [],
29
+ 'Em' => [],
30
+ 'Emaj7' => [],
31
+ 'E7' => [],
32
+ 'Em7' => [],
33
+ 'Em7b5' => [],
34
+ 'F' => [],
35
+ 'Fm' => [],
36
+ 'Fmaj7' => [],
37
+ 'F7' => [],
38
+ 'Fm7' => [],
39
+ 'Fm7b5' => [],
40
+ 'F#' => [],
41
+ 'F#m' => [],
42
+ 'F#maj7' => [],
43
+ 'F#7' => [],
44
+ 'F#m7' => [],
45
+ 'F#m7b5' => [],
46
+ 'G' => [],
47
+ 'Gm' => [],
48
+ 'Gmaj7' => [],
49
+ 'G7' => [],
50
+ 'Gm7' => [],
51
+ 'Gm7b5' => [],
52
+ 'G#' => [],
53
+ 'G#m' => [],
54
+ 'G#maj7' => [],
55
+ 'G#7' => [],
56
+ 'G#m7' => [],
57
+ 'G#m7b5' => [],
58
+ 'A' => [],
59
+ 'Am' => [],
60
+ 'Amaj7' => [],
61
+ 'A7' => [],
62
+ 'Am7' => [],
63
+ 'Am7b5' => [],
64
+ 'A#' => [],
65
+ 'A#m' => [],
66
+ 'A#maj7' => [],
67
+ 'A#7' => [],
68
+ 'A#m7' => [],
69
+ 'A#m7b5' => [],
70
+ 'B' => [],
71
+ 'Bm' => [],
72
+ 'Bmaj7' => [],
73
+ 'B7' => [],
74
+ 'Bm7' => [],
75
+ 'Bm7b5' => []
76
+ }
77
+
78
+ def [](name)
79
+ GuitarChord.new_from_frets(*CHORDS[name])
80
+ end
81
+ end
82
+ end