coltrane 1.0.2 → 1.0.11

Sign up to get free protection for your applications and to get access to all the features.
data/lib/cli/scale.rb CHANGED
@@ -1,8 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Coltrane
4
2
  module Cli
5
- # Interfaces commands with the scales functionality
6
3
  class Scale
7
4
  def self.parse(str)
8
5
  *scale_name, tone = str.split('-')
@@ -15,10 +12,8 @@ module Coltrane
15
12
  notes = NoteSet[*notes]
16
13
  elsif chords.any?
17
14
  puts "\nSearching for scales containing #{chords.join(', ')}:\n\n"
18
- notes = chords.reduce(NoteSet[]) do |memo, c|
19
- memo + Coltrane::Chord.new(name: c).notes
20
- end
21
- else raise BadFindScales
15
+ notes = chords.reduce(NoteSet[]) {|memo, c| memo + Coltrane::Chord.new(name: c).notes }
16
+ else raise BadFindScales.new
22
17
  end
23
18
  render_search(notes)
24
19
  puts "\nUnderlined means the scale has all notes"
@@ -29,24 +24,25 @@ module Coltrane
29
24
  output = []
30
25
  scale_width = search.results.keys.map(&:size).max
31
26
  search.results.each do |name, scales_by_tone|
32
- output << name.ljust(scale_width + 1, ' ')
27
+ output << name.ljust(scale_width+1, ' ')
33
28
  scales_by_tone.each do |tone_number, notes|
34
- p = (notes.size.to_f / searched_notes.size) * 100
35
- l = p == 100 ? p : (p + 20) * 0.4
36
- und = p == 100 ? :underline : nil
37
- color = Color::HSL.new(30, p, l / 2).html
29
+ p = notes.size.to_f / searched_notes.size
30
+ l = p == 1 ? p : (p + 0.2) * 0.4
31
+ hue, val, sat = 30, val = (l * 100).round, sat = p
32
+ und = p == 1 ? :underline : nil
33
+ color = "hsv(#{hue},#{sat},#{val})".paint.to_hex
38
34
  output << Paint["#{Note[tone_number].name}(#{notes.size})", color, und]
39
- output << ' '
35
+ output << " "
40
36
  end
41
37
  output << "\n"
42
38
  end
43
39
  puts output.join
44
40
  end
45
41
 
46
- def initialize(scale, on: :text, flavor: 'degrees')
42
+ def initialize(scale, on: :text, flavor: 'degrees', notes: [], chords: [])
47
43
  desc = "This is the #{scale.tone.name} #{scale.name} scale:"
48
44
  Coltrane::Cli::Notes.new(scale.notes, on: on, desc: desc, flavor: flavor)
49
45
  end
50
46
  end
51
47
  end
52
- end
48
+ end
data/lib/cli/text.rb CHANGED
@@ -1,16 +1,13 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Coltrane
4
2
  module Cli
5
- # A text representation
6
3
  class Text < Representation
7
4
  def render
8
5
  case @flavor
9
6
  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
7
+ when :intervals then @notes.map {|n| (@notes.first - n).name}.join(' ')
8
+ else raise WrongFlavorError.new
12
9
  end
13
10
  end
14
11
  end
15
12
  end
16
- end
13
+ end
data/lib/cli/ukulele.rb CHANGED
@@ -1,14 +1,11 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Coltrane
4
2
  module Cli
5
- # Renders notes in a common most popular ukulele scheme
6
3
  class Ukulele < Guitar
7
- SPECIAL_FRETS = [5, 7, 9, 12].freeze
4
+ SPECIAL_FRETS = [5, 7, 9, 12]
8
5
 
9
6
  def initialize(notes, flavor, tuning: %w[G C E A], frets: 12)
10
7
  super
11
8
  end
12
9
  end
13
10
  end
14
- end
11
+ end
@@ -1,8 +1,6 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'mercenary'
4
2
  require 'paint'
5
- require 'color'
3
+ require 'chroma'
6
4
 
7
5
  require 'cli/errors'
8
6
 
@@ -15,4 +13,4 @@ require 'cli/ukulele'
15
13
 
16
14
  require 'cli/notes'
17
15
  require 'cli/chord'
18
- require 'cli/scale'
16
+ require 'cli/scale'
data/lib/coltrane.rb CHANGED
@@ -1,8 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'forwardable'
4
2
  require 'facets/multiton'
5
- require 'core_ext'
6
3
  require 'ostruct'
7
4
 
8
5
  require 'coltrane/version'
@@ -27,4 +24,4 @@ require 'coltrane/note'
27
24
  require 'coltrane/pitch'
28
25
  require 'coltrane/progression'
29
26
  require 'coltrane/scale'
30
- require 'coltrane/mode'
27
+ require 'coltrane/mode'
@@ -1,35 +1,34 @@
1
- # frozen_string_literal: true
2
-
3
1
  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
2
  class Cache
7
3
  class << self
8
4
  def find_or_record(key, &block)
9
- unless (cached = fetch(key))
5
+ if (cached = fetch(key))
6
+ return cached
7
+ else
10
8
  cached = yield block
11
9
  record(key, cached)
10
+ cached
12
11
  end
13
- cached
14
12
  end
15
13
 
16
14
  private
17
15
 
18
16
  def dir
19
- dir = File.expand_path('../../../', __FILE__) + '/cache/'
17
+ dir = File.expand_path('../../../', __FILE__) + "/cache/"
20
18
  Dir.mkdir(dir) unless Dir.exist?(dir)
21
19
  dir
22
20
  end
23
21
 
24
22
  def fetch(key)
25
- Marshal.load File.read(dir + key) if File.file?(dir + key)
23
+ return unless File.file?(dir+key)
24
+ Marshal.load File.read(dir+key)
26
25
  end
27
26
 
28
27
  def record(key, contents)
29
- File.open(dir + key, 'w') do |f|
28
+ File.open(dir+key, "w") do |f|
30
29
  f.write Marshal.dump(contents)
31
30
  end
32
31
  end
33
32
  end
34
33
  end
35
- end
34
+ end
@@ -1,6 +1,4 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Coltrane
4
2
  class Cadence
5
3
  end
6
- end
4
+ end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Coltrane
4
2
  # It describe a chord
5
3
  class Chord < NoteSet
@@ -18,25 +16,19 @@ module Coltrane
18
16
  elsif !name.nil?
19
17
  @root_note, @quality, @notes = parse_from_name(name)
20
18
  else
21
- raise WrongKeywordsError,
22
- '[notes:] || [root_note:, quality:] || [name:]'
19
+ raise WrongKeywords.new('[notes:] || [root_note:, quality:] || [name:]')
23
20
  end
24
21
  end
25
22
 
26
23
  def name
27
- return @notes.names.join('/') unless named?
24
+ return @notes.names.join('/') if !named?
28
25
  "#{root_note.name}#{quality.name}"
29
26
  end
30
27
 
31
- def pretty_name
32
- return @notes.names.join('/') unless named?
33
- "#{root_note.pretty_name}#{quality.name}"
34
- end
35
-
36
28
  def named?
37
29
  notes.size >= 3 &&
38
- !root_note.nil? &&
39
- !quality&.name.nil?
30
+ !root_note.nil? &&
31
+ !quality&.name.nil?
40
32
  end
41
33
 
42
34
  def intervals
@@ -48,14 +40,14 @@ module Coltrane
48
40
  end
49
41
 
50
42
  def scales
51
- Scale.having_chord(name)
43
+ Scale.having_chord(self.name)
52
44
  end
53
45
 
54
46
  def next_inversion
55
47
  Chord.new(notes.rotate(1))
56
48
  end
57
49
 
58
- def invert(n = 1)
50
+ def invert(n=1)
59
51
  Chord.new(notes.rotate(n))
60
52
  end
61
53
 
@@ -66,11 +58,11 @@ module Coltrane
66
58
  protected
67
59
 
68
60
  def parse_from_name(name)
69
- _, name, quality_name = name.match(/([A-Z](?:#|b)?)(.*)/).to_a
61
+ _, name, quality_name = name.match(/([A-Z]#?)(.*)/).to_a
70
62
  root = Note[name]
71
63
  quality = ChordQuality.new(name: quality_name)
72
64
  notes = quality.notes_for(root)
73
65
  [root, quality, notes]
74
66
  end
75
67
  end
76
- end
68
+ 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
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Coltrane
4
2
  # It describe the quality of a chord, like maj7 or dim.
5
3
  class ChordQuality < IntervalSequence
@@ -8,15 +6,18 @@ module Coltrane
8
6
 
9
7
  def initialize(name: nil, notes: nil)
10
8
  if !name.nil?
11
- raise ChordNotFoundError unless (intervals = CHORD_QUALITIES[name])
12
- @name = name
13
- super(intervals: intervals)
9
+ if(intervals = CHORD_QUALITIES[name])
10
+ @name = name
11
+ super(intervals: intervals)
12
+ else
13
+ raise ChordNotFoundError.new
14
+ end
14
15
  elsif !notes.nil?
15
16
  super(notes: notes)
16
17
  @name = CHORD_QUALITIES.key(intervals_semitones)
17
18
  else
18
- raise WrongKeywordsError, '[name:] || [notes:]'
19
+ raise WrongKeywords.new('[name:] || [notes:]')
19
20
  end
20
21
  end
21
22
  end
22
- end
23
+ end
@@ -1,16 +1,13 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Coltrane
4
- # It's totally a wip yet.
5
2
  module ClassicProgressions
6
3
  PROGRESSIONS = {
7
- pop: [:major, [1, 5, 6, 4]],
8
- fifties: [:major, [1, 6, 4, 5]],
9
- blues: [:major, [1, 4, 1, 5, 4, 1]],
10
- jazz: [:major, [2, 5, 1]],
11
- jazz_minor: [:minor, [2, 5, 1]],
12
- andalusian: [:minor, [1, 7, 6, 5]]
13
- }.freeze
4
+ pop: [:major, [1,5,6,4]],
5
+ fifties: [:major, [1,6,4,5]],
6
+ blues: [:major, [1,4,1,5,4,1]],
7
+ jazz: [:major, [2,5,1]],
8
+ jazz_minor: [:minor, [2,5,1]],
9
+ andalusian: [:minor, [1,7,6,5]]
10
+ }
14
11
 
15
12
  def pop(tone)
16
13
  scale, degrees = PROGRESSIONS[:pop]
@@ -1,28 +1,36 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Coltrane
4
- # This module deals with well known scales on music
5
2
  module ClassicScales
3
+
6
4
  SCALES = {
7
- 'Major' => [2, 2, 1, 2, 2, 2, 1],
8
- 'Pentatonic Major' => [2, 2, 3, 2, 3],
9
- 'Blues Major' => [2, 1, 1, 3, 2, 3],
10
- 'Natural Minor' => [2, 1, 2, 2, 1, 2, 2],
11
- 'Harmonic Minor' => [2, 1, 2, 2, 1, 3, 1],
12
- 'Hungarian Minor' => [2, 1, 2, 1, 1, 3, 1],
13
- 'Pentatonic Minor' => [3, 2, 2, 3, 2],
14
- 'Blues Minor' => [3, 2, 1, 1, 3, 2],
15
- 'Whole Tone' => [2, 2, 2, 2, 2, 2],
16
- 'Flamenco' => [1, 3, 1, 2, 1, 2, 2]
17
- }.freeze
5
+ 'Major' => [2,2,1,2,2,2,1],
6
+ 'Pentatonic Major' => [2,2,3,2,3],
7
+ 'Blues Major' => [2,1,1,3,2,3],
8
+ 'Natural Minor' => [2,1,2,2,1,2,2],
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
+ }
18
16
 
19
17
  MODES = {
20
18
  'Major' => %w[Ionian Dorian Phrygian Lydian Mixolydian Aeolian Locrian]
21
- }.freeze
19
+ }
20
+
21
+ private
22
+
23
+ # A little helper to build method names
24
+ # just make the code more clear
25
+ def self.methodize(string)
26
+ string.downcase.gsub(' ', '_')
27
+ end
28
+
29
+ public
22
30
 
23
31
  # Creates factories for scales
24
32
  SCALES.each do |name, distances|
25
- define_method name.underscore do |tone = 'C', mode = 1|
33
+ define_method methodize(name) do |tone='C', mode=1|
26
34
  new(*distances, tone: tone, mode: mode, name: name)
27
35
  end
28
36
  end
@@ -30,33 +38,34 @@ module Coltrane
30
38
  # Creates factories for Greek Modes and possibly others
31
39
  MODES.each do |scale, modes|
32
40
  modes.each_with_index do |mode, index|
41
+ scale_method = methodize(scale)
33
42
  mode_name = mode
34
43
  mode_n = index + 1
35
- define_method mode.underscore do |tone = 'C'|
44
+ define_method methodize(mode) do |tone='C'|
36
45
  new(*SCALES[scale], tone: tone, mode: mode_n, name: mode_name)
37
46
  end
38
47
  end
39
48
  end
40
49
 
41
- alias minor natural_minor
42
- alias pentatonic pentatonic_major
43
- alias blues blues_major
50
+ alias_method :minor, :natural_minor
51
+ alias_method :pentatonic, :pentatonic_major
52
+ alias_method :blues, :blues_major
44
53
 
45
54
  def known_scales
46
55
  SCALES.keys
47
56
  end
48
57
 
49
- def fetch(name, tone = nil)
58
+ def fetch(name, tone=nil)
50
59
  Coltrane::Scale.public_send(name, tone)
51
60
  end
52
61
 
53
62
  def from_key(key)
54
- key_regex = /([A-G][#b]?)([mM]?)/
55
- _, note, s = *key.match(key_regex)
56
- scale = s == 'm' ? :minor : :major
57
- Scale.public_send(scale, note)
63
+ scale = key.delete!('m') ? :minor : :major
64
+ note = key
65
+ Scale.public_send(scale.nil? || scale == 'M' ? :major : :minor, note)
58
66
  end
59
67
 
68
+
60
69
  # Will output a OpenStruct like the following:
61
70
  # {
62
71
  # scales: [array of scales]
@@ -68,15 +77,19 @@ module Coltrane
68
77
  # }
69
78
 
70
79
  def having_notes(notes)
80
+
71
81
  format = { scales: [], results: {} }
72
82
  OpenStruct.new(
73
83
  SCALES.each_with_object(format) do |(name, intervals), output|
74
84
  Note.all.each.map do |tone|
75
85
  scale = new(*intervals, tone: tone, name: scale)
76
86
  output[:results][name] ||= {}
77
- next if output[:results][name].key?(tone.number)
78
- output[:scales] << scale if scale.include?(notes)
79
- output[:results][name][tone.number] = scale.notes & notes
87
+ if output[:results][name].has_key?(tone.number)
88
+ next
89
+ else
90
+ output[:scales] << scale if scale.include?(notes)
91
+ output[:results][name][tone.number] = scale.notes & notes
92
+ end
80
93
  end
81
94
  end
82
95
  )
@@ -90,6 +103,6 @@ module Coltrane
90
103
  having_notes(notes)
91
104
  end
92
105
 
93
- alias having_chord having_chords
106
+ alias_method :having_chord, :having_chords
94
107
  end
95
- end
108
+ end
@@ -1,7 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Style/Documentation
4
-
5
1
  module Coltrane
6
2
  class ColtraneError < StandardError
7
3
  def initialize(msg)
@@ -9,41 +5,40 @@ module Coltrane
9
5
  end
10
6
  end
11
7
 
12
- class BadConstructorError < ColtraneError
13
- def initialize(msg = nil)
8
+ class BadConstructor < ColtraneError
9
+ def initialize(msg=nil)
14
10
  super "Bad constructor. #{msg}"
15
11
  end
16
12
  end
17
13
 
18
- class WrongKeywordsError < BadConstructorError
14
+ class WrongKeywords < BadConstructor
19
15
  def initialize(msg)
20
16
  super "Use one of the following set of keywords: #{msg}"
21
17
  end
22
18
  end
23
19
 
24
- class InvalidNoteError < BadConstructorError
20
+ class InvalidNote < BadConstructor
25
21
  def initialize(note)
26
22
  super "#{note} is not a valid note"
27
23
  end
28
24
  end
29
25
 
30
- class InvalidNotesError < BadConstructorError
26
+ class InvalidNotes < BadConstructor
31
27
  def initialize(notes)
32
28
  super "#{notes} are not a valid set of notes"
33
29
  end
34
30
  end
35
31
 
36
- class HasNoNotesError < BadConstructorError
32
+ class HasNoNotes < BadConstructor
37
33
  def initialize(obj)
38
34
  super "The given object (#{obj.inspect} does not respond to :notes, "\
39
35
  "thereby it can't be used for this operation)"
40
36
  end
41
37
  end
42
38
 
43
- class WrongDegreeError
39
+ class WrongDegree
44
40
  def initialize(degree)
45
- super "#{degree} is not a valid degree. Degrees for this scale must be"\
46
- "between 1 and #{degrees}"
41
+ super "#{degree} is not a valid degree. Degrees for this scale must be between 1 and #{degrees}"
47
42
  end
48
43
  end
49
44
 
@@ -52,10 +47,8 @@ module Coltrane
52
47
  super "The chord you provided wasn't found. "\
53
48
  "If you're sure this chord exists, "\
54
49
  "would you mind to suggest it's inclusion here: "\
55
- 'https://github.com/pedrozath/coltrane/issues '\
50
+ "https://github.com/pedrozath/coltrane/issues "\
56
51
  "\n\nA tip tho: always include the letter M for major"
57
52
  end
58
53
  end
59
- end
60
-
61
- # rubocop:enable Style/Documentation
54
+ end